| // 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]); |