@nestjs/cli
Version:
Nest - modern, fast, powerful node.js web framework (@cli)
145 lines (144 loc) • 6 kB
JavaScript
import { readFileSync } from 'fs';
import { createRequire } from 'module';
import { join } from 'path';
import { defaultTsconfigFilename } from '../../configuration/defaults.js';
import { appendTsExtension } from '../helpers/append-extension.js';
const require = createRequire(import.meta.url);
/**
* Wraps a `require` of an optional webpack dependency so a missing package
* reports the install command instead of a raw MODULE_NOT_FOUND. Every
* optional webpack dependency must be loaded through here — they are declared
* as optional peers, so any unguarded `require` surfaces as a bare crash.
*/
function withInstallAdvice(load) {
try {
return load();
}
catch (e) {
if (e?.code !== 'MODULE_NOT_FOUND' && e?.code !== 'ERR_MODULE_NOT_FOUND') {
// Only the "package missing" branch is wrapped with install advice.
// Surfacing unrelated errors as "package not found" would mislead
// users into reinstalling perfectly valid dependencies.
throw e;
}
// Use a non-greedy match so error messages that contain multiple
// quoted paths (e.g. ESM "Cannot find package 'foo' imported from
// '/abs/path.mjs'") still extract the package name rather than the
// importing file path.
const pkg = e?.message?.match?.(/Cannot find.*?'([^']+)'/)?.[1] ?? 'webpack';
throw new Error(`The "${pkg}" package is required when using the webpack compiler but could not be found. ` +
`Please install it:\n\n npm install --save-dev webpack webpack-node-externals tsconfig-paths-webpack-plugin ts-loader fork-ts-checker-webpack-plugin\n`, { cause: e });
}
}
function loadWebpackDeps() {
return withInstallAdvice(() => {
const wp = require('webpack');
const externals = require('webpack-node-externals');
const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');
return { webpack: wp, nodeExternals: externals, TsconfigPathsPlugin };
});
}
function getBaseUrl(tsConfigFile) {
try {
const { compilerOptions } = JSON.parse(readFileSync(tsConfigFile, 'utf8'));
return compilerOptions?.baseUrl ?? '.';
}
catch {
return '.';
}
}
export const webpackDefaultsFactory = (sourceRoot, relativeSourceRoot, entryFilename, isDebugEnabled = false, tsConfigFile = defaultTsconfigFilename, plugins) => {
const { webpack: wp, nodeExternals: externals, TsconfigPathsPlugin, } = loadWebpackDeps();
const isPluginRegistered = isAnyPluginRegistered(plugins);
const webpackConfiguration = {
entry: appendTsExtension(join(sourceRoot, entryFilename)),
devtool: isDebugEnabled ? 'inline-source-map' : false,
target: 'node',
output: {
filename: join(relativeSourceRoot, `${entryFilename}.js`).replace(/\\/g, '/'),
},
ignoreWarnings: [/^(?!CriticalDependenciesWarning$)/],
externals: [externals()],
externalsPresets: { node: true },
module: {
rules: [
{
test: /.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: !isPluginRegistered,
configFile: tsConfigFile,
getCustomTransformers: (program) => ({
before: plugins.beforeHooks.map((hook) => hook(program)),
after: plugins.afterHooks.map((hook) => hook(program)),
afterDeclarations: plugins.afterDeclarationsHooks.map((hook) => hook(program)),
}),
},
},
],
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
plugins: [
new TsconfigPathsPlugin({
configFile: tsConfigFile,
baseUrl: getBaseUrl(tsConfigFile),
}),
],
},
mode: 'none',
optimization: {
nodeEnv: false,
},
node: {
__filename: false,
__dirname: false,
},
plugins: [
new wp.IgnorePlugin({
checkResource(resource) {
const lazyImports = [
'@nestjs/microservices',
'@nestjs/microservices/microservices-module',
'@nestjs/websockets/socket-module',
'class-validator',
'class-transformer',
'class-transformer/storage',
];
if (!lazyImports.includes(resource)) {
return false;
}
try {
require.resolve(resource, {
paths: [process.cwd()],
});
}
catch {
return true;
}
return false;
},
}),
],
};
if (!isPluginRegistered) {
const ForkTsCheckerWebpackPlugin = withInstallAdvice(() => require('fork-ts-checker-webpack-plugin'));
webpackConfiguration.plugins.push(new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: tsConfigFile,
},
}));
}
return webpackConfiguration;
};
function isAnyPluginRegistered(plugins) {
return ((plugins.afterHooks && plugins.afterHooks.length > 0) ||
(plugins.beforeHooks && plugins.beforeHooks.length > 0) ||
(plugins.afterDeclarationsHooks &&
plugins.afterDeclarationsHooks.length > 0));
}