You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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中的moduleResolutionnode改为bundler,但问题依旧
  • 确认ts-loader的规则已经正确匹配了.ts/.tsx文件
  • 检查package.json的type: "module"设置是否生效

还是没找到问题所在,希望各位能帮我看看哪里配置有问题,或者需要添加什么设置才能让Webpack正确处理TypeScript模块,最终的bundle文件里不再引用.ts文件。非常感谢!

火山引擎 最新活动