blob: 5c1c2a3ff6fe188f22d6ef9ce9eb4a7fec33b839 [file] [log] [blame]
// Copyright (c) 2017, 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:async';
// ignore: deprecated_member_use
import 'package:analyzer/analyzer.dart';
import 'package:build/build.dart';
import 'package:build_modules/build_modules.dart';
import 'common.dart';
import 'dart2js_bootstrap.dart';
import 'dev_compiler_bootstrap.dart';
const ddcBootstrapExtension = '.dart.bootstrap.js';
const jsEntrypointExtension = '.dart.js';
const jsEntrypointSourceMapExtension = '.dart.js.map';
const jsEntrypointArchiveExtension = '.dart.js.tar.gz';
const digestsEntrypointExtension = '.digests';
/// Which compiler to use when compiling web entrypoints.
enum WebCompiler {
Dart2Js,
DartDevc,
}
/// The top level keys supported for the `options` config for the
/// [WebEntrypointBuilder].
const _supportedOptions = [
_compiler,
_dart2jsArgs,
];
const _compiler = 'compiler';
const _dart2jsArgs = 'dart2js_args';
/// The deprecated keys for the `options` config for the [WebEntrypointBuilder].
const _deprecatedOptions = [
'enable_sync_async',
'ignore_cast_failures',
];
/// A builder which compiles entrypoints for the web.
///
/// Supports `dart2js` and `dartdevc`.
class WebEntrypointBuilder implements Builder {
final WebCompiler webCompiler;
final List<String> dart2JsArgs;
const WebEntrypointBuilder(this.webCompiler, {this.dart2JsArgs = const []});
factory WebEntrypointBuilder.fromOptions(BuilderOptions options) {
validateOptions(
options.config, _supportedOptions, 'build_web_compilers|entrypoint',
deprecatedOptions: _deprecatedOptions);
var compilerOption = options.config[_compiler] as String ?? 'dartdevc';
WebCompiler compiler;
switch (compilerOption) {
case 'dartdevc':
compiler = WebCompiler.DartDevc;
break;
case 'dart2js':
compiler = WebCompiler.Dart2Js;
break;
default:
throw ArgumentError.value(compilerOption, _compiler,
'Only `dartdevc` and `dart2js` are supported.');
}
if (options.config[_dart2jsArgs] is! List) {
throw ArgumentError.value(options.config[_dart2jsArgs], _dart2jsArgs,
'Expected a list for $_dart2jsArgs.');
}
var dart2JsArgs = (options.config[_dart2jsArgs] as List)
?.map((arg) => '$arg')
?.toList() ??
const <String>[];
return WebEntrypointBuilder(compiler, dart2JsArgs: dart2JsArgs);
}
@override
final buildExtensions = const {
'.dart': [
ddcBootstrapExtension,
jsEntrypointExtension,
jsEntrypointSourceMapExtension,
jsEntrypointArchiveExtension,
digestsEntrypointExtension,
],
};
@override
Future<void> build(BuildStep buildStep) async {
var dartEntrypointId = buildStep.inputId;
var isAppEntrypoint = await _isAppEntryPoint(dartEntrypointId, buildStep);
if (!isAppEntrypoint) return;
if (webCompiler == WebCompiler.DartDevc) {
try {
await bootstrapDdc(buildStep, requiredAssets: _ddcSdkResources);
} on MissingModulesException catch (e) {
log.severe('$e');
}
} else if (webCompiler == WebCompiler.Dart2Js) {
await bootstrapDart2Js(buildStep, dart2JsArgs);
}
}
}
/// Returns whether or not [dartId] is an app entrypoint (basically, whether
/// or not it has a `main` function).
Future<bool> _isAppEntryPoint(AssetId dartId, AssetReader reader) async {
assert(dartId.extension == '.dart');
// Skip reporting errors here, dartdevc will report them later with nicer
// formatting.
// ignore: deprecated_member_use
var parsed = parseCompilationUnit(await reader.readAsString(dartId),
suppressErrors: true);
// Allow two or fewer arguments so that entrypoints intended for use with
// [spawnUri] get counted.
//
// TODO: This misses the case where a Dart file doesn't contain main(),
// but has a part that does, or it exports a `main` from another library.
return parsed.declarations.any((node) {
return node is FunctionDeclaration &&
node.name.name == 'main' &&
node.functionExpression.parameters.parameters.length <= 2;
});
}
/// Files copied from the SDK that are required at runtime to run a DDC
/// application.
final _ddcSdkResources = [
AssetId('build_web_compilers', 'lib/src/dev_compiler/dart_sdk.js'),
AssetId('build_web_compilers', 'lib/src/dev_compiler/require.js'),
];