diff --git a/package.json b/package.json index 1786242..c8aadaa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aurelia-webpack-plugin", - "version": "2.0.0-rc.5", + "version": "2.0.0-rc.6", "description": "A plugin for webpack that enables bundling Aurelia applications.", "keywords": [ "aurelia", @@ -35,17 +35,17 @@ }, "dependencies": { "aurelia-loader-webpack": "^2.1.0", - "bundle-loader": "^0.5.5", - "html-loader": "^0.4.5", + "bundle-loader": "^0.5.6", + "html-loader": "^0.5.5", "minimatch": "^3.0.4" }, "peerDependencies": { - "webpack": ">= 2.2.0" + "webpack": ">= 4.0.0" }, "devDependencies": { - "@types/minimatch": "^2.0.29", - "@types/node": "^7.0.28", - "typescript": "^2.3.4" + "@types/minimatch": "^3.0.3", + "@types/node": "^9.4.6", + "typescript": "^2.7.2" }, "aurelia": { "documentation": { diff --git a/src/AureliaDependenciesPlugin.ts b/src/AureliaDependenciesPlugin.ts index 3224bd0..1fbb8da 100644 --- a/src/AureliaDependenciesPlugin.ts +++ b/src/AureliaDependenciesPlugin.ts @@ -1,6 +1,8 @@ import { IncludeDependency } from "./IncludeDependency"; import BasicEvaluatedExpression = require("webpack/lib/BasicEvaluatedExpression"); +const TAP_NAME = "Aurelia:Dependencies"; + class AureliaDependency extends IncludeDependency { constructor(request: string, public range: [number, number], @@ -25,16 +27,18 @@ class ParserPlugin { let dep = new AureliaDependency(module, range, options); parser.state.current.addDependency(dep); return true; - } + } // The parser will only apply "call PLATFORM.moduleName" on free variables. // So we must first trick it into thinking PLATFORM.moduleName is an unbound identifier // in the various situations where it is not. + const hooks = parser.hooks; + // This covers native ES module, for example: // import { PLATFORM } from "aurelia-pal"; // PLATFORM.moduleName("id"); - parser.plugin("evaluate Identifier imported var.moduleName", (expr: Webpack.MemberExpression) => { + hooks.evaluateIdentifier.tap("imported var.moduleName", TAP_NAME, (expr: Webpack.MemberExpression) => { if (expr.property.name === "moduleName" && expr.object.name === "PLATFORM" && expr.object.type === "Identifier") { @@ -49,7 +53,7 @@ class ParserPlugin { // Or (note: no renaming supported): // const PLATFORM = require("aurelia-pal").PLATFORM; // PLATFORM.moduleName("id"); - parser.plugin("evaluate MemberExpression", (expr: Webpack.MemberExpression) => { + hooks.evaluate.tap("MemberExpression", TAP_NAME, expr => { if (expr.property.name === "moduleName" && (expr.object.type === "MemberExpression" && expr.object.property.name === "PLATFORM" || expr.object.type === "Identifier" && expr.object.name === "PLATFORM")) { @@ -59,7 +63,7 @@ class ParserPlugin { }); for (let method of this.methods) { - parser.plugin("call " + method, (expr: Webpack.CallExpression) => { + hooks.call.tap(method, TAP_NAME, (expr: Webpack.CallExpression) => { if (expr.arguments.length === 0 || expr.arguments.length > 2) return; @@ -119,14 +123,14 @@ export class AureliaDependenciesPlugin { } apply(compiler: Webpack.Compiler) { - compiler.plugin("compilation", (compilation, params) => { + compiler.hooks.compilation.tap(TAP_NAME, (compilation, params) => { const normalModuleFactory = params.normalModuleFactory; compilation.dependencyFactories.set(AureliaDependency, normalModuleFactory); compilation.dependencyTemplates.set(AureliaDependency, new Template()); - normalModuleFactory.plugin("parser", parser => { - parser.apply(this.parserPlugin); + normalModuleFactory.hooks.parser.for("javascript/auto").tap(TAP_NAME, parser => { + this.parserPlugin.apply(parser); }); }); } diff --git a/src/AureliaPlugin.ts b/src/AureliaPlugin.ts index 716cff0..bf84a97 100644 --- a/src/AureliaPlugin.ts +++ b/src/AureliaPlugin.ts @@ -140,12 +140,11 @@ export class AureliaPlugin { if (opts.includeAll) { // Grab everything approach - compiler.apply( - // This plugin ensures that everything in /src is included in the bundle. - // This prevents splitting in several chunks but is super easy to use and setup, - // no change in existing code or PLATFORM.nameModule() calls are required. - new GlobDependenciesPlugin({ [emptyEntryModule]: opts.includeAll + "/**" }) - ); + + // This plugin ensures that everything in /src is included in the bundle. + // This prevents splitting in several chunks but is super easy to use and setup, + // no change in existing code or PLATFORM.nameModule() calls are required. + new GlobDependenciesPlugin({ [emptyEntryModule]: opts.includeAll + "/**" }).apply(compiler); needsEmptyEntry = true; } else if (opts.aureliaApp) { @@ -160,7 +159,7 @@ export class AureliaPlugin { let aureliaModules = dllRefPlugins.map(plugin => { let content = plugin["options"].manifest.content; return Object.keys(content) - .map(k => content[k].meta["aurelia-id"]) + .map(k => content[k].buildMeta["aurelia-id"]) .filter(id => !!id) as string[]; }); globalDependencies = globalDependencies.concat(...aureliaModules); @@ -181,7 +180,7 @@ export class AureliaPlugin { } if (!opts.noInlineView) { - compiler.apply(new InlineViewDependenciesPlugin()); + new InlineViewDependenciesPlugin().apply(compiler); } if (globalDependencies.length > 0) { @@ -192,27 +191,25 @@ export class AureliaPlugin { if (needsEmptyEntry) { this.addEntry(compiler.options, emptyEntryModule); } - - compiler.apply( - // Aurelia libs contain a few global defines to cut out unused features - new DefinePlugin(defines), - // Adds some dependencies that are not documented by `PLATFORM.moduleName` - new ModuleDependenciesPlugin(dependencies), - // This plugin traces dependencies in code that are wrapped in PLATFORM.moduleName() calls - new AureliaDependenciesPlugin(...opts.moduleMethods), - // This plugin adds dependencies traced by html-requires-loader - // Note: the config extension point for this one is html-requires-loader.attributes. - new HtmlDependenciesPlugin(), - // This plugin looks for companion files by swapping extensions, - // e.g. the view of a ViewModel. @useView and co. should use PLATFORM.moduleName(). - // We use it always even with `includeAll` because libs often don't `@useView` (they should). - new ConventionDependenciesPlugin(opts.viewsFor, opts.viewsExtensions), - // This plugin preserves module names for dynamic loading by aurelia-loader - new PreserveModuleNamePlugin(dllPlugin), - // This plugin supports preserving specific exports names when dynamically loading modules - // with aurelia-loader, while still enabling tree shaking all other exports. - new PreserveExportsPlugin(), - ); + + // Aurelia libs contain a few global defines to cut out unused features + new DefinePlugin(defines).apply(compiler); + // Adds some dependencies that are not documented by `PLATFORM.moduleName` + new ModuleDependenciesPlugin(dependencies).apply(compiler); + // This plugin traces dependencies in code that are wrapped in PLATFORM.moduleName() calls + new AureliaDependenciesPlugin(...opts.moduleMethods).apply(compiler); + // This plugin adds dependencies traced by html-requires-loader + // Note: the config extension point for this one is html-requires-loader.attributes. + new HtmlDependenciesPlugin().apply(compiler); + // This plugin looks for companion files by swapping extensions, + // e.g. the view of a ViewModel. @useView and co. should use PLATFORM.moduleName(). + // We use it always even with `includeAll` because libs often don't `@useView` (they should). + new ConventionDependenciesPlugin(opts.viewsFor, opts.viewsExtensions).apply(compiler); + // This plugin preserves module names for dynamic loading by aurelia-loader + new PreserveModuleNamePlugin(dllPlugin).apply(compiler); + // This plugin supports preserving specific exports names when dynamically loading modules + // with aurelia-loader, while still enabling tree shaking all other exports. + new PreserveExportsPlugin().apply(compiler); } addEntry(options: Webpack.Options, modules: string|string[]) { diff --git a/src/BaseIncludePlugin.ts b/src/BaseIncludePlugin.ts index 73d512c..fa09683 100644 --- a/src/BaseIncludePlugin.ts +++ b/src/BaseIncludePlugin.ts @@ -1,16 +1,18 @@ import { IncludeDependency } from "./IncludeDependency"; import NullDependency = require("webpack/lib/dependencies/NullDependency"); +const TAP_NAME = "Aurelia:BaseInclude"; + export type AddDependency = (request: string | DependencyOptionsEx) => void; export class BaseIncludePlugin { apply(compiler: Webpack.Compiler) { - compiler.plugin("compilation", (compilation, data) => { + compiler.hooks.compilation.tap(TAP_NAME, (compilation, data) => { const normalModuleFactory = data.normalModuleFactory; compilation.dependencyFactories.set(IncludeDependency, normalModuleFactory); compilation.dependencyTemplates.set(IncludeDependency, new NullDependency.Template()); - normalModuleFactory.plugin("parser", parser => { + normalModuleFactory.hooks.parser.for("javascript/auto").tap(TAP_NAME, parser => { function addDependency(request: string | DependencyOptionsEx) { let options = typeof request === 'object' ? request : undefined; let name = options ? options.name : (request); diff --git a/src/ConventionDependenciesPlugin.ts b/src/ConventionDependenciesPlugin.ts index 5dbdf19..625b876 100644 --- a/src/ConventionDependenciesPlugin.ts +++ b/src/ConventionDependenciesPlugin.ts @@ -23,7 +23,7 @@ export class ConventionDependenciesPlugin extends BaseIncludePlugin { parser(compilation: Webpack.Compilation, parser: Webpack.Parser, addDependency: AddDependency) { const root = path.resolve(); - parser.plugin("program", () => { + parser.hooks.program.tap("Aurelia:ConventionDependencies", () => { const { resource: file, rawRequest } = parser.state.current; if (!file) return; // We don't want to bring in dependencies of the async! loader diff --git a/src/DistPlugin.ts b/src/DistPlugin.ts index bf9caa7..5674cb2 100644 --- a/src/DistPlugin.ts +++ b/src/DistPlugin.ts @@ -19,12 +19,13 @@ export class DistPlugin { apply(resolver: Webpack.Resolver) { if (!this.dist) return; - resolver.plugin("before-described-resolve", (request, cb) => { + resolver.getHook("before-described-resolve") + .tapAsync("Aurelia:Dist", (request: Webpack.ResolveRequest, resolveContext: object, cb: (err?: any, result?: any) => void) => { // If the request contains /dist/xxx/, try /dist/{dist}/ first let rewritten = request.request.replace(/\/dist\/[^/]+\//i, this.dist); if (rewritten !== request.request) { let newRequest = Object.assign({}, request, { request: rewritten }); - resolver.doResolve("described-resolve", newRequest, "try alternate " + this.dist, cb); + resolver.doResolve(resolver.getHook("described-resolve"), newRequest, "try alternate " + this.dist, {}, cb); } else cb(); // Path does not contain /dist/xxx/, continue normally diff --git a/src/GlobDependenciesPlugin.ts b/src/GlobDependenciesPlugin.ts index c2ab3e8..0abe186 100644 --- a/src/GlobDependenciesPlugin.ts +++ b/src/GlobDependenciesPlugin.ts @@ -2,6 +2,8 @@ import { BaseIncludePlugin, AddDependency } from "./BaseIncludePlugin"; import { Minimatch } from "minimatch"; import path = require("path"); +const TAP_NAME = "Aurelia:GlobDependencies"; + declare module "minimatch" { interface IMinimatch { match(fname: string, partial: boolean): boolean; // Missing overload in current minimatch tds @@ -56,19 +58,18 @@ export class GlobDependenciesPlugin extends BaseIncludePlugin { const hashKeys = Object.getOwnPropertyNames(this.hash); if (hashKeys.length === 0) return; - compiler.plugin("before-compile", (params, cb) => { + compiler.hooks.beforeCompile.tapPromise(TAP_NAME, () => { // Map the modules passed in ctor to actual resources (files) so that we can // recognize them no matter what the rawRequest was (loaders, relative paths, etc.) this.modules = { }; - const resolve: (request: string, cb: (err: any, resource: string) => void) => void = - compiler.resolvers.normal.resolve.bind(compiler.resolvers.normal, null, this.root); - let countdown = hashKeys.length; - for (let module of hashKeys) { - resolve(module, (err, resource) => { - this.modules[resource] = this.hash[module]; - if (--countdown === 0) cb(); - }); - } + const resolver = compiler.resolverFactory.get("normal", {}); + return Promise.all( + hashKeys.map(module => new Promise(resolve => { + resolver.resolve(null, this.root, module, {}, (err, resource) => { + this.modules[resource] = this.hash[module]; + resolve(); + }); + }))); }); super.apply(compiler); @@ -82,7 +83,7 @@ export class GlobDependenciesPlugin extends BaseIncludePlugin { .filter(x => !x.startsWith("..")) .map(x => new RegExp("^" + x + "/", "ig")); - parser.plugin("program", () => { + parser.hooks.program.tap(TAP_NAME, () => { const globs = this.modules[parser.state.module.resource]; if (!globs) return; diff --git a/src/HtmlDependenciesPlugin.ts b/src/HtmlDependenciesPlugin.ts index 12dc042..3fce17a 100644 --- a/src/HtmlDependenciesPlugin.ts +++ b/src/HtmlDependenciesPlugin.ts @@ -3,7 +3,7 @@ import { htmlSymbol } from "./html-requires-loader"; export class HtmlDependenciesPlugin extends BaseIncludePlugin { parser(compilation: Webpack.Compilation, parser: Webpack.Parser, addDependency: AddDependency) { - parser.plugin("program", () => { + parser.hooks.program.tap("Aurelia:HtmlDependencies", () => { const deps = parser.state.current[htmlSymbol]; if (!deps) return; deps.forEach(addDependency); diff --git a/src/InlineViewDependenciesPlugin.ts b/src/InlineViewDependenciesPlugin.ts index 3161450..91a3ac5 100644 --- a/src/InlineViewDependenciesPlugin.ts +++ b/src/InlineViewDependenciesPlugin.ts @@ -4,6 +4,8 @@ import { BaseIncludePlugin, AddDependency } from "./BaseIncludePlugin"; import BasicEvaluatedExpression = require("webpack/lib/BasicEvaluatedExpression"); import htmlLoader = require("./html-requires-loader"); +const TAP_NAME = "Aurelia:InlineViewDependencies"; + export class InlineViewDependenciesPlugin extends BaseIncludePlugin { parser(compilation: Webpack.Compilation, parser: Webpack.Parser, add: AddDependency) { // The parser will only apply "call inlineView" on free variables. @@ -13,7 +15,7 @@ export class InlineViewDependenciesPlugin extends BaseIncludePlugin { // This covers native ES module, for example: // import { inlineView } from "aurelia-framework"; // inlineView("