浅色圆锥曲线爱好者PostsNotesAbout

About webpack externals

由于一些原因需要使用webpack交叉编译一个ts + yarn workspace的 app。web 端没有什么遇到什么问题,但是交叉编译服务器端一直报错,查阅了很多资料终于解决。

项目构成

. ├── package.json ├── packages │   ├── script │   │   ├── package.json │   │   ├── server.ts │   │   ├── tsconfig.webpack.json │   │   └── webpack.server.ts │   └── server │   ├── index.ts │   └── package.json ├── tsconfig.json └── yarn.lock 3 directories, 9 files

简单的 server.ts

ts
import express from "express"
const startServer = () => {
const app = express()
app.get("/", (_, res) => {
res.send("ok")
})
app.listen(3000, () => {
console.log("listening on 3000")
})
}
export default startServer

最开始的 webpack.server.ts长这个样子。没什么特别的

ts
import path from "path"
import webpack, { Configuration } from "webpack"
const serverSrc = path.resolve(__dirname, "./server.ts")
const distRoot = path.resolve(__dirname, "./dist")
const serverConfig: Configuration = {
mode: "production",
target: "node",
node: {
__dirname: false,
__filename: false,
},
entry: {
server: serverSrc,
},
output: {
path: distRoot,
filename: "server.js",
},
resolve: { extensions: [".js", ".ts"] },
plugins: [
new webpack.DefinePlugin({
"process.env": JSON.stringify(process.env),
}),
new webpack.ProgressPlugin(),
],
module: {
rules: [
{
test: /\.ts$/,
loader: [
{
loader: "ts-loader",
options: {
transpileOnly: true,
},
},
],
},
],
},
}
export default serverConfig

运行得到以下错误

WARNING in /home/yue/projects/webpack-express-ts-monorepo/node_modules/express/lib/view.js 81:13-25 Critical dependency: the request of a dependency is an expression @ /home/yue/projects/webpack-express-ts-monorepo/node_modules/express/lib/application.js @ /home/yue/projects/webpack-express-ts-monorepo/node_modules/express/lib/express.js @ /home/yue/projects/webpack-express-ts-monorepo/node_modules/express/index.js @ /home/yue/projects/webpack-express-ts-monorepo/node_modules/@package/server/index.ts @ ./server.ts ERROR in server.js from Terser Unexpected token: punc (:) [server.js:8974,12] error Command failed with exit code 2.

经过查询,最常见的方法是使用 webpack-node-externals这个库将部分不适合 bundle 的库除外。于是进行修改

diff
+ externals: [nodeExternals()],

再度编译还是得到相同错误。

ERROR in server.js from Terser Unexpected token: punc (:) [server.js:8974,12]

再度查阅资料在这个问题里有提到yarn workspace需要做不同的处理,遇上按照这个回答再度修改,将根目录的node_modules也加入 exclude 对象。

diff
- externals: [nodeExternals()],
+ externals: [
+ nodeExternals(),
+ nodeExternals({
+ modulesDir: path.resolve(__dirname, '../../node_modules'),
+ }),
+ ],

yarn build 得到以下结果

yarn run v1.22.4 $ yarn workspace @package/script build warning package.json: No license field $ TS_NODE_PROJECT=./tsconfig.webpack.json webpack --config webpack.server.ts Hash: 571930a4d43752f5eb7a Version: webpack 4.42.1 Time: 163ms Built at: 04/01/2020 11:47:55 PM Asset Size Chunks Chunk Names server.js 1.12 KiB 0 [emitted] server Entrypoint server = server.js [0] ./server.ts 296 bytes {0} [built] [1] external "@package/server" 42 bytes {0} [built] Done in 3.23s.

交叉编译成功了诶!可以洗洗睡了!睡前yarn start运行一下交叉编译成果吧。

yarn run v1.22.4 $ yarn workspace @package/script start warning package.json: No license field $ node ./dist/server.js /home/yue/projects/webpack-express-ts-monorepo/packages/server/index.ts:1 import express from "express"; ^^^^^^ SyntaxError: Cannot use import statement outside a module at wrapSafe (internal/modules/cjs/loader.js:1063:16) at Module._compile (internal/modules/cjs/loader.js:1111:27) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10) at Module.load (internal/modules/cjs/loader.js:996:32) at Function.Module._load (internal/modules/cjs/loader.js:896:14) at Module.require (internal/modules/cjs/loader.js:1036:19) at require (internal/modules/cjs/helpers.js:72:18) at Object.<anonymous> (/home/yue/projects/webpack-express-ts-monorepo/packages/script/dist/server.js:1:1118) at r (/home/yue/projects/webpack-express-ts-monorepo/packages/script/dist/server.js:1:110) at Object.<anonymous> (/home/yue/projects/webpack-express-ts-monorepo/packages/script/dist/server.js:1:1077) error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command. error Command failed. Exit code: 1 Command: /usr/bin/node Arguments: /usr/lib/node_modules/yarn/lib/cli.js start Directory: /home/yue/projects/webpack-express-ts-monorepo/packages/script Output: info Visit https://yarnpkg.com/en/docs/cli/workspace for documentation about this command. error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

什么,packages/server根本没有被交叉编译!!??

看一眼交叉编译出来的文件:

js
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),n(r(1)).default()},function(e,t){e.exports=require("@package/server")}]);

require("@package/server")是个什么鬼嘛 (╯°□°)╯︵ ┻━┻

再度回到刚才那个 stackoverflow 的问题。仔细查看之后得出以下结论

  • 我们需要交叉编译所有自己写的 ts 文件
  • 我们没理由交叉编译所有第三方库

于是查阅 webpack 的官方文档再再次出做修改

ts
externals: [
(_, req, cb) => {
if (!/^@package/.test(req) && req[0] !== "." && req[0] !== "/") {
return cb(null, `commonjs ${req}`);
}
cb(null, undefined);
}
],

当一个 import path 是本地的 package(@package开头),相对路径("."开头), 绝对路径("/"开头)时进行交叉编译,不是的时候直接使用原来的commonjs模块。

再度编译结果正常。

yarn run v1.22.4 $ yarn workspace @package/script build warning package.json: No license field $ TS_NODE_PROJECT=./tsconfig.webpack.json webpack --config webpack.server.ts Hash: f0adf7b87e9622543054 Version: webpack 4.42.1 Time: 176ms Built at: 04/02/2020 12:02:19 AM Asset Size Chunks Chunk Names server.js 1.42 KiB 0 [emitted] server Entrypoint server = server.js [0] ./server.ts 296 bytes {0} [built] [2] external "express" 42 bytes {0} [built] + 1 hidden module Done in 3.22s.

尝试运行yarn start

yarn run v1.22.4 $ yarn workspace @package/script start warning package.json: No license field $ node ./dist/server.js listening on 3000

到此为止解决了问题 💞

© 2023