blob: 34037d95fd4d818e806a40b18d7bccb9fbfd62af [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.
/**
* Holds path information that is used by the WebUI compiler to find files,
* compute their output location and relative paths between them.
*/
library polymer.src.paths;
import 'info.dart' show UrlInfo;
import 'messages.dart';
import 'summary.dart';
import 'utils.dart' show path, pathToUrl;
/**
* Stores information about paths and computes mappings between input and output
* path locations.
*/
class PathMapper {
/**
* Common prefix to all input paths that are read from the file system. The
* output generated by the compiler will reflect the directory structure
* starting from [_baseDir]. For instance, if [_baseDir] is `a/b/c` and
* [_outputDir] is `g/h/`, then the corresponding output file for
* `a/b/c/e/f.html` will be under `g/h/e/f.html.dart`.
*/
final String _baseDir;
/** Base path where all output is generated. */
final String _outputDir;
/** The package root directory. */
final String packageRoot;
/** Whether to add prefixes and to output file names. */
final bool _mangleFilenames;
final bool _rewriteUrls;
bool get _rewritePackageImports => _rewriteUrls || !_mangleFilenames;
/** Default prefix added to all filenames. */
static const String _DEFAULT_PREFIX = '_';
PathMapper(String baseDir, String outputDir, this.packageRoot,
bool forceMangle, this._rewriteUrls)
: _baseDir = baseDir,
_outputDir = outputDir,
_mangleFilenames = forceMangle || (baseDir == outputDir);
/** Add a prefix and [suffix] if [_mangleFilenames] is true */
String mangle(String name, String suffix, [bool forceSuffix = false]) =>
_mangleFilenames ? "$_DEFAULT_PREFIX$name$suffix"
: (forceSuffix ? "$name$suffix" : name);
/**
* Checks that `input.resolvedPath` is a valid input path. It must be in
* [_baseDir] and must not be in the [_outputDir]. If not, an error message
* is added to [messages].
*/
bool checkInputPath(UrlInfo input, Messages messages) {
if (_mangleFilenames) return true;
var canonicalized = path.normalize(input.resolvedPath);
var parentDir = '..${path.separator}';
if (!path.relative(canonicalized, from: _outputDir).startsWith(parentDir)) {
messages.error(
'The file ${input.resolvedPath} cannot be processed. '
'Files cannot be under the output folder (${_outputDir}).',
input.sourceSpan);
return false;
}
if (path.relative(canonicalized, from: _baseDir).startsWith(parentDir)) {
messages.error(
'The file ${input.resolvedPath} cannot be processed. '
'All processed files must be under the base folder (${_baseDir}), you'
' can specify the base folder using the --basedir flag.',
input.sourceSpan);
return false;
}
return true;
}
/**
* The path to the output file corresponding to [input], by adding
* [_DEFAULT_PREFIX] and a [suffix] to its file name.
*/
String outputPath(String input, String suffix, [bool forceSuffix = false]) =>
path.join(outputDirPath(input),
mangle(path.basename(input), suffix, forceSuffix));
/** The path to the output file corresponding to [info]. */
String outputLibraryPath(LibrarySummary lib) =>
path.join(outputDirPath(lib.dartCodeUrl.resolvedPath),
lib.outputFilename);
/** The corresponding output directory for [input]'s directory. */
String outputDirPath(String input) {
return _rewritePackages(path.normalize(
path.join(_outputDir, path.relative(
path.dirname(input), from: _baseDir))));
}
/**
* We deal with `packages/` directories in a very special way. We assume it
* points to resources loaded from other pub packages. If an output directory
* is specified, the compiler will create a packages symlink so that
* `package:` imports work.
*
* To make it possible to share components through pub, we allow using tags of
* the form `<link rel="import" href="packages/...">`, so that you can
* refer to components within the packages symlink. Regardless of whether an
* --out option was given to the compiler, we don't want to generate files
* inside `packages/` for those components. Instead we will generate such
* code in a special directory called `_from_packages/`.
*/
String _rewritePackages(String outputPath) {
// TODO(jmesserly): this should match against packageRoot instead.
if (!outputPath.contains('packages')) return outputPath;
if (!_rewritePackageImports) return outputPath;
var segments = path.split(outputPath);
return path.joinAll(
segments.map((s) => s == 'packages' ? '_from_packages' : s));
}
/**
* Returns a url to import/export the output library represented by [target]
* from the output library of [src]. In other words, a url to import or export
* `target.outputFilename` from `src.outputFilename`.
*/
String importUrlFor(LibrarySummary src, LibrarySummary target) {
if (!_rewritePackageImports &&
target.dartCodeUrl.url.startsWith('package:')) {
return pathToUrl(path.join(path.dirname(target.dartCodeUrl.url),
target.outputFilename));
}
var srcDir = path.dirname(src.dartCodeUrl.resolvedPath);
var relDir = path.relative(
path.dirname(target.dartCodeUrl.resolvedPath), from: srcDir);
return pathToUrl(_rewritePackages(path.normalize(
path.join(relDir, target.outputFilename))));
}
/**
* Transforms a [target] url seen in [src] (e.g. a Dart import, a .css href in
* an HTML file, etc) into a corresponding url from the output file associated
* with [src]. This will keep 'package:', 'dart:', path-absolute, and absolute
* urls intact, but it will fix relative paths to walk from the output
* directory back to the input directory. An exception will be thrown if
* [target] is not under [_baseDir].
*/
String transformUrl(String src, String target) {
var uri = Uri.parse(target);
if (uri.isAbsolute) return target;
if (!uri.scheme.isEmpty) return target;
if (!uri.host.isEmpty) return target;
if (uri.path.isEmpty) return target; // Implies standalone ? or # in URI.
if (path.isAbsolute(target)) return target;
return pathToUrl(path.normalize(path.relative(
path.join(path.dirname(src), target), from: outputDirPath(src))));
}
}
/**
* Returns a "mangled" name, with a prefix and [suffix] depending on the
* compiler's settings. [forceSuffix] causes [suffix] to be appended even if
* the compiler is not mangling names.
*/
typedef String NameMangler(String name, String suffix, [bool forceSuffix]);