OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /// Transfomer used for pub-serve and pub-deploy. | |
6 library polymer.transformer; | |
7 | |
8 import 'package:barback/barback.dart'; | |
9 import 'package:web_components/transformer.dart' as web_components; | |
10 import 'package:observe/transformer.dart'; | |
11 | |
12 import 'src/build/build_filter.dart'; | |
13 import 'src/build/common.dart'; | |
14 import 'src/build/index_page_builder.dart'; | |
15 import 'src/build/html_finalizer.dart'; | |
16 import 'src/build/linter.dart'; | |
17 import 'src/build/build_log_combiner.dart'; | |
18 import 'src/build/polyfill_injector.dart'; | |
19 import 'src/build/polymer_bootstrap.dart'; | |
20 | |
21 /// The Polymer transformer, which internally runs several phases that will: | |
22 /// * Extract inlined script tags into their separate files | |
23 /// * Apply the observable transformer on every Dart script. | |
24 /// * Inline imported html files | |
25 /// * Combine scripts from multiple files into a single script tag | |
26 /// * Inject extra polyfills needed to run on all browsers. | |
27 /// | |
28 /// At the end of these phases, this tranformer produces a single entrypoint | |
29 /// HTML file with a single Dart script that can later be compiled with dart2js. | |
30 class PolymerTransformerGroup implements TransformerGroup { | |
31 final Iterable<Iterable> phases; | |
32 | |
33 PolymerTransformerGroup(TransformOptions options) | |
34 : phases = createDeployPhases(options); | |
35 | |
36 PolymerTransformerGroup.asPlugin(BarbackSettings settings) | |
37 : this(_parseSettings(settings)); | |
38 } | |
39 | |
40 TransformOptions _parseSettings(BarbackSettings settings) { | |
41 var args = settings.configuration; | |
42 bool releaseMode = settings.mode == BarbackMode.RELEASE; | |
43 bool jsOption = args['js']; | |
44 bool csp = args['csp'] == true; // defaults to false | |
45 bool injectBuildLogs = | |
46 !releaseMode && args['inject_build_logs_in_output'] != false; | |
47 bool injectWebComponentsJs = true; | |
48 if (args['inject_platform_js'] != null) { | |
49 print( | |
50 'Deprecated polymer transformer option `inject_platform_js`. This has ' | |
51 'been renamed `inject_web_components_js` to match the new file name.'); | |
52 injectWebComponentsJs = args['inject_platform_js'] != false; | |
53 } | |
54 if (args['inject_webcomponents_js'] != null) { | |
55 injectWebComponentsJs = args['inject_webcomponents_js'] != false; | |
56 } | |
57 return new TransformOptions( | |
58 entryPoints: readFileList(args['entry_points']), | |
59 inlineStylesheets: _readInlineStylesheets(args['inline_stylesheets']), | |
60 directlyIncludeJS: jsOption == null ? releaseMode : jsOption, | |
61 contentSecurityPolicy: csp, | |
62 releaseMode: releaseMode, | |
63 lint: _parseLintOption(args['lint']), | |
64 injectBuildLogsInOutput: injectBuildLogs, | |
65 injectWebComponentsJs: injectWebComponentsJs); | |
66 } | |
67 | |
68 // Lint option can be empty (all files), false, true, or a map indicating | |
69 // include/exclude files. | |
70 _parseLintOption(value) { | |
71 var lint = null; | |
72 if (value == null || value == true) return new LintOptions(); | |
73 if (value == false) return new LintOptions.disabled(); | |
74 if (value is Map && value.length == 1) { | |
75 var key = value.keys.single; | |
76 var files = readFileList(value[key]); | |
77 if (key == 'include') { | |
78 return new LintOptions.include(files); | |
79 } else if (key == 'exclude') { | |
80 return new LintOptions.exclude(files); | |
81 } | |
82 } | |
83 | |
84 // Any other case it is an error: | |
85 print('Invalid value for "lint" in the polymer transformer. ' | |
86 'Expected one of the following: \n' | |
87 ' lint: true # or\n' | |
88 ' lint: false # or\n' | |
89 ' lint: \n' | |
90 ' include: \n' | |
91 ' - file1 \n' | |
92 ' - file2 # or \n' | |
93 ' lint: \n' | |
94 ' exclude: \n' | |
95 ' - file1 \n' | |
96 ' - file2 \n'); | |
97 return new LintOptions(); | |
98 } | |
99 | |
100 readFileList(value) { | |
101 if (value == null) return null; | |
102 var files = []; | |
103 bool error; | |
104 if (value is List) { | |
105 files = value; | |
106 error = value.any((e) => e is! String); | |
107 } else if (value is String) { | |
108 files = [value]; | |
109 error = false; | |
110 } else { | |
111 error = true; | |
112 } | |
113 if (error) { | |
114 print('Invalid value for "entry_points" in the polymer transformer.'); | |
115 } | |
116 return files; | |
117 } | |
118 | |
119 Map<String, bool> _readInlineStylesheets(settingValue) { | |
120 if (settingValue == null) return null; | |
121 var inlineStylesheets = {}; | |
122 bool error = false; | |
123 if (settingValue is Map) { | |
124 settingValue.forEach((key, value) { | |
125 if (value is! bool || key is! String) { | |
126 error = true; | |
127 return; | |
128 } | |
129 if (key == 'default') { | |
130 inlineStylesheets[key] = value; | |
131 return; | |
132 } | |
133 | |
134 key = systemToAssetPath(key); | |
135 // Special case package urls, convert to AssetId and use serialized form. | |
136 var packageMatch = _PACKAGE_PATH_REGEX.matchAsPrefix(key); | |
137 if (packageMatch != null) { | |
138 var package = packageMatch[1]; | |
139 var path = 'lib/${packageMatch[2]}'; | |
140 key = new AssetId(package, path).toString(); | |
141 } | |
142 inlineStylesheets[key] = value; | |
143 }); | |
144 } else if (settingValue is bool) { | |
145 inlineStylesheets['default'] = settingValue; | |
146 } else { | |
147 error = true; | |
148 } | |
149 if (error) { | |
150 print('Invalid value for "inline_stylesheets" in the polymer transformer.'); | |
151 } | |
152 return inlineStylesheets; | |
153 } | |
154 | |
155 /// Create deploy phases for Polymer. Note that inlining HTML Imports | |
156 /// comes first (other than linter, if [options.linter] is enabled), which | |
157 /// allows the rest of the HTML-processing phases to operate only on HTML that | |
158 /// is actually imported. | |
159 List<List<Transformer>> createDeployPhases(TransformOptions options, | |
160 {String sdkDir}) { | |
161 // TODO(sigmund): this should be done differently. We should lint everything | |
162 // that is reachable and have the option to lint the rest (similar to how | |
163 // dart2js can analyze reachable code or entire libraries). | |
164 var phases = []; | |
165 | |
166 phases.addAll([ | |
167 /// Must happen first, temporarily rewrites <link rel="x-dart-test"> tags to | |
168 /// <script type="application/dart" _was_test></script> tags. | |
169 [new web_components.RewriteXDartTestToScript(options.entryPoints)], | |
170 [new web_components.ScriptCompactorTransformer(options.entryPoints)], | |
171 [new PolymerBootstrapTransformer(options)], | |
172 ]); | |
173 | |
174 // Lint after injecting @HtmlImport imports otherwise we will likely have | |
175 // incorrect warnings about missing elements. | |
176 if (options.lint.enabled) phases.add([new Linter(options)]); | |
177 | |
178 phases.addAll([ | |
179 [ | |
180 new web_components.ImportInlinerTransformer( | |
181 options.entryPoints, ['[[', '{{']) | |
182 ], | |
183 [new HtmlFinalizer(options)], | |
184 [ | |
185 new ObservableTransformer( | |
186 releaseMode: options.releaseMode, | |
187 injectBuildLogsInOutput: options.injectBuildLogsInOutput) | |
188 ], | |
189 // TODO(jakemac): Move to web_components. | |
190 [new PolyfillInjector(options)], | |
191 [new BuildFilter(options)], | |
192 [new BuildLogCombiner(options)], | |
193 ]); | |
194 if (!options.releaseMode) { | |
195 phases.add([new IndexPageBuilder(options)]); | |
196 } | |
197 /// Must happen last, rewrites | |
198 /// <script type="application/dart" _was_test></script> tags back to | |
199 /// <link rel="x-dart-test"> tags. | |
200 phases.add( | |
201 [new web_components.RewriteScriptToXDartTest(options.entryPoints)]); | |
202 return phases; | |
203 } | |
204 | |
205 final RegExp _PACKAGE_PATH_REGEX = new RegExp(r'packages\/([^\/]+)\/(.*)'); | |
OLD | NEW |