Webpack打包后bundle文件仍引用TypeScript模块(.ts)而非转译后的JavaScript模块(.js)的问题求助
Webpack打包后bundle文件仍引用TypeScript模块(.ts)而非转译后的JavaScript模块(.js)的问题求助
各位前辈好,我最近在搭建TypeScript+Webpack的Node项目时遇到了一个棘手的问题,想请大家帮忙诊断一下:
我的项目入口是src/app.ts,里面导入了同目录下的my-module.ts模块。但当我执行npm run webpack:build生成dist/app.bundle.js后,发现这个bundle文件里居然还直接引用了./src/my-module.ts文件,完全没有把这个TypeScript模块转译打包进去,也没有替换成转译后的JS文件路径。我期望的是Webpack能把所有TypeScript模块都转译为JavaScript,并且最终的bundle里只存在对JS文件的引用(或者直接把模块代码打包进bundle里),彻底消除对.ts文件的依赖。
下面是我项目里的核心代码和配置文件,麻烦大家帮我看看哪里配置错了:
1. 入口文件 src/app.ts
import "core-js/stable/index.js"; // ES Modules-compatible dotenv loading import path from 'path'; import { fileURLToPath } from "node:url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename) import dotenv from 'dotenv'; dotenv.config({ path: __dirname + '/../.env' }); /*, quiet: true*/ function logPoint(p: Point) { console.log(`${p.x}, ${p.y}`); console.log(process.env.PORT); } // logs "12, 26" const point = { x: 12, y: 26 }; logPoint(point); // Use ESM imports for local modules in NodeNext; note explicit .js extension import myDefault, { greet } from './my-module'; console.log(myDefault + ' comes before ' + greet()); interface Point { x: number; y: number; }
2. 模块文件 src/my-module.ts
export default 44; export function greet() { return 'Hello from a modern module!'; }
3. package.json 配置
{ "name": "my-node-app", "version": "1.0.0", "__comment": { "main": "index.js", "type": "module", "exports": { "import": "./module.js", "require": "./module.cjs" } }, "browserslist": [ "defaults and fully supports es6-module", "maintained node versions", "> 0.25%, not dead" ], "scripts": { "tsc:watch": "tsc-watch --emitDeclarationOnly false --preserveWatchOutput --watch", "babel-build:watch": "babel --ignore 'src/**/*.d.ts' --copy-files --no-copy-ignored --extensions '.ts, .tsx, .js, . jsx, cjs,.mjs' ./src --out-dir dist --watch --verbose", "node:run": "node ./dist/app.js", "tsnode:run": "ts-node ./src/app.ts", "all": "concurrently \"npm run tsc:watch\" \"npm run babel-build:watch\" \"npm run tsnode:run\"", "webpack:build": "webpack --config webpack.config.ts", "test": "jest" }, "devDependencies": { "@babel/cli": "^7.28.3", "@babel/core": "^7.28.5", "@babel/node": "^7.28.0", "@babel/plugin-transform-runtime": "^7.28.5", "@babel/preset-env": "^7.28.5", "@babel/preset-flow": "^7.27.1", "@babel/preset-react": "^7.28.5", "@babel/preset-typescript": "^7.28.5", "@babel/register": "^7.28.3", "@eslint/js": "^9.39.1", "@types/lodash": "^4.17.21", "@types/node": "^24.10.1", "@types/webpack": "^5.28.5", "@types/webpack-dev-server": "^4.7.1", "babel-jest": "^30.2.0", "babel-loader": "^10.0.0", "babel-watch": "^7.8.1", "concurrently": "^9.2.1", "eslint": "^9.39.1", "eslint-plugin-react": "^7.37.5", "globals": "^16.5.0", "jest": "^30.2.0", "jiti": "^1.21.6", "rollup": "^4.53.2", "ts-loader": "^9.5.4", "ts-node": "^10.9.2", "tsc-watch": "^7.2.0", "tslib": "^2.8.1", "typescript": "^5.9.3", "typescript-eslint": "^8.47.0", "webpack": "^5.103.0", "webpack-cli": "^6.0.1", "webpack-dev-server": "^5.2.2" }, "dependencies": { "@babel/runtime": "^7.28.4", "@types/react": "^19.2.7", "common.js": "^1.1.1", "core-js": "^3.46.0", "dotenv": "^17.2.3", "lodash": "^4.17.21", "node-polyfill-webpack-plugin": "^4.1.0", "url": "^0.11.4" }, "description": "", "type": "module", "main": "./dist/app.js", "private": true, "__comment2": { "types": "./dist/app.d.ts" }, "keywords": [], "./package.json": "./package.json", "author": "Fakhar Anwar", "license": "ISC" }
4. tsconfig.json 配置
{ // Visit https://aka.ms/tsconfig to read more about this file "compilerOptions": { // File Layout "rootDir": "./src", "outDir": "./dist/", // Environment Settings // See also https://aka.ms/tsconfig/module "module": "esnext", "moduleResolution": "node", // Also, tried "bundler" here "target": "esnext", "types": ["node"], "allowImportingTsExtensions": true, "rewriteRelativeImportExtensions": true, "noEmit": false, "noEmitOnError": false, "emitDeclarationOnly": false, "declaration": true, // For nodejs: "lib": ["esnext", "dom"], //"types": ["node"], // and npm install -D @types/node "noImplicitAny": true, "removeComments": true, "preserveConstEnums": true, // Other Outputs "sourceMap": false, "declarationMap": false, "declarationDir": "./types", // Stricter Typechecking Options "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, // Style Options // "noImplicitReturns": true, // "noImplicitOverride": true, // "noUnusedLocals": true, // "noUnusedParameters": true, // "noFallthroughCasesInSwitch": true, // "noPropertyAccessFromIndexSignature": true, // Recommended Options "strict": true, "jsx": "react-jsx", //react | preserve "verbatimModuleSyntax": true, "isolatedModules": true, "noUncheckedSideEffectImports": true, "moduleDetection": "force", "skipLibCheck": true, "allowJs": true, "forceConsistentCasingInFileNames": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx"], "exclude": ["node_modules"] }
5. webpack.config.ts 配置
//webpack.config.ts import path from 'path'; import webpack from 'webpack'; // in case you run into any typescript error when configuring `devServer` //import 'webpack-dev-server'; import { createRequire } from 'node:module'; // Create a 'require' function relative to the current module's path const require = createRequire(import.meta.url); import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename) import NodePolyfillPlugin from 'node-polyfill-webpack-plugin'; const config: webpack.Configuration = { mode: 'development', entry: './src/app.ts', target: ["web", "node24.11"], experiments: { outputModule: true, }, output: { path: path.resolve(__dirname, 'dist'), filename: 'app.bundle.js', module: true, chunkFormat: 'module', // Ensures the output bundle uses ESM syntax for exports library: { type: 'module', }, //clean: true }, externals: '/node_modules', // devServer: { // static: { // directory: path.resolve(__dirname, 'dist'), // }, // port: 3000, // }, module: { rules: [ { test: /\.tsx?$/, // Matches both .ts and .tsx files, Alt: /\.(js|jsx|ts|tsx)$/ exclude: [/node_modules/, '/src/scss/'], use: 'ts-loader', //[ // { // loader: 'babel-loader', // Second: Transpile with Babel // options: { // presets: [ // '@babel/preset-env', // For modern JS features to older JS // '@babel/preset-typescript', // Enables Babel to understand TypeScript syntax // '@babel/preset-react',// Add other presets here, e.g., '@babel/preset-react' for React projects // ], // sourceType: 'module', // //cacheDirectory: true // Optional: for faster builds // } // } // , // { // loader: 'ts-loader', // First: Type-check with ts-loader // options: { // transpileOnly: true // } // } //], } // , // { // test: /\.js$/, // exclude: [/node_modules/, '/src/scss/'], // use: { // loader: 'babel-loader', // options: { // presets: [ // '@babel/preset-env', // ], // sourceType: 'module', // }, // }, // } ], }, infrastructureLogging: { level: 'verbose', // or 'debug', 'info', 'warn', 'error', 'none' debug: [/webpack-dev-server/], // Enable debug for the dev server }, stats: { loggingDebug: true, // Enable debug info for plugins/loaders }, plugins: [ new NodePolyfillPlugin(), new webpack.NormalModuleReplacementPlugin(/^node:/, (resource) => { resource.request = resource.request.replace(/^node:/, ''); }) ], resolve: { fallback: { "url": require.resolve("url") }, extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'], }, }; export default config;
我已经尝试过的调整
- 将tsconfig中的
moduleResolution从node改为bundler,但问题依旧 - 确认ts-loader的规则已经正确匹配了.ts/.tsx文件
- 检查package.json的
type: "module"设置是否生效
还是没找到问题所在,希望各位能帮我看看哪里配置有问题,或者需要添加什么设置才能让Webpack正确处理TypeScript模块,最终的bundle文件里不再引用.ts文件。非常感谢!




