blob: 5fa901fcde5d475930e803972fd8f616aebfce07 [file] [log] [blame]
// 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.
/**
* Datatypes holding information extracted by the analyzer and used by later
* phases of the compiler.
*/
library polymer.src.info;
import 'dart:collection' show SplayTreeMap, LinkedHashMap;
import 'package:csslib/visitor.dart';
import 'package:html5lib/dom.dart';
import 'package:source_maps/span.dart' show Span;
import 'messages.dart';
import 'utils.dart';
/**
* Information that is global. Roughly corresponds to `window` and `document`.
*/
class GlobalInfo {
/**
* Pseudo-element names exposed in a component via a pseudo attribute.
* The name is only available from CSS (not Dart code) so they're mangled.
* The same pseudo-element in different components maps to the same
* mangled name (as the pseudo-element is scoped inside of the component).
*/
final Map<String, String> pseudoElements = <String, String>{};
/** All components declared in the application. */
final Map<String, ComponentInfo> components = new SplayTreeMap();
}
/**
* Information for any library-like input. We consider each HTML file a library,
* and each component declaration a library as well. Hence we use this as a base
* class for both [FileInfo] and [ComponentInfo]. Both HTML files and components
* can have .dart code provided by the user for top-level user scripts and
* component-level behavior code. This code can either be inlined in the HTML
* file or included in a script tag with the "src" attribute.
*/
abstract class LibraryInfo {
/** Parsed cssSource. */
List<StyleSheet> styleSheets = [];
}
/** Information extracted at the file-level. */
class FileInfo extends LibraryInfo {
/** Relative path to this file from the compiler's base directory. */
final UrlInfo inputUrl;
/**
* All custom element definitions in this file. This may contain duplicates.
* Normally you should use [components] for lookup.
*/
final List<ComponentInfo> declaredComponents = new List<ComponentInfo>();
/**
* All custom element definitions defined in this file or imported via
*`<link rel='components'>` tag. Maps from the tag name to the component
* information. This map is sorted by the tag name.
*/
final Map<String, ComponentInfo> components =
new SplayTreeMap<String, ComponentInfo>();
/** Files imported with `<link rel="import">` */
final List<UrlInfo> componentLinks = <UrlInfo>[];
/** Files imported with `<link rel="stylesheet">` */
final List<UrlInfo> styleSheetHrefs = <UrlInfo>[];
FileInfo(this.inputUrl);
}
/** Information about a web component definition declared locally. */
class ComponentInfo extends LibraryInfo {
/** The component tag name, defined with the `name` attribute on `element`. */
final String tagName;
/**
* The tag name that this component extends, defined with the `extends`
* attribute on `element`.
*/
final String extendsTag;
/**
* The component info associated with the [extendsTag] name, if any.
* This will be `null` if the component extends a built-in HTML tag, or
* if the analyzer has not run yet.
*/
ComponentInfo extendsComponent;
/** The declaring `<element>` tag. */
final Node element;
/**
* True if [tagName] was defined by more than one component. If this happened
* we will skip over the component.
*/
bool hasConflict = false;
ComponentInfo(this.element, this.tagName, this.extendsTag);
/**
* Gets the HTML tag extended by the base of the component hierarchy.
* Equivalent to [extendsTag] if this inherits directly from an HTML element,
* in other words, if [extendsComponent] is null.
*/
String get baseExtendsTag =>
extendsComponent == null ? extendsTag : extendsComponent.baseExtendsTag;
Span get sourceSpan => element.sourceSpan;
/** Is apply-author-styles enabled. */
bool get hasAuthorStyles =>
element.attributes.containsKey('apply-author-styles');
String toString() => '#<ComponentInfo $tagName>';
}
/**
* Information extracted about a URL that refers to another file. This is
* mainly introduced to be able to trace back where URLs come from when
* reporting errors.
*/
class UrlInfo {
/** Original url. */
final String url;
/** Path that the URL points to. */
final String resolvedPath;
/** Original source location where the URL was extracted from. */
final Span sourceSpan;
UrlInfo(this.url, this.resolvedPath, this.sourceSpan);
/**
* Resolve a path from an [url] found in a file located at [inputUrl].
* Returns null for absolute [url]. Unless [ignoreAbsolute] is true, reports
* an error message if the url is an absolute url.
*/
static UrlInfo resolve(String url, UrlInfo inputUrl, Span span,
String packageRoot, Messages messages, {bool ignoreAbsolute: false}) {
var uri = Uri.parse(url);
if (uri.host != '' || (uri.scheme != '' && uri.scheme != 'package')) {
if (!ignoreAbsolute) {
messages.error('absolute paths not allowed here: "$url"', span);
}
return null;
}
var target;
if (url.startsWith('package:')) {
target = path.join(packageRoot, url.substring(8));
} else if (path.isAbsolute(url)) {
if (!ignoreAbsolute) {
messages.error('absolute paths not allowed here: "$url"', span);
}
return null;
} else {
target = path.join(path.dirname(inputUrl.resolvedPath), url);
url = pathToUrl(path.normalize(path.join(
path.dirname(inputUrl.url), url)));
}
target = path.normalize(target);
return new UrlInfo(url, target, span);
}
bool operator ==(UrlInfo other) =>
url == other.url && resolvedPath == other.resolvedPath;
int get hashCode => resolvedPath.hashCode;
String toString() => "#<UrlInfo url: $url, resolvedPath: $resolvedPath>";
}