diff --git a/pkg/dev_compiler/lib/src/codegen/js_codegen.dart b/pkg/dev_compiler/lib/src/codegen/js_codegen.dart index 2b3fddff52f2..a4e3c60ce577 100644 --- a/pkg/dev_compiler/lib/src/codegen/js_codegen.dart +++ b/pkg/dev_compiler/lib/src/codegen/js_codegen.dart @@ -3589,7 +3589,8 @@ class JSGenerator extends CodeGenerator { ? Uri.parse('http://${flags.host}:${flags.port}/') : null; return writeJsLibrary(module, out, compiler.inputBaseDir, serverUri, - emitSourceMaps: options.emitSourceMaps); + emitSourceMaps: options.emitSourceMaps, + fileSystem: compiler.fileSystem); } } diff --git a/pkg/dev_compiler/lib/src/codegen/js_printer.dart b/pkg/dev_compiler/lib/src/codegen/js_printer.dart index 65932a6afd75..50ce6ba4ecc4 100644 --- a/pkg/dev_compiler/lib/src/codegen/js_printer.dart +++ b/pkg/dev_compiler/lib/src/codegen/js_printer.dart @@ -12,16 +12,15 @@ import 'package:source_maps/source_maps.dart' show SourceMapSpan; import 'package:source_span/source_span.dart' show SourceLocation; import '../js/js_ast.dart' as JS; -import '../utils.dart' show computeHash, locationForOffset; +import '../utils.dart' show FileSystem, computeHash, locationForOffset; import 'js_names.dart' show TemporaryNamer; String writeJsLibrary( JS.Program jsTree, String outputPath, String inputDir, Uri serverUri, - {bool emitSourceMaps: false}) { + {bool emitSourceMaps: false, FileSystem fileSystem}) { var outFilename = path.basename(outputPath); var outDir = path.dirname(outputPath); - new Directory(outDir).createSync(recursive: true); JS.JavaScriptPrintingContext context; if (emitSourceMaps) { @@ -54,11 +53,11 @@ String writeJsLibrary( // "names": ["state","print"] sourceMapText = sourceMapText.replaceAll('\n ', '').replaceAll('\n ]', ']'); - new File('$outputPath.map').writeAsStringSync('$sourceMapText\n'); + fileSystem.writeAsStringSync('$outputPath.map', '$sourceMapText\n'); } else { text = (context as JS.SimpleJavaScriptPrintingContext).getText(); } - new File(outputPath).writeAsStringSync(text); + fileSystem.writeAsStringSync(outputPath, text); if (jsTree.scriptTag != null) { // Mark executable. // TODO(jmesserly): should only do this if the input file was executable? diff --git a/pkg/dev_compiler/lib/src/compiler.dart b/pkg/dev_compiler/lib/src/compiler.dart index 097a2dc07b56..33b453392778 100644 --- a/pkg/dev_compiler/lib/src/compiler.dart +++ b/pkg/dev_compiler/lib/src/compiler.dart @@ -32,7 +32,7 @@ import 'info.dart' import 'options.dart'; import 'report.dart'; import 'report/html_reporter.dart'; -import 'utils.dart' show isStrongModeError; +import 'utils.dart' show FileSystem, isStrongModeError; /// Sets up the type checker logger to print a span that highlights error /// messages. @@ -100,12 +100,13 @@ class BatchCompiler extends AbstractCompiler { final _pendingLibraries = []; BatchCompiler(AnalysisContext context, CompilerOptions options, - {AnalysisErrorListener reporter}) + {AnalysisErrorListener reporter, + FileSystem fileSystem: const FileSystem()}) : super( context, options, - new ErrorCollector( - reporter ?? AnalysisErrorListener.NULL_LISTENER)) { + new ErrorCollector(reporter ?? AnalysisErrorListener.NULL_LISTENER), + fileSystem) { _inputBaseDir = options.inputBaseDir; if (outputDir != null) { _jsGen = new JSGenerator(this); @@ -264,8 +265,7 @@ class BatchCompiler extends AbstractCompiler { output.lastModifiedSync() == input.lastModifiedSync()) { continue; } - new Directory(path.dirname(output.path)).createSync(recursive: true); - input.copySync(output.path); + fileSystem.copySync(input.path, output.path); } } @@ -304,10 +304,8 @@ class BatchCompiler extends AbstractCompiler { } } - var outputFile = getOutputPath(source.uri); - new File(outputFile) - ..createSync(recursive: true) - ..writeAsStringSync(document.outerHtml + '\n'); + fileSystem.writeAsStringSync( + getOutputPath(source.uri), document.outerHtml + '\n'); } html.DocumentFragment _linkLibraries( @@ -357,8 +355,10 @@ abstract class AbstractCompiler { final CompilerOptions options; final AnalysisContext context; final AnalysisErrorListener reporter; + final FileSystem fileSystem; - AbstractCompiler(this.context, this.options, [AnalysisErrorListener listener]) + AbstractCompiler(this.context, this.options, + [AnalysisErrorListener listener, this.fileSystem = const FileSystem()]) : reporter = listener ?? AnalysisErrorListener.NULL_LISTENER; String get outputDir => options.codegenOptions.outputDir; diff --git a/pkg/dev_compiler/lib/src/transformer/asset_source.dart b/pkg/dev_compiler/lib/src/transformer/asset_source.dart new file mode 100644 index 000000000000..34477184199b --- /dev/null +++ b/pkg/dev_compiler/lib/src/transformer/asset_source.dart @@ -0,0 +1,65 @@ +// Copyright (c) 2016, 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. + +import 'package:analyzer/src/generated/engine.dart'; +import 'package:analyzer/src/generated/source.dart' show Source, UriKind; +import 'package:barback/barback.dart' show Asset, TimestampedData; +import 'package:path/path.dart' as path; + +class AssetSource implements Source { + final Uri uri; + final Asset asset; + final String contentString; + AssetSource(this.uri, this.asset, this.contentString); + + @override toString() => 'AssetSource($uri, ${asset.id})'; + + @override + TimestampedData get contents => + new TimestampedData(modificationStamp, contentString); + + @override + String get encoding => null; + + @override + bool exists() => true; + + @override + String get fullName => uri.toString(); + + @override + bool get isInSystemLibrary => uriKind == UriKind.DART_URI; + + @override + int get modificationStamp => 0; + + @override + Uri resolveRelativeUri(Uri relativeUri) { + var resolvedPath = path.join(path.dirname(uri.path), relativeUri.path); + return new Uri(scheme: uri.scheme, path: resolvedPath); + } + + @override + String get shortName => uri.toString(); + + @override + Source get source => this; + + @override + UriKind get uriKind { + switch (uri.scheme) { + case 'package': + return UriKind.PACKAGE_URI; + + case 'dart': + return UriKind.DART_URI; + + case 'file': + return UriKind.FILE_URI; + + default: + throw new StateError(uri.toString()); + } + } +} diff --git a/pkg/dev_compiler/lib/src/transformer/asset_universe.dart b/pkg/dev_compiler/lib/src/transformer/asset_universe.dart new file mode 100644 index 000000000000..5283589234f8 --- /dev/null +++ b/pkg/dev_compiler/lib/src/transformer/asset_universe.dart @@ -0,0 +1,52 @@ +// Copyright (c) 2016, 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 dev_compiler.src.transformer.asset_universe; + +import 'dart:async'; + +import 'package:analyzer/analyzer.dart' show UriBasedDirective, parseDirectives; +import 'package:barback/barback.dart' show Asset, AssetId; + +import 'asset_source.dart'; +import 'uri_resolver.dart' show assetIdToUri, resolveAssetId; + +/// Set of assets sources available for analysis / compilation. +class AssetUniverse { + final _assetCache = {}; + + Iterable get assetIds => _assetCache.keys; + + AssetSource getAssetSource(AssetId id) { + var source = _assetCache[id]; + if (source == null) { + throw new ArgumentError(id.toString()); + } + return source; + } + + /// Recursively loads the asset with [id] and all its transitive dependencies. + Future scanSources(AssetId id, Future getInput(AssetId id)) async { + if (_assetCache.containsKey(id)) return; + + var asset = await getInput(id); + var contents = await asset.readAsString(); + _assetCache[id] = + new AssetSource(Uri.parse(assetIdToUri(id)), asset, contents); + + var deps = _getDependentAssetIds(id, contents); + await Future.wait(deps.map((depId) => scanSources(depId, getInput))); + } + + Iterable _getDependentAssetIds(AssetId id, String contents) sync* { + var directives = parseDirectives(contents, suppressErrors: true).directives; + for (var directive in directives) { + if (directive is UriBasedDirective) { + var uri = directive.uri.stringValue; + var assetId = resolveAssetId(Uri.parse(uri), fromAssetId: id); + if (assetId != null) yield assetId; + } + } + } +} diff --git a/pkg/dev_compiler/lib/src/transformer/error_listener.dart b/pkg/dev_compiler/lib/src/transformer/error_listener.dart new file mode 100644 index 000000000000..01c3ca6a73b3 --- /dev/null +++ b/pkg/dev_compiler/lib/src/transformer/error_listener.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2016, 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 dev_compiler.src.transformer.error_listener; + +import 'package:barback/barback.dart' show TransformLogger; +import 'package:analyzer/analyzer.dart' + show AnalysisError, ErrorSeverity, AnalysisErrorListener; + +class TransformAnalysisErrorListener extends AnalysisErrorListener { + TransformLogger _logger; + TransformAnalysisErrorListener(this._logger); + + @override + void onError(AnalysisError error) { + // TODO(ochafik): Proper location / span. + switch (error.errorCode.errorSeverity) { + case ErrorSeverity.ERROR: + _logger.error(error.message); + break; + case ErrorSeverity.WARNING: + _logger.warning(error.message); + break; + default: + _logger.info(error.message); + break; + } + } +} diff --git a/pkg/dev_compiler/lib/src/transformer/transformer.dart b/pkg/dev_compiler/lib/src/transformer/transformer.dart new file mode 100644 index 000000000000..44a198f8ca44 --- /dev/null +++ b/pkg/dev_compiler/lib/src/transformer/transformer.dart @@ -0,0 +1,134 @@ +// Copyright (c) 2016, 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 dev_compiler.src.transformer.transformer; + +import 'dart:async'; +import 'dart:io'; + +import 'package:barback/barback.dart'; +import 'package:analyzer/src/generated/engine.dart' + show AnalysisEngine, AnalysisOptionsImpl; +import 'package:path/path.dart' as path; + +import 'asset_universe.dart'; +import 'error_listener.dart'; +import 'uri_resolver.dart' show assetIdToUri, createSourceFactory; +import '../compiler.dart'; +import '../options.dart'; +import '../utils.dart'; +import 'package:analyzer/src/generated/engine.dart'; + +const String _fakeRuntimeDir = ""; + +/// Disclaimer: this transformer is experimental and not optimized. It may run +/// out of memory for large applications: please use DDC's command-line runner +/// instead whenever possible. +class DdcTransformer extends AggregateTransformer { + final List _ddcArgs; + + DdcTransformer.asPlugin(BarbackSettings settings) + : _ddcArgs = settings.configuration['args'] ?? []; + + @override + apply(AggregateTransform transform) async { + var inputs = await transform.primaryInputs.toList(); + + // The analyzer's source factory mechanism is synchronous, so we can't + // have it wait upon transform.getInput. Instead, we build the whole + // universe (scanning transitive dependencies and reading their sources), + // so as to get a non-async source getter. + // Note: This means we use a lot of memory: one way to fix it would be to + // propagate asynchonous calls throughout the analyzer. + var universe = new AssetUniverse(); + await Future.wait( + inputs.map((a) => universe.scanSources(a.id, transform.getInput))); + + // TODO(ochafik): invesigate the us of createAnalysisContextWithSources + // instead. + var context = AnalysisEngine.instance.createAnalysisContext(); + context.analysisOptions = _makeAnalysisOptions(); + context.sourceFactory = createSourceFactory(universe.getAssetSource); + + // Virtual file system that writes into the transformer's outputs. + var fileSystem = new _TransformerFileSystem( + transform.logger, transform.package, transform.addOutput, + // Seed the runtime files into our file system: + inputs: await _readRuntimeFiles(transform.getInput)); + + var compiler = new BatchCompiler( + context, + // Note that the output directory needs not exist, and the runtime + // directory is a special value that corresponds to the seeding of + // runtimeFiles above. + parseOptions([] + ..addAll(_ddcArgs) + ..addAll([ + '-o', + fileSystem.outputDir.path, + '--runtime-dir', + _fakeRuntimeDir + ])), + reporter: new TransformAnalysisErrorListener(transform.logger), + fileSystem: fileSystem); + + for (var asset in inputs) { + compiler.compileFromUriString(assetIdToUri(asset.id)); + } + } + + // TODO(ochafik): Provide more control over these options. + AnalysisOptions _makeAnalysisOptions() => new AnalysisOptionsImpl() + ..cacheSize = 256 // # of sources to cache ASTs for. + ..preserveComments = true + ..analyzeFunctionBodies = true + ..strongMode = true; + + /// Read the runtime files from the transformer (they're available as + /// resources of package:dev_compiler), + Future> _readRuntimeFiles( + Future getInput(AssetId id)) async { + var runtimeFiles = {}; + for (var file in defaultRuntimeFiles) { + var asset = + await getInput(new AssetId('dev_compiler', 'lib/runtime/$file')); + runtimeFiles[path.join(_fakeRuntimeDir, file)] = + await asset.readAsString(); + } + return runtimeFiles; + } + + /// We just transform all .dart and .html files in one go. + @override + classifyPrimary(AssetId id) => + id.extension == '.dart' || id.extension == '.html' ? '' : null; +} + +/// Type of [Transform.addOutput] and [AggregateTransform.addOutput]. +typedef void AssetOutputAdder(Asset asset); + +/// Virtual file system that outputs files into a transformer. +class _TransformerFileSystem implements FileSystem { + final String _package; + final Directory outputDir = Directory.current; + final String outputPrefix; + final AssetOutputAdder _addOutput; + final Map inputs; + final TransformLogger _logger; + _TransformerFileSystem(this._logger, this._package, this._addOutput, + {this.inputs, this.outputPrefix: 'web/'}); + + @override + void writeAsStringSync(String file, String contents) { + var id = new AssetId( + _package, outputPrefix + path.relative(file, from: outputDir.path)); + _logger.fine('Adding output $id'); + _addOutput(new Asset.fromString(id, contents)); + } + + @override + void copySync(String src, String dest) { + writeAsStringSync(dest, inputs[src] ?? new File(src).readAsStringSync()); + } +} diff --git a/pkg/dev_compiler/lib/src/transformer/uri_resolver.dart b/pkg/dev_compiler/lib/src/transformer/uri_resolver.dart new file mode 100644 index 000000000000..7d4aa6795e37 --- /dev/null +++ b/pkg/dev_compiler/lib/src/transformer/uri_resolver.dart @@ -0,0 +1,88 @@ +// Copyright (c) 2016, 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 dev_compiler.src.transformer.uri_resolver; + +import 'package:analyzer/src/generated/sdk.dart' show DartSdk; +import 'package:analyzer/src/generated/source.dart' show Source, SourceFactory; +import 'package:barback/barback.dart' show AssetId; +import 'package:code_transformers/resolver.dart' + show DartUriResolverProxy, DirectoryBasedDartSdkProxy; +import 'package:cli_util/cli_util.dart' as cli_util; +import 'package:path/path.dart' as path; + +import 'asset_source.dart'; + +typedef AssetSource AssetSourceGetter(AssetId id); + +/// Builds a package URI that corresponds to [id]. This is roughly the inverse +/// function of [resolveAssetId]. +/// +/// Note that if [id] points to a file outside the package's `lib` folder, that +/// file must be under `web` and the returned URI will not strictly correspond +/// to classic package URIs (but it will be invertible by [resolveAssetId]). +String assetIdToUri(AssetId id) { + var p = id.path; + if (p.startsWith('lib/web/')) { + throw new ArgumentError('Cannot convert $id to an unambiguous package uri'); + } + if (p.startsWith('lib/')) { + p = p.substring('lib/'.length); + } else if (!p.startsWith('web/')) { + throw new ArgumentError('Unexpected path in $id (expected {lib,web}/*'); + } + // Note: if the file is under web/, then we leave it as it is: resolveAssetId + // does the inverse transform. + return 'package:${id.package}/$p'; +} + +/// Gets the [AssetId] that corresponds to [uri]. +/// +/// If [fromAssetId] is not `null` and if [uri] is a relative URI, then +/// [fromAssetId] will be used to resolve the create a relative URI. In other +/// cases, this is roughly the inverse function of [assetIdToUri]. +AssetId resolveAssetId(Uri uri, {AssetId fromAssetId}) { + if (uri.scheme == 'dart') return null; + + if (uri.scheme == 'package') { + var segments = uri.pathSegments.toList(); + var package = segments[0]; + if (segments[1] == 'web') { + return new AssetId(package, path.url.joinAll(segments.skip(1))); + } else { + segments[0] = 'lib'; + return new AssetId(package, path.url.joinAll(segments)); + } + } + + if (uri.scheme == null || uri.scheme == '') { + if (fromAssetId == null) { + throw new ArgumentError('No asset to resolve relative URI from.'); + } + return new AssetId(fromAssetId.package, + path.normalize(path.join(path.dirname(fromAssetId.path), uri.path))); + } + + throw new ArgumentError('Unexpected uri: $uri (uri.scheme = ${uri.scheme})'); +} + +class _DdcUriResolver extends DartUriResolverProxy { + AssetSourceGetter _getAssetSource; + + _DdcUriResolver(DartSdk sdk, this._getAssetSource) : super(sdk); + + @override + Source resolveAbsolute(Uri uri, [Uri actualUri]) { + return uri.scheme == 'package' + ? _getAssetSource(resolveAssetId(uri)) + : super.resolveAbsolute(uri, actualUri); + } +} + +String get dartSdkDirectory => cli_util.getSdkDir()?.path; + +SourceFactory createSourceFactory(AssetSourceGetter getAssetSource) { + var sdk = new DirectoryBasedDartSdkProxy(dartSdkDirectory); + return new SourceFactory([new _DdcUriResolver(sdk, getAssetSource)]); +} diff --git a/pkg/dev_compiler/lib/src/utils.dart b/pkg/dev_compiler/lib/src/utils.dart index b1794f63cc3e..71d48b075363 100644 --- a/pkg/dev_compiler/lib/src/utils.dart +++ b/pkg/dev_compiler/lib/src/utils.dart @@ -477,3 +477,22 @@ class DirectedGraph { return reached; } } + +class FileSystem { + const FileSystem(); + + void _ensureParentExists(String file) { + var dir = new Directory(path.dirname(file)); + if (!dir.existsSync()) dir.createSync(recursive: true); + } + + void copySync(String source, String destination) { + _ensureParentExists(destination); + new File(source).copySync(destination); + } + + void writeAsStringSync(String file, String contents) { + _ensureParentExists(file); + new File(file).writeAsStringSync(contents); + } +} diff --git a/pkg/dev_compiler/lib/transformer.dart b/pkg/dev_compiler/lib/transformer.dart new file mode 100644 index 000000000000..ef8024dd7115 --- /dev/null +++ b/pkg/dev_compiler/lib/transformer.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2016, 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 dev_compiler.transformer; + +export 'src/transformer/transformer.dart'; diff --git a/pkg/dev_compiler/pubspec.yaml b/pkg/dev_compiler/pubspec.yaml index d79195b71653..be851fe986a0 100644 --- a/pkg/dev_compiler/pubspec.yaml +++ b/pkg/dev_compiler/pubspec.yaml @@ -10,6 +10,8 @@ homepage: https://github.com/dart-lang/dev_compiler dependencies: analyzer: ^0.27.2-alpha.1 + barback: ^0.15.2+7 + code_transformers: ^0.4.0 args: ^0.13.0 cli_util: ^0.0.1 crypto: ^0.9.0 @@ -30,6 +32,7 @@ dev_dependencies: # changes, may change the output format. dart_style: 0.2.2 test: ^0.12.0 + transformer_test: ^0.1.0 environment: sdk: ">=1.12.0 <2.0.0" diff --git a/pkg/dev_compiler/test/all_tests.dart b/pkg/dev_compiler/test/all_tests.dart index 26d1a2f23d24..6271ff6d8d52 100644 --- a/pkg/dev_compiler/test/all_tests.dart +++ b/pkg/dev_compiler/test/all_tests.dart @@ -14,6 +14,7 @@ import 'js/builder_test.dart' as builder_test; import 'end_to_end_test.dart' as e2e; import 'report_test.dart' as report_test; import 'dependency_graph_test.dart' as dependency_graph_test; +import 'transformer_test.dart' as transformer_test; import 'utils_test.dart' as utils_test; void main() { @@ -21,6 +22,7 @@ void main() { group('report', report_test.main); group('dependency_graph', dependency_graph_test.main); group('codegen', () => codegen_test.main([])); + group('transformer', transformer_test.main); group('closure', () { closure_annotation_test.main(); closure_type_test.main(); diff --git a/pkg/dev_compiler/test/transformer/hello_app/pubspec.yaml b/pkg/dev_compiler/test/transformer/hello_app/pubspec.yaml new file mode 100644 index 000000000000..a7a4dc23999d --- /dev/null +++ b/pkg/dev_compiler/test/transformer/hello_app/pubspec.yaml @@ -0,0 +1,16 @@ +name: hello_app +version: 0.0.1 +dependencies: + browser: any + hello_dep: + path: ../hello_dep +dev_dependencies: + dev_compiler: + path: ../../.. +transformers: +- dev_compiler: + args: + - --closure + - --destructure-named-params +- $dart2js: + $exclude: '**' diff --git a/pkg/dev_compiler/test/transformer/hello_app/web/index.html b/pkg/dev_compiler/test/transformer/hello_app/web/index.html new file mode 100644 index 000000000000..c4e75f5071b0 --- /dev/null +++ b/pkg/dev_compiler/test/transformer/hello_app/web/index.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pkg/dev_compiler/test/transformer/hello_app/web/main.dart b/pkg/dev_compiler/test/transformer/hello_app/web/main.dart new file mode 100644 index 000000000000..03781cb1436a --- /dev/null +++ b/pkg/dev_compiler/test/transformer/hello_app/web/main.dart @@ -0,0 +1,12 @@ +// Copyright (c) 2016, 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. + +import 'dart:html'; + +import 'package:hello_dep/simple.dart'; + +main() { + document.body.innerHtml = message; + print(message); +} diff --git a/pkg/dev_compiler/test/transformer/hello_dep/lib/simple.dart b/pkg/dev_compiler/test/transformer/hello_dep/lib/simple.dart new file mode 100644 index 000000000000..01bb50d01679 --- /dev/null +++ b/pkg/dev_compiler/test/transformer/hello_dep/lib/simple.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2016, 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. + +import 'utils.dart'; + +String get message => wrap("Hello, World!"); diff --git a/pkg/dev_compiler/test/transformer/hello_dep/lib/utils.dart b/pkg/dev_compiler/test/transformer/hello_dep/lib/utils.dart new file mode 100644 index 000000000000..c59c51f14f70 --- /dev/null +++ b/pkg/dev_compiler/test/transformer/hello_dep/lib/utils.dart @@ -0,0 +1,5 @@ +// Copyright (c) 2016, 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. + +wrap(String s) => "'$s'"; diff --git a/pkg/dev_compiler/test/transformer/hello_dep/pubspec.yaml b/pkg/dev_compiler/test/transformer/hello_dep/pubspec.yaml new file mode 100644 index 000000000000..65e7036cb66e --- /dev/null +++ b/pkg/dev_compiler/test/transformer/hello_dep/pubspec.yaml @@ -0,0 +1,2 @@ +name: hello_dep +version: 0.0.1 diff --git a/pkg/dev_compiler/test/transformer_test.dart b/pkg/dev_compiler/test/transformer_test.dart new file mode 100644 index 000000000000..092444fc8c40 --- /dev/null +++ b/pkg/dev_compiler/test/transformer_test.dart @@ -0,0 +1,96 @@ +library dev_compiler.test.transformer.transformer_test; + +import 'package:barback/barback.dart' show BarbackMode, BarbackSettings; +import 'package:dev_compiler/transformer.dart'; +import 'package:test/test.dart'; +import 'package:transformer_test/utils.dart'; +import 'package:dev_compiler/src/compiler.dart' show defaultRuntimeFiles; + +makePhases([Map config = const {}]) => [ + [ + new DdcTransformer.asPlugin( + new BarbackSettings(config, BarbackMode.RELEASE)) + ] + ]; + +final Map runtimeInput = new Map.fromIterable( + defaultRuntimeFiles, + key: (f) => 'dev_compiler|lib/runtime/$f', + value: (_) => ''); + +Map createInput(Map input) => + {}..addAll(input)..addAll(runtimeInput); + +void main() { + group('$DdcTransformer', () { + testPhases( + r'compiles simple code', + makePhases(), + createInput({ + 'foo|lib/Foo.dart': r''' + class Foo {} + ''' + }), + { + 'foo|web/foo/Foo.js': r''' +dart_library.library('foo/Foo', null, /* Imports */[ + 'dart/_runtime', + 'dart/core' +], /* Lazy imports */[ +], function(exports, dart, core) { + 'use strict'; + let dartx = dart.dartx; + class Foo extends core.Object {} + // Exports: + exports.Foo = Foo; +}); +//# sourceMappingURL=Foo.js.map +''' + .trimLeft() + }); + + testPhases( + r'honours arguments', + makePhases({ + 'args': ['--destructure-named-params', '--modules=es6'] + }), + createInput({ + 'foo|lib/Foo.dart': r''' + int foo({String s : '?'}) {} + ''' + }), + { + 'foo|web/foo/Foo.js': r''' +const exports = {}; +import dart from "../dart/_runtime"; +import core from "../dart/core"; +let dartx = dart.dartx; +function foo({s = '?'} = {}) { +} +dart.fn(foo, core.int, [], {s: core.String}); +// Exports: +exports.foo = foo; +export default exports; +//# sourceMappingURL=Foo.js.map +''' + .trimLeft() + }); + + testPhases( + 'forwards errors', + makePhases(), + createInput({ + 'foo|lib/Foo.dart': r''' + foo() { + var x = 1; + x = '2'; + } + ''' + }), + {}, + [ + "warning: A value of type \'String\' cannot be assigned to a variable of type \'int\'", + "error: Type check failed: '2' (String) is not of type int" + ]); + }); +} diff --git a/pkg/dev_compiler/tool/presubmit.sh b/pkg/dev_compiler/tool/presubmit.sh index 54308c101caa..638942d6b7ba 100755 --- a/pkg/dev_compiler/tool/presubmit.sh +++ b/pkg/dev_compiler/tool/presubmit.sh @@ -7,3 +7,4 @@ $DIR/browser_test.sh $DIR/node_test.sh $DIR/analyze.sh $DIR/format.sh +$DIR/transformer_test.sh diff --git a/pkg/dev_compiler/tool/transformer_test.sh b/pkg/dev_compiler/tool/transformer_test.sh new file mode 100755 index 000000000000..e733f86ae657 --- /dev/null +++ b/pkg/dev_compiler/tool/transformer_test.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -eu +DIR=$(dirname "${BASH_SOURCE[0]}") + +# TODO(ochafik): Run pub serve + launch a browser on +# http://localhost:8080/hello_app/web/ +cd test/transformer/hello_app +pub get +pub build + +expected_files=( + build/web/hello_app/web/main.js + build/web/hello_dep/simple.js + build/web/hello_dep/utils.js + # This is not an exhaustive check: + build/web/dev_compiler/runtime/dart/_runtime.js +) + +for file in "${expected_files[@]}" ; do + if [[ ! -f $file ]]; then + echo "Didn't find $file in build:" + find build | grep -v /packages/ + exit 1 + fi +done