Chromium Code Reviews| Index: lib/build/html_import_annotation_inliner.dart |
| diff --git a/lib/build/html_import_annotation_inliner.dart b/lib/build/html_import_annotation_inliner.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..82a7979c57c4da9ee48b4123039e31d20c6f9ea5 |
| --- /dev/null |
| +++ b/lib/build/html_import_annotation_inliner.dart |
| @@ -0,0 +1,114 @@ |
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| +library web_components.build.html_import_annotation_inliner; |
| + |
| +import 'dart:async'; |
| +import 'package:analyzer/analyzer.dart'; |
| +import 'package:barback/barback.dart'; |
| +import 'package:html5lib/dom.dart' as dom; |
| +import 'package:html5lib/parser.dart'; |
| +import 'package:initialize/plugin_transformer.dart'; |
| +import 'package:source_maps/refactor.dart'; |
| +import '../src/normalize_path.dart'; |
| + |
| +/// Given an html entry point with a single dart bootstrap file created by the |
| +/// `initialize` transformer, this will open that dart file and remove all |
| +/// `HtmlImport` initializers from it. Then it appends those imports to the head |
| +/// of the html entry point. |
| +/// Notes: Does not inline the import, it just puts the <link rel="import"> tag. |
| +/// This also has a few limitations, it only supports string literals (to avoid |
| +/// using the analyzer to resolve references) and doesn't support const |
| +/// references to HtmlImport annotations (for the same reason). |
| +class HtmlImportAnnotationInliner extends InitializePluginTransformer { |
|
Jennifer Messerly
2015/02/02 21:07:51
nice!
jakemac
2015/02/02 21:53:56
Acknowledged.
|
| + final String _bootstrapFile; |
| + final String _htmlEntryPoint; |
| + TransformLogger _logger; |
| + final Set<String> importPaths = new Set(); |
| + |
| + HtmlImportAnnotationInliner(String bootstrapFile, this._htmlEntryPoint) |
| + : super(bootstrapFile), |
| + _bootstrapFile = bootstrapFile; |
| + |
| + factory HtmlImportAnnotationInliner.asPlugin(BarbackSettings settings) { |
| + var bootstrapFile = settings.configuration['bootstrap_file']; |
| + if (bootstrapFile is! String || !bootstrapFile.endsWith('.dart')) { |
| + throw new ArgumentError( |
| + '`bootstrap_file` should be a string path to a dart file'); |
| + } |
| + var htmlEntryPoint = settings.configuration['html_entry_point']; |
| + if (htmlEntryPoint is! String || !htmlEntryPoint.endsWith('.html')) { |
| + throw new ArgumentError( |
| + '`html_entry_point` should be a string path to an html file'); |
| + } |
| + return new HtmlImportAnnotationInliner(bootstrapFile, htmlEntryPoint); |
| + } |
| + |
| + classifyPrimary(AssetId id) { |
| + var superValue = super.classifyPrimary(id); |
| + if (superValue != null) return superValue; |
| + // Group it with the bootstrap file. |
| + if (_htmlEntryPoint == id.path) return _bootstrapFile; |
| + return null; |
| + } |
| + |
| + apply(AggregateTransform transform) { |
| + return super.apply(transform).then((_) { |
| + var htmlEntryPoint = |
| + allAssets.firstWhere((asset) => asset.id.path == _htmlEntryPoint); |
| + return htmlEntryPoint.readAsString().then((html) { |
| + var doc = parse(html); |
| + for (var importPath in importPaths) { |
| + var import = new dom.Element.tag('link') |
| + ..attributes = {'rel': 'import', 'href': importPath,}; |
| + doc.head.append(import); |
| + } |
| + transform |
| + .addOutput(new Asset.fromString(htmlEntryPoint.id, doc.outerHtml)); |
| + }); |
| + }); |
| + } |
| + |
| + // Executed for each initializer constructor in the bootstrap file. We filter |
| + // out the HtmlImport ones and inline them. |
| + initEntry( |
| + InstanceCreationExpression expression, TextEditTransaction transaction) { |
| + // Filter out extraneous values. |
| + if (expression is! InstanceCreationExpression) return; |
| + var args = expression.argumentList.arguments; |
| + // Only support InstanceCreationExpressions. Const references to HtmlImport |
| + // annotations can't be cheaply discovered. |
| + if (args[0] is! InstanceCreationExpression) return; |
| + if (!'${args[0].constructorName.type.name}'.contains('.HtmlImport')) return; |
| + |
| + // Grab the raw path supplied to the HtmlImport. Only string literals are |
| + // supported for the transformer. |
| + var originalPath = args[0].argumentList.arguments[0]; |
| + if (originalPath is SimpleStringLiteral) { |
| + originalPath = originalPath.value; |
| + } else { |
| + _logger.warning('Found HtmlImport constructor which was supplied an ' |
| + 'expression. Only raw strings are currently supported for the ' |
| + 'transformer, so $originalPath will be injected dynamically'); |
| + return; |
| + } |
| + |
| + // Now grab the package from the LibraryIdentifier, we know its either a |
| + // string or null literal. |
| + var package = args[1].argumentList.arguments[1]; |
| + if (package is SimpleStringLiteral) { |
| + package = package.value; |
| + } else if (package is NullLiteral) { |
| + package = null; |
| + } |
|
Jennifer Messerly
2015/02/02 21:07:51
else log error?
it could happen if someone used ad
jakemac
2015/02/02 21:53:56
These are actually completely autogenerated, but I
|
| + |
| + // And finally get the original dart file path, this is always a string |
| + // literal. |
| + var dartPath = args[1].argumentList.arguments[2].value; |
| + |
| + // Add the normalized path to our list and remove the expression from the |
| + // bootstrap file. |
| + importPaths.add(normalizeHtmlImportPath(originalPath, package, dartPath)); |
| + removeInitializer(expression, transaction); |
| + } |
| +} |