mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-22 17:47:53 +00:00
Update aspnet-webpack-react to v2.0.0, now supporting Webpack 2+ and React Hot Loader 3+ only
This commit is contained in:
@@ -4,3 +4,8 @@ This NPM package is an internal implementation detail of the `Microsoft.AspNetCo
|
|||||||
|
|
||||||
You should not use this package directly in your own applications, because it is not supported, and there are no
|
You should not use this package directly in your own applications, because it is not supported, and there are no
|
||||||
guarantees about how its APIs will change in the future.
|
guarantees about how its APIs will change in the future.
|
||||||
|
|
||||||
|
## History
|
||||||
|
|
||||||
|
* Version 1.x amends the Webpack config to insert `react-transform` and `react-transform-hmr` entries on `babel-loader`.
|
||||||
|
* Version 2.x drops support for the Babel plugin, and instead amends the Webpack config to insert `react-hot-loader/webpack` and `react-hot-loader/patch` entries. This means it works with React Hot Loader v3.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aspnet-webpack-react",
|
"name": "aspnet-webpack-react",
|
||||||
"version": "1.0.5",
|
"version": "2.0.0",
|
||||||
"description": "Helpers for using Webpack with React in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
|
"description": "Helpers for using Webpack with React in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -16,22 +16,13 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/aspnet/JavaScriptServices.git"
|
"url": "https://github.com/aspnet/JavaScriptServices.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
|
||||||
"babel-core": "^6.7.2",
|
|
||||||
"babel-loader": "^6.2.4",
|
|
||||||
"babel-plugin-react-transform": "^2.0.2",
|
|
||||||
"babel-preset-es2015": "^6.6.0",
|
|
||||||
"babel-preset-react": "^6.5.0",
|
|
||||||
"react": "^15.0.0",
|
|
||||||
"react-transform-hmr": "^1.0.4"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/webpack": "^2.2.0",
|
"@types/webpack": "^2.2.0",
|
||||||
"rimraf": "^2.5.4",
|
"rimraf": "^2.5.4",
|
||||||
"typescript": "^2.0.0",
|
"typescript": "^2.0.0",
|
||||||
"webpack": "^1.12.14"
|
"webpack": "^2.2.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"webpack": "^1.13.2 || ^2.2.0"
|
"webpack": "^2.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +1,66 @@
|
|||||||
import * as webpack from 'webpack';
|
import * as webpack from 'webpack';
|
||||||
type OldOrNewModule = webpack.OldModule & webpack.NewModule;
|
|
||||||
|
|
||||||
export function addReactHotModuleReplacementBabelTransform(webpackConfig: webpack.Configuration) {
|
const reactHotLoaderWebpackLoader = 'react-hot-loader/webpack';
|
||||||
const moduleConfig = webpackConfig.module as OldOrNewModule;
|
const reactHotLoaderPatch = 'react-hot-loader/patch';
|
||||||
const moduleRules = moduleConfig.rules // Webpack >= 2.1.0 beta 23
|
const supportedTypeScriptLoaders = ['ts-loader', 'awesome-typescript-loader'];
|
||||||
|| moduleConfig.loaders; // Legacy/back-compat
|
|
||||||
|
export function addReactHotModuleReplacementConfig(webpackConfig: webpack.Configuration) {
|
||||||
|
const moduleConfig = webpackConfig.module as webpack.NewModule;
|
||||||
|
const moduleRules = moduleConfig.rules;
|
||||||
if (!moduleRules) {
|
if (!moduleRules) {
|
||||||
return; // Unknown rules list format
|
return; // Unknown rules list format. Might be Webpack 1.x, which is not supported.
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleRules.forEach(rule => {
|
// Find the rule that loads TypeScript files, and prepend 'react-hot-loader/webpack'
|
||||||
// Allow rules/loaders entries to be either { loader: ... } or { use: ... }
|
// to its array of loaders
|
||||||
// Ignore other config formats (too many combinations to support them all)
|
for (let ruleIndex = 0; ruleIndex < moduleRules.length; ruleIndex++) {
|
||||||
let loaderConfig =
|
// We only support NewUseRule (i.e., { use: ... }) because OldUseRule doesn't accept array values
|
||||||
(rule as webpack.NewUseRule).use // Recommended config format for Webpack 2.x
|
const rule = moduleRules[ruleIndex] as webpack.NewUseRule;
|
||||||
|| (rule as webpack.LoaderRule).loader; // Typical config format for Webpack 1.x
|
if (!rule.use) {
|
||||||
if (!loaderConfig) {
|
continue;
|
||||||
return; // Not a supported rule format (e.g., an array)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow use/loader values to be either { loader: 'name' } or 'name'
|
// We're looking for the first 'use' value that's a TypeScript loader
|
||||||
// We don't need to support other possible ways of specifying loaders (e.g., arrays),
|
const loadersArray = rule.use instanceof Array ? rule.use : [rule.use];
|
||||||
// so skip unrecognized formats.
|
const isTypescriptLoader = supportedTypeScriptLoaders.some(typeScriptLoaderName => containsLoader(loadersArray, typeScriptLoaderName));
|
||||||
const loaderNameString =
|
if (!isTypescriptLoader) {
|
||||||
(loaderConfig as (webpack.OldLoader | webpack.NewLoader)).loader
|
continue;
|
||||||
|| (loaderConfig as string);
|
|
||||||
if (!loaderNameString || (typeof loaderNameString !== 'string')) {
|
|
||||||
return; // Not a supported loader format (e.g., an array)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the babel-loader entry
|
// This is the one - prefix it with the react-hot-loader loader
|
||||||
if (loaderNameString.match(/\bbabel-loader\b/)) {
|
// (unless it's already in there somewhere)
|
||||||
// If the rule is of the form { use: 'name' }, then replace it
|
if (!containsLoader(loadersArray, reactHotLoaderWebpackLoader)) {
|
||||||
// with { use: { loader: 'name' }} so we can attach options
|
loadersArray.unshift(reactHotLoaderWebpackLoader);
|
||||||
if ((rule as webpack.NewUseRule).use && typeof loaderConfig === 'string') {
|
rule.use = loadersArray; // In case we normalised it to an array
|
||||||
loaderConfig = (rule as webpack.NewUseRule).use = { loader: loaderConfig };
|
}
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const configItemWithOptions = typeof loaderConfig === 'string'
|
// Ensure the entrypoint is prefixed with 'react-hot-loader/patch' (unless it's already in there).
|
||||||
? rule // The rule is of the form { loader: 'name' }, so put options on the rule
|
// We only support entrypoints of the form { name: value } (not just 'name' or ['name'])
|
||||||
: loaderConfig; // The rule is of the form { use/loader: { loader: 'name' }}, so put options on the use/loader
|
// because that gives us a place to prepend the new value
|
||||||
|
if (!webpackConfig.entry || typeof webpackConfig.entry === 'string' || webpackConfig.entry instanceof Array) {
|
||||||
|
throw new Error('Cannot enable React HMR because \'entry\' in Webpack config is not of the form { name: value }');
|
||||||
|
}
|
||||||
|
const entryConfig = webpackConfig.entry as webpack.Entry;
|
||||||
|
Object.getOwnPropertyNames(entryConfig).forEach(entrypointName => {
|
||||||
|
if (typeof(entryConfig[entrypointName]) === 'string') {
|
||||||
|
// Normalise to array
|
||||||
|
entryConfig[entrypointName] = [entryConfig[entrypointName] as string];
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the config has an 'options' (or a legacy 'query')
|
let entryValueArray = entryConfig[entrypointName] as string[];
|
||||||
let optionsObject =
|
if (entryValueArray.indexOf(reactHotLoaderPatch) < 0) {
|
||||||
(configItemWithOptions as webpack.NewLoader).options // Recommended config format for Webpack 2.x
|
entryValueArray.unshift(reactHotLoaderPatch);
|
||||||
|| (configItemWithOptions as webpack.OldLoaderRule).query; // Legacy
|
|
||||||
if (!optionsObject) {
|
|
||||||
// If neither options nor query was set, define a new value,
|
|
||||||
// using the legacy format ('query') for compatibility with Webpack 1.x
|
|
||||||
optionsObject = (configItemWithOptions as webpack.OldLoaderRule).query = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure Babel plugins includes 'react-transform'
|
|
||||||
const plugins = optionsObject['plugins'] = optionsObject['plugins'] || [];
|
|
||||||
const hasReactTransform = plugins.some(p => p && p[0] === 'react-transform');
|
|
||||||
if (!hasReactTransform) {
|
|
||||||
plugins.push(['react-transform', {}]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure 'react-transform' plugin is configured to use 'react-transform-hmr'
|
|
||||||
plugins.forEach(pluginConfig => {
|
|
||||||
if (pluginConfig && pluginConfig[0] === 'react-transform') {
|
|
||||||
const pluginOpts = pluginConfig[1] = pluginConfig[1] || {};
|
|
||||||
const transforms = pluginOpts.transforms = pluginOpts.transforms || [];
|
|
||||||
const hasReactTransformHmr = transforms.some(t => t.transform === 'react-transform-hmr');
|
|
||||||
if (!hasReactTransformHmr) {
|
|
||||||
transforms.push({
|
|
||||||
transform: 'react-transform-hmr',
|
|
||||||
imports: ['react'],
|
|
||||||
locals: ['module'] // Important for Webpack HMR
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function containsLoader(loadersArray: webpack.Loader[], loaderName: string) {
|
||||||
|
return loadersArray.some(loader => {
|
||||||
|
// Allow 'use' values to be either { loader: 'name' } or 'name'
|
||||||
|
// No need to support legacy webpack.OldLoader
|
||||||
|
const actualLoaderName = (loader as webpack.NewLoader).loader || (loader as string);
|
||||||
|
return actualLoaderName && new RegExp(`\\b${ loaderName }\\b`).test(actualLoaderName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,6 @@
|
|||||||
export { addReactHotModuleReplacementBabelTransform } from './HotModuleReplacement';
|
export { addReactHotModuleReplacementConfig } from './HotModuleReplacement';
|
||||||
|
|
||||||
|
// Temporarily alias addReactHotModuleReplacementConfig as addReactHotModuleReplacementBabelTransform for backward
|
||||||
|
// compatibility with aspnet-webpack 1.x. In aspnet-webpack 2.0, we can drop the old name (and also deprecate
|
||||||
|
// some other no-longer-supported functionality, such as LoadViaWebpack).
|
||||||
|
export { addReactHotModuleReplacementConfig as addReactHotModuleReplacementBabelTransform } from './HotModuleReplacement';
|
||||||
|
|||||||
Reference in New Issue
Block a user