blob: a5bf002e7633c6cf7f5087f28575e567fdc3a988 [file] [log] [blame]
// Copyright (c) 2015, 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.
/// Transformer used for pub serve and pub build
library web_components.build.web_components;
import 'dart:async';
import 'package:barback/barback.dart';
import 'package:code_transformers/assets.dart';
import 'package:code_transformers/messages/build_logger.dart';
import 'package:code_transformers/resolver.dart';
import 'package:html5lib/dom.dart' as dom;
import 'package:initialize/transformer.dart' show generateBootstrapFile;
import 'package:initialize/build/initializer_plugin.dart';
import 'package:path/path.dart' as path;
import 'package:web_components/transformer.dart';
import 'common.dart';
/// Public method that can be used inside any [Transformer] which already has a
/// [Resolver] and [Transform] to generate a bootstrap file for the
/// web_components package.
Asset generateWebComponentsBootstrap(Resolver resolver, Transform transform,
dom.Document document, AssetId scriptId, AssetId newScriptId,
{List<InitializerPlugin> extraPlugins: const []}) {
var htmlImportRecorder = new HtmlImportAnnotationRecorder();
var plugins = [htmlImportRecorder]..addAll(extraPlugins);
// Bootstrap the application using the `initialize` package and our
// plugins.
var initializeBootstrap = generateBootstrapFile(
resolver, transform, scriptId, newScriptId,
errorIfNotFound: false, plugins: plugins);
// Add all seen imports to the document, before the first dart script tag if
// it exists.
var dartScript =
document.head.querySelector('script[type="application/dart"]');
for (var importPath in htmlImportRecorder.importPaths) {
var import = new dom.Element.tag('link')
..attributes = {'rel': 'import', 'href': importPath,};
document.head.insertBefore(import, dartScript);
}
return initializeBootstrap;
}
/// A [Transformer] which runs the `initialize` transformer with
/// some special plugins and also inlines the html imports.
class WebComponentsTransformer extends Transformer {
final Resolvers _resolvers;
TransformOptions options;
WebComponentsTransformer(this.options) : _resolvers = new Resolvers.fromMock({
// The list of types below is derived from:
// * types that are used internally by the resolver (see
// _initializeFrom in resolver.dart).
// TODO(jakemac): Move this into code_transformers so it can be shared.
'dart:core': '''
library dart.core;
class Object {}
class Function {}
class StackTrace {}
class Symbol {}
class Type {}
class String extends Object {}
class bool extends Object {}
class num extends Object {}
class int extends num {}
class double extends num {}
class DateTime extends Object {}
class Null extends Object {}
class Deprecated extends Object {
final String expires;
const Deprecated(this.expires);
}
const Object deprecated = const Deprecated("next release");
class _Override { const _Override(); }
const Object override = const _Override();
class _Proxy { const _Proxy(); }
const Object proxy = const _Proxy();
class List<V> extends Object {}
class Map<K, V> extends Object {}
''',
'dart:html': '''
library dart.html;
class HtmlElement {}
''',
'dart:async': '''
library dart.async;
class Future<T> {}
''',
});
bool isPrimary(AssetId id) {
if (options.entryPoints != null) {
return options.entryPoints.contains(id.path);
}
if (id.path == 'web/index.bootstrap.dart') return true;
// If no entry point is supplied, then any html file under web/ or test/ is
// an entry point.
return (id.path.startsWith('web/') || id.path.startsWith('test/')) &&
id.path.endsWith('.html');
}
Future apply(Transform transform) {
var logger = new BuildLogger(transform);
var primaryInput = transform.primaryInput;
return primaryInput.readAsString().then((html) {
// Find the dart script in the page.
var doc = parseHtml(html, primaryInput.id.path);
var mainScriptTag = doc.querySelector('script[type="$dartType"]');
var scriptId = uriToAssetId(primaryInput.id,
mainScriptTag.attributes['src'], logger, mainScriptTag.sourceSpan);
return _resolvers.get(transform, [scriptId]).then((resolver) {
var newScriptId = new AssetId(scriptId.package,
'${path.url.withoutExtension(scriptId.path)}.initialize.dart');
var bootstrap = generateWebComponentsBootstrap(
resolver, transform, doc, scriptId, newScriptId);
// Swap out the main script tag for the bootstrap version.
mainScriptTag.attributes['src'] = path.url.relative(bootstrap.id.path,
from: path.url.dirname(primaryInput.id.path));
// Output the new document and bootstrap file.
transform
.addOutput(new Asset.fromString(primaryInput.id, doc.outerHtml));
transform.addOutput(bootstrap);
});
});
}
}