Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(248)

Side by Side Diff: pkg/polymer/lib/src/build/import_inliner.dart

Issue 159353005: code refactoring in import_inliner (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: moar refactoring Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** Transfomer that inlines polymer-element definitions from html imports. */ 5 /** Transfomer that inlines polymer-element definitions from html imports. */
6 library polymer.src.build.import_inliner; 6 library polymer.src.build.import_inliner;
7 7
8 import 'dart:async'; 8 import 'dart:async';
9 import 'dart:convert'; 9 import 'dart:convert';
10 10
11 import 'package:barback/barback.dart'; 11 import 'package:barback/barback.dart';
12 import 'package:path/path.dart' as path; 12 import 'package:path/path.dart' as path;
13 import 'package:html5lib/dom.dart' show 13 import 'package:html5lib/dom.dart' show
14 Document, DocumentFragment, Element, Node; 14 Document, DocumentFragment, Element, Node;
15 import 'package:html5lib/dom_parsing.dart' show TreeVisitor; 15 import 'package:html5lib/dom_parsing.dart' show TreeVisitor;
16 import 'package:source_maps/span.dart' show Span; 16 import 'package:source_maps/span.dart' show Span;
17 17
18 import 'code_extractor.dart'; // import just for documentation. 18 import 'code_extractor.dart'; // import just for documentation.
19 import 'common.dart'; 19 import 'common.dart';
20 20
21 /** 21 class _HtmlInliner extends PolymerTransformer {
22 * Recursively inlines the contents of HTML imports. Produces as output a single
23 * HTML file that inlines the polymer-element definitions, and a text file that
24 * contains, in order, the URIs to each library that sourced in a script tag.
25 *
26 * This transformer assumes that all script tags point to external files. To
27 * support script tags with inlined code, use this transformer after running
28 * [InlineCodeExtractor] on an earlier phase.
29 */
30 class ImportInliner extends Transformer with PolymerTransformer {
31 final TransformOptions options; 22 final TransformOptions options;
23 final Transform transform;
24 final TransformLogger logger;
25 final AssetId docId;
26 final seen = new Set<AssetId>();
27 final imported = new DocumentFragment();
28 final scriptIds = <AssetId>[];
32 29
33 ImportInliner(this.options); 30 _HtmlInliner(this.options, Transform transform)
31 : transform = transform,
32 logger = transform.logger,
33 docId = transform.primaryInput.id;
34 34
35 /** Only run on entry point .html files. */ 35 Future apply() {
36 Future<bool> isPrimary(Asset input) => 36 seen.add(docId);
37 new Future.value(options.isHtmlEntryPoint(input.id));
38 37
39 Future apply(Transform transform) { 38 Document document;
40 var logger = transform.logger;
41 var seen = new Set<AssetId>();
42 var documents = [];
43 var id = transform.primaryInput.id;
44 seen.add(id);
45 return readPrimaryAsHtml(transform).then((document) {
46 var future = _visitImports(document, id, transform, seen, documents);
47 return future.then((importsFound) {
48 // We produce a secondary asset with extra information for later phases.
49 var secondaryId = id.addExtension('.scriptUrls');
50 if (!importsFound) {
51 transform.addOutput(transform.primaryInput);
52 transform.addOutput(new Asset.fromString(secondaryId, '[]'));
53 return;
54 }
55 39
56 // Split Dart script tags from all the other elements. Now that Dartium 40 return readPrimaryAsHtml(transform).then((document) =>
57 // only allows a single script tag per page, we can't inline script 41 _visitImports(document, docId).then((importsFound) {
58 // tags. Instead, we collect the urls of each script tag so we import
59 // them directly from the Dart bootstrap code.
60 var scripts = [];
61 42
62 var fragment = new DocumentFragment(); 43 if (importsFound) {
63 for (var importedDoc in documents) { 44 document.body.insertBefore(imported, document.body.firstChild);
64 bool first = true; 45 transform.addOutput(new Asset.fromString(docId, document.outerHtml));
65 for (var e in importedDoc.queryAll('script')) { 46 } else {
66 if (e.attributes['type'] == 'application/dart') { 47 transform.addOutput(transform.primaryInput);
67 e.remove(); 48 }
68 49
69 // only one Dart script per document is supported in Dartium. 50 // We produce a secondary asset with extra information for later phases.
70 if (first) { 51 transform.addOutput(new Asset.fromString(
71 first = false; 52 docId.addExtension('.scriptUrls'),
72 scripts.add(e); 53 JSON.encode(scriptIds, toEncodable: (id) => id.serialize())));
73 } else { 54 }));
74 // TODO(jmesserly): remove this when we are running linter. 55 }
75 logger.warning('more than one Dart script per HTML document is '
76 'not supported. Script will be ignored.',
77 span: e.sourceSpan);
78 }
79 }
80 }
81 56
82 // TODO(jmesserly): should we merge the head too? 57 /**
83 fragment.nodes.addAll(importedDoc.body.nodes); 58 * Visits imports in [document] and add the imported documents to [documents].
84 } 59 * Documents are added in the order they appear, transitive imports are added
60 * first.
61 */
62 Future<bool> _visitImports(Document document, AssetId sourceId) {
63 bool hasImports = false;
85 64
86 document.body.insertBefore(fragment, document.body.firstChild); 65 // Note: we need to preserve the import order in the generated output.
66 return Future.forEach(document.querySelectorAll('link'), (Element tag) {
67 if (tag.attributes['rel'] != 'import') return null;
68 var href = tag.attributes['href'];
69 var id = resolve(sourceId, href, transform.logger, tag.sourceSpan);
70 hasImports = true;
87 71
88 for (var tag in document.queryAll('link')) { 72 tag.remove();
89 if (tag.attributes['rel'] == 'import') tag.remove(); 73 if (id == null || !seen.add(id) ||
90 } 74 (id.package == 'polymer' && id.path == 'lib/init.html')) return null;
91 75
92 transform.addOutput(new Asset.fromString(id, document.outerHtml)); 76 return _inlineImport(id);
77 }).then((_) => hasImports);
78 }
93 79
94 var scriptIds = []; 80 // Loads an asset identified by [id], visits its imports and collects its
95 for (var script in scripts) { 81 // html imports. Then inlines it into the main document.
82 Future _inlineImport(AssetId id) =>
83 readAsHtml(id, transform).then((doc) => _visitImports(doc, id).then((_) {
84
85 new _UrlNormalizer(transform, id).visit(doc);
86 _extractScripts(doc);
87
88 // TODO(jmesserly): figure out how this is working in vulcanizer.
89 // Do they produce a <body> tag with a <head> and <body> inside?
90 imported.nodes
91 ..addAll(doc.head.nodes)
92 ..addAll(doc.body.nodes);
93 }));
94
95 /**
96 * Split Dart script tags from all the other elements. Now that Dartium
97 * only allows a single script tag per page, we can't inline script
98 * tags. Instead, we collect the urls of each script tag so we import
99 * them directly from the Dart bootstrap code.
100 */
101 void _extractScripts(Document document) {
102 bool first = true;
103 for (var script in document.querySelectorAll('script')) {
104 if (script.attributes['type'] == 'application/dart') {
105 script.remove();
106
107 // only one Dart script per document is supported in Dartium.
108 if (first) {
109 first = false;
110
96 var src = script.attributes['src']; 111 var src = script.attributes['src'];
97 if (src == null) { 112 if (src == null) {
98 logger.warning('unexpected script without a src url. The ' 113 logger.warning('unexpected script without a src url. The '
99 'ImportInliner transformer should run after running the ' 114 'ImportInliner transformer should run after running the '
100 'InlineCodeExtractor', span: script.sourceSpan); 115 'InlineCodeExtractor', span: script.sourceSpan);
101 continue; 116 continue;
102 } 117 }
103 scriptIds.add(resolve(id, src, logger, script.sourceSpan)); 118 scriptIds.add(resolve(docId, src, logger, script.sourceSpan));
119
120 } else {
121 // TODO(jmesserly): remove this when we are running linter.
122 logger.warning('more than one Dart script per HTML '
123 'document is not supported. Script will be ignored.',
124 span: script.sourceSpan);
104 } 125 }
105 transform.addOutput(new Asset.fromString(secondaryId, 126 }
106 JSON.encode(scriptIds, toEncodable: (id) => id.serialize())));
107 });
108 });
109 }
110
111 /**
112 * Visits imports in [document] and add their polymer-element and script tags
113 * to [elements], unless they have already been [seen]. Elements are added in
114 * the order they appear, transitive imports are added first.
115 */
116 Future<bool> _visitImports(Document document, AssetId sourceId,
117 Transform transform, Set<AssetId> seen, List<Document> documents) {
118 var importIds = [];
119 bool hasImports = false;
120 for (var tag in document.queryAll('link')) {
121 if (tag.attributes['rel'] != 'import') continue;
122 var href = tag.attributes['href'];
123 var id = resolve(sourceId, href, transform.logger, tag.sourceSpan);
124 hasImports = true;
125 if (id == null || seen.contains(id) ||
126 (id.package == 'polymer' && id.path == 'lib/init.html')) continue;
127 importIds.add(id);
128 } 127 }
129
130 if (importIds.isEmpty) return new Future.value(hasImports);
131
132 // Note: we need to preserve the import order in the generated output.
133 return Future.forEach(importIds, (id) {
134 if (seen.contains(id)) return new Future.value(null);
135 seen.add(id);
136 return _collectImportedDocuments(id, transform, seen, documents);
137 }).then((_) => true);
138 }
139
140 /**
141 * Loads an asset identified by [id], visits its imports and collects it's
142 * polymer-element definitions and script tags.
143 */
144 Future _collectImportedDocuments(AssetId id, Transform transform,
145 Set<AssetId> seen, List documents) {
146 return readAsHtml(id, transform).then((document) {
147 return _visitImports(document, id, transform, seen, documents).then((_) {
148 new _UrlNormalizer(transform, id).visit(document);
149 documents.add(document);
150 });
151 });
152 } 128 }
153 } 129 }
154 130
131 /**
132 * Recursively inlines the contents of HTML imports. Produces as output a single
133 * HTML file that inlines the polymer-element definitions, and a text file that
134 * contains, in order, the URIs to each library that sourced in a script tag.
135 *
136 * This transformer assumes that all script tags point to external files. To
137 * support script tags with inlined code, use this transformer after running
138 * [InlineCodeExtractor] on an earlier phase.
139 */
140 class ImportInliner extends Transformer {
141 final TransformOptions options;
142
143 ImportInliner(this.options);
144
145 /** Only run on entry point .html files. */
146 Future<bool> isPrimary(Asset input) =>
147 new Future.value(options.isHtmlEntryPoint(input.id));
148
149 Future apply(Transform transform) =>
150 new _HtmlInliner(options, transform).apply();
151 }
152
153
155 /** Internally adjusts urls in the html that we are about to inline. */ 154 /** Internally adjusts urls in the html that we are about to inline. */
156 class _UrlNormalizer extends TreeVisitor { 155 class _UrlNormalizer extends TreeVisitor {
157 final Transform transform; 156 final Transform transform;
158 157
159 /** Asset where the original content (and original url) was found. */ 158 /** Asset where the original content (and original url) was found. */
160 final AssetId sourceId; 159 final AssetId sourceId;
161 160
162 _UrlNormalizer(this.transform, this.sourceId); 161 _UrlNormalizer(this.transform, this.sourceId);
163 162
164 visitElement(Element node) { 163 visitElement(Element node) {
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 'cite', // in blockquote, del, ins, q 218 'cite', // in blockquote, del, ins, q
220 'data', // in object 219 'data', // in object
221 'formaction', // in button, input 220 'formaction', // in button, input
222 'href', // in a, area, link, base, command 221 'href', // in a, area, link, base, command
223 'icon', // in command 222 'icon', // in command
224 'manifest', // in html 223 'manifest', // in html
225 'poster', // in video 224 'poster', // in video
226 'src', // in audio, embed, iframe, img, input, script, source, track, 225 'src', // in audio, embed, iframe, img, input, script, source, track,
227 // video 226 // video
228 ]; 227 ];
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698