Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalizes to support multiple migrators #16

Merged
merged 7 commits into from
Apr 18, 2019
Merged

Generalizes to support multiple migrators #16

merged 7 commits into from
Apr 18, 2019

Conversation

jathak
Copy link
Member

@jathak jathak commented Apr 1, 2019

Resolves #19.

There's now a Migrator class that additional migrators can extend.

Most migrators will want to extend MigratorBase, which handles the tree traversal. For now, MigratorBase only operates on a single stylesheet, with the dependency migration implemented only by ModuleMigrator.

There's now a `Migrator` class that additional migrators can extend.

Most migrators will want to extend `MigratorBase`, which handles the
tree traversal. For now, `MigratorBase` only operates on a single
stylesheet, with the dependency migration implemented only by
`ModuleMigrator`.
Copy link
Contributor

@nex3 nex3 left a comment

Choose a reason for hiding this comment

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

Are you planning to have Migrator stay non-recursive? If so, what's the plan for migrating entire projects at once?

bin/sass_migrator.dart Outdated Show resolved Hide resolved
bin/sass_migrator.dart Outdated Show resolved Hide resolved
bin/sass_migrator.dart Outdated Show resolved Hide resolved
bin/sass_migrator.dart Outdated Show resolved Hide resolved
bin/sass_migrator.dart Outdated Show resolved Hide resolved
lib/src/migrator_base.dart Outdated Show resolved Hide resolved
lib/src/migrators/module.dart Outdated Show resolved Hide resolved
lib/src/migrators/module.dart Outdated Show resolved Hide resolved
lib/src/migrators/module.dart Outdated Show resolved Hide resolved
test/migrator_test.dart Outdated Show resolved Hide resolved
--migrate-deps is now a global flag, so additional migrators should not
need to re-implement dependency migration unless they need additional
special logic like the module migrator needs.

The generic migration classes are now split into Migrator and
MigrationVisitor. Each Migrator is a Command and should contain state
for the entire migration run. Most migrators will also want to create a
private subclass of MigrationVisitor that they call from
Migrator.migrateFile. A new instance of the MigrationVisitor will be
created for each stylesheet being migrated, so it contains
per-stylesheet state, similar to what StylesheetMigration used to be for
the module migrator.
@jathak
Copy link
Member Author

jathak commented Apr 5, 2019

I've updated this to now have the generic migration support split into Migrator, which extends Command and contains state for the entire migration run, and MigrationVisitor, which migrators subclass and use to traverse the AST of a single stylesheet.

I've also made the --migrate-deps flag global. The module migrator has some special logic here since it needs to visit all dependencies even if they're not being migrated, but any migrators that don't rely on cross-stylesheet information should be able to support migrating dependencies without any additional code.

@jathak jathak requested a review from nex3 April 5, 2019 00:35
Copy link
Contributor

@nex3 nex3 left a comment

Choose a reason for hiding this comment

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

This is shaping up really nicely! Thanks for bearing with my intense reviews 😅.

lib/runner.dart Show resolved Hide resolved
lib/runner.dart Outdated Show resolved Hide resolved
lib/runner.dart Outdated Show resolved Hide resolved
lib/src/migration_visitor.dart Outdated Show resolved Hide resolved
lib/src/migration_visitor.dart Outdated Show resolved Hide resolved
lib/src/migrators/module.dart Outdated Show resolved Hide resolved
lib/src/utils.dart Show resolved Hide resolved
lib/src/utils.dart Outdated Show resolved Hide resolved
lib/src/utils.dart Outdated Show resolved Hide resolved
pubspec.yaml Outdated Show resolved Hide resolved
The migrator now uses URLs instead of paths, and loads files using the
FilesystemImporter.

`Migrator.migrated` has been removed in favor of having
`Migrator.migrateFile` and `MigrationVisitor.run` return it.
lib/runner.dart Outdated Show resolved Hide resolved
lib/runner.dart Outdated Show resolved Hide resolved
lib/src/migration_visitor.dart Outdated Show resolved Hide resolved
visitStylesheet(stylesheet);
var results = getMigratedContents();
if (results != null) {
migrator.migrated[path] = results;
Copy link
Contributor

Choose a reason for hiding this comment

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

That's a good question. I'd say make the migrated field private to MigrationVisitor and give it total responsibility for handling the output, but you need to pass it through newInstance() somehow...

What if we restructure this so that you don't need to construct multiple visitors at all? Rather than passing a URL to the constructor, pass it to run(). That also means that when we get around to de-duplicating repeated imports/uses (which will probably be necessary for large-scale performance), there's a natural place for that as well.

This does mean that per-file state can't just be stored as final fields on the subclass. But you can override visitStylesheet() to set it temporarily:

@override
void visitStylesheet(StylesheetNode node) {
  var oldNamespaces = _namespaces;
  var oldAdditionalUseRules = _additionalUseRules;
  var oldConfigurableVariables = _configurableVariables;
  _namespaces = {};
  _additionalUseRules = Set();
  _configurableVariables = normalizedSet();
  super.visitStylesheet(node);
  _namespaces = oldNamespaces;
  _additionalUseRules = oldAdditionalUseRules;
  _configurableVariables = oldConfigurableVariables;
}

This is basically how Dart Sass's EvaluateVisitor works. WDYT?

lib/src/migration_visitor.dart Outdated Show resolved Hide resolved
lib/src/migration_visitor.dart Outdated Show resolved Hide resolved
lib/src/utils.dart Show resolved Hide resolved
test/migrator_utils.dart Outdated Show resolved Hide resolved
@jathak
Copy link
Member Author

jathak commented Apr 12, 2019

Changed MigrationVisitor to reuse a single instance for dependencies.

Previously, the module migrator needed to access the _configurableVariables of its dependencies' visitors. In the process of changing this, I found #28 and (mostly) fixed this in my changes (though it doesn't yet remove an unnecessary use).

@jathak jathak requested a review from nex3 April 12, 2019 22:56
Copy link
Contributor

@nex3 nex3 left a comment

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 factor out the configurable-variables change into a separate PR? It's kind of tough to review this when it's changing multiple things at once.

lib/src/migration_visitor.dart Outdated Show resolved Hide resolved
lib/src/migration_visitor.dart Outdated Show resolved Hide resolved
A single MigrationVisitor is now reused instead of constructing a new
instance for each dependency.

This also removes support for configurable variables, since the previous
implementation of this was both hard to implement with a single
MigrationVisitor and incorrect when the configurable variables were
declared in an indirect dependency.

A subsequent PR will add this back with a new implementation that fixes
the bug with indirect dependencies.
@jathak
Copy link
Member Author

jathak commented Apr 17, 2019

The old behavior would be hard to implement with a single MigrationVisitor, so I removed support for configurable variables entirely and will add them back with the new implementation in a separate PR.

@jathak jathak requested a review from nex3 April 17, 2019 23:12
final _globalFunctions = normalizedMap<FunctionRule>();

/// Namespaces of modules used in this stylesheet.
Map<Uri, String> _namespaces = {};
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can leave off the initializer here; this value will never be used, since it's always going to be set in visitStylesheet().

if (results == null) return null;
var semicolon = _currentUrl.path.endsWith('.sass') ? "" : ";";
var uses = _additionalUseRules.map((use) => '@use "$use"$semicolon\n');
return uses.join("") + results;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return uses.join("") + results;
return uses.join() + results;

_localScope = _localScope.parent;
}

/// Adds a namespace to any function call that require it.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Adds a namespace to any function call that require it.
/// Adds a namespace to any function call that requires it.

lib/src/utils.dart Show resolved Hide resolved
@jathak jathak merged commit 29e9ffb into master Apr 18, 2019
@jathak jathak deleted the generalization branch April 19, 2019 22:11
@jathak jathak added module system Part of the module system migrator and removed module system Part of the module system migrator labels Sep 3, 2019
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.

Generalize tool to support multiple migrators
2 participants