Skip to content

Support import-only files with include rules#300

Merged
jathak merged 4 commits into
mainfrom
import-only-includes
Jun 8, 2026
Merged

Support import-only files with include rules#300
jathak merged 4 commits into
mainfrom
import-only-includes

Conversation

@jathak

@jathak jathak commented May 20, 2026

Copy link
Copy Markdown
Member

This enables migration of some nested/late imports that the migrator couldn't previously handle by first wrapping the file that's depended on via nested import in a mixin and then @includeing that mixin in the import-only file.

@jathak
jathak requested a review from nex3 May 20, 2026 18:26
@jathak

jathak commented May 20, 2026

Copy link
Copy Markdown
Member Author

#301 should fix the CI here

This enables migration of some nested/late imports that the migrator
couldn't previously handle by first wrapping the file that's depended on
via nested import in a mixin and then `@include`ing that mixin in the
import-only file.
@jathak
jathak force-pushed the import-only-includes branch from 094755e to c2f4ecf Compare May 21, 2026 13:56

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests that are exercising nested imports should probably mention that in their titles.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +714 to +717
throw MigrationSourceSpanException(
"Found a second @include rule in an import-only file. "
"Import-only files should contain at most one @include.",
node.span);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to support multiple rules if we stored _importOnlyIncludes' values as lists?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I initially avoided it because I thought it would be complicated turning one @import rule into multiple @include rules, but I forgot we already had logic for turning one rule into multiple because an @import rule can contain multiple imports. Done.

Comment thread lib/src/migrators/module.dart Outdated
'${_namespaceForDeclaration(references.variables[arg]!)}.\$$name',
VariableExpression(:var name) => '\$$name',
_ => throw MigrationSourceSpanException(
'Import-only @include rules may only pass variables as arguments.',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this restriction? It seems like at the very least it wouldn't hurt to allow literal values, and really the only semantically difficult argument I can think of would be user-defined functions (which function more or less like variables anyway).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially restricted it to avoid having to recursively serialize all expressions (making sure to add any additional @use rules as necessary), but I ended up writing a serializer that can add/change namespaces as necessary, so that eliminates the need for this restriction.

jathak added 2 commits May 22, 2026 11:33
This adds a `NamespacedSerializer` utility class, which allows us to
copy the include rules with arbitrary arguments while still ensuring
that we use the right namespaces and add any additional use rules when
migrating.

@jathak jathak left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added NamespacedSerializer utility class to allow for arbitrary arguments to be copied from the @include rule in the import-only file. It does copy a fair bit of logic from toString() methods on some of the AST nodes, so let me know if there's a better way to implement this to avoid the duplicated code.

Comment on lines +714 to +717
throw MigrationSourceSpanException(
"Found a second @include rule in an import-only file. "
"Import-only files should contain at most one @include.",
node.span);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I initially avoided it because I thought it would be complicated turning one @import rule into multiple @include rules, but I forgot we already had logic for turning one rule into multiple because an @import rule can contain multiple imports. Done.

Comment thread lib/src/migrators/module.dart Outdated
'${_namespaceForDeclaration(references.variables[arg]!)}.\$$name',
VariableExpression(:var name) => '\$$name',
_ => throw MigrationSourceSpanException(
'Import-only @include rules may only pass variables as arguments.',

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially restricted it to avoid having to recursively serialize all expressions (making sure to add any additional @use rules as necessary), but I ended up writing a serializer that can add/change namespaces as necessary, so that eliminates the need for this restriction.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@jathak
jathak requested a review from nex3 May 27, 2026 15:19
Comment thread lib/src/util/namespaced_serializer.dart Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider publicly exposing a function (or several functions) from this file rather than a class, since in practice the class is only ever used to call a single method and then discarded.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread lib/src/util/namespaced_serializer.dart Outdated
Comment on lines +10 to +11
/// A utility for serializing Sass expressions while adding or changing
/// namespaces of Sass references.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that the migrator now effectively has two different ways of modifying an expression: one by patching, and now this which serializes from scratch. Is it possible to consolidate these back down into a patch? For example, could you represent this as one patch that adds the original text of the @include from the import-only file, followed by additional patches that adjust its namespaces (possibly using something like Dart Sass's interpolation maps to realign spans from the original import-only file to the new @include location)? My two practical concerns here are:

  1. This is duplicating a lot of the Sass inspect/serialization logic, which means more work you'll have to do any time the Sass AST changes.
  2. This throws away any formatting/comments/etc from the original source.

If you think this distinction makes more sense than trying to make it all work via patching, I do think it's probably worth including in the documentation some discussion of when this is appropriate relative to the original patch-based approach.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As is, the migrator won't allow patches like this, since the patch offsets are always based on the original file, so after patching in the original text, it's not possible to add additional patches within it, but I do agree that the patch-based approach makes more sense if at all possible, so I'll look into adding something that can do the inverse: first patch the span of the original @include (without actually patching the underlying source file) and then apply that patch that patched span into the import-only file.

Comment thread lib/src/util/namespaced_serializer.dart Outdated
[for (var item in contents) serialize(item)].join(
switch (separator) {
.comma => ', ',
.space => ' ',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.space => ' ',
.space || .undecided => ' ',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As before, since this is testing nested includes it should mention that in its title.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@jathak jathak left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the logic here to use patching instead of serialization.

Comment thread lib/src/util/namespaced_serializer.dart Outdated

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@jathak
jathak requested a review from nex3 June 3, 2026 17:15
@jathak
jathak merged commit 045e956 into main Jun 8, 2026
18 checks passed
@jathak
jathak deleted the import-only-includes branch June 8, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants