From df177d2915be6639a2d30d39805985ea9a3e10e6 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Wed, 9 Nov 2016 18:48:41 +0100 Subject: [PATCH] Upgrade webpack to 2.0 Add extract-text-webpack-plugin Refs: https://webpack.js.org/configuration/module/ https://github.com/webpack/webpack/blob/master/schemas/webpackOptionsSchema.json https://github.com/webpack/extract-text-webpack-plugin#readme --- .../extract-text-webpack-plugin-tests.ts | 97 ++++++++++ extract-text-webpack-plugin/index.d.ts | 49 ++++++ extract-text-webpack-plugin/tsconfig.json | 19 ++ webpack/index.d.ts | 159 ++++++++++++++--- webpack/tsconfig.json | 2 +- webpack/webpack-tests.ts | 165 +++++++++--------- 6 files changed, 388 insertions(+), 103 deletions(-) create mode 100644 extract-text-webpack-plugin/extract-text-webpack-plugin-tests.ts create mode 100644 extract-text-webpack-plugin/index.d.ts create mode 100644 extract-text-webpack-plugin/tsconfig.json diff --git a/extract-text-webpack-plugin/extract-text-webpack-plugin-tests.ts b/extract-text-webpack-plugin/extract-text-webpack-plugin-tests.ts new file mode 100644 index 0000000000..62e522a348 --- /dev/null +++ b/extract-text-webpack-plugin/extract-text-webpack-plugin-tests.ts @@ -0,0 +1,97 @@ +import { optimize, Configuration, Plugin } from 'webpack' + +import * as ExtractTextPlugin from 'extract-text-webpack-plugin' + + +let configuration: Configuration + + +configuration = { + // The standard entry point and output config + entry: { + posts: "./posts", + post: "./post", + about: "./about" + }, + output: { + filename: "[name].js", + chunkFilename: "[id].js" + }, + module: { + rules: [ + // Extract css files + { + test: /\.css$/, + loader: ExtractTextPlugin.extract({ + fallbackLoader: "style-loader", + loader: "css-loader", + }) + }, + // Optionally extract less files + // or any other compile-to-css language + { + test: /\.less$/, + loader: ExtractTextPlugin.extract({ + fallbackLoader: "style-loader", + loader: ["css-loader", "less-loader"], + }) + } + // You could also use other loaders the same way. I. e. the autoprefixer-loader + ] + }, + // Use the plugin to specify the resulting filename (and add needed behavior to the compiler) + plugins: [ + new ExtractTextPlugin("[name].css") + ] +}; + +configuration = { + // ... + plugins: [ + new ExtractTextPlugin({ + filename: "style.css", + allChunks: true, + }) + ] +}; + +configuration = { + // ... + plugins: [ + new optimize.CommonsChunkPlugin("commons", "commons.js"), + new ExtractTextPlugin("[name].css") + ] +}; + +configuration = { + // ... + module: { + rules: [ + { test: /\.css$/, loader: ExtractTextPlugin.extract({ + fallbackLoader: "style-loader", + loader: "css-loader" + }) } + ] + }, + plugins: [ + new ExtractTextPlugin("styles.css") + ], +}; + +// multiple extract instances +let extractCSS = new ExtractTextPlugin('stylesheets/[name].css'); +let extractLESS = new ExtractTextPlugin('stylesheets/[name].less'); + +configuration = { + // ... + module: { + rules: [ + { test: /\.scss$/i, loader: extractCSS.extract(['css','sass']) }, + { test: /\.less$/i, loader: extractLESS.extract(['css','less']) }, + ] + }, + plugins: [ + extractCSS, + extractLESS, + ], +}; diff --git a/extract-text-webpack-plugin/index.d.ts b/extract-text-webpack-plugin/index.d.ts new file mode 100644 index 0000000000..21052b08c1 --- /dev/null +++ b/extract-text-webpack-plugin/index.d.ts @@ -0,0 +1,49 @@ +// Type definitions for extract-text-webpack-plugin 2.0.0 +// Project: https://github.com/webpack/extract-text-webpack-plugin +// Definitions by: flying-sheep +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +import { Plugin, OldLoader } from 'webpack' + +/** + * extract-text-webpack-plugin has no support for .options instead of .query yet. + * See https://github.com/webpack/extract-text-webpack-plugin/issues/281 + */ +type Loader = string | OldLoader + +interface ExtractPluginOptions { + /** the filename of the result file. May contain `[name]`, `[id]` and `[contenthash]` */ + filename: string + /** extract from all additional chunks too (by default it extracts only from the initial chunk(s)) */ + allChunks?: boolean + /** disables the plugin */ + disable?: boolean + /** Unique ident for this plugin instance. (For advanced usage only, by default automatically generated) */ + id?: string +} + +interface ExtractOptions { + /** the loader(s) that should be used for converting the resource to a css exporting module */ + loader: Loader | Loader[] + /** the loader(s) that should be used when the css is not extracted (i.e. in an additional chunk when `allChunks: false`) */ + fallbackLoader?: Loader | Loader[] + /** override the `publicPath` setting for this loader */ + publicPath?: string +} + +/** + * Use an `ExtractTextPlugin` instance and a loader returned by `extract` in concert to write files to disk instead of loading them into others. + * Usage example at https://github.com/webpack/extract-text-webpack-plugin#usage-example-with-css + */ +interface ExtractTextPlugin extends Plugin { + /** Create a plugin instance defining the extraction target file(s) for the files loaded by `extract` */ + new (options: string | ExtractPluginOptions): ExtractTextPlugin + /** + * Creates an extracting loader from an existing loader. + * Use the resulting loader in `module.rules`/`module.loaders`. + */ + extract: (loader: Loader | Loader[] | ExtractOptions) => Loader +} + +declare const extractTextPlugin: ExtractTextPlugin +export = extractTextPlugin diff --git a/extract-text-webpack-plugin/tsconfig.json b/extract-text-webpack-plugin/tsconfig.json new file mode 100644 index 0000000000..77520c362d --- /dev/null +++ b/extract-text-webpack-plugin/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "noImplicitAny": true, + "strictNullChecks": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "extract-text-webpack-plugin-tests.ts" + ] +} \ No newline at end of file diff --git a/webpack/index.d.ts b/webpack/index.d.ts index 770fd30225..468634d825 100644 --- a/webpack/index.d.ts +++ b/webpack/index.d.ts @@ -1,4 +1,4 @@ -// Type definitions for webpack 1.12.9 +// Type definitions for webpack 2.0.0 // Project: https://github.com/webpack/webpack // Definitions by: Qubo // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped @@ -63,7 +63,7 @@ declare namespace webpack { /** Add additional plugins to the compiler. */ plugins?: (Plugin | Function)[]; /** Stats options for logging */ - stats?: Configuration; + stats?: compiler.StatsToStringOptions; } interface Entry { @@ -124,13 +124,11 @@ declare namespace webpack { crossOriginLoading?: string | boolean; } - interface Module { - /** A array of automatically applied loaders. */ - loaders?: Loader[]; + interface BaseModule { /** A array of applied pre loaders. */ - preLoaders?: Loader[]; + preLoaders?: Rule[]; /** A array of applied post loaders. */ - postLoaders?: Loader[]; + postLoaders?: Rule[]; /** A RegExp or an array of RegExps. Don’t parse files matching. */ noParse?: RegExp | RegExp[]; unknownContextRequest?: string; @@ -145,6 +143,15 @@ declare namespace webpack { wrappedContextRecursive?: boolean; wrappedContextCritical?: boolean; } + interface OldModule extends BaseModule { + /** An array of automatically applied loaders. */ + loaders: Rule[]; + } + interface NewModule extends BaseModule { + /** An array of rules applied for modules. */ + rules: Rule[]; + } + type Module = OldModule | NewModule; interface Resolve { /** Replace modules by other modules or paths. */ @@ -214,23 +221,129 @@ declare namespace webpack { [nodeBuiltin: string]: boolean | string | undefined; } - type LoaderCondition = string | RegExp | ((absPath: string) => boolean); - - interface Loader { - /** A condition that must not be met */ - exclude?: LoaderCondition | LoaderCondition[]; - /** A condition that must be met */ - include?: LoaderCondition | LoaderCondition[]; - /** A condition that must be met */ - test: LoaderCondition | LoaderCondition[]; - /** A string of “!” separated loaders */ - loader?: string; - /** A array of loaders as string */ - loaders?: string[]; - query?: { - [name: string]: any; - } + interface BaseConditionSpec { + /** The Condition must match. The convention is the provide a string or array of strings here, but it's not enforced. */ + include?: Condition; + /** The Condition must NOT match. The convention is the provide a string or array of strings here, but it's not enforced. */ + exclude?: Condition; } + interface TestConditionSpec extends BaseConditionSpec { + /** The Condition must match. The convention is the provide a RegExp or array of RegExps here, but it's not enforced. */ + test: Condition; + } + interface AndConditionSpec extends BaseConditionSpec { + /** All Conditions must match. */ + and: Condition[]; + } + interface OrConditionSpec extends BaseConditionSpec { + /** Any Condition must match. */ + or: Condition[]; + } + interface NotConditionSpec extends BaseConditionSpec { + /** The Condition must NOT match. */ + not: Condition; + } + type ConditionSpec = TestConditionSpec | OrConditionSpec | AndConditionSpec | NotConditionSpec; + + interface ConditionArray extends Array {} + type Condition = string | RegExp | ((absPath: string) => boolean) | ConditionSpec | ConditionArray; + + interface OldLoader { + loader: string; + query?: { [name: string]: any }; + } + interface NewLoader { + loader: string; + options?: { [name: string]: any }; + } + type Loader = string | OldLoader | NewLoader; + + /** + * There are direct and delegate rules. Direct Rules need a test, Delegate rules delegate to subrules bringing their own. + * Direct rules can optionally contain delegate keys (oneOf, rules). + * + * These types exist to enforce that a rule has the keys `((loader XOR loaders) AND test) OR oneOf OR rules` + */ + interface BaseRule { + /** + * Specifies the category of the loader. No value means normal loader. + * + * There is also an additional category "inlined loader" which are loaders applied inline of the import/require. + * + * All loaders are sorted in the order post, inline, normal, pre and used in this order. + * + * All normal loaders can be omitted (overridden) by prefixing ! in the request. + * + * All normal and pre loaders can be omitted (overridden) by prefixing -! in the request. + * + * All normal, post and pre loaders can be omitted (overridden) by prefixing !! in the request. + * + * Inline loaders and ! prefixes should not be used as they are non-standard. They may be use by loader generated code. + */ + enforce?: 'pre' | 'post'; + /** A condition that must be met */ + test?: Condition | Condition[]; + /** A condition that must not be met */ + exclude?: Condition | Condition[]; + /** A condition that must be met */ + include?: Condition | Condition[]; + /** A Condition matched with the resource. */ + resource?: Condition | Condition[]; + /** A condition matched with the issuer */ + issuer?: Condition | Condition[]; + /** + * An object with parser options. All applied parser options are merged. + * + * For each different parser options object a new parser is created and plugins can apply plugins depending on the parser options. Many of the default plugins apply their parser plugins only if a property in the parser options is not set or true. + */ + parser?: { [optName: string]: any }; + /** An array of Rules that is also used when the Rule matches. */ + rules?: Rule[]; + /** An array of Rules from which only the first matching Rule is used when the Rule matches. */ + oneOf?: Rule[]; + } + interface BaseDirectRule extends BaseRule { + /** A condition that must be met */ + test: Condition | Condition[]; + } + // Direct Rules + interface BaseSingleLoaderRule extends BaseDirectRule { + /** Loader name or an object with name and options */ + loader: Loader; + } + interface OldLoaderRule extends BaseSingleLoaderRule { + /** + * Loader options + * @deprecated: + */ + query?: { [name: string]: any }; + } + interface NewLoaderRule extends BaseSingleLoaderRule { + options?: { [name: string]: any }; + } + type LoaderRule = OldLoaderRule | NewLoaderRule; + interface OldUseRule extends BaseDirectRule { + /** + * A array of loaders. + * @deprecated use `use` instead + */ + loaders: string[]; + } + interface NewUseRule extends BaseDirectRule { + /** A loader or array of loaders */ + use: Loader | Loader[]; + } + type UseRule = OldUseRule | NewUseRule; + + // Delegate Rules + interface RulesRule extends BaseRule { + /** An array of Rules that is also used when the Rule matches. */ + rules: Rule[]; + } + interface OneOfRule extends BaseRule { + oneOf: Rule[]; + } + type Rule = LoaderRule | UseRule | RulesRule | OneOfRule; interface Plugin { } diff --git a/webpack/tsconfig.json b/webpack/tsconfig.json index 67896791c3..d19613deb8 100644 --- a/webpack/tsconfig.json +++ b/webpack/tsconfig.json @@ -3,7 +3,7 @@ "module": "commonjs", "target": "es6", "noImplicitAny": true, - "strictNullChecks": false, + "strictNullChecks": true, "baseUrl": "../", "typeRoots": [ "../" diff --git a/webpack/webpack-tests.ts b/webpack/webpack-tests.ts index a7bb9bf44a..e51a539b24 100644 --- a/webpack/webpack-tests.ts +++ b/webpack/webpack-tests.ts @@ -1,10 +1,10 @@ import * as webpack from 'webpack'; -var configuration: webpack.Configuration; -var loader: webpack.Loader; -var plugin: webpack.Plugin; -declare var __dirname: string; +let configuration: webpack.Configuration; +let rule: webpack.Rule; +let plugin: webpack.Plugin; +declare const __dirname: string; // // https://webpack.github.io/docs/using-loaders.html @@ -24,9 +24,9 @@ configuration = { } }; -loader = { test: /\.png$/, loader: "url-loader?mimetype=image/png" }; +rule = { test: /\.png$/, loader: "url-loader?mimetype=image/png" }; -loader = { +rule = { test: /\.png$/, loader: "url-loader", query: { mimetype: "image/png" } @@ -97,61 +97,6 @@ configuration = { } }; -class ExtractTextPlugin implements webpack.Plugin { - static extract(...loaders: string[]): string { return null; } - constructor(...args: any[]) {} -} - -configuration = { - // The standard entry point and output config - entry: { - posts: "./posts", - post: "./post", - about: "./about" - }, - output: { - filename: "[name].js", - chunkFilename: "[id].js" - }, - module: { - loaders: [ - // Extract css files - { - test: /\.css$/, - loader: ExtractTextPlugin.extract("style-loader", "css-loader") - }, - // Optionally extract less files - // or any other compile-to-css language - { - test: /\.less$/, - loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader") - } - // You could also use other loaders the same way. I. e. the autoprefixer-loader - ] - }, - // Use the plugin to specify the resulting filename (and add needed behavior to the compiler) - plugins: [ - new ExtractTextPlugin("[name].css") - ] -}; - -configuration = { - // ... - plugins: [ - new ExtractTextPlugin("style.css", { - allChunks: true - }) - ] -}; - -configuration = { - // ... - plugins: [ - new webpack.optimize.CommonsChunkPlugin("commons", "commons.js"), - new ExtractTextPlugin("[name].css") - ] -}; - // // https://webpack.github.io/docs/optimization.html // @@ -252,7 +197,7 @@ configuration = { } }; -loader = { +rule = { test: /\.jsx$/, include: [ path.resolve(__dirname, "app/src"), @@ -282,23 +227,22 @@ configuration = { // https://webpack.github.io/docs/list-of-plugins.html // -var plugin: webpack.Plugin; -var resourceRegExp: any; -var newResource: any; -var contextRegExp: any; -var newContentResource: any; -var newContentRecursive: any; -var newContentRegExp: any; -var requestRegExp: any; -var options: any; -var definitions: any; -var paths: any; -var preferEntry = true; -var context: any; -var request: any; -var types: any; -var banner: any; -var plugins: webpack.Plugin[]; +let resourceRegExp: any; +let newResource: any; +let contextRegExp: any; +let newContentResource: any; +let newContentRecursive: any; +let newContentRegExp: any; +let requestRegExp: any; +let options: any; +let definitions: any; +let paths: any; +let preferEntry = true; +let context: any; +let request: any; +let types: any; +let banner: any; +let plugins: webpack.Plugin[] = []; plugin = new webpack.NormalModuleReplacementPlugin(resourceRegExp, newResource); plugin = new webpack.ContextReplacementPlugin( @@ -473,3 +417,66 @@ compiler.run(function(err, stats) { // ... var fileContent = fs.readFileSync("..."); }); + +// +// https://github.com/webpack/webpack/blob/master/test/configCases/rule-set/simple/webpack.config.js +// + +rule = { + test: { + or: [ + require.resolve("./a"), + require.resolve("./c"), + ] + }, + loader: "./loader", + options: "third" +} + +configuration = { + module: { + rules: [ + { oneOf: [ + { + test: { + and: [ + /a.\.js$/, + /b\.js$/ + ] + }, + loader: "./loader?first" + }, + { + test: [ + require.resolve("./a"), + require.resolve("./c"), + ], + issuer: require.resolve("./b"), + use: [ + "./loader?second-1", + { + loader: "./loader", + options: "second-2" + }, + { + loader: "./loader", + options: { + get: function() { return "second-3"; } + } + } + ] + }, + { + test: { + or: [ + require.resolve("./a"), + require.resolve("./c"), + ] + }, + loader: "./loader", + options: "third" + } + ]} + ] + } +}