Declare the sequence of phases for the full polymer transform and add tests for
it.
R=jmesserly@google.com
Review URL: https://codereview.chromium.org//23011045
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@26574 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/polymer/lib/src/transform.dart b/pkg/polymer/lib/src/transform.dart
index 48b78e7..ff5de2b 100644
--- a/pkg/polymer/lib/src/transform.dart
+++ b/pkg/polymer/lib/src/transform.dart
@@ -3,8 +3,22 @@
// BSD-style license that can be found in the LICENSE file.
/** Transfomers used for pub-serve and pub-deploy. */
+// TODO(sigmund): move into a plugin directory when pub supports it.
library polymer.src.transform;
+import 'package:observe/transform.dart';
+import 'transform/code_extractor.dart';
+import 'transform/import_inliner.dart';
+import 'transform/script_compactor.dart';
+
export 'transform/code_extractor.dart';
export 'transform/import_inliner.dart';
export 'transform/script_compactor.dart';
+
+/** Phases to deploy a polymer application. */
+var phases = [
+ [new InlineCodeExtractor()],
+ [new ObservableTransformer()],
+ [new ImportedElementInliner()],
+ [new ScriptCompactor()]
+];
diff --git a/pkg/polymer/lib/src/transform/code_extractor.dart b/pkg/polymer/lib/src/transform/code_extractor.dart
index 5f7058f..0e6d4d5 100644
--- a/pkg/polymer/lib/src/transform/code_extractor.dart
+++ b/pkg/polymer/lib/src/transform/code_extractor.dart
@@ -25,22 +25,34 @@
return getPrimaryContent(transform).then((content) {
var document = parseHtml(content, inputId.path, transform.logger);
int count = 0;
+ bool htmlChanged = false;
for (var tag in document.queryAll('script')) {
- if (tag.attributes['type'] == 'application/dart' &&
- !tag.attributes.containsKey('src')) {
- // TODO(sigmund): should we automatically include a library directive
- // if it doesn't have one?
- var filename = path.url.basename(inputId.path);
- tag.attributes['src'] = '$filename.$count.dart';
- var textContent = tag.nodes.first;
- var id = inputId.addExtension('.$count.dart');
- transform.addOutput(new Asset.fromString(id, textContent.value));
- textContent.remove();
- count++;
+ // Only process tags that have inline Dart code
+ if (tag.attributes['type'] != 'application/dart' ||
+ tag.attributes.containsKey('src')) {
+ continue;
}
+ htmlChanged = true;
+
+ // Remove empty tags
+ if (tag.nodes.length == 0) {
+ tag.remove();
+ continue;
+ }
+
+ // TODO(sigmund): should we automatically include a library directive
+ // if it doesn't have one?
+ var filename = path.url.basename(inputId.path);
+ // TODO(sigmund): ensure this filename is unique (dartbug.com/12618).
+ tag.attributes['src'] = '$filename.$count.dart';
+ var textContent = tag.nodes.first;
+ var id = inputId.addExtension('.$count.dart');
+ transform.addOutput(new Asset.fromString(id, textContent.value));
+ textContent.remove();
+ count++;
}
transform.addOutput(new Asset.fromString(inputId,
- count == 0 ? content : document.outerHtml));
+ htmlChanged ? document.outerHtml : content));
});
}
}
diff --git a/pkg/polymer/lib/src/transform/script_compactor.dart b/pkg/polymer/lib/src/transform/script_compactor.dart
index aa14728..9c2550a 100644
--- a/pkg/polymer/lib/src/transform/script_compactor.dart
+++ b/pkg/polymer/lib/src/transform/script_compactor.dart
@@ -38,11 +38,22 @@
var document = parseHtml(content, id.path, logger);
var libraries = [];
bool changed = false;
+ var dartLoaderTag = null;
for (var tag in document.queryAll('script')) {
+ var src = tag.attributes['src'];
+ if (src != null) {
+ if (src == 'packages/polymer/boot.js') {
+ tag.remove();
+ continue;
+ }
+ var last = src.split('/').last;
+ if (last == 'dart.js' || last == 'testing.js') {
+ dartLoaderTag = tag;
+ }
+ }
if (tag.attributes['type'] != 'application/dart') continue;
tag.remove();
changed = true;
- var src = tag.attributes['src'];
if (src == null) {
logger.warning('unexpected script without a src url. The '
'ScriptCompactor transformer should run after running the '
@@ -63,8 +74,18 @@
var bootstrapId = id.addExtension('_bootstrap.dart');
var filename = path.url.basename(bootstrapId.path);
- document.body.nodes.add(parseFragment(
- '<script type="application/dart" src="$filename"></script>'));
+
+ var bootstrapScript = parseFragment(
+ '<script type="application/dart" src="$filename"></script>');
+ if (dartLoaderTag == null) {
+ document.body.nodes.add(bootstrapScript);
+ document.body.nodes.add(parseFragment('<script type="text/javascript" '
+ 'src="packages/browser/dart.js"></script>'));
+ } else if (dartLoaderTag.parent != document.body) {
+ document.body.nodes.add(bootstrapScript);
+ } else {
+ document.body.insertBefore(bootstrapScript, dartLoaderTag);
+ }
var urls = libraries.map((id) => importUrlFor(id, bootstrapId, logger))
.where((url) => url != null).toList();
@@ -97,11 +118,9 @@
return null;
}
- var urlBuilder = path.url;
- var upPath = urlBuilder.joinAll(
- urlBuilder.split(sourceId.path).map((_) => '..'));
- return urlBuilder.normalize(
- urlBuilder.join(sourceId.path, upPath, id.path));
+ var builder = path.url;
+ return builder.relative(builder.join('/', id.path),
+ from: builder.join('/', builder.dirname(sourceId.path)));
}
}
diff --git a/pkg/polymer/test/run_all.dart b/pkg/polymer/test/run_all.dart
index 61afa25..cc666ed 100644
--- a/pkg/polymer/test/run_all.dart
+++ b/pkg/polymer/test/run_all.dart
@@ -20,6 +20,7 @@
import 'transform/code_extractor_test.dart' as code_extractor_test;
import 'transform/import_inliner_test.dart' as import_inliner_test;
import 'transform/script_compactor_test.dart' as script_compactor_test;
+import 'transform/all_phases_test.dart' as all_phases_test;
main() {
var args = new Options().arguments;
@@ -40,6 +41,7 @@
addGroup('transform/code_extractor_test.dart', code_extractor_test.main);
addGroup('transform/import_inliner_test.dart', import_inliner_test.main);
addGroup('transform/script_compactor_test.dart', script_compactor_test.main);
+ addGroup('transform/all_phases_test.dart', all_phases_test.main);
endToEndTests('data/unit/', 'data/out');
diff --git a/pkg/polymer/test/transform/all_phases_test.dart b/pkg/polymer/test/transform/all_phases_test.dart
new file mode 100644
index 0000000..a644fe5
--- /dev/null
+++ b/pkg/polymer/test/transform/all_phases_test.dart
@@ -0,0 +1,206 @@
+// Copyright (c) 2013, 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 polymer.test.transform.script_compactor_test;
+
+import 'package:polymer/src/transform.dart';
+import 'package:unittest/compact_vm_config.dart';
+
+import 'common.dart';
+
+void main() {
+ useCompactVMConfiguration();
+
+ testPhases('no changes', phases, {
+ 'a|web/test.html': '<!DOCTYPE html><html></html>',
+ }, {
+ 'a|web/test.html': '<!DOCTYPE html><html></html>',
+ });
+
+ testPhases('observable changes', phases, {
+ 'a|web/test.dart': _sampleObservable('A', 'foo'),
+ 'a|web/test2.dart': _sampleObservableOutput('B', 'bar'),
+ }, {
+ 'a|web/test.dart': _sampleObservableOutput('A', 'foo'),
+ 'a|web/test2.dart': _sampleObservableOutput('B', 'bar'),
+ });
+
+ testPhases('single script', phases, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><head>'
+ '<script type="application/dart" src="a.dart"></script>',
+ 'a|web/test.dart': _sampleObservable('A', 'foo'),
+ }, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><head></head><body>'
+ '<script type="application/dart" '
+ 'src="test.html_bootstrap.dart"></script>'
+ '<script type="text/javascript" '
+ 'src="packages/browser/dart.js"></script>'
+ '</body></html>',
+
+ 'a|web/test.html_bootstrap.dart':
+ '''library app_bootstrap;
+
+ import 'package:polymer/polymer.dart';
+ import 'dart:mirrors' show currentMirrorSystem;
+
+ import 'a.dart' as i0;
+
+ void main() {
+ initPolymer([
+ 'a.dart',
+ ], currentMirrorSystem().isolate.rootLibrary.uri.toString());
+ }
+ '''.replaceAll('\n ', '\n'),
+ 'a|web/test.dart': _sampleObservableOutput('A', 'foo'),
+ });
+
+ testPhases('single inline script', phases, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><head>'
+ '<script type="application/dart">'
+ '${_sampleObservable("B", "bar")}</script>',
+ }, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><head></head><body>'
+ '<script type="application/dart" '
+ 'src="test.html_bootstrap.dart"></script>'
+ '<script type="text/javascript" '
+ 'src="packages/browser/dart.js"></script>'
+ '</body></html>',
+
+ 'a|web/test.html_bootstrap.dart':
+ '''library app_bootstrap;
+
+ import 'package:polymer/polymer.dart';
+ import 'dart:mirrors' show currentMirrorSystem;
+
+ import 'test.html.0.dart' as i0;
+
+ void main() {
+ initPolymer([
+ 'test.html.0.dart',
+ ], currentMirrorSystem().isolate.rootLibrary.uri.toString());
+ }
+ '''.replaceAll('\n ', '\n'),
+ 'a|web/test.html.0.dart': _sampleObservableOutput("B", "bar"),
+ });
+
+ testPhases('several scripts', phases, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><head>'
+ '<script type="application/dart" src="a.dart"></script>'
+ '<script type="application/dart">'
+ '${_sampleObservable("B", "bar")}</script>'
+ '</head><body><div>'
+ '<script type="application/dart">'
+ '${_sampleObservable("C", "car")}</script>'
+ '</div>'
+ '<script type="application/dart" src="d.dart"></script>',
+ 'a|web/a.dart': _sampleObservable('A', 'foo'),
+ }, {
+ 'a|web/test.html':
+ '<!DOCTYPE html><html><head></head><body><div></div>'
+ '<script type="application/dart" '
+ 'src="test.html_bootstrap.dart"></script>'
+ '<script type="text/javascript" '
+ 'src="packages/browser/dart.js"></script>'
+ '</body></html>',
+
+ 'a|web/test.html_bootstrap.dart':
+ '''library app_bootstrap;
+
+ import 'package:polymer/polymer.dart';
+ import 'dart:mirrors' show currentMirrorSystem;
+
+ import 'a.dart' as i0;
+ import 'test.html.0.dart' as i1;
+ import 'test.html.1.dart' as i2;
+ import 'd.dart' as i3;
+
+ void main() {
+ initPolymer([
+ 'a.dart',
+ 'test.html.0.dart',
+ 'test.html.1.dart',
+ 'd.dart',
+ ], currentMirrorSystem().isolate.rootLibrary.uri.toString());
+ }
+ '''.replaceAll('\n ', '\n'),
+ 'a|web/a.dart': _sampleObservableOutput('A', 'foo'),
+ 'a|web/test.html.0.dart': _sampleObservableOutput("B", "bar"),
+ 'a|web/test.html.1.dart': _sampleObservableOutput("C", "car"),
+ });
+
+ testPhases('with imports', phases, {
+ 'a|web/index.html':
+ '<!DOCTYPE html><html><head>'
+ '<link rel="import" href="test2.html">'
+ '</head><body>'
+ '<script type="application/dart" src="b.dart"></script>'
+ '<script type="application/dart">'
+ '${_sampleObservable("C", "car")}</script>',
+ 'a|web/b.dart': _sampleObservable('B', 'bar'),
+ 'a|web/test2.html':
+ '<!DOCTYPE html><html><head>'
+ '</head><body><polymer-element>1'
+ '<script type="application/dart">'
+ '${_sampleObservable("A", "foo")}</script>'
+ '</polymer-element></html>',
+ }, {
+ 'a|web/index.html':
+ '<!DOCTYPE html><html><head></head><body>'
+ '<polymer-element>1</polymer-element>'
+ '<script type="application/dart" '
+ 'src="index.html_bootstrap.dart"></script>'
+ '<script type="text/javascript" '
+ 'src="packages/browser/dart.js"></script>'
+ '</body></html>',
+ 'a|web/index.html_bootstrap.dart':
+ '''library app_bootstrap;
+
+ import 'package:polymer/polymer.dart';
+ import 'dart:mirrors' show currentMirrorSystem;
+
+ import 'test2.html.0.dart' as i0;
+ import 'b.dart' as i1;
+ import 'index.html.0.dart' as i2;
+
+ void main() {
+ initPolymer([
+ 'test2.html.0.dart',
+ 'b.dart',
+ 'index.html.0.dart',
+ ], currentMirrorSystem().isolate.rootLibrary.uri.toString());
+ }
+ '''.replaceAll('\n ', '\n'),
+ 'a|web/test2.html.0.dart': _sampleObservableOutput("A", "foo"),
+ 'a|web/b.dart': _sampleObservableOutput('B', 'bar'),
+ 'a|web/index.html.0.dart': _sampleObservableOutput("C", "car"),
+ });
+}
+
+String _sampleObservable(String className, String fieldName) => '''
+import 'package:observe/observe.dart';
+
+class $className extends ObservableBase {
+ @observable int $fieldName;
+ $className(this.$fieldName);
+}
+''';
+
+String _sampleObservableOutput(String className, String fieldName) => '''
+import 'package:observe/observe.dart';
+
+class $className extends ChangeNotifierBase {
+ int __\$$fieldName;
+ int get $fieldName => __\$$fieldName;
+ set $fieldName(int value) {
+ __\$$fieldName = notifyPropertyChange(const Symbol('$fieldName'), __\$$fieldName, value);
+ }
+
+ $className($fieldName) : __\$$fieldName = $fieldName;
+}
+''';
diff --git a/pkg/polymer/test/transform/script_compactor_test.dart b/pkg/polymer/test/transform/script_compactor_test.dart
index d9a0a1c..b271e3c 100644
--- a/pkg/polymer/test/transform/script_compactor_test.dart
+++ b/pkg/polymer/test/transform/script_compactor_test.dart
@@ -27,6 +27,8 @@
'<!DOCTYPE html><html><head></head><body>'
'<script type="application/dart" '
'src="test.html_bootstrap.dart"></script>'
+ '<script type="text/javascript" '
+ 'src="packages/browser/dart.js"></script>'
'</body></html>',
'a|test.html_bootstrap.dart':
@@ -59,6 +61,8 @@
'<!DOCTYPE html><html><head></head><body><div></div>'
'<script type="application/dart" '
'src="test.html_bootstrap.dart"></script>'
+ '<script type="text/javascript" '
+ 'src="packages/browser/dart.js"></script>'
'</body></html>',
'a|test.html_bootstrap.dart':