ts + webpack bundling for node cannot find module 'path' - node.js

I know this has been asked before but I cannot get any accepted answer to work.
My objective is to get each of my entry points to bundle as a single [entry].js for portability. Ideally I would like to have [entry].js and [entry].[modules/vendor].js but not necessary.
What I have tried:
Using webpack-node-externals with allowlist. Results in deep modules not being found
Manual externals options path: "commonjs path". Does not change result
const {} = require('path') instead of import {} from 'path'. Also doesn't change anything
The configs below which I believe gets the closest result which is just that it cannot find built-ins.
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"outDir": "launch",
"noImplicitAny": false,
"target": "es5",
"allowJs": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
},
"exclude": [
"__tests__",
"dist",
"launch"
],
"include": [
"src/Auth",
"src/Gate",
"src/Patch",
"src/Account",
"src/Proxy",
]
}
webpack.config.js
const path = require('path');
module.exports = {
target: 'node',
mode: 'production',
entry: {
'gate': './src/Gate/index.ts',
'patch': './src/Patch/index.ts',
'auth': './src/Auth/index.ts',
'account': './src/Account/index.ts',
'proxy': './src/Proxy/index.ts',
},
stats: {warnings:false},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
},
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
optimization: {
usedExports: true,
chunkIds: 'natural',
splitChunks: {
// default splitChunks config
chunks: 'async',
minSize: 20000,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
}
}
};

I found the problem, it was the package cli that has its own type of managed module resolution that breaks with webpack. I have replaced it with commander.

Related

How to stop webpack compiling AWS libraries in node_modules?

The AWS compilation errors from within node_modules are occurring the build
ERROR in ../../../node_modules/#aws-sdk/client-dynamodb/models/models_0.ts 6321:51-59
TS2339: Property '$unknown' does not exist on type 'never'.
6319 | if (obj.NULL !== undefined) return { NULL: obj.NULL };
6320 | if (obj.BOOL !== undefined) return { BOOL: obj.BOOL };
> 6321 | if (obj.$unknown !== undefined) return { [obj.$unknown[0]]: "UNKNOWN" };
| ^^^^^^^^
6322 | };
6323 | }
6324 |
webpack compiled with 9 errors in 6844 ms
I have just updated AWS
"#aws-sdk/client-dynamodb": "^3.51.0",
"#aws-sdk/lib-dynamodb": "^3.51.0",
"#aws-sdk/util-dynamodb": "^3.51.0",
With webpack
const path = require('path');
const slsw = require('serverless-webpack');
const nodeExternals = require('webpack-node-externals');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
/*
This line is only required if you are specifying `TS_NODE_PROJECT` for whatever
reason.
*/
// delete process.env.TS_NODE_PROJECT;
module.exports = {
context: __dirname,
mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
entry: slsw.lib.entries,
devtool: slsw.lib.webpack.isLocal ? 'eval-cheap-module-source-map' : 'source-map',
resolve: {
extensions: ['.mjs', '.json', '.ts'],
symlinks: false,
cacheWithContext: false,
plugins: [
new TsconfigPathsPlugin({
configFile: './tsconfig.paths.json',
}),
],
},
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, '.webpack'),
filename: '[name].js',
},
target: 'node',
externals: [nodeExternals()],
module: {
rules: [
{
// Include ts, tsx, js, and jsx files.
test: /\.(ts|js)x?$/,
exclude: [
path.resolve(__dirname, 'node_modules'),
path.resolve(__dirname, '.serverless'),
path.resolve(__dirname, '.webpack'),
],
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: true,
}
},
'babel-loader'
]
}
]
},
plugins: [new ForkTsCheckerWebpackPlugin()],
};
And ts-config
{
"extends": "./tsconfig.paths.json",
"compilerOptions": {
"lib": ["ESNext"],
"moduleResolution": "node",
"esModuleInterop": true,
"noUnusedParameters": false,
"noUnusedLocals": false,
"resolveJsonModule": true,
"removeComments": true,
"sourceMap": true,
"target": "ES2020",
"outDir": "lib",
"paths": {
"#functions/*": ["src/functions/*"],
"#libs/*": ["src/libs/*"],
"#api/*": ["src/api/*"],
"~/*": [
"./*"
]
},
"types": [
"#types/node",
"#types/jest"
]
},
"include": ["src/**/*.ts", "serverless.ts"],
"exclude": [
"util/**/*",
"node_modules/**/*",
".serverless/**/*",
".webpack/**/*",
"_warmup/**/*",
".vscode/**/*" ],
"ts-node": {
"require": ["tsconfig-paths/register"]
}
}
Webpack looks to be still compiling node_modules regardless of it being in the excluded list.
Stop typescript type lookup traversing parent nodes
Incompatible types found by going up directories
Alter tsconfig.json to include typeRoots as a parameter as per Prevent require(...) from looking up modules in the parent directory
{
"extends": "./tsconfig.paths.json",
"compilerOptions": {
"lib": ["ESNext"],
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"module": "commonjs",
"esModuleInterop": true,
"noUnusedParameters": false,
"noUnusedLocals": false,
"resolveJsonModule": true,
"removeComments": true,
"sourceMap": true,
"target": "ES2020",
"outDir": "lib",
"paths": {
"#functions/*": ["src/functions/*"],
"#libs/*": ["src/libs/*"],
"#api/*": ["src/api/*"],
"~/*": [
"./*"
]
},
"typeRoots": [
"./"
],
"types": [
"#types/node",
"#types/jest"
]
},
"include": ["src/**/*.ts", "serverless.ts"],
"exclude": [
"util/**/*",
"node_modules",
"node_modules/**/*",
".serverless/**/*",
".webpack/**/*",
"_warmup/**/*",
".vscode/**/*"
],
"ts-node": {
"require": ["tsconfig-paths/register"]
}
}

Handler is undefined or not exported

when trying to run the lambda function I get the error "out/get-user-messages.handler is undefined or not exported". downloading the zip I see the "out" folder in the root and get-user-messages.js inside though. and I am exporting the handler
get-user-messages.ts
import "reflect-metadata";
import { APIGatewayEvent, APIGatewayEventRequestContext, APIGatewayProxyEvent } from "aws-lambda";
export const handler = async (event: APIGatewayProxyEvent, context: APIGatewayEventRequestContext) => {
/** Code **/
}
tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"module": "es2015",
"target": "esnext",
"noImplicitAny": false,
"outDir": "./out",
"strict": false,
"resolveJsonModule": true,
"strictNullChecks": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"esModuleInterop": true,
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"],
"paths": {
"#/*": ["src/*"]
}
}
webpack.config.js
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
var path = require('path');
/** #type {import('webpack').Configuration} */
module.exports = {
mode: "production",
module: {
mode: "production",
rules: [
{
test: /\.js|.ts|.tsx/,
exclude: /node_modules/,
use: 'babel-loader'
},
],
},
resolve: {
extensions: ['*', '.js', '.jsx', '.tsx', '.ts', '.json'],
},
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: ["babel-loader"]
}
]
},
plugins: [new ForkTsCheckerWebpackPlugin()],
externals: ['aws-sdk'], //avoid un-needed modules since aws-sdk exists in aws lambdas already
entry: {
//list of compiled files
"get-user-messages": path.join(__dirname, "./src/lambda/get-user-messages.ts"),
"insert-user-message": path.join(__dirname, "./src/lambda/insert-user-message.ts"),
"mark-read-user-message": path.join(__dirname, "./src/lambda/mark-read-user-message.ts"),
"get-application-release-notes": path.join(__dirname, "./src/lambda/get-application-release-notes.ts"),
"insert-application-release-note": path.join(__dirname, "./src/lambda/insert-application-release-note.ts")
},
target: 'node',
output: {
path: path.join(__dirname, "./out"),
filename: "[name].js",
library: "[name]",
libraryTarget: 'commonjs'
}
};
.babelrc
{
"presets": [
["#babel/preset-env",{"targets":{"node":"14"}, "modules":false}],
"#babel/preset-typescript"
],
"sourceType": "unambiguous",
"plugins": [
"babel-plugin-transform-typescript-metadata",
["#babel/plugin-proposal-decorators", { "legacy": true }],
"#babel/plugin-proposal-class-properties",
"#babel/plugin-transform-runtime"
]
}
the issue was the "library" option in the output section of webpack.config.js, removing that allowed me to successfully run the lambda function

Building es6 module in typescript project with webpack

I am trying to use imagemin node package in my project. It is a es6 module and I am getting error while using it.
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module. with "type" as "module" in package.json
ReferenceError: require is not defined with "type" as "commonjs" in package.json
My webpack config:
export default env => {
return {
mode: env.production ? 'production' : 'development',
entry: {
main: './src/main.ts',
worker: './src/worker.ts'
},
target: ['node', 'es2019'],
devtool: env.production ? false : 'inline-source-map',
watch: !env.production,
resolve: {
extensions: ['.ts', '.js'],
alias: {
src: _resolve(process.cwd(), './src')
}
},
module: {
rules: [{
test: /\.ts$/,
include: _resolve(process.cwd(), "src"),
use: 'ts-loader',
exclude: [/node_modules/, /\.spec\.ts/, /\.e2e-spec\.ts/]
}],
},
externalsPresets: { node: true },
externals: [nodeExternals()],
output: {
filename: '[name].js',
path: OUTPUT_DIR,
clean: !!env.production,
devtoolModuleFilenameTemplate: 'file:///[absolute-resource-path]',
library: {
type: 'module'
}
},
optimization: {
minimize: false, //!!env.production
mangleExports: true,
minimizer: [new terserPlugin({
terserOptions: {
output: {
comments: false
},
keep_classnames: true,
},
extractComments: false,
})]
},
experiments: {
outputModule: true
}
};
};
My tsconfig:
{
"compilerOptions": {
"module": "ES2020",
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2019",
"sourceMap": true,
"strict": true,
"baseUrl": ".",
"esModuleInterop": true,
"noImplicitAny": true,
"moduleResolution": "node",
"outDir": "../bin/server",
"typeRoots": [
"node_module/#types",
"./typings"
],
"types": [
"node",
"#types/jest"
]
},
"exclude": [
"node_modules"
]
}
When I try to "import" imagemin in one of my .ts file, webpack convert it to "require". I have also tried using import() but it doesn't work either.
I have made a repo on github
Is there a way to get es6 bundle (preferred) or use imagemin with commonjs bundle?
I couldn't found a way to bundle es6 format through webpack but i was able to use es6 module in commonjs system while bundling through webpack.
externals: [
nodeExternals({
allowlist: ['imagemin', 'imagemin-mozjpeg']
}),
async function ({ request }) {
if (request === 'imagemin' || request === 'imagemin-mozjpeg')
return `import ${request}`;
},
]
This is how i got imagemin to work.
Change your start script
"start": "node --experimental-modules src/index.mjs "
and In package.json Add
{
...
"type": "module",
...
}

SyntaxError: Cannot use import statement outside a module webpack-typescript

Here is webpack.config.ts
// const path = require("path");
import path from "path";
import HtmlWebpackPlugin from "html-webpack-plugin";
import CopyWebpackPlugin from "copy-webpack-plugin";
import webpack from "webpack";
import WebpackDevServer from "webpack-dev-server";
declare module "webpack" {
interface Configuration {
devServer?: WebpackDevServer.Configuration;
}
}
const config: webpack.Configuration = {
mode: "development",
target: "web",
entry: ["regenerator-runtime/runtime", "./src/index.tsx"],
output: {
filename: "bundle.js",
path: path.join(__dirname, "build"),
publicPath: "/",
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".css"],
alias: {
// add as many aliases as you like!
components: path.resolve(__dirname, "src/components"),
},
fallback: {
// path: require.resolve("path-browserify"),
fs: false,
assert: require.resolve("assert/"),
os: require.resolve("os-browserify/browser"),
constants: require.resolve("constants-browserify"),
},
},
devtool: "eval-cheap-source-map",
module: {
rules: [
{ test: /\.(ts|tsx)/, loader: "babel-loader", exclude: /node_modules/ },
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
{
test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: "file-loader",
options: {
name: "[name].[contenthash].[ext]",
outputPath: "fonts/",
},
},
],
},
{
test: /\.svg$/,
use: [
{
loader: "svg-url-loader",
options: {
limit: 10000,
},
},
],
},
],
},
devServer: {
contentBase: path.join(__dirname, "build"),
historyApiFallback: true,
overlay: true,
},
plugins: [
new HtmlWebpackPlugin({
title: "esBUild",
template: "src/index.html",
}),
new CopyWebpackPlugin({
patterns: [{ from: "assets", to: "assets" }],
}),
new webpack.ProvidePlugin({
process: "process/browser",
Buffer: ["buffer", "Buffer"],
React: "react",
}),
],
};
export default config;
here is tsconfig.json:
{
"compilerOptions": {
// The standard typing to be included in the type checking process.
"lib": ["dom", "dom.iterable", "esnext"],
// Whether to allow JavaScript files to be compiled.
"allowJs": true,
//This allows default imports from modules with no default export in the type checking process.
"allowSyntheticDefaultImports": true,
// Whether to skip type checking of all the type declaration files (*.d.ts).
"skipLibCheck": true,
// This enables compatibility with Babel.
"esModuleInterop": true,
"strict": true,
// Ensures that the casing of referenced file names is consistent during the type checking process.
"forceConsistentCasingInFileNames": true,
// This allows modules to be in .json files which are useful for configuration files.
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"module": "es6",
"target": "es5",
"sourceMap": true,
"noEmit": true,
"jsx": "react",
"baseUrl": "src",
"paths": {
"components": ["components/*"]
}
},
"include": ["src"]
}
THis is the error I get :
import path from "path";
^^^^^^
SyntaxError: Cannot use import statement outside a module
I googled it and see some people suggest adding "type":"module", it did not work.
in tsconfig.json
"module": "es6" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
I tried each option but this did not work neither.
If I discard the typescript configuration, set up webpack with common.js, it works
Also .babelrc
{
"presets": [
"#babel/preset-react",
"#babel/preset-env",
"#babel/preset-typescript"
],
"plugins": [
"#babel/plugin-proposal-class-properties",
"#babel/plugin-proposal-object-rest-spread"
]
}
In my case I have to change to "module": "commonjs", inside tsconfig.json. Refer https://www.typescriptlang.org/tsconfig#module
In my case I have to install typescript types of plugins (vscode suggested it after restart the ide).
For example:
For dotenv-webpack package, I installed #types/dotenv-webpack.
Please refer to this section of the Webpack configuration documentation:
The above sample assumes version >= 2.7 or newer of TypeScript is used with the new esModuleInterop and allowSyntheticDefaultImports compiler options in your tsconfig.json file.
Note that you'll also need to check your tsconfig.json file. If the module in compilerOptions in tsconfig.json is commonjs, the setting is complete, else webpack will fail with an error. This occurs because ts-node does not support any module syntax other than commonjs.
There are three solutions to this issue:
Modify tsconfig.json.
Modify tsconfig.json and add settings for ts-node.
Install tsconfig-paths.
You've set the correct values for "esModuleInterop" and "allowSyntheticDefaultImports" in your tsconfig.json, so, to get rid of the error, you should do one of the above three options.
The first one suggests you to change the "module" compiler option's value to "commonjs".
The second one allows you to keep the "module" option's value as is, but you should add the settings for ts-node in your tsconfig.json.
The third one requires additional package installation and a separate TS configuration file.

Typescript not reading null when returning

I have a problem with Typescript not reading my return type correctly.
I am defining a function with a return type of something | null. And what typescript does is to read only the something type ignoring the null, which can be returned, when I call the function.
function nullornot(): null | string { // here is says it returns null | string
return null
}
const response = nullornot() // but here is says it returns string
My tsconfig.json file
{
"extends": "./tsconfig.paths.json",
"compilerOptions": {
"typeRoots": ["node_modules/#types", "src/#types"],
"allowSyntheticDefaultImports": true,
"lib": ["ESNext"],
"moduleResolution": "node",
"noUnusedLocals": true,
"noUnusedParameters": true,
"removeComments": true,
"sourceMap": true,
"target": "ESNext",
"outDir": "lib"
},
"files": ["src/#types/graphql.d.ts"],
"include": ["src/**/*.ts"],
"exclude": [
"node_modules/**/*",
".serverless/**/*",
".webpack/**/*",
"_warmup/**/*",
".vscode/**/*"
]
}
My webpack.config.js file
module.exports = {
context: __dirname,
mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
entry: slsw.lib.entries,
devtool: slsw.lib.webpack.isLocal ? 'eval-cheap-module-source-map' : 'source-map',
resolve: {
extensions: ['.mjs', '.json', '.ts'],
symlinks: false,
cacheWithContext: false,
plugins: [
new TsconfigPathsPlugin({
configFile: './tsconfig.paths.json',
}),
],
},
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, '.webpack'),
filename: '[name].js',
},
optimization: {
concatenateModules: false,
},
target: 'node',
externals: [nodeExternals()],
module: {
rules: [
// all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
{
test: /\.(tsx?)$/,
loader: 'ts-loader',
exclude: [
[
path.resolve(__dirname, 'node_modules'),
path.resolve(__dirname, '.serverless'),
path.resolve(__dirname, '.webpack'),
],
],
options: {
transpileOnly: true,
experimentalWatchApi: true,
},
},
{
test: /\.graphql?$/,
use: [
{
loader: 'webpack-graphql-loader',
options: {
// validate: true,
// schema: "./src/schema.graphql",
// removeUnusedFragments: true
// etc. See "Loader Options" below
}
}
]
}
],
},
plugins: [],
};
Thanks for all help!
To type the result as string | null, you need to enable strictNullChecks in compilerOptions like this:
"compilerOptions": {
"strictNullChecks": true
}
See the TypeScript docs for more details.
It's because you don't have the strictNullChecks flag set to true in the tsconfig. Therefore TS won't enforce null checks, so for all intents and purposes it's just a string.
See how in this playground link where strict null checks are off it behaves the same as you're seeing, but once you turn it on the type of response is string | null
If this is a new project I'd recommend activating the strictest settings for the best type safety.

Resources