运行npm run build报错:Unexpected token: punc (.) 可选链兼容问题
问题:TSDX构建React npm库时可选链操作符(?.)报错
Unexpected token: punc (.) 执行npm run build(基于tsdx build)时,代码中的可选链操作符?.触发语法错误,错误提示来自terser插件,推测是编译配置未正确处理该语法。
完整错误日志
npm run build > portshare-frontend-libary-components@0.1.0 build > tsdx build @rollup/plugin-replace: 'preventAssignment' currently defaults to false. It is recommended to set this option to `true`, as the next major version will default this option to `true`. @rollup/plugin-replace: 'preventAssignment' currently defaults to false. It is recommended to set this option to `true`, as the next major version will default this option to `true`. ✓ Creating entry file 1.3 secs ⠦ Building modules 902 | header: /*#__PURE__*/React__default.createElement("div", { 903 | className: `contact-header ${type === exports.CardType.ADD ? 'addCard' : ''}` > 904 | }, /*#__PURE__*/React__default.createElement("h2", null, type === exports.CardType.ADD ? 'Add Contact' : contactCard?.element?.name), type !== exports.CardType.ADD && /*#__PURE__*/React__default.createElement(OptionButtonWithDropdown, { | ^ Unexpected token: punc (.) 905 | options: [{ 906 | label: 'remove', (terser plugin) SyntaxError: Unexpected token: punc (.) SyntaxError: Unexpected token: punc (.) at ne (D:\2. repo\portshare-frontend-libary-components\node_modules\tsdx\node_modules\terser\dist\bundle.min.js:1:19592) at c (D:\2. repo\portshare-frontend-libary-components\node_modules\tsdx\node_modules\terser\dist\bundle.min.js:1:28298) at l (D:\2. repo\portshare-frontend-libary-components\node_modules\tsdx\node_modules\terser\dist\bundle.min.js:1:28389) at f (D:\2. repo\portshare-frontend-libary-components\node_modules\tsdx\node_modules\terser\dist\bundle.min.js:1:28442) at U (D:\2. repo\portshare-frontend-libary-components\node_modules\tsdx\node_modules\terser\dist\bundle.min.js:1:42256) at me (D:\2. repo\portshare-frontend-libary-components\node_modules\tsdx\node_modules\terser\dist\bundle.min.js:1:48326) at D:\2. repo\portshare-frontend-libary-components\node_modules\tsdx\node_modules\terser\dist\bundle.min.js:1:49206 at ve (D:\2. repo\portshare-frontend-libary-components\node_modules\tsdx\node_modules\terser\dist\bundle.min.js:1:49221) at ke (D:\2. repo\portshare-frontend-libary-components\node_modules\tsdx\node_modules\terser\dist\bundle.min.js:1:50475) at We (D:\2. repo\portshare-frontend-libary-components\node_modules\tsdx\node_modules\terser\dist\bundle.min.js:1:50707) PS D:\2. repo\portshare-frontend-libary-components>
相关配置文件
tsconfig.json
{ // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs "include": ["src", "types"], "compilerOptions": { "module": "esnext", "lib": ["dom", "esnext"], "importHelpers": true, // output .d.ts declaration files for consumers "declaration": true, // output .js.map sourcemap files for consumers "sourceMap": true, // match output dir to input dir. e.g. dist/index instead of dist/src/index "rootDir": "./src", // stricter type-checking for stronger correctness. Recommended by TS "strict": true, // linter checks for common issues "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative "noUnusedLocals": true, "noUnusedParameters": true, // use Node's module resolution algorithm, instead of the legacy TS one "moduleResolution": "node", // transpile JSX to React.createElement "jsx": "react", // interop between ESM and CJS modules. Recommended by TS "esModuleInterop": true, // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS "skipLibCheck": true, // error out if import and file system have a casing mismatch. Recommended by TS "forceConsistentCasingInFileNames": true, // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` "noEmit": true, "resolveJsonModule": true, // Enable importing JSON files "allowSyntheticDefaultImports": true, // Enable synthetic default imports "allowUmdGlobalAccess": true, // Allow accessing UMD globals "types": ["node"], // Include the 'node' type definitions "target": "es5", "baseUrl": ".", "paths": { "*": ["types/*"] }, "downlevelIteration": true } }
.babelrc.json
{ "sourceType": "unambiguous", "presets": [ [ "@babel/preset-env", { "targets": { "chrome": 100 } } ], "@babel/preset-typescript", "@babel/preset-react" ], "plugins": [] }
package.json
{ "version": "0.1.0", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ "dist", "src" ], "scripts": { "start": "tsdx watch", "build": "tsdx build", "test": "tsdx test --passWithNoTests", "lint": "tsdx lint", "prepare": "tsdx build", "size": "size-limit", "analyze": "size-limit --why", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" }, "peerDependencies": { "react": ">=16" }, "husky": { "hooks": { "pre-commit": "tsdx lint" } }, "prettier": { "printWidth": 80, "semi": true, "singleQuote": true, "trailingComma": "es5" }, "name": "portshare-frontend-libary-components", "author": "Leon Salenbacher", "module": "dist/portshare-frontend-libary-components.esm.js", "size-limit": [ { "path": "dist/portshare-frontend-libary-components.cjs.production.min.js", "limit": "10 KB" }, { "path": "dist/portshare-frontend-libary-components.esm.js", "limit": "10 KB" } ], "devDependencies": { "@babel/preset-env": "^7.22.5", "@babel/preset-react": "^7.22.5", "@babel/preset-typescript": "^7.22.5", "@size-limit/preset-small-lib": "^8.2.4", "@storybook/addon-essentials": "^7.0.21", "@storybook/addon-interactions": "^7.0.21", "@storybook/addon-links": "^7.0.21", "@storybook/blocks": "^7.0.21", "@storybook/react": "^7.0.21", "@storybook/react-webpack5": "^7.0.21", "@storybook/testing-library": "^0.0.14-next.2", "@types/react": "^18.2.14", "@types/react-dom": "^18.2.5", "husky": "^8.0.3", "node-sass": "^9.0.0", "portshare-frontend-libary-objects": "^1.0.8", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^4.9.0", "react-loading": "^2.0.3", "react-router-dom": "^6.13.0", "rollup-plugin-scss": "^4.0.0", "rollup-plugin-terser": "^7.0.2", "sass": "^1.63.6", "sass-loader": "^13.3.2", "size-limit": "^8.2.4", "storybook": "^7.0.21", "tsdx": "^0.14.1", "tslib": "^2.5.3" }, "dependencies": { "typescript": "^5.1.3", "react-simple-star-rating": "^5.1.7" } }
解决方案
原因分析
错误来自terser压缩阶段,说明TS编译后代码仍保留了?.语法,而当前terser版本不支持该语法;或者TSDX的默认构建流程未正确调用Babel转译可选链。
方案1:升级terser版本并配置支持ES2020+
- 卸载旧版
rollup-plugin-terser,安装新版@rollup/plugin-terser:
npm uninstall rollup-plugin-terser && npm install @rollup/plugin-terser --save-dev
- 新建或修改
tsdx.config.js替换插件:
const terser = require('@rollup/plugin-terser'); module.exports = { rollup(config, options) { config.plugins = config.plugins.map(plugin => plugin.name === 'terser' ? terser() : plugin ); return config; }, };
方案2:调整TS配置让编译器转译可选链
- 修改
tsconfig.json的compilerOptions,升级lib并添加转译配置:
"lib": ["dom", "es2020"], "useDefineForClassFields": true, "target": "es2018"
方案3:让TSDX使用Babel处理转译
- 安装
@rollup/plugin-babel和运行时插件:
npm install @rollup/plugin-babel @babel/plugin-transform-runtime @babel/runtime --save-dev
- 新建
tsdx.config.js配置Babel:
const babel = require('@rollup/plugin-babel'); module.exports = { rollup(config, options) { config.plugins.push( babel({ extensions: ['.js', '.jsx', '.ts', '.tsx'], exclude: 'node_modules/**', babelHelpers: 'runtime', }) ); return config; }, };
- 修改
.babelrc.json添加插件:
"plugins": ["@babel/plugin-transform-runtime"]
方案4:升级TSDX版本
当前使用的TSDX0.14.1版本较旧,尝试升级到最新版:
npm uninstall tsdx && npm install tsdx --save-dev
内容的提问来源于Stack Exchange,提问作者Leon
相关产品推荐
相关产品推荐

