我的项目是基于Create React App,但我不得不退出几个原因。尽管如此,我还是尽力手动保持它符合当前的CRA标准。不过,我的最后一次更新不太顺利。当尝试运行"start"时,我得到以下错误命令:
./apps/index.tsx
Error: [BABEL] ./apps/index.tsx: .presets[1][0] must be a string, object, function
at Array.forEach (<anonymous>)
at Array.forEach (<anonymous>)
我手动添加了一些日志到。/node_modules/@babel/core/lib/config/validation/option- asserations .js,这给了我以下信息:
LOG "assertPluginItem ERROR inside IF block:"
LOG "Item access(loc, 0)"
{
type: 'access',
name: 0,
parent: {
type: 'access',
name: 1,
parent: { type: 'option', name: 'presets', parent: [Object] }
}
}
LOG "Item loc"
{
type: 'access',
name: 1,
parent: {
type: 'option',
name: 'presets',
parent: { type: 'root', source: 'preset' }
}
}
LOG "Item value"
[ undefined, { development: true, runtime: 'automatic' } ]
LOG "pluginLoc"
{
type: 'option',
name: 'presets',
parent: { type: 'root', source: 'preset' }
}
LOG "pluginValue"
[
[ [Function], { useBuiltIns: 'entry', corejs: 3, exclude: [Array] } ],
[ undefined, { development: true, runtime: 'automatic' } ],
[ [Function] ]
]
很明显,问题是这个"未定义"。在[ undefined, { development: true, runtime: 'automatic' } ]
中,但基于传递给这个失败插件的选项,代码不在我的设置文件中,而是在Babel内部的某个地方。
我试过在Babel核心的各种文件中添加日志,但没有成功,我想不出哪个插件失败了,为什么。你有什么好主意能帮我吗?
我的webpack.config.js如下:
const path = require('path');
const webpack = require('webpack');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const getTheme = require('../theme');
const paths = require('../paths');
const getClientEnvironment = require('../env/getClientEnvironment');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const postcssNormalize = require('postcss-normalize');
const appPackageJson = require(paths.appPackageJson);
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
const webpackDevClientEntry = require.resolve('react-dev-utils/webpackHotDevClient');
const reactRefreshOverlayEntry = require.resolve('react-dev-utils/refreshOverlayInterop');
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
const cssRegex = /.css$/;
const hasJsxRuntime = (() => {
if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
return false;
}
try {
require.resolve('react/jsx-runtime');
return true;
}
catch (e) {
return false;
}
})();
module.exports = function getConfig(appName, webpackEnv = 'development', isHotModuleReloadEnabled = true) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
const isEnvProductionProfile =
isEnvProduction && process.argv.includes('--profile');
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
const shouldUseReactRefresh = env.raw.FAST_REFRESH;
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && require.resolve('style-loader'),
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
options: paths.publicUrlOrPath.startsWith('.')
? { publicPath: '../../' }
: {},
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
postcssNormalize(),
],
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
},
},
].filter(Boolean);
if (preProcessor) {
loaders.push(
{
loader: require.resolve('resolve-url-loader'),
options: {
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
root: paths.appSrc,
},
},
{
loader: require.resolve(preProcessor),
options: {
sourceMap: true,
globalVars: getTheme(appName),
},
},
);
}
return loaders;
};
const alias = {
...paths.alias,
app: paths.getAppPath(appName),
};
return {
stats: 'verbose',
name: appName,
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
bail: isEnvProduction,
devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map'
: false
: isEnvDevelopment && 'cheap-module-source-map',
entry:
isEnvDevelopment && !shouldUseReactRefresh
? [
webpackDevClientEntry,
paths.appStyles,
paths.appIndexJs,
]
: [
paths.appStyles,
paths.appIndexJs,
],
output: {
path: isEnvProduction ? paths.appBuild(appName) : undefined,
pathinfo: isEnvDevelopment,
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
futureEmitAssets: true,
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
publicPath: paths.publicUrlOrPath,
devtoolModuleFilenameTemplate: isEnvProduction
? (info) =>
path
.relative(paths.appsPath, info.absoluteResourcePath)
.replace(/\/g, '/')
: isEnvDevelopment &&
((info) => path.resolve(info.absoluteResourcePath).replace(/\/g, '/')),
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
globalObject: 'this',
},
optimization: {
minimize: isEnvProduction,
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
keep_classnames: isEnvProductionProfile, // eslint-disable-line camelcase
keep_fnames: isEnvProductionProfile, // eslint-disable-line camelcase
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
sourceMap: shouldUseSourceMap,
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: shouldUseSourceMap
? {
inline: false,
annotation: true,
}
: false,
},
cssProcessorPluginOptions: {
preset: ['default', { minifyFontValues: { removeQuotes: false } }],
},
}),
],
splitChunks: {
cacheGroups: {
app: {
test: /[\/]apps[\/]/,
name: 'app',
chunks: 'all',
},
shared: {
test: /[\/]shared[\/]/,
name: 'shared',
chunks: 'all',
},
libs: {
test: /[\/]libs[\/]/,
name: 'libs',
chunks: 'all',
},
vendors: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
runtimeChunk: {
name: (entrypoint) => `runtime-${entrypoint.name}`,
},
},
resolve: {
modules: [
'node_modules',
paths.nodeModules,
paths.sharedSrc,
paths.libSrc,
].concat(
process.env.NODE_PATH.split(path.delimiter).filter(Boolean),
),
extensions: ['.tsx', '.ts', '.js'],
alias,
plugins: [
PnpWebpackPlugin,
],
},
resolveLoader: {
plugins: [
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
{ parser: { requireEnsure: false } },
{
oneOf: [
{
test: [/.bmp$/, /.gif$/, /.jpe?g$/, /.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
{
test: /.(js|mjs|jsx|ts|tsx)$/,
include: [
paths.appsPath,
paths.sharedSrc,
paths.libSrc,
],
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides',
),
presets: [
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
},
],
],
plugins: [
['add-module-exports'],
['@babel/plugin-transform-modules-commonjs'], // only until no more module.exports = *
['module-resolver', { extensions: ['.js', '.ts', '.tsx'], alias }],
[require.resolve('babel-plugin-named-asset-import'), {
loaderMap: {
svg: {
ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]',
},
},
}],
isEnvDevelopment && shouldUseReactRefresh && require.resolve('react-refresh/babel'),
].filter(Boolean),
cacheDirectory: true,
cacheCompression: false,
compact: isEnvProduction,
},
},
{
test: /.(js|mjs)$/,
exclude: /@babel(?:/|\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
cacheCompression: false,
sourceMaps: shouldUseSourceMap,
inputSourceMap: shouldUseSourceMap,
},
},
{
test: cssRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
}),
sideEffects: true,
},
{
test: /.(less)$/,
use: [
isEnvDevelopment && require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 0,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
},
},
{
loader: 'less-loader',
options: {
logLevel: 2,
relativeUrls: true,
globalVars: getTheme(appName),
env: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
},
},
require.resolve('import-glob-loader'),
].filter(Boolean),
sideEffects: true,
},
{
loader: require.resolve('file-loader'),
exclude: [/.(js|mjs|jsx|ts|tsx)$/, /.html$/, /.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined,
),
),
isEnvProduction &&
shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
new InterpolateHtmlPlugin(
HtmlWebpackPlugin,
Object.assign({ FAVICON: `favicon-${appName}.ico?v3` }, env.raw),
),
new ModuleNotFoundPlugin(paths.rootDirectory, paths.yarnLockFile),
new webpack.DefinePlugin(env.stringified),
isEnvDevelopment && isHotModuleReloadEnabled && new webpack.HotModuleReplacementPlugin(),
isEnvDevelopment &&
shouldUseReactRefresh &&
new ReactRefreshWebpackPlugin({
overlay: {
entry: webpackDevClientEntry,
module: reactRefreshOverlayEntry,
sockIntegration: false,
},
}),
isEnvDevelopment && new CaseSensitivePathsPlugin(),
isEnvDevelopment &&
new WatchMissingNodeModulesPlugin(paths.nodeModules),
isEnvProduction &&
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: paths.publicUrlOrPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path; // eslint-disable-line immutable/no-mutation
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
(fileName) => !fileName.endsWith('.map'),
);
return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
},
}),
new webpack.IgnorePlugin(/^./locale$/, /moment$/),
].filter(Boolean),
node: {
module: 'empty',
dgram: 'empty',
dns: 'mock',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
performance: false,
};
};
多亏了Logan Smyth,我才能够解决这个实例。如果其他人遇到类似的问题,下面是发生的事情:
在上面的webpack配置中,在babel-loader的模块规则中,我有babel-plugin-add-module-exports,它可以与我使用的旧依赖项实现一些互操作性。升级所有依赖后,该模块与https://github.com/facebook/create-react-app/blob/22f46a8d5dfc46fe0f613cd7efbc82344823f461/packages/babel-preset-react-app/create.js#L91发生冲突,https://github.com/facebook/create-react-app/blob/22f46a8d5dfc46fe0f613cd7efbc82344823f461/packages/babel-preset-react-app/create.js#L91内联require("@babel/preset-env").default
,而default
属性被add-module-exports删除。使其求值为undefined
。