所以我正在创建一个Web应用程序,我们将调用WebApp
,它是一个使用来自自定义React模块的组件的React模块,我们将调用CustomModule
,它也位于我的本地计算机上。我一直在尝试从CustomModule
中的WebApp
导入某些组件,并且遇到了在webpack-dev-server -d
中运行WebApp
时出现的以下错误:
ERROR in /CustomModule/Components/LoadingSpinner/LoadingSpinner.jsx
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: /CustomModule/Components/LoadingSpinner/LoadingSpinner.jsx: Unexpected token, expected ";" (11:17)
9 |
10 | export const Spinner = () => {
> 11 | var imgStyle : object = {
| ^
12 | height:"75%",
13 | padding:"2px 0 0 2px"
14 | } ;
at Parser.raise (/WebApp/node_modules/@babel/parser/lib/index.js:3851:17)
at Parser.unexpected (/WebApp/node_modules/@babel/parser/lib/index.js:5167:16)
at Parser.semicolon (/WebApp/node_modules/@babel/parser/lib/index.js:5149:40)
at Parser.parseVarStatement (/WebApp/node_modules/@babel/parser/lib/index.js:7763:10)
at Parser.parseStatementContent (/WebApp/node_modules/@babel/parser/lib/index.js:7358:21)
at Parser.parseStatement (/WebApp/node_modules/@babel/parser/lib/index.js:7291:17)
at Parser.parseBlockOrModuleBlockBody (/WebApp/node_modules/@babel/parser/lib/index.js:7868:25)
at Parser.parseBlockBody (/WebApp/node_modules/@babel/parser/lib/index.js:7855:10)
at Parser.parseBlock (/WebApp/node_modules/@babel/parser/lib/index.js:7839:10)
at Parser.parseFunctionBody (/WebApp/node_modules/@babel/parser/lib/index.js:6909:24)
@ /CustomModule/Components/LoadingSpinner/index.js 1:0-65 1:0-65
@ /CustomModule/Components/index.js
@ ./ClientScripts/DataExplorer/Dashboard/containers/DashboardContent.jsx
@ ./ClientScripts/DataExplorer/Dashboard/index.js
@ ./ClientScripts/Route/RouteConfig.jsx
@ ./ClientScripts/Route/index.js
@ ./ClientScripts/Main.jsx
@ ./ClientScripts/index.js
@ multi (webpack)-dev-server/client?http://localhost:9000 ./ClientScripts/index.js
两个模块都使用Flow
,babel
和webpack
。我在每个模块中相应地设置了package.json
,webpack.config.js
,.flowconfig
和.babelrc
文件。然后我用CustomModule
象征性地将WebApp
链接到npm link
。我使用CustomModule
构建webpack
,然后尝试构建包含import语句的WebApp
,以使用CustomModule
中的组件。
版本
node: v9.0.0
npm: 5.5.1
@babel/cli: 7.2.3
@babel/core: 7.4.3
@babel/preset-flow: 7.0.0
babel-loader: 8.0.5
flow: 0.2.3
flow-webpack-plugin: 1.2.0
webpack: 4.16.5
WebApp webpack.config.js
const webpack = require('webpack');
const path = require('path');
const glob = require('glob');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const FlowWebpackPlugin = require('flow-webpack-plugin');
module.exports = {
entry: {
vendor: ['babel-polyfill', 'react', 'react-dom'],
annotationService: glob.sync('./ClientScripts/AnnotationService/*.js'),
repositoryService: glob.sync('./ClientScripts/RepositoryService/*.js'),
timelineService: glob.sync('./ClientScripts/TimelineService/*.js'),
filterService: glob.sync('./ClientScripts/DataExplorer/Dashboard/FilterServices/*.js'),
platform: './ClientScripts/index.js',
objects: glob.sync("./ClientScripts/RepositoryService/Objects/*.js"),
sass: './sass/main.scss'
},
output: {
path: path.join(__dirname, 'reactDist'),
filename: 'js/[name].js',
sourceMapFilename: 'map/[name].map'
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
},
resolve: {
alias: {
Interfaces: path.resolve(__dirname, 'ClientScripts/Interfaces/'),
Layout: path.resolve(__dirname, 'ClientScripts/Layout/'),
Navigation: path.resolve(__dirname, 'ClientScripts/Navigation/'),
Redux: path.resolve(__dirname, 'ClientScripts/Redux/'),
RepositoryService: path.resolve(__dirname, 'ClientScripts/RepositoryService/'),
TimelineService: path.resolve(__dirname, 'ClientScripts/TimelineService/'),
FilterService: path.resolve(__dirname, 'ClientScripts/DataExplorer/Dashboard/FilterServices/'),
AnnotationService: path.resolve(__dirname, 'ClientScripts/AnnotationService/'),
Route: path.resolve(__dirname, 'ClientScripts/Route/'),
Timeline: path.resolve(__dirname, 'ClientScripts/Timeline/'),
TimelineEditor: path.resolve(__dirname, 'ClientScripts/TimelineEditor/'),
Utilities: path.resolve(__dirname, 'ClientScripts/jsutils/'),
ReactUtils: path.resolve(__dirname, 'ClientScripts/reactUtils/'),
Images: path.resolve(__dirname, 'img/'),
},
symlinks: true
},
target: 'web',
node: {
fs: "empty"
},
externals: {
'winston': 'require("winston")
},
module: {
rules: [
{ test: /\.js$/, loader: 'babel-loader' },
{ test: /\.jsx$/, loader: 'babel-loader' },
{ test: /\.env$/, loader: "file-loader?name=index.[ext]", exclude: [/node_modules/] },
{
test: /\.scss$|\.css$/,
exclude: /node_modules/,
loader: ExtractTextPlugin.extract({
use: [{
loader: "css-loader",
options: {
minimize: true
}
},'sass-loader']
})
},
{ test: /\.(jpe?g|png|gif|svg)$/,
loader: 'file-loader?name=img/[name].[ext]?',
options: {
name (file) {
if (process.env.environment === 'prod') {
return '[path][name].[hash].[ext]'
}
return '[path][name].[ext]'
}
}
}
]
},
plugins: [
new ExtractTextPlugin({ filename: 'css/timeline.[md5:contenthash:hex:20].css', disable: false, allChunks: true }),
new FlowWebpackPlugin()
]
}
WebApp .flowconfig
[ignore]
.*/node_modules/flow-webpack-plugin/.*
.*/node_modules/.*\.json$
.*/node_modules/\.staging/.*
[libs]
flow-typed
[options]
module.name_mapper='^Interfaces\/\(.*\)$' -> '<PROJECT_ROOT>/ClientScripts/Interfaces/\1'
module.name_mapper='^Layout\/\(.*\)$' -> '<PROJECT_ROOT>/ClientScripts/Layout/\1'
module.name_mapper='^Navigation\/\(.*\)$' -> '<PROJECT_ROOT>/ClientScripts/Navigation/\1'
module.name_mapper='^Redux\/\(.*\)$' -> '<PROJECT_ROOT>/ClientScripts/Redux/\1'
module.name_mapper='^RepositoryService\/\(.*\)$' -> '<PROJECT_ROOT>/ClientScripts/RepositoryService/\1'
module.name_mapper='^Route\/\(.*\)$' -> '<PROJECT_ROOT>/ClientScripts/Route/\1'
module.name_mapper='^Timeline\/\(.*\)$' -> '<PROJECT_ROOT>/ClientScripts/Timeline/\1'
module.name_mapper='^TimelineEditor\/\(.*\)$' -> '<PROJECT_ROOT>/ClientScripts/TimelineEditor/\1'
module.name_mapper='^Utilities\/\(.*\)$' -> '<PROJECT_ROOT>/ClientScripts/jsutils/\1'
module.name_mapper='^Images\/\(.*\)$' -> '<PROJECT_ROOT>/img/\1'
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.svg
module.file_ext=.json
CustomModule webpack.config.js
const webpack = require('webpack');
const path = require('path');
const glob = require('glob');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const FlowWebpackPlugin = require('flow-webpack-plugin');
module.exports = {
entry: {
vendor: ['babel-polyfill', 'react', 'react-dom'],
components: './Components/index.js',
sass: './sass/main.scss'
},
output: {
path: path.join(__dirname, 'reactDist'),
filename: 'js/[name].js',
sourceMapFilename: 'map/[name].map'
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
},
resolve: {
alias: {
Components: path.resolve(__dirname, 'Components/'),
Images: path.resolve(__dirname, 'img/'),
Utilities: path.resolve(__dirname, 'Utilities/')
},
// extensions: ['', '.js', '.jsx']
},
target: 'web',
node: {
fs: "empty"
},
externals: {
'winston': 'require("winston")'
},
module: {
rules: [
{ test: /\.js$/, loader: 'babel-loader', exclude: [/node_modules/] },
{
test: /\.jsx$/,
loader: 'babel-loader',
exclude: [/node_modules/],
query: {
presets: ['@babel/preset-flow']
}
},
{ test: /\.env$/, loader: "file-loader?name=index.[ext]", exclude: [/node_modules/] },
{
test: /\.scss$|\.css$/,
exclude: /node_modules/,
loader: ExtractTextPlugin.extract({
use: [{
loader: "css-loader",
options: {
minimize: true
}
},'sass-loader']
})
},
{
test: /\.(jpe?g|png|gif|svg)$/,
loader: 'file-loader?name=img/[name].[ext]?',
options: {
name (file) {
if (process.env.environment === 'prod') {
return '[path][name].[hash].[ext]'
}
return '[path][name].[ext]'
}
}
}
]
},
plugins: [
new ExtractTextPlugin({ filename: 'css/timeline.[md5:contenthash:hex:20].css', disable: false, allChunks: true }),
new FlowWebpackPlugin(),
]
}
CustomModule .flowconfig
[ignore]
.*/node_modules/flow-webpack-plugin/.*
.*/node_modules/.*\.json$
.*/node_modules/\.staging/.*
[libs]
flow-typed
[options]
module.name_mapper='^Components\/\(.*\)$' -> '<PROJECT_ROOT>/Components/\1'
module.name_mapper='^Images\/\(.*\)$' -> '<PROJECT_ROOT>/img/\1'
module.name_mapper='^Utilities\/\(.*\)$' -> '<PROJECT_ROOT>/Utilities/\1'
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.svg
module.file_ext=.json
两个模块使用相同的.babelrc
/*
./.babelrc
*/
{
"presets":[
"@babel/preset-env", "@babel/preset-react", "@babel/preset-flow"
],
"plugins": [
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-import-meta",
"@babel/plugin-transform-flow-strip-types",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-json-strings",
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
"@babel/plugin-proposal-function-sent",
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-proposal-numeric-separator",
"@babel/plugin-proposal-throw-expressions"
]
}
我希望webpack-dev-server
的结果不会产生错误,而不是在babel解析期间抛出的Module build failed
错误。
Webpack
的babel
插件未能在您的自定义imgStyle
文件中解析您的LoadingSpinner.jsx
对象声明。这是因为你的spinner的样式对象在你的样式对象声明中有一个:
。这会导致解析错误,因为这不是有效的JavaScript
语法,所以你得到以下语法错误:
SyntaxError:/CustomModule/Components/LoadingSpinner/LoadingSpinner.jsx:意外的令牌,预期“;” (11时17分)
查看堆栈跟踪,它指出第11行是罪魁祸首:
10 | export const Spinner = () => {
> 11 | var imgStyle : object = {
| ^
12 | height:"75%",
13 | padding:"2px 0 0 2px"
14 | } ;
注意这一行:var imgStyle : object = { // extra colon before the object declaration
只需删除自定义微调器样式对象声明中的:
,webpack
的babel
插件现在应该能够成功解析此文件。类似于以下内容:
var imgStyleObject = {
height:"75%",
padding:"2px 0 0 2px"
};
希望这有帮助!