├── .gitignore ├── .test_config ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── codereview.settings ├── lib ├── build │ ├── initializer_plugin.dart │ └── loader_replacer.dart ├── initialize.dart ├── src │ ├── init_method.dart │ ├── initialize_tracker.dart │ ├── initializer.dart │ ├── mirror_loader.dart │ └── static_loader.dart └── transformer.dart ├── pubspec.yaml ├── test ├── common.dart ├── cycle_a.dart ├── cycle_b.dart ├── deferred_library_test.dart ├── deferred_library_test.html ├── foo.dart ├── foo │ └── bar.dart ├── init_method_test.dart ├── init_method_test.html ├── initializer_custom_filter_test.dart ├── initializer_custom_filter_test.html ├── initializer_cycle_error_test.dart ├── initializer_cycle_error_test.html ├── initializer_from_test.dart ├── initializer_from_test.html ├── initializer_parts_test.dart ├── initializer_parts_test.html ├── initializer_super_test.dart ├── initializer_super_test.html ├── initializer_test.dart ├── initializer_test.html ├── initializer_type_filter_test.dart ├── initializer_type_filter_test.html ├── parts │ ├── bar.dart │ └── foo.dart └── transformer_test.dart ├── test_package ├── README.md ├── lib │ ├── bar.dart │ └── foo.dart └── pubspec.yaml └── tool ├── all_tests.sh └── rename_build_outputs.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Pub files. 2 | /build/ 3 | packages 4 | .pub 5 | pubspec.lock 6 | .packages 7 | 8 | # Files created by dart2js. 9 | *.dart.js 10 | *.dart.precompiled.js 11 | *.js_ 12 | *.js.deps 13 | *.js.map 14 | *.sw? 15 | 16 | # Intellij files. 17 | .idea/ 18 | -------------------------------------------------------------------------------- /.test_config: -------------------------------------------------------------------------------- 1 | { 2 | "test_package": true 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: dart 2 | 3 | dart: 4 | - dev 5 | - stable 6 | 7 | script: ./tool/all_tests.sh 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.6.2+7 2 | 3 | * Strong mode fixes. 4 | 5 | ## 0.6.2+6 6 | 7 | * Small bug fixes for https://github.com/dart-lang/web-components/issues/54. 8 | 9 | ## 0.6.2+5 10 | 11 | * Update analyzer minimum version to `0.27.2`. 12 | 13 | ## 0.6.2+4 14 | 15 | * Stop using deprecated analyzer apis. 16 | 17 | ## 0.6.2+3 18 | 19 | * Update code_transformers, analyzer, and html version constraints. 20 | 21 | ## 0.6.2+2 22 | 23 | * Update `transformer_test` dep to `0.2.x`. 24 | 25 | ## 0.6.2+1 26 | 27 | * Add support for code_transformers `0.4.x`. 28 | 29 | ## 0.6.2 30 | 31 | * Update analyzer to `>=0.27.0 <0.28.0`. 32 | 33 | ## 0.6.1+2 34 | 35 | * Update analyzer to `<0.27.0` and dart_style to `<0.3.0`. 36 | 37 | ## 0.6.1+1 38 | 39 | * Update to work with deferred loaded libararies in reflective mode, (but not 40 | yet in the transformer). 41 | 42 | ## 0.6.1 43 | 44 | * Update to analyzer `<0.26.0`. 45 | 46 | ## 0.6.0+5 47 | 48 | * Fix bootstrap files to return the result of the original main. 49 | 50 | ## 0.6.0+4 51 | 52 | * Switch `html5lib` package dependency to `html`. 53 | 54 | ## 0.6.0+3 55 | 56 | * Make sure to always use the canonical libraries and super declarations in 57 | development mode. This eliminates an uncommon issue where a single initializer 58 | could be ran more than once. 59 | 60 | ## 0.6.0+2 61 | 62 | * Private identifiers will now be evaluated and inlined into the bootstrap file 63 | by the transformer, [29](https://github.com/dart-lang/initialize/issues/29). 64 | 65 | ## 0.6.0+1 66 | 67 | * Fix for `LibraryIdentifier` paths when initializer starts from inline scripts 68 | inside of subfolders. 69 | 70 | ## 0.6.0 71 | 72 | * Added the `from` option to `run`. This should be a `Uri` pointing to a library 73 | in the mirror system, and throws if not in mirrors mode. This can be used to 74 | run initializers in a custom order at development time. 75 | * This package no longer tries to handle initializing scripts found in html 76 | imports. If you need this feature please use `initWebComponents` from the 77 | `web_components` package. 78 | 79 | ## 0.5.1+8 80 | 81 | * Make sure to crawl the entire supertype chain for annotations, and run them 82 | in reverse order. 83 | 84 | ## 0.5.1+7 85 | 86 | * Change to source order-crawling of directives instead of alphabetical. The one 87 | exception is for `part` directives, those are still crawled in alphabetical 88 | order since we can't currently get the original source order from the mirror 89 | system. 90 | 91 | ## 0.5.1+6 92 | 93 | * Fix some analyzer warnings. 94 | 95 | ## 0.5.1+5 96 | 97 | * Fix an issue where for certain programs the transformer could fail, 98 | [33](https://github.com/dart-lang/polymer-dart/issues/33). 99 | 100 | 101 | ## 0.5.1+4 102 | 103 | * Update to use mock dart sdk from `code_transformers` and update the `analyzer` 104 | and `code_transformers` dependencies. 105 | 106 | ## 0.5.1+3 107 | 108 | * Fix up mirror based import crawling so it detects dartium and only crawl all 109 | libraries in the mirror system in that case. 110 | 111 | ## 0.5.1+2 112 | 113 | * Fix handling of exported libraries. Specifically, annotations on exported 114 | libraries will now be reached and imports in exported libraries will now be 115 | reached. 116 | * Add support for scripts living in html imports without adding an html 117 | dependency by crawling all libraries in the mirror system in reverse order, 118 | instead of just the root one. 119 | 120 | ## 0.5.1+1 121 | 122 | * Make sure to always use `path.url` in the transformer. 123 | 124 | ## 0.5.1 125 | 126 | * Added support for more types of expressions in constructor annotations. More 127 | specifically, any const expressions that evaluate to a `String`, `int`, 128 | `double`, or `bool` are now allowed. The evaluated value is what will be inlined 129 | in the bootstrap file in this case. 130 | 131 | 132 | ## 0.5.0 133 | 134 | * The `InitializePluginTransformer` is gone in favor of a new 135 | `InitializerPlugin` class which you can pass a list of to the 136 | `InitializeTransformer`. These plugins now have access to the fully resolved ast 137 | nodes and can directly control what is output in the bootstrap file. 138 | 139 | ## 0.4.0 140 | 141 | Lots of transformer updates: 142 | 143 | * The `new_entry_point` option is gone. The bootstrapped file will now always 144 | just be the original name but `.dart` will be replaced with `.initialize.dart`. 145 | * The `html_entry_point` option is gone, and the file extension is now used to 146 | detect if it is an html or dart file. You should no longer list the dart file 147 | contained in the html file. Effectively resolves 148 | [13](https://github.com/dart-lang/initialize/issues/13). 149 | * The `entry_point` option has been renamed `entry_points` and now accepts 150 | either a single file path or list of file paths. Additionally, it now supports 151 | Glob syntax so many files can be selected at once. Resolves 152 | [19](https://github.com/dart-lang/initialize/issues/19). 153 | 154 | ## 0.3.1 155 | 156 | * Added `InitializePluginTransformer` class in `plugin_transformer.dart` which 157 | provides a base transformer class which can be extended to perform custom 158 | transformations for annotations. These transformers should be included after the 159 | main `initialize` transformer and work by parsing the bootstrap file so the 160 | program doesn't need to be re-analyzed. 161 | 162 | ## 0.3.0 163 | 164 | * Library initializers now pass a `LibraryIdentifier` to `initialize` instead of 165 | just a `Symbol`. This provides the package and path to the library in addition 166 | to the symbol so that paths can be normalized. 167 | 168 | ## 0.2.0 169 | 170 | * `entryPoint` and `newEntryPoint` transformer options were renamed to 171 | `entry_point` and `new_entry_pont`. 172 | 173 | * Added `html_entry_point` option to the transformer. This will search that file 174 | for any script tag whose src is `entry_point` and rewrite it to point at the 175 | bootstrapped file `new_entry_point`. 176 | 177 | * Top level properties and static class properties are now supported in 178 | initializer constructors, as well as List and Map literals, 179 | [5](https://github.com/dart-lang/initialize/issues/5). 180 | 181 | ## 0.1.0+1 182 | 183 | Quick fix for the transformer on windows. 184 | 185 | ## 0.1.0 186 | 187 | Initial beta release. There is one notable missing feature in the release 188 | regarding constructor arguments, see 189 | [5](https://github.com/dart-lang/initialize/issues/5). 190 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015, the Dart project authors. All rights reserved. 2 | Redistribution and use in source and binary forms, with or without 3 | modification, are permitted provided that the following conditions are 4 | met: 5 | 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above 9 | copyright notice, this list of conditions and the following 10 | disclaimer in the documentation and/or other materials provided 11 | with the distribution. 12 | * Neither the name of Google Inc. nor the names of its 13 | contributors may be used to endorse or promote products derived 14 | from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Initialize [![Build Status](https://travis-ci.org/dart-lang/initialize.svg?branch=master)](https://travis-ci.org/dart-lang/initialize) 2 | 3 | This package provides a common interface for initialization annotations on top 4 | level methods, classes, and libraries. The interface looks like this: 5 | 6 | ```dart 7 | abstract class Initializer { 8 | dynamic initialize(T target); 9 | } 10 | ``` 11 | 12 | The `initialize` method will be called once for each annotation. The type `T` is 13 | determined by what was annotated. For libraries it will be a `LibraryIdentifier` 14 | representing that library, for a class it will be the `Type` representing that 15 | class, and for a top level method it will be the `Function` object representing 16 | that method. 17 | 18 | If a future is returned from the initialize method, it will wait until the future 19 | completes before running the next initializer. 20 | 21 | ## Usage 22 | 23 | ### @initMethod 24 | 25 | There is one initializer which comes with this package, `@initMethod`. Annotate 26 | any top level function with this and it will be invoked automatically. For 27 | example, the program below will print `hello`: 28 | 29 | ```dart 30 | import 'package:initialize/initialize.dart'; 31 | 32 | @initMethod 33 | printHello() => print('hello'); 34 | 35 | main() => run(); 36 | ``` 37 | 38 | ### Running the initializers 39 | 40 | In order to run all the initializers, you need to import 41 | `package:initialize/initialize.dart` and invoke the `run` method. This should 42 | typically be the first thing to happen in your main. That method returns a Future, 43 | so you should put the remainder of your program inside the chained then call. 44 | 45 | ```dart 46 | import 'package:initialize/initialize.dart'; 47 | 48 | main() { 49 | run().then((_) { 50 | print('hello world!'); 51 | }); 52 | } 53 | ``` 54 | 55 | ## Transformer 56 | 57 | During development a mirror based system is used to find and run the initializers, 58 | but for deployment there is a transformer which can replace that with a static list 59 | of initializers to be ran. 60 | 61 | This will create a new entry point which bootstraps your existing app, this will 62 | have the same file name except `.dart` with be replaced with `.initialize.dart`. 63 | If you supply an html file to `entry_points` then it will bootstrap the dart 64 | script tag on that page and replace the `src` attribute to with the new 65 | bootstrap file. 66 | 67 | Below is an example pubspec with the transformer: 68 | 69 | name: my_app 70 | dependencies: 71 | initialize: any 72 | transformers: 73 | - initialize: 74 | entry_points: web/index.html 75 | 76 | ## Creating your own initializer 77 | 78 | Lets look at a slightly simplified version of the `@initMethod` class: 79 | 80 | ```dart 81 | class InitMethod implements Initializer { 82 | const InitMethod(); 83 | 84 | @override 85 | initialize(Function method) => method(); 86 | } 87 | ``` 88 | 89 | You would now be able to add `@InitMethod()` in front of any function and it 90 | will be automatically invoked when the user calls `run()`. 91 | 92 | For classes which are stateless, you can usually just have a single const 93 | instance, and that is how the actual InitMethod implementation works. Simply add 94 | something like the following: 95 | 96 | ```dart 97 | const initMethod = const InitMethod(); 98 | ``` 99 | 100 | Now when people use the annotation, it just looks like `@initMethod` without any 101 | parenthesis, and its a bit more efficient since there is a single instance. You 102 | can also make your class private to force users into using the static instance. 103 | 104 | ## Creating custom transformer plugins 105 | 106 | It is possible to create a custom plugin for the initialize transformer which 107 | allows you to have full control over what happens to your annotations at compile 108 | time. Implement `InitializerPlugin` class and pass that in to the 109 | `InitializeTransformer` to make it take effect. 110 | 111 | You will need to be familiar with the `analyzer` package in order to write these 112 | plugins, but they can be extremely powerful. See the `DefaultInitializerPlugin` 113 | in `lib/build/initializer_plugin.dart` as a reference. Chances are you may want 114 | to extend that class in order to get a lot of the default functionality. 115 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | strong-mode: true 3 | -------------------------------------------------------------------------------- /codereview.settings: -------------------------------------------------------------------------------- 1 | CODE_REVIEW_SERVER: http://codereview.chromium.org/ 2 | VIEW_VC: https://github.com/dart-lang/initialize/commit/ 3 | CC_LIST: reviews@dartlang.org 4 | -------------------------------------------------------------------------------- /lib/build/initializer_plugin.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | library initialize.build.initializer_plugin; 5 | 6 | import 'package:analyzer/dart/ast/ast.dart'; 7 | import 'package:analyzer/dart/element/element.dart'; 8 | import 'package:analyzer/src/generated/constant.dart'; 9 | import 'package:barback/barback.dart'; 10 | import 'package:code_transformers/resolver.dart'; 11 | import 'package:initialize/transformer.dart'; 12 | import 'package:path/path.dart' as path; 13 | 14 | /// A plug which allows an initializer to write out an [InitEntry] given some 15 | /// [InitializerData] from an annotation that was found. 16 | abstract class InitializerPlugin { 17 | /// Whether or not this plugin should be applied to an [Initializer] given 18 | /// some [InitializerData]. If [true] is returned then this plugin will take 19 | /// ownership of this [InitializerData] and no subsequent plugins will have 20 | /// an opportunity to access it. 21 | bool shouldApply(InitializerPluginData data); 22 | 23 | /// Returns a [String] or [null]. The [String] should represent dart code 24 | /// which creates a new [InitEntry] and that entry is added to the static 25 | /// initializers list. If [null] is returned then no entry is added at all for 26 | /// this [InitializerData]. 27 | String apply(InitializerPluginData data); 28 | } 29 | 30 | /// A class which wraps all the default data passed to an [InitializerPlugin] 31 | /// for each annotation. 32 | class InitializerPluginData { 33 | final InitializerData initializer; 34 | final AssetId bootstrapId; 35 | final Map libraryPrefixes; 36 | final TransformLogger logger; 37 | final Resolver resolver; 38 | InitializerPluginData(this.initializer, this.bootstrapId, 39 | this.libraryPrefixes, this.resolver, this.logger); 40 | } 41 | 42 | /// The basic [InitializerPlugin]. This generates a new [InitEntry] to be added 43 | /// to the static initializers list, and applies to every item it sees. 44 | class DefaultInitializerPlugin implements InitializerPlugin { 45 | const DefaultInitializerPlugin(); 46 | 47 | /// Applies to everything. Put other plugins before this one to override this 48 | /// behaviour. 49 | bool shouldApply(InitializerPluginData data) => true; 50 | 51 | /// Creates a normal [InitEntry] string. 52 | String apply(InitializerPluginData pluginData) { 53 | var target = buildTarget(pluginData); 54 | var meta = buildMeta(pluginData); 55 | return 'new InitEntry($meta, $target)'; 56 | } 57 | 58 | /// Builds a [String] representing the meta of an [InitEntry] given an 59 | /// [ElementAnnotation] that was found. 60 | String buildMeta(InitializerPluginData pluginData) { 61 | var logger = pluginData.logger; 62 | var elementAnnotation = pluginData.initializer.annotationElement; 63 | var elementAnnotationElement = elementAnnotation.element; 64 | if (elementAnnotationElement is ConstructorElement) { 65 | return buildConstructorMeta(elementAnnotation, pluginData); 66 | } else if (elementAnnotationElement is PropertyAccessorElement) { 67 | return buildPropertyMeta(elementAnnotation, pluginData); 68 | } else { 69 | logger.error('Unsupported annotation type. Only constructors and ' 70 | 'properties are supported as initializers.'); 71 | } 72 | return null; 73 | } 74 | 75 | /// Builds a [String] representing the meta of an [InitEntry] given an 76 | /// [ElementAnnotation] whose element was a [ConstructorElement]. 77 | String buildConstructorMeta( 78 | ElementAnnotation elementAnnotation, InitializerPluginData pluginData) { 79 | var logger = pluginData.logger; 80 | var node = pluginData.initializer.targetNode; 81 | var metaPrefix = 82 | pluginData.libraryPrefixes[elementAnnotation.element.library]; 83 | 84 | var annotation = pluginData.initializer.annotationNode; 85 | if (annotation == null) { 86 | logger.error( 87 | 'Initializer annotations are only supported on libraries, classes, ' 88 | 'and top level methods. Found $node.'); 89 | } 90 | var clazz = annotation.name; 91 | var constructor = annotation.constructorName == null 92 | ? '' 93 | : '.${annotation.constructorName}'; 94 | var args = buildArgumentList(annotation.arguments, pluginData); 95 | return 'const $metaPrefix.${clazz}$constructor$args'; 96 | } 97 | 98 | /// Builds a [String] representing the meta of an [InitEntry] given an 99 | /// [ElementAnnotation] whose element was a [PropertyAccessorElement]. 100 | String buildPropertyMeta( 101 | ElementAnnotation annotation, InitializerPluginData pluginData) { 102 | var metaPrefix = pluginData.libraryPrefixes[annotation.element.library]; 103 | return '$metaPrefix.${annotation.element.name}'; 104 | } 105 | 106 | /// Builds a [String] for the target of an [InitEntry] given an [Element] that 107 | /// was annotated. 108 | String buildTarget(InitializerPluginData pluginData) { 109 | var element = pluginData.initializer.targetElement; 110 | var logger = pluginData.logger; 111 | if (element is LibraryElement) { 112 | return buildLibraryTarget(element, pluginData); 113 | } else if (element is ClassElement) { 114 | return buildClassTarget(element, pluginData); 115 | } else if (element is FunctionElement) { 116 | return buildFunctionTarget(element, pluginData); 117 | } else { 118 | logger.error('Initializers can only be applied to top level functions, ' 119 | 'libraries, and classes.'); 120 | } 121 | return null; 122 | } 123 | 124 | /// Builds a [String] for the target of an [InitEntry] given [element] which 125 | /// is an annotated class. 126 | String buildClassTarget( 127 | ClassElement element, InitializerPluginData pluginData) => 128 | buildSimpleTarget(element, pluginData); 129 | 130 | /// Builds a [String] for the target of an [InitEntry] given [element] which 131 | /// is an annotated function. 132 | String buildFunctionTarget( 133 | FunctionElement element, InitializerPluginData pluginData) => 134 | buildSimpleTarget(element, pluginData); 135 | 136 | /// Builds a [String] for the target of an [InitEntry] for a simple [Element]. 137 | /// This is just the library prefix followed by the element name. 138 | String buildSimpleTarget(Element element, InitializerPluginData pluginData) => 139 | '${pluginData.libraryPrefixes[element.library]}.${element.name}'; 140 | 141 | /// Builds a [String] for the target of an [InitEntry] given [element] which 142 | /// is an annotated library. 143 | String buildLibraryTarget( 144 | LibraryElement element, InitializerPluginData pluginData) { 145 | var bootstrapId = pluginData.bootstrapId; 146 | var logger = pluginData.logger; 147 | var segments = element.source.uri.pathSegments; 148 | var package = segments[0]; 149 | var libraryPath; 150 | var packageString; 151 | if (bootstrapId.package == package && 152 | bootstrapId.path.startsWith('${segments[1]}/')) { 153 | // reset `package` to null, we will do a relative path in this case. 154 | packageString = 'null'; 155 | libraryPath = path.url.relative( 156 | path.url.joinAll(segments.getRange(1, segments.length)), 157 | from: path.url.dirname(path.url.join(bootstrapId.path))); 158 | } else if (segments[1] == 'lib') { 159 | packageString = "'$package'"; 160 | libraryPath = path.url.joinAll(segments.getRange(2, segments.length)); 161 | } else { 162 | logger.error('Unable to import `${element.source.uri.path}` from ' 163 | '${bootstrapId.path}.'); 164 | } 165 | 166 | return "const LibraryIdentifier" 167 | "(#${element.name}, $packageString, '$libraryPath')"; 168 | } 169 | 170 | /// Builds a [String] representing an [ArgumentList] taking into account the 171 | /// [libraryPrefixes] from [pluginData]. 172 | String buildArgumentList( 173 | ArgumentList args, InitializerPluginData pluginData) { 174 | var buffer = new StringBuffer(); 175 | buffer.write('('); 176 | var first = true; 177 | for (var arg in args.arguments) { 178 | if (!first) buffer.write(', '); 179 | first = false; 180 | 181 | Expression expression; 182 | if (arg is NamedExpression) { 183 | buffer.write('${arg.name.label.name}: '); 184 | expression = arg.expression; 185 | } else { 186 | expression = arg; 187 | } 188 | 189 | buffer.write(buildExpression(expression, pluginData)); 190 | } 191 | buffer.write(')'); 192 | return buffer.toString(); 193 | } 194 | 195 | /// Builds a [String] representing [expression] taking into account the 196 | /// [libraryPrefixes] from [pluginData]. 197 | String buildExpression( 198 | Expression expression, InitializerPluginData pluginData) { 199 | var logger = pluginData.logger; 200 | var libraryPrefixes = pluginData.libraryPrefixes; 201 | var buffer = new StringBuffer(); 202 | if (expression is StringLiteral && expression.stringValue != null) { 203 | buffer.write(_stringValue(expression.stringValue)); 204 | } else if (expression is BooleanLiteral || 205 | expression is DoubleLiteral || 206 | expression is IntegerLiteral || 207 | expression is NullLiteral) { 208 | buffer.write('${expression}'); 209 | } else if (expression is ListLiteral) { 210 | buffer.write('const ['); 211 | var first = true; 212 | for (Expression listExpression in expression.elements) { 213 | if (!first) buffer.write(', '); 214 | first = false; 215 | buffer.write(buildExpression(listExpression, pluginData)); 216 | } 217 | buffer.write(']'); 218 | } else if (expression is MapLiteral) { 219 | buffer.write('const {'); 220 | var first = true; 221 | for (MapLiteralEntry entry in expression.entries) { 222 | if (!first) buffer.write(', '); 223 | first = false; 224 | buffer.write(buildExpression(entry.key, pluginData)); 225 | buffer.write(': '); 226 | buffer.write(buildExpression(entry.value, pluginData)); 227 | } 228 | buffer.write('}'); 229 | } else if (expression is Identifier) { 230 | var element = expression.bestElement; 231 | if (element == null) { 232 | logger.error('Unable to get `bestElement` for expression: $expression'); 233 | } else if (!element.isPublic) { 234 | // Inline the evaluated value of private identifiers. 235 | buffer.write(_evaluateExpression(expression, pluginData)); 236 | } else { 237 | libraryPrefixes.putIfAbsent( 238 | element.library, () => 'i${libraryPrefixes.length}'); 239 | 240 | buffer.write('${libraryPrefixes[element.library]}.'); 241 | if (element is ClassElement) { 242 | buffer.write(element.name); 243 | } else if (element is PropertyAccessorElement) { 244 | var variable = element.variable; 245 | if (variable is FieldElement) { 246 | buffer.write('${variable.enclosingElement.name}.'); 247 | } 248 | buffer.write('${variable.name}'); 249 | } else { 250 | logger.error('Unsupported argument to initializer constructor.'); 251 | } 252 | } 253 | } else if (expression is PropertyAccess) { 254 | buffer.write(buildExpression(expression.target, pluginData)); 255 | buffer.write('.${expression.propertyName}'); 256 | } else if (expression is InstanceCreationExpression) { 257 | logger.error('Unsupported expression in initializer, found $expression. ' 258 | 'Instance creation expressions are not supported (yet). Instead, ' 259 | 'please assign it to a const variable and use that instead.'); 260 | } else { 261 | buffer.write(_evaluateExpression(expression, pluginData)); 262 | } 263 | return buffer.toString(); 264 | } 265 | 266 | _evaluateExpression(Expression expression, InitializerPluginData pluginData) { 267 | var logger = pluginData.logger; 268 | var result = pluginData.resolver.evaluateConstant( 269 | pluginData.initializer.targetElement.library, expression); 270 | if (!result.isValid) { 271 | logger.error('Invalid expression in initializer, found $expression. ' 272 | 'And got the following errors: ${result.errors}.'); 273 | return null; 274 | } 275 | 276 | var value = _getValue(result.value); 277 | 278 | if (value == null) { 279 | logger.error('Unsupported expression in initializer, found ' 280 | '$expression. Please file a bug at ' 281 | 'https://github.com/dart-lang/initialize/issues'); 282 | } 283 | 284 | if (value is String) value = _stringValue(value); 285 | 286 | return value; 287 | } 288 | 289 | // Returns an expression for a string value. Wraps it in single quotes and 290 | // escapes existing single quotes and escapes. 291 | _stringValue(String value) { 292 | value = value.replaceAll(r'\', r'\\').replaceAll(r"'", r"\'"); 293 | return "'$value'"; 294 | } 295 | 296 | // Gets an actual value for a [DartObject]. 297 | _getValue(DartObject object) { 298 | if (object == null) return null; 299 | var value = object.toBoolValue() ?? 300 | object.toDoubleValue() ?? 301 | object.toIntValue() ?? 302 | object.toStringValue(); 303 | if (value == null) { 304 | List list = object.toListValue(); 305 | if (list != null) { 306 | return list.map((DartObject element) => _getValue(element)).toList(); 307 | } 308 | Map map = object.toMapValue(); 309 | if (map != null) { 310 | Map result = {}; 311 | map.forEach((DartObject key, DartObject value) { 312 | dynamic mappedKey = _getValue(key); 313 | if (mappedKey != null) { 314 | result[mappedKey] = _getValue(value); 315 | } 316 | }); 317 | return result; 318 | } 319 | } 320 | return value; 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /lib/build/loader_replacer.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | library initialize.build.loader_replacer; 5 | 6 | import 'dart:async'; 7 | import 'package:barback/barback.dart'; 8 | 9 | /// Removes `mirror_loader.dart` and replaces it with `static_loader.dart`. 10 | class LoaderReplacer extends Transformer { 11 | LoaderReplacer.asPlugin(); 12 | 13 | bool isPrimary(AssetId id) => 14 | id.package == 'initialize' && id.path == 'lib/initialize.dart'; 15 | 16 | Future apply(Transform transform) { 17 | var id = transform.primaryInput.id; 18 | return transform.primaryInput.readAsString().then((code) { 19 | transform.addOutput(new Asset.fromString( 20 | id, code.replaceFirst('mirror_loader.dart', 'static_loader.dart'))); 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/initialize.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | library initialize; 5 | 6 | // The `loader_replacer` transformer will replace this with a static_loader. 7 | import 'src/mirror_loader.dart' as loader; 8 | import 'dart:async'; 9 | import 'dart:collection'; 10 | 11 | part 'src/init_method.dart'; 12 | part 'src/initializer.dart'; 13 | 14 | /// Top level function which crawls the dependency graph and runs initializers. 15 | /// If [typeFilter] and/or [customFilter] are supplied then only those types of 16 | /// annotations will be parsed. If both filters are supplied they are treated 17 | /// as an AND. 18 | /// 19 | /// If [from] is supplied then initializers will be found starting from the 20 | /// library at the supplied uri. 21 | /// 22 | /// **Warning**: Do not use [from] directly in your code unless you are building 23 | /// a framework that will use a transformer to remove this argument later. This 24 | /// parameter is supported in Dartium, but [run] will throw if you use the 25 | /// argument after building an application with `pub build` or `pub serve`. 26 | Future run({List typeFilter, InitializerFilter customFilter, Uri from}) { 27 | return _runInitQueue(loader.loadInitializers( 28 | typeFilter: typeFilter, customFilter: customFilter, from: from)); 29 | } 30 | 31 | Future _runInitQueue(Queue initializers) { 32 | if (initializers.isEmpty) return new Future.value(null); 33 | 34 | var initializer = initializers.removeFirst(); 35 | var val = initializer(); 36 | if (val is! Future) val = new Future.value(val); 37 | 38 | return val.then((_) => _runInitQueue(initializers)); 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/init_method.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | part of initialize; 5 | 6 | /// Metadata used to label static or top-level methods that are called 7 | /// automatically when calling static_init.run(). This class is private because 8 | /// it shouldn't be used directly in annotations, instead use the `initMethod` 9 | /// singleton below. 10 | typedef dynamic _ZeroArg(); 11 | 12 | class _InitMethod implements Initializer<_ZeroArg> { 13 | const _InitMethod(); 14 | 15 | @override 16 | initialize(_ZeroArg method) => method(); 17 | } 18 | 19 | /// We only ever need one instance of the `_InitMethod` class, this is it. 20 | const initMethod = const _InitMethod(); 21 | -------------------------------------------------------------------------------- /lib/src/initialize_tracker.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | library initialize.test.initialize_tracker; 5 | 6 | import 'package:initialize/initialize.dart'; 7 | 8 | // Initializer that just saves everything it sees. 9 | class InitializeTracker implements Initializer { 10 | static final List seen = []; 11 | 12 | const InitializeTracker(); 13 | 14 | @override 15 | void initialize(value) => seen.add(value); 16 | } 17 | 18 | const initializeTracker = const InitializeTracker(); 19 | -------------------------------------------------------------------------------- /lib/src/initializer.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | part of initialize; 5 | 6 | /// Implement this class to create your own initializer. 7 | /// 8 | /// Hello world example: 9 | /// 10 | /// class Print implements Initializer { 11 | /// final String message; 12 | /// const Print(this.message); 13 | /// 14 | /// @override 15 | /// initialize(Type t) => print('$t says `$message`'); 16 | /// } 17 | /// 18 | /// @Print('hello world!') 19 | /// class Foo {} 20 | /// 21 | /// Call [run] from your main and this will print 'Foo says `hello world!`' 22 | /// 23 | abstract class Initializer { 24 | dynamic initialize(T target); 25 | } 26 | 27 | /// Typedef for a custom filter function. 28 | typedef bool InitializerFilter(Initializer initializer); 29 | 30 | /// When annotating libraries, this is passed to the initializer. 31 | class LibraryIdentifier { 32 | // The qualified name of the library. 33 | final Symbol name; 34 | 35 | // The package this library lives in. May be null if its the same as the root 36 | // package. 37 | final String package; 38 | 39 | // The path to the library. 40 | final String path; 41 | 42 | const LibraryIdentifier(this.name, this.package, this.path); 43 | 44 | bool operator ==(dynamic other) => 45 | other is LibraryIdentifier && 46 | name == other.name && 47 | package == other.package && 48 | path == other.path; 49 | 50 | String toString() => '$name: $package:$path'; 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/mirror_loader.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | library initialize.mirror_loader; 5 | 6 | import 'dart:collection' show Queue; 7 | import 'dart:mirrors'; 8 | import 'package:path/path.dart' as path; 9 | import 'package:initialize/initialize.dart'; 10 | 11 | final LibraryMirror _root = currentMirrorSystem().isolate.rootLibrary; 12 | final Map _libs = currentMirrorSystem().libraries; 13 | 14 | Queue loadInitializers( 15 | {List typeFilter, InitializerFilter customFilter, Uri from}) { 16 | return new InitializationCrawler(typeFilter, customFilter, from: from).run(); 17 | } 18 | 19 | // Crawls a library and all its dependencies for `Initializer` annotations using 20 | // mirrors 21 | class InitializationCrawler { 22 | // Set of all visited annotations, keys are the declarations that were 23 | // annotated, values are the annotations that have been processed. 24 | static final _annotationsFound = 25 | new Map>(); 26 | 27 | // If non-null, then only these annotations should be processed. 28 | final List typeFilter; 29 | 30 | // If non-null, then only annotations which return true when passed to this 31 | // function will be processed. 32 | final InitializerFilter customFilter; 33 | 34 | /// The library to start crawling from. 35 | final LibraryMirror _rootLibrary; 36 | 37 | /// Note: The [from] argument is only supported in the mirror_loader.dart. It 38 | /// is not supported statically. 39 | InitializationCrawler(this.typeFilter, this.customFilter, {Uri from}) 40 | : _rootLibrary = from == null ? _root : _libs[from] { 41 | if (_rootLibrary == null) throw 'Unable to find library at $from.'; 42 | } 43 | 44 | // The primary function in this class, invoke it to crawl and collect all the 45 | // annotations into a queue of init functions. 46 | Queue run() { 47 | var librariesSeen = new Set(); 48 | var queue = new Queue(); 49 | 50 | _readLibraryDeclarations(_rootLibrary, librariesSeen, queue); 51 | return queue; 52 | } 53 | 54 | /// Returns the canonical [LibraryMirror] for a given [LibraryMirror]. This 55 | /// is defined as the one loaded from a `package:` url if available, otherwise 56 | /// it is just [lib]. 57 | LibraryMirror _canonicalLib(LibraryMirror lib) { 58 | var uri = lib.uri; 59 | if (_isHttpStylePackageUrl(uri)) { 60 | var packageUri = _packageUriFor(uri); 61 | if (_libs.containsKey(packageUri)) return _libs[packageUri]; 62 | } 63 | return lib; 64 | } 65 | 66 | /// Returns the canonical [ClassMirror] for a given [ClassMirror]. This is 67 | /// defined as the one that appears in the canonical owner [LibararyMirror]. 68 | ClassMirror _canonicalClassDeclaration(ClassMirror declaration) => 69 | _canonicalLib(declaration.owner).declarations[declaration.simpleName]; 70 | 71 | /// Whether [uri] is an http URI that contains a 'packages' segment, and 72 | /// therefore could be converted into a 'package:' URI. 73 | bool _isHttpStylePackageUrl(Uri uri) { 74 | var uriPath = uri.path; 75 | return uri.scheme == _root.uri.scheme && 76 | // Don't process cross-domain uris. 77 | uri.authority == _root.uri.authority && 78 | uriPath.endsWith('.dart') && 79 | (uriPath.contains('/packages/') || uriPath.startsWith('packages/')); 80 | } 81 | 82 | /// Returns a `package:` version of [uri]. 83 | Uri _packageUriFor(Uri uri) { 84 | var packagePath = uri.path 85 | .substring(uri.path.lastIndexOf('packages/') + 'packages/'.length); 86 | return Uri.parse('package:$packagePath'); 87 | } 88 | 89 | // Reads Initializer annotations on this library and all its dependencies in 90 | // post-order. 91 | Queue _readLibraryDeclarations(LibraryMirror lib, 92 | Set librariesSeen, Queue queue) { 93 | lib = _canonicalLib(lib); 94 | if (librariesSeen.contains(lib)) return queue; 95 | librariesSeen.add(lib); 96 | 97 | // First visit all our dependencies. 98 | for (var dependency in lib.libraryDependencies) { 99 | // Skip dart: imports, they never use this package. 100 | var targetLibrary = dependency.targetLibrary; 101 | if (targetLibrary == null || targetLibrary.uri.scheme == 'dart') continue; 102 | _readLibraryDeclarations(dependency.targetLibrary, librariesSeen, queue); 103 | } 104 | 105 | // Second parse the library directive annotations. 106 | _readAnnotations(lib, queue); 107 | 108 | // Last, parse all class and method annotations. 109 | for (var declaration in _sortedDeclarationsWithMetadata(lib)) { 110 | _readAnnotations(declaration, queue); 111 | // Check classes for static annotations which are not supported 112 | if (declaration is ClassMirror) { 113 | for (var classDeclaration in declaration.declarations.values) { 114 | _readAnnotations(classDeclaration, queue); 115 | } 116 | } 117 | } 118 | 119 | return queue; 120 | } 121 | 122 | Iterable _sortedDeclarationsWithMetadata( 123 | LibraryMirror lib) { 124 | return new List() 125 | ..addAll(_sortDeclarations( 126 | lib, 127 | lib.declarations.values 128 | .where((d) => d is MethodMirror && d.metadata.isNotEmpty))) 129 | ..addAll(_sortDeclarations( 130 | lib, 131 | lib.declarations.values 132 | .where((d) => d is ClassMirror && d.metadata.isNotEmpty))); 133 | } 134 | 135 | List _sortDeclarations( 136 | LibraryMirror sourceLib, Iterable declarations) { 137 | var declarationList = declarations.toList(); 138 | declarationList.sort((DeclarationMirror a, DeclarationMirror b) { 139 | // If in the same file, compare by line. 140 | var aSourceUri = a.location.sourceUri; 141 | var bSourceUri = b.location.sourceUri; 142 | if (aSourceUri == bSourceUri) { 143 | return a.location.line.compareTo(b.location.line); 144 | } 145 | 146 | // Run parts first if one is from the original library. 147 | if (aSourceUri == sourceLib.uri) return 1; 148 | if (bSourceUri == sourceLib.uri) return -1; 149 | 150 | // Sort parts alphabetically. 151 | return aSourceUri.path.compareTo(bSourceUri.path); 152 | }); 153 | return declarationList; 154 | } 155 | 156 | /// Reads annotations on a [DeclarationMirror] and adds them to [_initQueue] 157 | /// if they are [Initializer]s. 158 | void _readAnnotations(DeclarationMirror declaration, Queue queue) { 159 | var annotations = 160 | declaration.metadata.where((m) => _filterMetadata(declaration, m)); 161 | for (var meta in annotations) { 162 | _annotationsFound.putIfAbsent( 163 | declaration, () => new Set()); 164 | _annotationsFound[declaration].add(meta); 165 | 166 | // Initialize super classes first, if they are in the same library, 167 | // otherwise we throw an error. This can only be the case if there are 168 | // cycles in the imports. 169 | if (declaration is ClassMirror && declaration.superclass != null) { 170 | if (declaration.superclass.owner == declaration.owner) { 171 | _readAnnotations(declaration.superclass, queue); 172 | } else { 173 | // Make sure to check the canonical superclass declaration, the one 174 | // we get here is not always that. Specifically, this occurs if all of 175 | // the following conditions are met: 176 | // 177 | // 1. The current library is never loaded via a `package:` dart 178 | // import anywhere in the program. 179 | // 2. The current library loads the superclass via a relative file 180 | // import. 181 | // 3. The super class is imported via a `package:` import somewhere 182 | // else in the program. 183 | var canonicalSuperDeclaration = 184 | _canonicalClassDeclaration(declaration.superclass); 185 | var superMetas = canonicalSuperDeclaration.metadata 186 | .where((m) => _filterMetadata(canonicalSuperDeclaration, m)) 187 | .toList(); 188 | if (superMetas.isNotEmpty) { 189 | throw new UnsupportedError( 190 | 'We have detected a cycle in your import graph when running ' 191 | 'initializers on ${declaration.qualifiedName}. This means the ' 192 | 'super class ${canonicalSuperDeclaration.qualifiedName} has a ' 193 | 'dependency on this library (possibly transitive).'); 194 | } 195 | } 196 | } 197 | 198 | var annotatedValue; 199 | if (declaration is ClassMirror) { 200 | annotatedValue = declaration.reflectedType; 201 | } else if (declaration is MethodMirror) { 202 | if (declaration.owner is! LibraryMirror) { 203 | // TODO(jakemac): Support static class methods. 204 | throw _TOP_LEVEL_FUNCTIONS_ONLY; 205 | } 206 | annotatedValue = (declaration.owner as ObjectMirror) 207 | .getField(declaration.simpleName) 208 | .reflectee; 209 | } else if (declaration is LibraryMirror) { 210 | var package; 211 | var filePath; 212 | Uri uri = declaration.uri; 213 | // Convert to a package style uri if possible. 214 | if (_isHttpStylePackageUrl(uri)) { 215 | uri = _packageUriFor(uri); 216 | } 217 | if (uri.scheme == 'file' || uri.scheme.startsWith('http')) { 218 | filePath = path.url.relative(uri.path, 219 | from: _root.uri.path.endsWith('/') 220 | ? _root.uri.path 221 | : path.url.dirname(_root.uri.path)); 222 | } else if (uri.scheme == 'package') { 223 | var segments = uri.pathSegments; 224 | package = segments[0]; 225 | filePath = path.url.joinAll(segments.getRange(1, segments.length)); 226 | } else { 227 | throw new UnsupportedError('Unsupported uri scheme ${uri.scheme} for ' 228 | 'library ${declaration}.'); 229 | } 230 | annotatedValue = 231 | new LibraryIdentifier(declaration.qualifiedName, package, filePath); 232 | } else { 233 | throw _UNSUPPORTED_DECLARATION; 234 | } 235 | queue.addLast(() => meta.reflectee.initialize(annotatedValue)); 236 | } 237 | } 238 | 239 | // Filter function that returns true only if `meta` is an `Initializer`, 240 | // it passes the `typeFilter` and `customFilter` if they exist, and it has not 241 | // yet been seen. 242 | bool _filterMetadata(DeclarationMirror declaration, InstanceMirror meta) { 243 | if (meta.reflectee is! Initializer) return false; 244 | if (typeFilter != null && 245 | !typeFilter.any((t) => meta.reflectee.runtimeType == t)) { 246 | return false; 247 | } 248 | if (customFilter != null && !customFilter(meta.reflectee)) return false; 249 | if (!_annotationsFound.containsKey(declaration)) return true; 250 | if (_annotationsFound[declaration].contains(meta)) return false; 251 | return true; 252 | } 253 | } 254 | 255 | final _TOP_LEVEL_FUNCTIONS_ONLY = new UnsupportedError( 256 | 'Only top level methods are supported for initializers'); 257 | 258 | final _UNSUPPORTED_DECLARATION = new UnsupportedError( 259 | 'Initializers are only supported on libraries, classes, and top level ' 260 | 'methods'); 261 | -------------------------------------------------------------------------------- /lib/src/static_loader.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | library initialize.static_loader; 5 | 6 | import 'dart:collection' show Queue; 7 | import 'package:initialize/initialize.dart'; 8 | 9 | /// This represents an annotation/declaration pair. 10 | class InitEntry { 11 | /// The annotation instance. 12 | final Initializer meta; 13 | 14 | /// The target of the annotation to pass to initialize. 15 | final T target; 16 | 17 | InitEntry(this.meta, this.target); 18 | } 19 | 20 | /// Set of initializers that are invoked by `run`. This is initialized with 21 | /// code automatically generated by the transformer. 22 | Queue initializers = new Queue(); 23 | 24 | /// Returns initializer functions matching the supplied filters and removes them 25 | /// from `initializers` so they won't be ran again. 26 | Queue loadInitializers( 27 | {List typeFilter, InitializerFilter customFilter, Uri from}) { 28 | if (from != null) { 29 | throw 'The `from` option is not supported in deploy mode.'; 30 | } 31 | Queue result = new Queue(); 32 | 33 | var matchesFilters = (initializer) { 34 | if (typeFilter != null && 35 | !typeFilter.any((t) => initializer.meta.runtimeType == t)) { 36 | return false; 37 | } 38 | if (customFilter != null && !customFilter(initializer.meta)) { 39 | return false; 40 | } 41 | return true; 42 | }; 43 | 44 | result.addAll(initializers 45 | .where(matchesFilters) 46 | .map((i) => () => i.meta.initialize(i.target))); 47 | 48 | initializers.removeWhere(matchesFilters); 49 | 50 | return result; 51 | } 52 | -------------------------------------------------------------------------------- /lib/transformer.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | library initialize.transformer; 5 | 6 | import 'dart:async'; 7 | import 'dart:collection' show Queue; 8 | import 'package:analyzer/dart/ast/ast.dart'; 9 | import 'package:analyzer/dart/element/element.dart'; 10 | import 'package:analyzer/dart/element/type.dart'; 11 | import 'package:barback/barback.dart'; 12 | import 'package:code_transformers/assets.dart'; 13 | import 'package:code_transformers/resolver.dart'; 14 | import 'package:code_transformers/src/dart_sdk.dart' as dart_sdk; 15 | import 'package:dart_style/dart_style.dart'; 16 | import 'package:glob/glob.dart'; 17 | import 'package:html/dom.dart' as dom; 18 | import 'package:html/parser.dart' show parse; 19 | import 'package:path/path.dart' as path; 20 | 21 | import 'build/initializer_plugin.dart'; 22 | export 'build/initializer_plugin.dart'; 23 | 24 | /// Create a new [Asset] which inlines your [Initializer] annotations into 25 | /// a new file that bootstraps your application. 26 | Asset generateBootstrapFile(Resolver resolver, Transform transform, 27 | AssetId primaryAssetId, AssetId newEntryPointId, 28 | {bool errorIfNotFound: true, 29 | List plugins, 30 | bool appendDefaultPlugin: true}) { 31 | if (appendDefaultPlugin) { 32 | if (plugins == null) plugins = []; 33 | plugins.add(const DefaultInitializerPlugin()); 34 | } 35 | return new _BootstrapFileBuilder( 36 | resolver, transform, primaryAssetId, newEntryPointId, errorIfNotFound, 37 | plugins: plugins) 38 | .run(); 39 | } 40 | 41 | /// Transformer which removes the mirror-based initialization logic and replaces 42 | /// it with static logic. 43 | class InitializeTransformer extends Transformer { 44 | final Resolvers _resolvers; 45 | final Iterable _entryPointGlobs; 46 | final bool _errorIfNotFound; 47 | final List plugins; 48 | 49 | InitializeTransformer(List entryPoints, 50 | {bool errorIfNotFound: true, this.plugins}) 51 | : _entryPointGlobs = entryPoints.map((e) => new Glob(e)), 52 | _errorIfNotFound = errorIfNotFound, 53 | _resolvers = new Resolvers.fromMock(dart_sdk.mockSdkSources); 54 | 55 | factory InitializeTransformer.asPlugin(BarbackSettings settings) => 56 | new InitializeTransformer(_readFileList(settings, 'entry_points')); 57 | 58 | bool isPrimary(AssetId id) => _entryPointGlobs.any((g) => g.matches(id.path)); 59 | 60 | Future apply(Transform transform) { 61 | if (transform.primaryInput.id.path.endsWith('.dart')) { 62 | return _buildBootstrapFile(transform); 63 | } else if (transform.primaryInput.id.path.endsWith('.html')) { 64 | return transform.primaryInput.readAsString().then((html) { 65 | var document = parse(html); 66 | var originalDartFile = 67 | _findMainScript(document, transform.primaryInput.id, transform); 68 | return _buildBootstrapFile(transform, primaryId: originalDartFile) 69 | .then((AssetId newDartFile) { 70 | return _replaceEntryWithBootstrap(transform, document, 71 | transform.primaryInput.id, originalDartFile, newDartFile); 72 | }); 73 | }); 74 | } else { 75 | transform.logger.warning( 76 | 'Invalid entry point ${transform.primaryInput.id}. Must be either a ' 77 | '.dart or .html file.'); 78 | } 79 | return new Future.value(); 80 | } 81 | 82 | // Returns the AssetId of the newly created bootstrap file. 83 | Future _buildBootstrapFile(Transform transform, 84 | {AssetId primaryId}) { 85 | if (primaryId == null) primaryId = transform.primaryInput.id; 86 | var newEntryPointId = new AssetId(primaryId.package, 87 | '${path.url.withoutExtension(primaryId.path)}.initialize.dart'); 88 | return transform.hasInput(newEntryPointId).then((exists) { 89 | if (exists) { 90 | transform.logger 91 | .error('New entry point file $newEntryPointId already exists.'); 92 | return null; 93 | } 94 | 95 | return _resolvers.get(transform, [primaryId]).then((resolver) { 96 | transform.addOutput(generateBootstrapFile( 97 | resolver, transform, primaryId, newEntryPointId, 98 | errorIfNotFound: _errorIfNotFound, plugins: plugins)); 99 | resolver.release(); 100 | return newEntryPointId; 101 | }); 102 | }); 103 | } 104 | 105 | // Finds the first (and only) dart script on an html page and returns the 106 | // [AssetId] that points to it 107 | AssetId _findMainScript( 108 | dom.Document document, AssetId entryPoint, Transform transform) { 109 | var scripts = _getScripts(document); 110 | if (scripts.length != 1) { 111 | transform.logger.error('Expected exactly one dart script in $entryPoint ' 112 | 'but found ${scripts.length}.'); 113 | return null; 114 | } 115 | 116 | var src = _getScriptAttribute(scripts[0]); 117 | if (src == null) { 118 | // TODO(jakemac): Support inline scripts, 119 | transform.logger.error('Inline scripts are not supported at this time, ' 120 | 'see https://github.com/dart-lang/initialize/issues/20.'); 121 | return null; 122 | } 123 | 124 | return uriToAssetId( 125 | entryPoint, src, transform.logger, scripts[0].sourceSpan); 126 | } 127 | 128 | // Replaces script tags pointing to [originalDartFile] with [newDartFile] in 129 | // [entryPoint]. 130 | void _replaceEntryWithBootstrap(Transform transform, dom.Document document, 131 | AssetId entryPoint, AssetId originalDartFile, AssetId newDartFile) { 132 | var scripts = _getScripts(document).where((script) { 133 | var assetId = uriToAssetId(entryPoint, _getScriptAttribute(script), 134 | transform.logger, script.sourceSpan); 135 | return assetId == originalDartFile; 136 | }).toList(); 137 | 138 | if (scripts.length != 1) { 139 | transform.logger 140 | .error('Expected exactly one script pointing to $originalDartFile in ' 141 | '$entryPoint, but found ${scripts.length}.'); 142 | return; 143 | } 144 | _setScriptAttribute( 145 | scripts[0], 146 | path.url 147 | .relative(newDartFile.path, from: path.dirname(entryPoint.path))); 148 | transform.addOutput(new Asset.fromString(entryPoint, document.outerHtml)); 149 | } 150 | 151 | String _getScriptAttribute(dom.Element element) { 152 | switch (element.localName) { 153 | case 'script': 154 | return element.attributes['src']; 155 | case 'link': 156 | return element.attributes['href']; 157 | default: 158 | throw 'Unrecognized element $element'; 159 | } 160 | } 161 | 162 | void _setScriptAttribute(dom.Element element, String path) { 163 | switch (element.localName) { 164 | case 'script': 165 | element.attributes['src'] = path; 166 | break; 167 | case 'link': 168 | element.attributes['href'] = path; 169 | break; 170 | } 171 | } 172 | 173 | List _getScripts(dom.Document document) => 174 | document.querySelectorAll( 175 | 'script[type="application/dart"], link[rel="x-dart-test"]'); 176 | } 177 | 178 | // Class which builds a bootstrap file. 179 | class _BootstrapFileBuilder { 180 | final Resolver _resolver; 181 | final Transform _transform; 182 | final bool _errorIfNotFound; 183 | AssetId _entryPoint; 184 | AssetId _newEntryPoint; 185 | 186 | /// The resolved initialize library. 187 | LibraryElement _initializeLibrary; 188 | 189 | /// The resolved Initializer class from the initialize library. 190 | ClassElement _initializer; 191 | 192 | /// Queue for intialization annotations. 193 | final _initQueue = new Queue(); 194 | 195 | /// All the annotations we have seen for each element 196 | final _seenAnnotations = new Map>(); 197 | 198 | /// The list of [InitializerPlugin]s to apply. The first plugin which asks to 199 | /// be applied to a given initializer is the only one that will apply. 200 | List _plugins; 201 | 202 | TransformLogger _logger; 203 | 204 | _BootstrapFileBuilder(this._resolver, this._transform, this._entryPoint, 205 | this._newEntryPoint, this._errorIfNotFound, 206 | {List plugins}) { 207 | _logger = _transform.logger; 208 | _initializeLibrary = 209 | _resolver.getLibrary(new AssetId('initialize', 'lib/initialize.dart')); 210 | if (_initializeLibrary != null) { 211 | _initializer = _initializeLibrary.getType('Initializer'); 212 | } else if (_errorIfNotFound) { 213 | _logger.warning('Unable to read "package:initialize/initialize.dart". ' 214 | 'This file must be imported via $_entryPoint or a transitive ' 215 | 'dependency.'); 216 | } 217 | _plugins = plugins != null ? plugins : [const DefaultInitializerPlugin()]; 218 | } 219 | 220 | /// Creates and returns the new bootstrap file. 221 | Asset run() { 222 | var entryLib = _resolver.getLibrary(_entryPoint); 223 | _readLibraries(entryLib); 224 | 225 | return new Asset.fromString(_newEntryPoint, _buildNewEntryPoint(entryLib)); 226 | } 227 | 228 | /// Reads Initializer annotations on this library and all its dependencies in 229 | /// post-order. 230 | void _readLibraries(LibraryElement library, [Set seen]) { 231 | if (seen == null) seen = new Set(); 232 | seen.add(library); 233 | 234 | // Visit all our dependencies. 235 | for (var library in _sortedLibraryDependencies(library)) { 236 | // Don't include anything from the sdk. 237 | if (library.isInSdk) continue; 238 | if (seen.contains(library)) continue; 239 | _readLibraries(library, seen); 240 | } 241 | 242 | // Read annotations in this order: library, top level methods, classes. 243 | _readAnnotations(library); 244 | for (var method in _topLevelMethodsOfLibrary(library, seen)) { 245 | _readAnnotations(method); 246 | } 247 | for (var clazz in _classesOfLibrary(library, seen)) { 248 | readSuperClassAnnotations(InterfaceType superClass) { 249 | if (superClass == null) return; 250 | readSuperClassAnnotations(superClass.superclass); 251 | if (_readAnnotations(superClass.element) && 252 | superClass.element.library != clazz.library) { 253 | _logger.warning( 254 | 'We have detected a cycle in your import graph when running ' 255 | 'initializers on ${clazz.name}. This means the super class ' 256 | '${superClass.name} has a dependency on this library ' 257 | '(possibly transitive).'); 258 | } 259 | } 260 | 261 | readSuperClassAnnotations(clazz.supertype); 262 | _readAnnotations(clazz); 263 | } 264 | } 265 | 266 | bool _readAnnotations(Element element) { 267 | var found = false; 268 | // analyzer 0.29 doesn't allow this optimization : 269 | //if (element.metadata.isEmpty) return found; 270 | 271 | var metaNodes; 272 | var node = element.computeNode(); 273 | if (node is SimpleIdentifier && node.parent is LibraryIdentifier) { 274 | metaNodes = (node.parent.parent as AnnotatedNode).metadata; 275 | } else if (node is ClassDeclaration || node is FunctionDeclaration) { 276 | metaNodes = (node as AnnotatedNode).metadata; 277 | } else { 278 | return found; 279 | } 280 | 281 | metaNodes.where((Annotation metaNode) { 282 | // First filter out anything that is not a Initializer. 283 | var meta = metaNode.elementAnnotation; 284 | var e = meta.element; 285 | if (e is PropertyAccessorElement) { 286 | // 'as dynamic' is because evaluationResult is a property on an impl class, e.g. one that 287 | // isn't supposed to be used externally. 288 | return _isInitializer( 289 | (e.variable as dynamic).evaluationResult.value.type); 290 | } else if (e is ConstructorElement) { 291 | return _isInitializer(e.returnType); 292 | } 293 | return false; 294 | }).where((Annotation metaNode) { 295 | var meta = metaNode.elementAnnotation; 296 | _seenAnnotations.putIfAbsent(element, () => new Set()); 297 | return !_seenAnnotations[element].contains(meta); 298 | }).forEach((Annotation metaNode) { 299 | var meta = metaNode.elementAnnotation; 300 | _seenAnnotations[element].add(meta); 301 | _initQueue.addLast(new InitializerData._(node, metaNode)); 302 | found = true; 303 | }); 304 | return found; 305 | } 306 | 307 | String _buildNewEntryPoint(LibraryElement entryLib) { 308 | var importsBuffer = new StringBuffer(); 309 | var initializersBuffer = new StringBuffer(); 310 | var libraryPrefixes = new Map(); 311 | 312 | // Import the static_loader, initializer, and original entry point. 313 | importsBuffer 314 | .writeln("import 'package:initialize/src/static_loader.dart';"); 315 | importsBuffer.writeln("import 'package:initialize/initialize.dart';"); 316 | libraryPrefixes[entryLib] = 'i0'; 317 | 318 | initializersBuffer.writeln('initializers.addAll(['); 319 | while (_initQueue.isNotEmpty) { 320 | var next = _initQueue.removeFirst(); 321 | 322 | libraryPrefixes.putIfAbsent( 323 | next.targetElement.library, () => 'i${libraryPrefixes.length}'); 324 | libraryPrefixes.putIfAbsent(next.annotationElement.element.library, 325 | () => 'i${libraryPrefixes.length}'); 326 | 327 | // Run the first plugin which asks to be ran and then stop. 328 | var data = new InitializerPluginData( 329 | next, _newEntryPoint, libraryPrefixes, _resolver, _logger); 330 | var plugin = _plugins.firstWhere((p) => p.shouldApply(data), orElse: () { 331 | _logger.error('No InitializerPlugin handled the annotation: ' 332 | '${next.annotationElement} on: ${next.targetElement}.'); 333 | }); 334 | if (plugin == null) continue; 335 | 336 | var text = plugin.apply(data); 337 | if (text != null) initializersBuffer.writeln('$text,'); 338 | } 339 | initializersBuffer.writeln(']);'); 340 | 341 | libraryPrefixes 342 | .forEach((lib, prefix) => _writeImport(lib, prefix, importsBuffer)); 343 | 344 | // TODO(jakemac): copyright and library declaration 345 | return new DartFormatter().format(''' 346 | $importsBuffer 347 | main() { 348 | $initializersBuffer 349 | return i0.main(); 350 | } 351 | '''); 352 | } 353 | 354 | _writeImport(LibraryElement lib, String prefix, StringBuffer buffer) { 355 | AssetId id = (lib.source as dynamic).assetId; 356 | 357 | if (id.path.startsWith('lib/')) { 358 | var packagePath = id.path.replaceFirst('lib/', ''); 359 | buffer.write("import 'package:${id.package}/${packagePath}'"); 360 | } else if (id.package != _newEntryPoint.package) { 361 | _logger.error("Can't import `${id}` from `${_newEntryPoint}`"); 362 | } else if (path.url.split(id.path)[0] == 363 | path.url.split(_newEntryPoint.path)[0]) { 364 | var relativePath = path.url 365 | .relative(id.path, from: path.url.dirname(_newEntryPoint.path)); 366 | buffer.write("import '${relativePath}'"); 367 | } else { 368 | _logger.error("Can't import `${id}` from `${_newEntryPoint}`"); 369 | } 370 | buffer.writeln(' as $prefix;'); 371 | } 372 | 373 | bool _isInitializer(InterfaceType type) { 374 | // If `_initializer` wasn't found then it was never loaded (even 375 | // transitively), and so no annotations can be initializers. 376 | if (_initializer == null) return false; 377 | if (type == null) return false; 378 | if (type.element.type == _initializer.type) return true; 379 | if (_isInitializer(type.superclass)) return true; 380 | for (var interface in type.interfaces) { 381 | if (_isInitializer(interface)) return true; 382 | } 383 | return false; 384 | } 385 | 386 | /// Retrieves all top-level methods that are visible if you were to import 387 | /// [lib]. This includes exported methods from other libraries too. 388 | List _topLevelMethodsOfLibrary( 389 | LibraryElement library, Set seen) { 390 | var methods = []; 391 | 392 | var orderedExports = new List.from(library.exports) 393 | ..sort((a, b) => a.uriOffset.compareTo(b.uriOffset)); 394 | for (var export in orderedExports) { 395 | if (seen.contains(export.exportedLibrary)) continue; 396 | methods.addAll(_topLevelMethodsOfLibrary(export.exportedLibrary, seen)); 397 | } 398 | 399 | for (CompilationUnitElement unit in _orderedUnits(library)) { 400 | methods.addAll(new List.from(unit.functions) 401 | ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset))); 402 | } 403 | 404 | return methods; 405 | } 406 | 407 | /// Retrieves all classes that are visible if you were to import [lib]. This 408 | /// includes exported classes from other libraries. 409 | List _classesOfLibrary( 410 | LibraryElement library, Set seen) { 411 | var classes = []; 412 | 413 | var orderedExports = new List.from(library.exports) 414 | ..sort((a, b) => a.uriOffset.compareTo(b.uriOffset)); 415 | for (var export in orderedExports) { 416 | if (seen.contains(export.exportedLibrary)) continue; 417 | classes.addAll(_classesOfLibrary(export.exportedLibrary, seen)); 418 | } 419 | 420 | for (var unit in _orderedUnits(library)) { 421 | classes.addAll(new List.from(unit.types) 422 | ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset))); 423 | } 424 | 425 | return classes; 426 | } 427 | 428 | List _orderedUnits(LibraryElement library) { 429 | var definingUnit = library.definingCompilationUnit; 430 | // The first item is the source library, remove it for now. 431 | return new List.from(library.units) 432 | ..sort((a, b) { 433 | if (a == definingUnit) return 1; 434 | if (b == definingUnit) return -1; 435 | return a.uri.compareTo(b.uri); 436 | }); 437 | } 438 | 439 | Iterable _sortedLibraryDependencies(LibraryElement library) { 440 | // TODO(jakemac): Investigate supporting annotations on part-of directives. 441 | getLibrary(UriReferencedElement element) { 442 | if (element is ImportElement) return element.importedLibrary; 443 | if (element is ExportElement) return element.exportedLibrary; 444 | } 445 | 446 | return (new List.from(library.imports) 447 | ..addAll(library.exports) 448 | ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset))) 449 | .map(getLibrary); 450 | } 451 | } 452 | 453 | /// An [Initializer] annotation and the target of that annotation. 454 | class InitializerData { 455 | /// The target [AstNode] of the annotation. 456 | final AstNode targetNode; 457 | 458 | /// The [Annotation] representing the annotation itself. 459 | final Annotation annotationNode; 460 | 461 | /// The [ElementAnnotation] representing the annotation itself. 462 | ElementAnnotation get annotationElement => annotationNode.elementAnnotation; 463 | 464 | /// The target [Element] of the annotation. 465 | Element get targetElement { 466 | if (targetNode is SimpleIdentifier && 467 | targetNode.parent is LibraryIdentifier) { 468 | return (targetNode.parent.parent as LibraryDirective).element; 469 | } else if (targetNode is ClassDeclaration || 470 | targetNode is FunctionDeclaration) { 471 | return (targetNode as dynamic).element; 472 | } else { 473 | return null; 474 | } 475 | } 476 | 477 | InitializerData._(this.targetNode, this.annotationNode); 478 | } 479 | 480 | // Reads a file list from a barback settings configuration field. 481 | _readFileList(BarbackSettings settings, String field) { 482 | var value = settings.configuration[field]; 483 | if (value == null) return null; 484 | var files = []; 485 | bool error; 486 | if (value is List) { 487 | files = value; 488 | error = value.any((e) => e is! String); 489 | } else if (value is String) { 490 | files = [value]; 491 | error = false; 492 | } else { 493 | error = true; 494 | } 495 | if (error) { 496 | print('Bad value for "$field" in the initialize transformer. ' 497 | 'Expected either one String or a list of Strings.'); 498 | } 499 | return files; 500 | } 501 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: initialize 2 | version: 0.6.2+7 3 | author: Polymer.dart Authors 4 | description: Generic building blocks for doing static initialization. 5 | homepage: https://github.com/dart-lang/initialize 6 | dependencies: 7 | analyzer: '>=0.27.2 <0.30.0' 8 | barback: '>=0.14.2 <0.16.0' 9 | code_transformers: '>=0.3.0 <0.6.0' 10 | dart_style: '>=0.1.3 <0.3.0' 11 | glob: ">=1.0.4 <2.0.0" 12 | html: '>=0.12.0 <0.14.0' 13 | path: '>=1.3.0 <2.0.0' 14 | dev_dependencies: 15 | test_package: 16 | path: test_package 17 | test: '>=0.12.0 <0.13.0' 18 | transformer_test: '>=0.2.0 <0.3.0' 19 | environment: 20 | sdk: ">=1.9.0-dev.7.1 <2.0.0" 21 | transformers: 22 | - initialize/build/loader_replacer: 23 | $include: lib/initialize.dart 24 | - initialize: 25 | $include: '**/*_test.*' 26 | entry_points: 27 | - test/deferred_library_test.html 28 | - test/initializer_test.html 29 | - test/initializer_from_test.html 30 | - test/initializer_parts_test.html 31 | - test/initializer_super_test.html 32 | - test/initializer_cycle_error_test.html 33 | - test/initializer_custom_filter_test.html 34 | - test/initializer_type_filter_test.html 35 | - test/init_method_test.html 36 | - test/pub_serve: 37 | $include: test/**_test{.*,}.dart 38 | - $dart2js: 39 | $include: test/*_test.initialize{.*,}.dart 40 | -------------------------------------------------------------------------------- /test/common.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | library initialize.test.build.common; 5 | 6 | import 'package:barback/barback.dart'; 7 | import 'package:transformer_test/src/test_harness.dart'; 8 | import 'package:test/test.dart'; 9 | 10 | testPhases(String testName, List> phases, 11 | Map inputFiles, Map expectedFiles, 12 | [List expectedMessages]) { 13 | test(testName, () { 14 | var helper = new TestHelper(phases, inputFiles, expectedMessages)..run(); 15 | return helper.checkAll(expectedFiles).whenComplete(() => helper.tearDown()); 16 | }); 17 | } 18 | 19 | // Simple mock of initialize. 20 | const mockInitialize = ''' 21 | library initialize; 22 | 23 | abstract class Initializer {} 24 | 25 | class _InitMethod implements Initializer { 26 | const _InitMethod(); 27 | } 28 | const _InitMethod initMethod = const _InitMethod();'''; 29 | 30 | // Some simple initializers for use in tests. 31 | const commonInitializers = ''' 32 | library test_initializers; 33 | 34 | import 'package:initialize/initialize.dart'; 35 | 36 | class _ConstInit extends Initializer { 37 | const ConstInit(); 38 | } 39 | const _ConstInit constInit = const _ConstInit(); 40 | 41 | class DynamicInit extends Initializer { 42 | final dynamic _value; 43 | const DynamicInit(this._value); 44 | } 45 | 46 | class NamedArgInit extends Initializer { 47 | final dynamic _first; 48 | final dynamic name; 49 | const NamedArgInit(this._first, {this.name}); 50 | } 51 | '''; 52 | -------------------------------------------------------------------------------- /test/cycle_a.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | library initialize.test.cycle_a; 5 | 6 | import 'package:initialize/src/initialize_tracker.dart'; 7 | import 'cycle_b.dart' as cycle_b; 8 | 9 | /// Uses [cycle_b]. 10 | @initializeTracker 11 | class CycleA {} 12 | -------------------------------------------------------------------------------- /test/cycle_b.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | library initialize.test.cycle_b; 5 | 6 | import 'package:initialize/src/initialize_tracker.dart'; 7 | import 'cycle_a.dart'; 8 | 9 | @initializeTracker 10 | class CycleB extends CycleA {} 11 | -------------------------------------------------------------------------------- /test/deferred_library_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | // TODO(jakemac): swap this to @TestOn('pub-serve') once 6 | // https://github.com/dart-lang/test/issues/388 is completed. 7 | @TestOn('!js') 8 | @initializeTracker 9 | library initialize.deferred_library_test; 10 | 11 | import 'foo.dart' deferred as foo; 12 | import 'package:initialize/src/initialize_tracker.dart'; 13 | import 'package:initialize/initialize.dart'; 14 | import 'package:test/test.dart'; 15 | 16 | main() { 17 | test('annotations can be loaded lazily', () { 18 | // Initialize everything not in deferred imports. 19 | return run().then((_) { 20 | expect(InitializeTracker.seen.length, 1); 21 | 22 | // Now load the foo library and re-run initializers. 23 | return foo.loadLibrary().then((_) => run()).then((_) { 24 | expect(InitializeTracker.seen.length, 5); 25 | }); 26 | }); 27 | }, 28 | skip: 'Should be skipped only in pub-serve mode, blocked on ' 29 | 'https://github.com/dart-lang/test/issues/388.'); 30 | } 31 | -------------------------------------------------------------------------------- /test/deferred_library_test.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/foo.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | @initializeTracker 5 | library initialize.test.foo; 6 | 7 | import 'package:initialize/src/initialize_tracker.dart'; 8 | 9 | @initializeTracker 10 | class Foo {} 11 | 12 | @initializeTracker 13 | fooBar() {} 14 | 15 | @initializeTracker 16 | foo() {} 17 | -------------------------------------------------------------------------------- /test/foo/bar.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | @initializeTracker 5 | library initialize.test.foo.bar; 6 | 7 | export '../foo.dart'; 8 | import '../foo.dart'; 9 | import 'package:initialize/src/initialize_tracker.dart'; 10 | 11 | // Foo should be initialized first. 12 | @initializeTracker 13 | class Bar extends Foo {} 14 | 15 | @initializeTracker 16 | bar() {} 17 | -------------------------------------------------------------------------------- /test/init_method_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | // TODO(jakemac): swap this to @TestOn('pub-serve') once 6 | // https://github.com/dart-lang/test/issues/388 is completed. 7 | @TestOn('!js') 8 | library initialize.init_method_test; 9 | 10 | import 'package:initialize/initialize.dart'; 11 | import 'package:test/test.dart'; 12 | 13 | int calledFoo = 0; 14 | int calledBar = 0; 15 | 16 | main() { 17 | // Run all initializers. 18 | return run().then((_) { 19 | test('initMethod annotation invokes functions once', () { 20 | expect(calledFoo, 1); 21 | expect(calledBar, 1); 22 | // Re-run all initializers, should be a no-op. 23 | return run().then((_) { 24 | expect(calledFoo, 1); 25 | expect(calledBar, 1); 26 | }); 27 | }); 28 | }); 29 | } 30 | 31 | @initMethod 32 | foo() => calledFoo++; 33 | 34 | @initMethod 35 | bar() => calledBar++; 36 | -------------------------------------------------------------------------------- /test/init_method_test.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/initializer_custom_filter_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | // TODO(jakemac): swap this to @TestOn('pub-serve') once 6 | // https://github.com/dart-lang/test/issues/388 is completed. 7 | @TestOn('!js') 8 | library initialize.initializer_custom_filter_test; 9 | 10 | import 'dart:async'; 11 | import 'package:initialize/initialize.dart'; 12 | import 'package:test/test.dart'; 13 | import 'package:initialize/src/initialize_tracker.dart'; 14 | 15 | main() { 16 | test('filter option limits which types of annotations will be ran', () { 17 | var originalSize; 18 | return runPhase(1) 19 | .then((_) { 20 | // Even though Baz extends Bar, only Baz should be run. 21 | expect(InitializeTracker.seen, [Baz]); 22 | }) 23 | .then((_) => runPhase(2)) 24 | .then((_) { 25 | expect(InitializeTracker.seen, [Baz, foo]); 26 | }) 27 | .then((_) => runPhase(3)) 28 | .then((_) { 29 | expect(InitializeTracker.seen, [Baz, foo, Foo]); 30 | }) 31 | .then((_) => runPhase(4)) 32 | .then((_) { 33 | expect(InitializeTracker.seen, [Baz, foo, Foo, Bar]); 34 | }) 35 | .then((_) { 36 | originalSize = InitializeTracker.seen.length; 37 | }) 38 | .then((_) => runPhase(1)) 39 | .then((_) => runPhase(2)) 40 | .then((_) => runPhase(3)) 41 | .then((_) => runPhase(4)) 42 | .then((_) => run()) 43 | .then((_) { 44 | expect(InitializeTracker.seen.length, originalSize); 45 | }); 46 | }); 47 | } 48 | 49 | Future runPhase(int phase) => run( 50 | customFilter: (Initializer meta) => 51 | meta is PhasedInitializer && meta.phase == phase); 52 | 53 | @PhasedInitializer(3) 54 | class Foo {} 55 | 56 | @PhasedInitializer(2) 57 | foo() {} 58 | 59 | @PhasedInitializer(4) 60 | class Bar {} 61 | 62 | @PhasedInitializer(1) 63 | class Baz extends Bar {} 64 | 65 | // Initializer that has a phase associated with it, this can be used in 66 | // combination with a custom filter to run intialization in phases. 67 | class PhasedInitializer extends InitializeTracker { 68 | final int phase; 69 | 70 | const PhasedInitializer(this.phase); 71 | } 72 | -------------------------------------------------------------------------------- /test/initializer_custom_filter_test.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/initializer_cycle_error_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | // TODO(jakemac): swap this to @TestOn('pub-serve') once 6 | // https://github.com/dart-lang/test/issues/388 is completed. 7 | @TestOn('!js') 8 | library initialize.initializer_cycle_error_test; 9 | 10 | import 'cycle_a.dart' as cycle_a; // Causes a cycle. 11 | import 'package:initialize/initialize.dart'; 12 | import 'package:test/test.dart'; 13 | 14 | /// Uses [cycle_a]. 15 | main() { 16 | test('super class cycles are not supported', () { 17 | expect(run, throwsUnsupportedError); 18 | }, 19 | skip: 'Should be skipped only in pub-serve mode, blocked on ' 20 | 'https://github.com/dart-lang/test/issues/388.'); 21 | } 22 | -------------------------------------------------------------------------------- /test/initializer_cycle_error_test.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/initializer_from_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | // TODO(jakemac): swap this to @TestOn('pub-serve') once 6 | // https://github.com/dart-lang/test/issues/388 is completed. 7 | @TestOn('!js') 8 | @initializeTracker 9 | library initialize.test.initializer_from_test; 10 | 11 | import 'package:initialize/src/initialize_tracker.dart'; 12 | import 'package:initialize/initialize.dart'; 13 | import 'package:test/test.dart'; 14 | import 'package:test_package/bar.dart' as bar; 15 | 16 | /// Uses [bar] 17 | main() { 18 | test('The `from` option', () async { 19 | final expectedNames = []; 20 | 21 | // First just run on the test packages bar.dart file. 22 | await run(from: Uri.parse('package:test_package/bar.dart')); 23 | expectedNames.add( 24 | const LibraryIdentifier(#test_package.bar, 'test_package', 'bar.dart')); 25 | expect(InitializeTracker.seen, expectedNames); 26 | 27 | // Now we run on the rest (just this file). 28 | await run(); 29 | expect(InitializeTracker.seen.length, 2); 30 | // Don't know what the path will be, so have to explicitly check fields 31 | // and use an [endsWith] matcher for the path. 32 | expect( 33 | InitializeTracker.seen[1].name, #initialize.test.initializer_from_test); 34 | expect(InitializeTracker.seen[1].package, isNull); 35 | expect( 36 | InitializeTracker.seen[1].path, endsWith('initializer_from_test.dart')); 37 | }, 38 | skip: 'Should be skipped only in pub-serve mode, blocked on ' 39 | 'https://github.com/dart-lang/test/issues/388.'); 40 | } 41 | -------------------------------------------------------------------------------- /test/initializer_from_test.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/initializer_parts_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | // TODO(jakemac): swap this to @TestOn('pub-serve') once 6 | // https://github.com/dart-lang/test/issues/388 is completed. 7 | @TestOn('!js') 8 | @initializeTracker 9 | library initialize.initializer_parts_test; 10 | 11 | import 'package:initialize/src/initialize_tracker.dart'; 12 | import 'package:initialize/initialize.dart'; 13 | import 'package:test/test.dart'; 14 | 15 | part 'parts/foo.dart'; 16 | part 'parts/bar.dart'; 17 | 18 | main() { 19 | // Run all initializers. 20 | return run().then((_) { 21 | test('parts', () { 22 | var expectedNames = [ 23 | const LibraryIdentifier(#initialize.initializer_parts_test, null, 24 | 'initializer_parts_test.dart'), 25 | bar2, 26 | bar, 27 | foo, 28 | baz, 29 | Bar2, 30 | Bar, 31 | Foo, 32 | Baz, 33 | ]; 34 | expect(InitializeTracker.seen, expectedNames); 35 | }); 36 | }); 37 | } 38 | 39 | @initializeTracker 40 | class Baz {} 41 | 42 | @initializeTracker 43 | baz() {} 44 | -------------------------------------------------------------------------------- /test/initializer_parts_test.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/initializer_super_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | // TODO(jakemac): swap this to @TestOn('pub-serve') once 6 | // https://github.com/dart-lang/test/issues/388 is completed. 7 | @TestOn('!js') 8 | library initialize.initializer_super_test; 9 | 10 | import 'package:initialize/src/initialize_tracker.dart'; 11 | import 'package:initialize/initialize.dart'; 12 | import 'package:test/test.dart'; 13 | 14 | main() { 15 | // Run all initializers. 16 | return run().then((_) { 17 | test('annotations are seen in post-order with superclasses first', () { 18 | var expectedNames = [ 19 | A, 20 | C, 21 | B, 22 | E, 23 | D, 24 | ]; 25 | expect(InitializeTracker.seen, expectedNames); 26 | }); 27 | }); 28 | } 29 | 30 | @initializeTracker 31 | class D extends E {} 32 | 33 | @initializeTracker 34 | class E extends B {} 35 | 36 | @initializeTracker 37 | class B extends C {} 38 | 39 | @initializeTracker 40 | class C extends A {} 41 | 42 | @initializeTracker 43 | class A {} 44 | -------------------------------------------------------------------------------- /test/initializer_super_test.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/initializer_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | // TODO(jakemac): swap this to @TestOn('pub-serve') once 6 | // https://github.com/dart-lang/test/issues/388 is completed. 7 | @TestOn('!js') 8 | @initializeTracker 9 | library initialize.initializer_test; 10 | 11 | import 'foo/bar.dart'; 12 | import 'package:initialize/src/initialize_tracker.dart'; 13 | import 'package:initialize/initialize.dart'; 14 | import 'package:test_package/foo.dart' as test_foo; 15 | import 'package:test/test.dart'; 16 | 17 | /// Uses [test_foo]. 18 | main() { 19 | // Run all initializers. 20 | return run().then((_) { 21 | test('annotations are seen in post-order with superclasses first', () { 22 | var expectedNames = [ 23 | const LibraryIdentifier(#initialize.test.foo, null, 'foo.dart'), 24 | fooBar, 25 | foo, 26 | Foo, 27 | const LibraryIdentifier(#initialize.test.foo.bar, null, 'foo/bar.dart'), 28 | bar, 29 | Bar, 30 | const LibraryIdentifier(#test_package.bar, 'test_package', 'bar.dart'), 31 | const LibraryIdentifier(#test_package.foo, 'test_package', 'foo.dart'), 32 | const LibraryIdentifier( 33 | #initialize.initializer_test, null, 'initializer_test.dart'), 34 | zap, 35 | Zoop, // Zap extends Zoop, so Zoop comes first. 36 | Zap, 37 | ]; 38 | expect(InitializeTracker.seen, expectedNames); 39 | }); 40 | 41 | test('annotations only run once', () { 42 | // Run the initializers again, should be a no-op. 43 | var originalSize = InitializeTracker.seen.length; 44 | return run().then((_) { 45 | expect(InitializeTracker.seen.length, originalSize); 46 | }); 47 | }); 48 | }); 49 | } 50 | 51 | @initializeTracker 52 | class Zoop {} 53 | 54 | @initializeTracker 55 | class Zap extends Zoop {} 56 | 57 | @initializeTracker 58 | zap() {} 59 | -------------------------------------------------------------------------------- /test/initializer_test.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/initializer_type_filter_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | // TODO(jakemac): swap this to @TestOn('pub-serve') once 6 | // https://github.com/dart-lang/test/issues/388 is completed. 7 | @TestOn('!js') 8 | library initialize.initializer_type_filter_test; 9 | 10 | import 'package:initialize/initialize.dart'; 11 | import 'package:test/test.dart'; 12 | 13 | // Initializers will mess with this value, and it gets reset to 0 at the 14 | // start of every test. 15 | var total; 16 | 17 | main() { 18 | setUp(() { 19 | total = 0; 20 | }); 21 | 22 | test('filter option limits which types of annotations will be ran', () { 23 | return run(typeFilter: const [_Adder]) 24 | .then((_) { 25 | expect(total, 2); 26 | }) 27 | .then((_) => run(typeFilter: const [_Subtractor])) 28 | .then((_) { 29 | expect(total, 0); 30 | }) 31 | .then((_) => run(typeFilter: const [_Adder])) 32 | .then((_) { 33 | // Sanity check, future calls should be no-ops 34 | expect(total, 0); 35 | }) 36 | .then((_) => run(typeFilter: const [_Subtractor])) 37 | .then((_) { 38 | expect(total, 0); 39 | }); 40 | }); 41 | } 42 | 43 | @adder 44 | a() {} 45 | @subtractor 46 | b() {} 47 | @adder 48 | @subtractor 49 | c() {} 50 | 51 | // Initializer that increments `total` by one. 52 | class _Adder implements Initializer { 53 | const _Adder(); 54 | 55 | @override 56 | initialize(_) => total++; 57 | } 58 | 59 | const adder = const _Adder(); 60 | 61 | // Initializer that decrements `total` by one. 62 | class _Subtractor implements Initializer { 63 | const _Subtractor(); 64 | 65 | @override 66 | initialize(_) => total--; 67 | } 68 | 69 | const subtractor = const _Subtractor(); 70 | -------------------------------------------------------------------------------- /test/initializer_type_filter_test.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/parts/bar.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | part of initialize.initializer_parts_test; 5 | 6 | @initializeTracker 7 | class Bar2 {} 8 | 9 | @initializeTracker 10 | class Bar {} 11 | 12 | @initializeTracker 13 | bar2() {} 14 | 15 | @initializeTracker 16 | bar() {} 17 | -------------------------------------------------------------------------------- /test/parts/foo.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | part of initialize.initializer_parts_test; 5 | 6 | @initializeTracker 7 | class Foo {} 8 | 9 | @initializeTracker 10 | foo() {} 11 | -------------------------------------------------------------------------------- /test/transformer_test.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | @TestOn('vm') 5 | library initialize.transformer_test; 6 | 7 | import 'common.dart'; 8 | import 'package:analyzer/dart/element/element.dart'; 9 | import 'package:dart_style/dart_style.dart'; 10 | import 'package:initialize/transformer.dart'; 11 | import 'package:test/test.dart'; 12 | 13 | var formatter = new DartFormatter(); 14 | 15 | main() { 16 | group('Html entry points', htmlEntryPointTests); 17 | group('Dart entry points', dartEntryPointTests); 18 | group('InitializerPlugins', pluginTests); 19 | } 20 | 21 | void htmlEntryPointTests() { 22 | var phases = [ 23 | [ 24 | new InitializeTransformer(['web/*.html']) 25 | ] 26 | ]; 27 | 28 | testPhases('basic', phases, { 29 | 'a|web/index.html': ''' 30 | 31 | 32 | 33 | ''' 34 | .replaceAll(' ', ''), 35 | 'a|web/index.dart': ''' 36 | library web_foo; 37 | 38 | import 'foo.dart'; 39 | ''', 40 | 'a|web/foo.dart': ''' 41 | @constInit 42 | library foo; 43 | 44 | import 'package:initialize/initialize.dart'; 45 | import 'package:test_initializers/common.dart'; 46 | import 'package:bar/bar.dart'; 47 | 48 | @constInit 49 | class Foo extends Bar {} 50 | 51 | @initMethod 52 | foo() {} 53 | ''', 54 | 'bar|lib/bar.dart': ''' 55 | @DynamicInit('bar') 56 | @DynamicInit('bar2') 57 | library bar; 58 | 59 | import 'package:initialize/initialize.dart'; 60 | import 'package:test_initializers/common.dart'; 61 | import 'baz.dart'; 62 | 63 | @DynamicInit('Bar') 64 | @DynamicInit('Bar2') 65 | class Bar {} 66 | 67 | @DynamicInit('bar()') 68 | @initMethod 69 | bar() {} 70 | ''', 71 | 'bar|lib/baz.dart': ''' 72 | @constInit 73 | library baz; 74 | 75 | import 'package:test_initializers/common.dart'; 76 | ''', 77 | // Mock out the Initialize package plus some initializers. 78 | 'initialize|lib/initialize.dart': mockInitialize, 79 | 'test_initializers|lib/common.dart': commonInitializers, 80 | }, { 81 | 'a|web/index.html': ''' 82 | 83 | 84 | 85 | ''' 86 | .replaceAll(' ', ''), 87 | 'a|web/index.initialize.dart': formatter.format(''' 88 | import 'package:initialize/src/static_loader.dart'; 89 | import 'package:initialize/initialize.dart'; 90 | import 'index.dart' as i0; 91 | import 'package:bar/baz.dart' as i1; 92 | import 'package:test_initializers/common.dart' as i2; 93 | import 'package:bar/bar.dart' as i3; 94 | import 'package:initialize/initialize.dart' as i4; 95 | import 'foo.dart' as i5; 96 | 97 | main() { 98 | initializers.addAll([ 99 | new InitEntry(i2.constInit, const LibraryIdentifier(#baz, 'bar', 'baz.dart')), 100 | new InitEntry(const i2.DynamicInit('bar'), const LibraryIdentifier(#bar, 'bar', 'bar.dart')), 101 | new InitEntry(const i2.DynamicInit('bar2'), const LibraryIdentifier(#bar, 'bar', 'bar.dart')), 102 | new InitEntry(const i2.DynamicInit('bar()'), i3.bar), 103 | new InitEntry(i4.initMethod, i3.bar), 104 | new InitEntry(const i2.DynamicInit('Bar'), i3.Bar), 105 | new InitEntry(const i2.DynamicInit('Bar2'), i3.Bar), 106 | new InitEntry(i2.constInit, const LibraryIdentifier(#foo, null, 'foo.dart')), 107 | new InitEntry(i4.initMethod, i5.foo), 108 | new InitEntry(i2.constInit, i5.Foo), 109 | ]); 110 | 111 | return i0.main(); 112 | } 113 | ''') 114 | }, []); 115 | } 116 | 117 | void dartEntryPointTests() { 118 | var phases = [ 119 | [ 120 | new InitializeTransformer(['web/index.dart']) 121 | ] 122 | ]; 123 | 124 | testPhases('constructor arguments', phases, { 125 | 'a|web/index.dart': ''' 126 | @DynamicInit(foo) 127 | @DynamicInit(_foo) 128 | @DynamicInit(Foo.foo) 129 | @DynamicInit(bar.Foo.bar) 130 | @DynamicInit(bar.Foo.foo) 131 | @DynamicInit(const [foo, Foo.foo, 'foo']) 132 | @DynamicInit(const {'foo': foo, 'Foo.foo': Foo.foo, 'bar': 'bar'}) 133 | @DynamicInit('foo') 134 | @DynamicInit(true) 135 | @DynamicInit(null) 136 | @DynamicInit(1) 137 | @DynamicInit(1.1) 138 | @DynamicInit('foo-\$x\${y}') 139 | @DynamicInit(1 + 2) 140 | @DynamicInit(1.0 + 0.2) 141 | @DynamicInit(1 == 1) 142 | @NamedArgInit(1, name: 'Bill') 143 | library web_foo; 144 | 145 | import 'package:test_initializers/common.dart'; 146 | import 'foo.dart'; 147 | import 'foo.dart' as bar; 148 | 149 | const x = 'x'; 150 | const y = 'y'; 151 | const _foo = '_foo'; 152 | 153 | class MyConst { 154 | const MyConst; 155 | } 156 | ''', 157 | 'a|web/foo.dart': ''' 158 | library foo; 159 | 160 | const String foo = 'foo'; 161 | 162 | class Bar { 163 | const Bar(); 164 | } 165 | 166 | class Foo { 167 | static foo = 'Foo.foo'; 168 | static bar = const Bar(); 169 | } 170 | ''', 171 | // Mock out the Initialize package plus some initializers. 172 | 'initialize|lib/initialize.dart': mockInitialize, 173 | 'test_initializers|lib/common.dart': commonInitializers, 174 | }, { 175 | 'a|web/index.initialize.dart': formatter.format(''' 176 | import 'package:initialize/src/static_loader.dart'; 177 | import 'package:initialize/initialize.dart'; 178 | import 'index.dart' as i0; 179 | import 'package:test_initializers/common.dart' as i1; 180 | import 'foo.dart' as i2; 181 | 182 | main() { 183 | initializers.addAll([ 184 | new InitEntry(const i1.DynamicInit(i2.foo), const LibraryIdentifier(#web_foo, null, 'index.dart')), 185 | new InitEntry(const i1.DynamicInit('_foo'), const LibraryIdentifier(#web_foo, null, 'index.dart')), 186 | new InitEntry(const i1.DynamicInit(i2.Foo.foo), const LibraryIdentifier(#web_foo, null, 'index.dart')), 187 | new InitEntry(const i1.DynamicInit(i2.Foo.bar), const LibraryIdentifier(#web_foo, null, 'index.dart')), 188 | new InitEntry(const i1.DynamicInit(i2.Foo.foo), const LibraryIdentifier(#web_foo, null, 'index.dart')), 189 | new InitEntry(const i1.DynamicInit(const [i2.foo, i2.Foo.foo, 'foo']), const LibraryIdentifier(#web_foo, null, 'index.dart')), 190 | new InitEntry(const i1.DynamicInit(const {'foo': i2.foo, 'Foo.foo': i2.Foo.foo, 'bar': 'bar'}), const LibraryIdentifier(#web_foo, null, 'index.dart')), 191 | new InitEntry(const i1.DynamicInit('foo'), const LibraryIdentifier(#web_foo, null, 'index.dart')), 192 | new InitEntry(const i1.DynamicInit(true), const LibraryIdentifier(#web_foo, null, 'index.dart')), 193 | new InitEntry(const i1.DynamicInit(null), const LibraryIdentifier(#web_foo, null, 'index.dart')), 194 | new InitEntry(const i1.DynamicInit(1), const LibraryIdentifier(#web_foo, null, 'index.dart')), 195 | new InitEntry(const i1.DynamicInit(1.1), const LibraryIdentifier(#web_foo, null, 'index.dart')), 196 | new InitEntry(const i1.DynamicInit('foo-xy'), const LibraryIdentifier(#web_foo, null, 'index.dart')), 197 | new InitEntry(const i1.DynamicInit(3), const LibraryIdentifier(#web_foo, null, 'index.dart')), 198 | new InitEntry(const i1.DynamicInit(1.2), const LibraryIdentifier(#web_foo, null, 'index.dart')), 199 | new InitEntry(const i1.DynamicInit(true), const LibraryIdentifier(#web_foo, null, 'index.dart')), 200 | new InitEntry(const i1.NamedArgInit(1, name: 'Bill'), const LibraryIdentifier(#web_foo, null, 'index.dart')), 201 | ]); 202 | 203 | return i0.main(); 204 | } 205 | ''') 206 | }, []); 207 | 208 | testPhases('exported library annotations', phases, { 209 | 'a|web/index.dart': ''' 210 | library web_foo; 211 | 212 | export 'foo.dart'; 213 | ''', 214 | 'a|web/foo.dart': ''' 215 | @constInit 216 | library foo; 217 | 218 | import 'package:test_initializers/common.dart'; 219 | 220 | @constInit 221 | foo() {}; 222 | 223 | @constInit 224 | class Foo {} 225 | ''', 226 | // Mock out the Initialize package plus some initializers. 227 | 'initialize|lib/initialize.dart': mockInitialize, 228 | 'test_initializers|lib/common.dart': commonInitializers, 229 | }, { 230 | 'a|web/index.initialize.dart': formatter.format(''' 231 | import 'package:initialize/src/static_loader.dart'; 232 | import 'package:initialize/initialize.dart'; 233 | import 'index.dart' as i0; 234 | import 'foo.dart' as i1; 235 | import 'package:test_initializers/common.dart' as i2; 236 | 237 | main() { 238 | initializers.addAll([ 239 | new InitEntry(i2.constInit, const LibraryIdentifier(#foo, null, 'foo.dart')), 240 | new InitEntry(i2.constInit, i1.foo), 241 | new InitEntry(i2.constInit, i1.Foo), 242 | ]); 243 | 244 | return i0.main(); 245 | } 246 | ''') 247 | }, []); 248 | 249 | testPhases('imports from exported libraries', phases, { 250 | 'a|web/index.dart': ''' 251 | library web_foo; 252 | 253 | export 'foo.dart'; 254 | ''', 255 | 'a|web/foo.dart': ''' 256 | library foo; 257 | 258 | import 'foo/bar.dart'; 259 | ''', 260 | 'a|web/foo/bar.dart': ''' 261 | @constInit 262 | library bar; 263 | 264 | import 'package:test_initializers/common.dart'; 265 | 266 | @constInit 267 | bar() {}; 268 | 269 | @constInit 270 | class Bar {} 271 | ''', 272 | // Mock out the Initialize package plus some initializers. 273 | 'initialize|lib/initialize.dart': mockInitialize, 274 | 'test_initializers|lib/common.dart': commonInitializers, 275 | }, { 276 | 'a|web/index.initialize.dart': formatter.format(''' 277 | import 'package:initialize/src/static_loader.dart'; 278 | import 'package:initialize/initialize.dart'; 279 | import 'index.dart' as i0; 280 | import 'foo/bar.dart' as i1; 281 | import 'package:test_initializers/common.dart' as i2; 282 | 283 | main() { 284 | initializers.addAll([ 285 | new InitEntry(i2.constInit, const LibraryIdentifier(#bar, null, 'foo/bar.dart')), 286 | new InitEntry(i2.constInit, i1.bar), 287 | new InitEntry(i2.constInit, i1.Bar), 288 | ]); 289 | 290 | return i0.main(); 291 | } 292 | ''') 293 | }, []); 294 | 295 | testPhases('library parts and exports', phases, { 296 | 'a|web/index.dart': ''' 297 | @constInit 298 | library index; 299 | 300 | import 'package:test_initializers/common.dart'; 301 | export 'export.dart'; 302 | 303 | part 'foo.dart'; 304 | part 'bar.dart'; 305 | 306 | @constInit 307 | index() {}; 308 | 309 | @constInit 310 | class Index {}; 311 | ''', 312 | 'a|web/foo.dart': ''' 313 | part of index; 314 | 315 | @constInit 316 | foo() {}; 317 | 318 | @constInit 319 | class Foo {}; 320 | ''', 321 | 'a|web/bar.dart': ''' 322 | part of index; 323 | 324 | @constInit 325 | bar() {}; 326 | 327 | @constInit 328 | class Bar {}; 329 | ''', 330 | 'a|web/export.dart': ''' 331 | @constInit 332 | library export; 333 | 334 | import 'package:test_initializers/common.dart'; 335 | 336 | @constInit 337 | class Export {}; 338 | ''', 339 | // Mock out the Initialize package plus some initializers. 340 | 'initialize|lib/initialize.dart': mockInitialize, 341 | 'test_initializers|lib/common.dart': commonInitializers, 342 | }, { 343 | 'a|web/index.initialize.dart': formatter.format(''' 344 | import 'package:initialize/src/static_loader.dart'; 345 | import 'package:initialize/initialize.dart'; 346 | import 'index.dart' as i0; 347 | import 'export.dart' as i1; 348 | import 'package:test_initializers/common.dart' as i2; 349 | 350 | main() { 351 | initializers.addAll([ 352 | new InitEntry(i2.constInit, const LibraryIdentifier(#export, null, 'export.dart')), 353 | new InitEntry(i2.constInit, i1.Export), 354 | new InitEntry(i2.constInit, const LibraryIdentifier(#index, null, 'index.dart')), 355 | new InitEntry(i2.constInit, i0.bar), 356 | new InitEntry(i2.constInit, i0.foo), 357 | new InitEntry(i2.constInit, i0.index), 358 | new InitEntry(i2.constInit, i0.Bar), 359 | new InitEntry(i2.constInit, i0.Foo), 360 | new InitEntry(i2.constInit, i0.Index), 361 | ]); 362 | 363 | return i0.main(); 364 | } 365 | ''') 366 | }, []); 367 | } 368 | 369 | class SkipConstructorsPlugin extends InitializerPlugin { 370 | bool shouldApply(InitializerPluginData data) { 371 | return data.initializer.annotationElement.element is ConstructorElement; 372 | } 373 | 374 | String apply(_) => null; 375 | } 376 | 377 | void pluginTests() { 378 | var phases = [ 379 | [ 380 | new InitializeTransformer(['web/index.dart'], 381 | plugins: [new SkipConstructorsPlugin()]) 382 | ] 383 | ]; 384 | 385 | testPhases('can omit statements', phases, { 386 | 'a|web/index.dart': ''' 387 | library index; 388 | 389 | import 'package:initialize/initialize.dart'; 390 | import 'package:test_initializers/common.dart'; 391 | import 'foo.dart'; 392 | 393 | @initMethod 394 | @DynamicInit('index') 395 | index() {} 396 | ''', 397 | 'a|web/foo.dart': ''' 398 | library foo; 399 | 400 | import 'package:initialize/initialize.dart'; 401 | import 'package:test_initializers/common.dart'; 402 | 403 | @initMethod 404 | @DynamicInit('Foo') 405 | foo() {} 406 | ''', 407 | // Mock out the Initialize package plus some initializers. 408 | 'initialize|lib/initialize.dart': mockInitialize, 409 | 'test_initializers|lib/common.dart': commonInitializers, 410 | }, { 411 | 'a|web/index.initialize.dart': formatter.format(''' 412 | import 'package:initialize/src/static_loader.dart'; 413 | import 'package:initialize/initialize.dart'; 414 | import 'index.dart' as i0; 415 | import 'foo.dart' as i1; 416 | import 'package:initialize/initialize.dart' as i2; 417 | import 'package:test_initializers/common.dart' as i3; 418 | 419 | main() { 420 | initializers.addAll([ 421 | new InitEntry(i2.initMethod, i1.foo), 422 | new InitEntry(i2.initMethod, i0.index), 423 | ]); 424 | 425 | return i0.main(); 426 | } 427 | ''') 428 | }, []); 429 | } 430 | -------------------------------------------------------------------------------- /test_package/README.md: -------------------------------------------------------------------------------- 1 | Initialize tests package 2 | ======================== 3 | 4 | Package used in the `initialize` packages tests. This is just a helper to make 5 | sure that it correctly normalizes urls in mirror mode. 6 | -------------------------------------------------------------------------------- /test_package/lib/bar.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | @initializeTracker 5 | library test_package.bar; 6 | 7 | import 'package:initialize/src/initialize_tracker.dart'; 8 | -------------------------------------------------------------------------------- /test_package/lib/foo.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | @initializeTracker 5 | library test_package.foo; 6 | 7 | import 'bar.dart'; // Keep for the annotation 8 | import 'package:initialize/src/initialize_tracker.dart'; 9 | -------------------------------------------------------------------------------- /test_package/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: test_package 2 | dependencies: 3 | initialize: 4 | path: ../ 5 | -------------------------------------------------------------------------------- /tool/all_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 4 | # for details. All rights reserved. Use of this source code is governed by a 5 | # BSD-style license that can be found in the LICENSE file. 6 | 7 | # Fast fail the script on failures. 8 | set -e 9 | 10 | dartanalyzer --fatal-warnings lib/initialize.dart lib/transformer.dart 11 | 12 | # Run the un-transformed command-line tests. 13 | dart test/deferred_library_test.dart 14 | dart test/init_method_test.dart 15 | dart test/initializer_custom_filter_test.dart 16 | dart test/initializer_cycle_error_test.dart 17 | dart test/initializer_from_test.dart 18 | dart test/initializer_parts_test.dart 19 | dart test/initializer_super_test.dart 20 | dart test/initializer_test.dart 21 | dart test/initializer_type_filter_test.dart 22 | dart test/transformer_test.dart 23 | 24 | pub build test --mode=debug 25 | 26 | # Run the transformed command-line tests. 27 | # TODO(jakemac): Add back once initialize supports deferred libraries. 28 | # dart test/deferred_library_test.dart 29 | dart build/test/init_method_test.initialize.dart 30 | dart build/test/initializer_custom_filter_test.initialize.dart 31 | dart build/test/initializer_test.initialize.dart 32 | dart build/test/initializer_parts_test.initialize.dart 33 | dart build/test/initializer_super_test.initialize.dart 34 | dart build/test/initializer_type_filter_test.initialize.dart 35 | -------------------------------------------------------------------------------- /tool/rename_build_outputs.dart: -------------------------------------------------------------------------------- 1 | library initialize.tool.rename_build_outputs; 2 | 3 | import 'dart:io'; 4 | 5 | import 'package:path/path.dart'; 6 | 7 | main() { 8 | var scriptPath = Platform.script.path; 9 | if (context.style.name == 'windows') scriptPath = scriptPath.substring(1); 10 | var dir = join(dirname(dirname(scriptPath)), 'build', 'test'); 11 | for (var file in new Directory(dir).listSync()) { 12 | var filepath = file.path; 13 | var name = basename(filepath); 14 | if (name.endsWith('.initialize.dart')) { 15 | var newPath = join(dirname(filepath), 16 | name.replaceFirst('.initialize.dart', '.initialize_test.dart')); 17 | print('Copying $filepath to $newPath'); 18 | new File(filepath).copySync(newPath); 19 | } 20 | } 21 | } 22 | --------------------------------------------------------------------------------