Home > database >  Build express server with webpack typescript and esm
Build express server with webpack typescript and esm

Time:01-24

I'm trying to build a small project node.js ts using webpack.

tsconfig.json
{
  "compilerOptions": {
    "lib": ["ESNext"],
    "target": "ES2020",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true,
    "types": ["node"],
    "allowSyntheticDefaultImports": true,
    "rootDir": "./"
  },
}

I added it to the package.json line "type": "module"

webpack.config.ts
import * as path         from 'path';
import * as webpack      from 'webpack';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const PATHS = {
  entry:  path.resolve(__dirname, 'src/server/index.ts'),
  output: path.resolve(__dirname, 'dist'),
};

const config: webpack.Configuration = {
  target:  'node',
  entry:   PATHS.entry,
  output:  {
    path:     PATHS.output,
    filename: 'bundle.js',
  },
  module:  {
    rules: [
      {
        test:    /\.ts(x?)$/,
        use:     'ts-loader',
        exclude: '/node_modules/',
      },
    ],
  },
  resolve: {
    extensions: ['*', '.js', '.jsx', '.json', '.ts', '.tsx'],
  },
};

export default config;

By running the command "build": "webpack --config config/webpack.config.ts --mode development", I get an error :

[webpack-cli] Failed to load '/node_esm/webpack.config.ts' config [webpack-cli] TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /node_esm/webpack.config.ts

I tried adding to my config.json "ts-node": { "esm": true} this didn't help solve my problem. What do I need to do in order to build an application using webpack?

  • node: v16.18.1
  • ts-loader: v9.4.2
  • typescript: v4.9.4
  • webpack: v5.75.0
  • webpack-cli: 5.0.1

UPD I've tried change tsconfig.The first option is to open your tsconfig.json file and look for compilerOptions. Set target to "ES5" and module to "CommonJS" (or completely remove the module option).

{
  "compilerOptions": {
    "target": "ES5",
    "module": "CommonJS",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true,
    "types": ["node"],
    "allowSyntheticDefaultImports": true,
    "rootDir": "./"
  }
}

the error remained the same. The second option is to add settings for ts-node:

You can keep "module": "ESNext" for tsc, and if you use webpack, or another build tool, set an override for ts-node.

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true,
    "types": ["node"],
    "allowSyntheticDefaultImports": true,
    "rootDir": "./"
  },
  "ts-node": {
    "compilerOptions": {
      "module": "CommonJS"
    }
  }
}

also at this point I tried to add

{
  "compilerOptions": {
    "module": "ESNext" // or ES2015, ES2020
  },
  "ts-node": {
    // Tell ts-node CLI to install the --loader automatically, explained below
    "esm": true
  }
}

as stated in the typescript documentation. The third option is to install the tsconfig-paths package it also didn't help.in all cases, the error remained the same as it was written before.

package.json
{
  "name": "node_esm",
  "version": "1.0.0",
  "description": "",
  "type": "module",
  "main": "index.js",
  "scripts": {
    "watch": "webpack --config config/webpack.config.ts --mode development --watch",
    "build": "webpack --config config/webpack.config.ts --mode development"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cross-env": "^7.0.3",
    "express": "^4.18.2"
  },
  "devDependencies": {
    "@types/express": "^4.17.15",
    "@types/node": "^18.11.18",
    "@types/webpack": "^5.28.0",
    "circular-dependency-plugin": "^5.2.2",
    "clean-webpack-plugin": "^4.0.0",
    "nodemon": "^2.0.20",
    "ts-loader": "^9.4.2",
    "ts-node": "^10.9.1",
    "tsconfig-paths": "^4.1.2",
    "tsconfig-paths-webpack-plugin": "^4.0.0",
    "typescript": "^4.9.4",
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.1",
    "webpack-dev-server": "^4.11.1",
    "webpack-node-externals": "^3.0.0"
  }
}

CodePudding user response:

The main issue is that Webpack uses the ts-node API, not the CLI so you need to configure the node loader appropriately.

I got it working using the following configuration.

package.json

"scripts": {
  "build": "NODE_OPTIONS='--loader ts-node/esm' webpack --mode development"
},

See the ts-node documentation on Native ECMAScript modules...

If you are not using our CLI, pass the loader flag to node.


Note: I didn't need anything special in tsconfig.json. No ts-node section is required since that only applied to the CLI.


Aaaaand, I just found this troubleshooting guide in the Webpack CLI reference

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for ./webpack.config.ts

You might encounter this error in the case of using native ESM in TypeScript (i.e. type: "module" in package.json).

webpack-cli supports configuration in both CommonJS and ESM format, at first it tries to load a configuration using require(), once it fails with an error code of 'ERR_REQUIRE_ESM' (a special code for this case) it would try to load the configuration using import(). However, the import() method won't work with ts-node without loader hooks enabled (described at TypeStrong/ts-node#1007).

To fix the error above use the following command:

NODE_OPTIONS="--loader ts-node/esm" npx webpack --entry ./src/index.js --mode production

CodePudding user response:

this is .babelrc

{
  "presets": ["@babel/preset-env", "@babel/preset-typescript"],
}

this is webpack.config.js

 resolve: {
    extensions: [".ts", ".js", ".json"],
    modules: [path.resolve(__dirname, "./src"), "node_modules"],
  },

ts.config.json

// since you have `module` set in package.json
"module": "NodeNext" 
"esModuleInterop": true 

package.json

"build": "webpack",
"dev:start": "nodemon --watch src  --exec \"node -r --trace-deprecation  dist/bundle.js \" ",

Also in paths

entry:  path.resolve(__dirname, 'src/server/index.ts')

I think this is not how you should be using .resolve

entry:  path.resolve(__dirname, 'src','server','index.ts')

Because .resolve's job is to determine to use "/" or \ based on the os

  • Related