| // 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. |
| |
| library pub.barback.transformer_config; |
| |
| import 'package:glob/glob.dart'; |
| import 'package:path/path.dart' as p; |
| import 'package:source_span/source_span.dart'; |
| import 'package:yaml/yaml.dart'; |
| |
| import 'transformer_id.dart'; |
| |
| /// The configuration for a transformer. |
| /// |
| /// This corresponds to the transformers listed in a pubspec, which have both an |
| /// [id] indicating the location of the transformer and configuration specific |
| /// to that use of the transformer. |
| class TransformerConfig { |
| /// The [id] of the transformer [this] is configuring. |
| final TransformerId id; |
| |
| /// The configuration to pass to the transformer. |
| /// |
| /// Any pub-specific configuration (i.e. keys starting with "$") will have |
| /// been stripped out of this and handled separately. This will be an empty |
| /// map if no configuration was provided. |
| final Map configuration; |
| |
| /// The source span from which this configuration was parsed. |
| final SourceSpan span; |
| |
| /// The primary input inclusions. |
| /// |
| /// Each inclusion is an asset path. If this set is non-empty, then *only* |
| /// matching assets are allowed as a primary input by this transformer. If |
| /// `null`, all assets are included. |
| /// |
| /// This is processed before [excludes]. If a transformer has both includes |
| /// and excludes, then the set of included assets is determined and assets |
| /// are excluded from that resulting set. |
| final Set<Glob> includes; |
| |
| /// The primary input exclusions. |
| /// |
| /// Any asset whose pach is in this is not allowed as a primary input by |
| /// this transformer. |
| /// |
| /// This is processed after [includes]. If a transformer has both includes |
| /// and excludes, then the set of included assets is determined and assets |
| /// are excluded from that resulting set. |
| final Set<Glob> excludes; |
| |
| /// Returns whether this config excludes certain asset ids from being |
| /// processed. |
| bool get hasExclusions => includes != null || excludes != null; |
| |
| /// Returns whether this transformer might transform a file that's visible to |
| /// the package's dependers. |
| bool get canTransformPublicFiles { |
| if (includes == null) return true; |
| return includes.any((glob) { |
| // Check whether the first path component of the glob is "lib", "bin", or |
| // contains wildcards that may cause it to match "lib" or "bin". |
| var first = p.posix.split(glob.toString()).first; |
| if (first.contains('{') || |
| first.contains('*') || |
| first.contains('[') || |
| first.contains('?')) { |
| return true; |
| } |
| |
| return first == 'lib' || first == 'bin'; |
| }); |
| } |
| |
| /// Parses [identifier] as a [TransformerId] with [configuration]. |
| /// |
| /// [identifierSpan] is the source span for [identifier]. |
| factory TransformerConfig.parse(String identifier, SourceSpan identifierSpan, |
| YamlMap configuration) => |
| new TransformerConfig( |
| new TransformerId.parse(identifier, identifierSpan), |
| configuration); |
| |
| factory TransformerConfig(TransformerId id, YamlMap configurationNode) { |
| parseField(key) { |
| if (!configurationNode.containsKey(key)) return null; |
| var fieldNode = configurationNode.nodes[key]; |
| var field = fieldNode.value; |
| |
| if (field is String) { |
| return new Set.from([new Glob(field, context: p.url, recursive: true)]); |
| } |
| |
| if (field is! List) { |
| throw new SourceSpanFormatException( |
| '"$key" field must be a string or list.', |
| fieldNode.span); |
| } |
| |
| return new Set.from(field.nodes.map((node) { |
| if (node.value is String) { |
| return new Glob(node.value, context: p.url, recursive: true); |
| } |
| |
| throw new SourceSpanFormatException( |
| '"$key" field may contain only strings.', |
| node.span); |
| })); |
| } |
| |
| var includes = null; |
| var excludes = null; |
| |
| var configuration; |
| var span; |
| if (configurationNode == null) { |
| configuration = {}; |
| span = id.span; |
| } else { |
| // Don't write to the immutable YAML map. |
| configuration = new Map.from(configurationNode); |
| span = configurationNode.span; |
| |
| // Pull out the exclusions/inclusions. |
| includes = parseField("\$include"); |
| configuration.remove("\$include"); |
| excludes = parseField("\$exclude"); |
| configuration.remove("\$exclude"); |
| |
| // All other keys starting with "$" are unexpected. |
| for (var key in configuration.keys) { |
| if (key is! String || !key.startsWith(r'$')) continue; |
| throw new SourceSpanFormatException( |
| 'Unknown reserved field.', |
| configurationNode.nodes[key].span); |
| } |
| } |
| |
| return new TransformerConfig._(id, configuration, span, includes, excludes); |
| } |
| |
| TransformerConfig._(this.id, this.configuration, this.span, this.includes, |
| this.excludes); |
| |
| String toString() => id.toString(); |
| |
| /// Returns whether the include/exclude rules allow the transformer to run on |
| /// [pathWithinPackage]. |
| /// |
| /// [pathWithinPackage] must be a URL-style path relative to the containing |
| /// package's root directory. |
| bool canTransform(String pathWithinPackage) { |
| if (excludes != null) { |
| // If there are any excludes, it must not match any of them. |
| for (var exclude in excludes) { |
| if (exclude.matches(pathWithinPackage)) return false; |
| } |
| } |
| |
| // If there are any includes, it must match one of them. |
| return includes == null || |
| includes.any((include) => include.matches(pathWithinPackage)); |
| } |
| } |