fix(core): stop inlining tsconfig-paths-webpack-plugin and use version with fix (#8357)

This commit is contained in:
Phillip Barta 2022-01-27 14:59:46 +01:00 committed by GitHub
parent 78c9f158a7
commit be91d1f99a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 19 additions and 1068 deletions

View File

@ -235,7 +235,7 @@
"ts-loader": "^9.2.6",
"ts-node": "9.1.1",
"tsconfig-paths": "^3.9.0",
"tsconfig-paths-webpack-plugin": "3.4.1",
"tsconfig-paths-webpack-plugin": "3.5.2",
"tslib": "^2.3.0",
"tslint": "6.1.3",
"tslint-to-eslint-config": "^2.4.0",

View File

@ -41,7 +41,7 @@
"enhanced-resolve": "^5.8.3",
"ts-loader": "^9.2.6",
"tsconfig-paths": "^3.9.0",
"tsconfig-paths-webpack-plugin": "3.4.1",
"tsconfig-paths-webpack-plugin": "3.5.2",
"webpack-node-externals": "^3.0.0",
"fork-ts-checker-webpack-plugin": "6.2.10",
"rxjs": "^6.5.4",

View File

@ -2,16 +2,11 @@ import { createWebpackConfig, prepareConfig } from './config';
import { NextBuildBuilderOptions } from '@nrwl/next';
import { dirname } from 'path';
import { importConstants } from './require-shim';
// Inlining tsconfig-paths-webpack-plugin with a patch
// See: https://github.com/dividab/tsconfig-paths-webpack-plugin/pull/85
// TODO(jack): Remove once the patch lands in original package
import { TsconfigPathsPlugin } from '@nrwl/web/src/utils/webpack/plugins/tsconfig-paths/tsconfig-paths.plugin';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
const { PHASE_PRODUCTION_BUILD } = importConstants();
jest.mock(
'@nrwl/web/src/utils/webpack/plugins/tsconfig-paths/tsconfig-paths.plugin'
);
jest.mock('tsconfig-paths-webpack-plugin');
jest.mock('next/dist/server/config', () => ({
__esModule: true,
default: () => ({

View File

@ -10,7 +10,7 @@ import type {
PHASE_PRODUCTION_SERVER,
} from 'next/dist/shared/lib/constants';
import { join, resolve } from 'path';
import { TsconfigPathsPlugin } from '@nrwl/web/src/utils/webpack/plugins/tsconfig-paths/tsconfig-paths.plugin';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
import { Configuration } from 'webpack';
import {
FileReplacement,
@ -71,7 +71,7 @@ export function createWebpackConfig(
configFile: tsConfigPath,
extensions,
mainFields,
}),
}) as never, // TODO: Remove never type when 'tsconfig-paths-webpack-plugin' types fixed
];
fileReplacements

View File

@ -46,7 +46,7 @@
"tree-kill": "1.2.2",
"ts-loader": "^9.2.6",
"tsconfig-paths": "^3.9.0",
"tsconfig-paths-webpack-plugin": "3.4.1",
"tsconfig-paths-webpack-plugin": "3.5.2",
"tslib": "^2.3.0",
"webpack": "^5.58.1",
"webpack-merge": "^5.8.0",

View File

@ -5,7 +5,7 @@ import type { ProjectGraph } from '@nrwl/workspace/src/core/project-graph';
import buildExecutor from './build.impl';
import { BuildNodeBuilderOptions } from '../../utils/types';
jest.mock('../../utils/webpack/plugins/tsconfig-paths/tsconfig-paths.plugin');
jest.mock('tsconfig-paths-webpack-plugin');
jest.mock('../../utils/run-webpack', () => ({
runWebpack: jest.fn(),
}));

View File

@ -8,11 +8,7 @@ import { BuildBuilderOptions } from './types';
import { loadTsPlugins } from './load-ts-plugins';
import CopyWebpackPlugin = require('copy-webpack-plugin');
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
// Inlining tsconfig-paths-webpack-plugin with a patch
// See: https://github.com/dividab/tsconfig-paths-webpack-plugin/pull/85
// TODO(jack): Remove once the patch lands in original package
import TsConfigPathsPlugin from './webpack/plugins/tsconfig-paths/tsconfig-paths.plugin';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
export const OUT_FILENAME_TEMPLATE = '[name].js';
@ -85,11 +81,11 @@ export function getBaseWebpackPartial(
extensions,
alias: getAliases(options),
plugins: [
new TsConfigPathsPlugin({
new TsconfigPathsPlugin({
configFile: options.tsConfig,
extensions,
mainFields,
}),
}) as never, // TODO: Remove never type when 'tsconfig-paths-webpack-plugin' types fixed
],
mainFields,
},

View File

@ -1,9 +1,9 @@
import { join } from 'path';
import { getNodeWebpackConfig } from './node.config';
import TsConfigPathsPlugin from './webpack/plugins/tsconfig-paths/tsconfig-paths.plugin';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
import { BuildNodeBuilderOptions } from './types';
jest.mock('./webpack/plugins/tsconfig-paths/tsconfig-paths.plugin');
jest.mock('tsconfig-paths-webpack-plugin');
jest.mock('@nrwl/tao/src/utils/app-root', () => ({
get appRootPath() {
return join(__dirname, '../../../..');
@ -21,7 +21,7 @@ describe('getNodePartial', () => {
fileReplacements: [],
statsJson: false,
};
(<any>TsConfigPathsPlugin).mockImplementation(
(<any>TsconfigPathsPlugin).mockImplementation(
function MockPathsPlugin() {}
);
});

View File

@ -1,83 +0,0 @@
import { Console } from 'console';
import { Chalk } from 'chalk';
import { Options } from './tsconfig-paths.options';
// tslint:disable-next-line:no-any
type InternalLoggerFunc = (whereToLog: any, message: string) => void;
export type LoggerFunc = (message: string) => void;
export interface Logger {
log: LoggerFunc;
logInfo: LoggerFunc;
logWarning: LoggerFunc;
logError: LoggerFunc;
}
enum LogLevel {
INFO = 1,
WARN = 2,
ERROR = 3,
}
const stderrConsole = new Console(process.stderr);
const stdoutConsole = new Console(process.stdout);
const doNothingLogger = (_message: string) => {
/* Do nothing */
};
const makeLoggerFunc = (options: Options): InternalLoggerFunc =>
options.silent
? (_whereToLog: Console, _message: string) => {
/* Do nothing */
}
: (whereToLog: Console, message: string) => whereToLog.log(message);
const makeExternalLogger =
(loaderOptions: Options, logger: InternalLoggerFunc) => (message: string) =>
logger(
loaderOptions.logInfoToStdOut ? stdoutConsole : stderrConsole,
message
);
const makeLogInfo = (
options: Options,
logger: InternalLoggerFunc,
green: Chalk
) =>
LogLevel[options.logLevel] <= LogLevel.INFO
? (message: string) =>
logger(
options.logInfoToStdOut ? stdoutConsole : stderrConsole,
green(message)
)
: doNothingLogger;
const makeLogError = (
options: Options,
logger: InternalLoggerFunc,
red: Chalk
) =>
LogLevel[options.logLevel] <= LogLevel.ERROR
? (message: string) => logger(stderrConsole, red(message))
: doNothingLogger;
const makeLogWarning = (
options: Options,
logger: InternalLoggerFunc,
yellow: Chalk
) =>
LogLevel[options.logLevel] <= LogLevel.WARN
? (message: string) => logger(stderrConsole, yellow(message))
: doNothingLogger;
export function makeLogger(options: Options, colors: Chalk): Logger {
const logger = makeLoggerFunc(options);
return {
log: makeExternalLogger(options, logger),
logInfo: makeLogInfo(options, logger, colors.green),
logWarning: makeLogWarning(options, logger, colors.yellow),
logError: makeLogError(options, logger, colors.red),
};
}

View File

@ -1,81 +0,0 @@
export type LogLevel = 'INFO' | 'WARN' | 'ERROR';
export interface Options {
readonly configFile: string;
readonly extensions: ReadonlyArray<string>;
readonly baseUrl: string | undefined;
readonly silent: boolean;
readonly logLevel: LogLevel;
readonly logInfoToStdOut: boolean;
readonly context: string | undefined;
readonly colors: boolean;
readonly mainFields: string[];
}
type ValidOptions = keyof Options;
const validOptions: ReadonlyArray<ValidOptions> = [
'configFile',
'extensions',
'baseUrl',
'silent',
'logLevel',
'logInfoToStdOut',
'context',
'mainFields',
];
/**
* Takes raw options from the webpack config,
* validates them and adds defaults for missing options
*/
export function getOptions(rawOptions: {}): Options {
validateOptions(rawOptions);
const options = makeOptions(rawOptions);
return options;
}
/**
* Validate the supplied loader options.
* At present this validates the option names only; in future we may look at validating the values too
* @param rawOptions
*/
function validateOptions(rawOptions: {}): void {
const loaderOptionKeys = Object.keys(rawOptions);
for (let i = 0; i < loaderOptionKeys.length; i++) {
const option = loaderOptionKeys[i];
const isUnexpectedOption =
(validOptions as ReadonlyArray<string>).indexOf(option) === -1;
if (isUnexpectedOption) {
throw new Error(`tsconfig-paths-webpack-plugin was supplied with an unexpected loader option: ${option}
Please take a look at the options you are supplying; the following are valid options:
${validOptions.join(' / ')}
`);
}
}
}
function makeOptions(rawOptions: Partial<Options>): Options {
const options: Options = {
...({
configFile: 'tsconfig.json',
extensions: ['.ts', '.tsx'],
baseUrl: undefined,
silent: false,
logLevel: 'WARN',
logInfoToStdOut: false,
context: undefined,
colors: true,
mainFields: ['main'],
} as Options),
...rawOptions,
};
const options2: Options = {
...options,
logLevel: options.logLevel.toUpperCase() as LogLevel,
};
return options2;
}

View File

@ -1,353 +0,0 @@
// Adapted from https://github.com/dividab/tsconfig-paths-webpack-plugin
import * as chalk from 'chalk';
import * as TsconfigPaths from 'tsconfig-paths';
import * as path from 'path';
import * as Options from './tsconfig-paths.options';
import * as Logger from './tsconfig-paths.logger';
import * as fs from 'fs';
import { ResolveContext, ResolveRequest } from 'enhanced-resolve';
type ResolvePluginInstance = any;
type Resolver = any;
type FileSystem = Resolver['fileSystem'];
type TapAsyncCallback = (
request: ResolveRequest,
context: ResolveContext,
callback: TapAsyncInnerCallback
) => void;
type TapAsyncInnerCallback = (
error?: Error | null | false,
result?: null | ResolveRequest
) => void;
export interface LegacyResolverPlugin {
readonly apply: (resolver: LegacyResolver) => void;
}
export interface LegacyResolver {
readonly apply: (plugin: LegacyResolverPlugin) => void;
readonly plugin: (source: string, cb: ResolverCallbackLegacy) => void;
readonly doResolve: doResolveLegacy | doResolve;
readonly join: (relativePath: string, innerRequest: Request) => Request;
readonly fileSystem: LegacyResolverFileSystem;
readonly getHook: (hook: string) => Tapable;
}
export type doResolveLegacy = (
target: string,
req: Request,
desc: string,
callback: Callback
) => void;
export type doResolve = (
hook: Tapable,
req: Request,
message: string,
resolveContext: LegacyResolveContext,
callback: Callback
) => void;
export type ReadJsonCallback = (error: Error | undefined, result?: {}) => void;
export type ReadJson = (path2: string, callback: ReadJsonCallback) => void;
export type LegacyResolverFileSystem = typeof fs & { readJson?: ReadJson };
export interface LegacyResolveContext {
log?: string;
stack?: string;
missing?: string;
}
export interface Tapable {
readonly tapAsync: (
options: TapableOptions,
callback: TapAsyncCallback
) => void;
}
export interface TapableOptions {
readonly name: string;
}
export type ResolverCallbackLegacy = (
request: Request,
callback: Callback
) => void;
export type ResolverCallback = (
request: Request,
resolveContext: LegacyResolveContext,
callback: Callback
) => void;
type CreateInnerCallback = (
callback: Callback,
options: Callback,
message?: string,
messageOptional?: string
) => Callback;
type CreateInnerContext = (
options: {
log?: string;
stack?: string;
missing?: string;
},
message?: string,
messageOptional?: string
) => ResolveContext;
type getInnerRequest = (
resolver: Resolver | LegacyResolver,
request: ResolveRequest | Request
) => string;
export interface Request {
readonly request?: Request | string;
readonly relativePath: string;
readonly path: string;
readonly context: {
readonly issuer: string;
};
}
export interface Callback {
(err?: Error, result?: string): void;
log?: string;
stack?: string;
missing?: string;
}
const getInnerRequest: getInnerRequest = require('enhanced-resolve/lib/getInnerRequest');
export class TsconfigPathsPlugin implements ResolvePluginInstance {
source: string = 'described-resolve';
target: string = 'resolve';
log: Logger.Logger;
baseUrl: string;
absoluteBaseUrl: string;
extensions: ReadonlyArray<string>;
matchPath: TsconfigPaths.MatchPathAsync;
constructor(rawOptions: Partial<Options.Options> = {}) {
const options = Options.getOptions(rawOptions);
this.extensions = options.extensions;
// const colors = new chalk.constructor({ enabled: options.colors });
this.log = Logger.makeLogger(
options,
new chalk.Instance({ level: options.colors ? undefined : 0 })
);
const context = options.context || process.cwd();
const loadFrom = options.configFile || context;
const loadResult = TsconfigPaths.loadConfig(loadFrom);
if (loadResult.resultType === 'failed') {
this.log.logError(`Failed to load ${loadFrom}: ${loadResult.message}`);
} else {
this.log.logInfo(
`tsconfig-paths-webpack-plugin: Using config file at ${loadResult.configFileAbsolutePath}`
);
this.baseUrl = options.baseUrl || loadResult.baseUrl;
this.absoluteBaseUrl = options.baseUrl
? path.resolve(options.baseUrl)
: loadResult.absoluteBaseUrl;
this.matchPath = TsconfigPaths.createMatchPathAsync(
this.absoluteBaseUrl,
loadResult.paths,
options.mainFields
);
}
}
apply(resolver: Resolver): void {
if (!resolver) {
this.log.logWarning(
'tsconfig-paths-webpack-plugin: Found no resolver, not applying tsconfig-paths-webpack-plugin'
);
return;
}
const { baseUrl } = this;
if (!baseUrl) {
// Nothing to do if there is no baseUrl
this.log.logWarning(
'tsconfig-paths-webpack-plugin: Found no baseUrl in tsconfig.json, not applying tsconfig-paths-webpack-plugin'
);
return;
}
// The file system only exists when the plugin is in the resolve context. This means it's also properly placed in the resolve.plugins array.
// If not, we should warn the user that this plugin should be placed in resolve.plugins and not the plugins array of the root config for example.
// This should hopefully prevent issues like: https://github.com/dividab/tsconfig-paths-webpack-plugin/issues/9
if (!('fileSystem' in resolver)) {
this.log.logWarning(
'tsconfig-paths-webpack-plugin: No file system found on resolver.' +
" Please make sure you've placed the plugin in the correct part of the configuration." +
' This plugin is a resolver plugin and should be placed in the resolve part of the Webpack configuration.'
);
return;
}
resolver.getHook(this.source).tapAsync(
{ name: 'TsconfigPathsPlugin' },
// @ts-ignore
createPluginCallback(
this.matchPath,
resolver,
this.absoluteBaseUrl,
// @ts-ignore
resolver.getHook(this.target),
this.extensions
)
);
}
}
function createPluginCallback(
matchPath: TsconfigPaths.MatchPathAsync,
resolver: Resolver,
absoluteBaseUrl: string,
hook: Tapable,
extensions: ReadonlyArray<string>
): TapAsyncCallback {
const fileExistAsync = createFileExistAsync(resolver.fileSystem);
const readJsonAsync = createReadJsonAsync(resolver.fileSystem);
return (
request: ResolveRequest,
resolveContext: ResolveContext,
callback: TapAsyncInnerCallback
) => {
const innerRequest = getInnerRequest(resolver, request);
// innerRequest never starts with '.' in new enhanced-resolve
if (
!innerRequest ||
request?.request?.startsWith('.') ||
request?.request?.startsWith('..')
) {
return callback();
}
matchPath(
innerRequest,
readJsonAsync,
fileExistAsync,
extensions,
(err, foundMatch) => {
if (err) {
return callback(err);
}
if (!foundMatch) {
return callback();
}
const newRequest = {
...request,
request: foundMatch,
path: absoluteBaseUrl,
};
// Only at this point we are sure we are dealing with the latest Webpack version (>= 4.0.0)
// So only now can we require the createInnerContext function.
// (It doesn't exist in legacy versions)
const createInnerContext: CreateInnerContext = require('enhanced-resolve/lib/createInnerContext');
return resolver.doResolve(
hook,
newRequest as never,
`Resolved request '${innerRequest}' to '${foundMatch}' using tsconfig.json paths mapping`,
// tslint:disable-next-line:no-any
createInnerContext({ ...(resolveContext as any) }),
(err2: Error, result2: ResolveRequest): void => {
// Pattern taken from:
// https://github.com/webpack/enhanced-resolve/blob/42ff594140582c3f8f86811f95dea7bf6774a1c8/lib/AliasPlugin.js#L44
if (err2) {
return callback(err2);
}
// Don't allow other aliasing or raw request
if (result2 === undefined) {
return callback(undefined, undefined);
}
// tslint:disable-next-line:no-any
callback(undefined, result2);
}
);
}
);
};
}
function readJson(
fileSystem: FileSystem,
path2: string,
callback: ReadJsonCallback
): void {
if ('readJson' in fileSystem && fileSystem.readJson) {
return fileSystem.readJson(path2, callback);
}
fileSystem.readFile(path2, (err, buf) => {
if (err) {
return callback(err);
}
let data;
try {
// @ts-ignore This will crash if buf is undefined, which I guess it can be...
data = JSON.parse(buf.toString('utf-8'));
} catch (e) {
return callback(e);
}
return callback(undefined, data);
});
}
function createReadJsonAsync(
filesystem: FileSystem
): TsconfigPaths.ReadJsonAsync {
// tslint:disable-next-line:no-any
return (path2: string, callback2: (err?: Error, content?: any) => void) => {
readJson(filesystem, path2, (err, json) => {
// If error assume file does not exist
if (err || !json) {
callback2();
return;
}
callback2(undefined, json);
});
};
}
function createFileExistAsync(
filesystem: FileSystem
): TsconfigPaths.FileExistsAsync {
return (
path2: string,
callback2: (err?: Error, exists?: boolean) => void
) => {
filesystem.stat(path2, (err: Error, stats: fs.Stats) => {
// If error assume file does not exist
if (err) {
callback2(undefined, false);
return;
}
callback2(undefined, stats ? stats.isFile() : false);
});
};
}
export default TsconfigPathsPlugin;

View File

@ -36,6 +36,6 @@
"core-js": "^3.6.5",
"semver": "7.3.4",
"ts-loader": "^9.2.6",
"tsconfig-paths-webpack-plugin": "3.4.1"
"tsconfig-paths-webpack-plugin": "3.5.2"
}
}

View File

@ -102,7 +102,7 @@
"ts-loader": "^9.2.6",
"ts-node": "~9.1.1",
"tsconfig-paths": "^3.9.0",
"tsconfig-paths-webpack-plugin": "3.4.1",
"tsconfig-paths-webpack-plugin": "3.5.2",
"tslib": "^2.3.0",
"webpack": "^5.58.1",
"webpack-merge": "^5.8.0",

View File

@ -6,11 +6,7 @@ import * as CopyWebpackPlugin from 'copy-webpack-plugin';
import TerserPlugin = require('terser-webpack-plugin');
import { AssetGlobPattern, BuildBuilderOptions } from './shared-models';
import { getOutputHashFormat } from './hash-format';
// Inlining tsconfig-paths-webpack-plugin with a patch
// See: https://github.com/dividab/tsconfig-paths-webpack-plugin/pull/85
// TODO(jack): Remove once the patch lands in original package
import { TsconfigPathsPlugin } from './webpack/plugins/tsconfig-paths/tsconfig-paths.plugin';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const IGNORED_WEBPACK_WARNINGS = [
@ -113,13 +109,11 @@ export function getBaseWebpackPartial(
extensions,
alias: getAliases(options),
plugins: [
// TODO Remove the never type when module is updated
// PR opened for the proper typing here; https://github.com/dividab/tsconfig-paths-webpack-plugin/pull/66
new TsconfigPathsPlugin({
configFile: options.tsConfig,
extensions,
mainFields,
}),
}) as never, // TODO: Remove never type when 'tsconfig-paths-webpack-plugin' types fixed
],
mainFields,
},

View File

@ -1,83 +0,0 @@
import { Console } from 'console';
import { Chalk } from 'chalk';
import { Options } from './tsconfig-paths.options';
// tslint:disable-next-line:no-any
type InternalLoggerFunc = (whereToLog: any, message: string) => void;
export type LoggerFunc = (message: string) => void;
export interface Logger {
log: LoggerFunc;
logInfo: LoggerFunc;
logWarning: LoggerFunc;
logError: LoggerFunc;
}
enum LogLevel {
INFO = 1,
WARN = 2,
ERROR = 3,
}
const stderrConsole = new Console(process.stderr);
const stdoutConsole = new Console(process.stdout);
const doNothingLogger = (_message: string) => {
/* Do nothing */
};
const makeLoggerFunc = (options: Options): InternalLoggerFunc =>
options.silent
? (_whereToLog: Console, _message: string) => {
/* Do nothing */
}
: (whereToLog: Console, message: string) => whereToLog.log(message);
const makeExternalLogger =
(loaderOptions: Options, logger: InternalLoggerFunc) => (message: string) =>
logger(
loaderOptions.logInfoToStdOut ? stdoutConsole : stderrConsole,
message
);
const makeLogInfo = (
options: Options,
logger: InternalLoggerFunc,
green: Chalk
) =>
LogLevel[options.logLevel] <= LogLevel.INFO
? (message: string) =>
logger(
options.logInfoToStdOut ? stdoutConsole : stderrConsole,
green(message)
)
: doNothingLogger;
const makeLogError = (
options: Options,
logger: InternalLoggerFunc,
red: Chalk
) =>
LogLevel[options.logLevel] <= LogLevel.ERROR
? (message: string) => logger(stderrConsole, red(message))
: doNothingLogger;
const makeLogWarning = (
options: Options,
logger: InternalLoggerFunc,
yellow: Chalk
) =>
LogLevel[options.logLevel] <= LogLevel.WARN
? (message: string) => logger(stderrConsole, yellow(message))
: doNothingLogger;
export function makeLogger(options: Options, colors: Chalk): Logger {
const logger = makeLoggerFunc(options);
return {
log: makeExternalLogger(options, logger),
logInfo: makeLogInfo(options, logger, colors.green),
logWarning: makeLogWarning(options, logger, colors.yellow),
logError: makeLogError(options, logger, colors.red),
};
}

View File

@ -1,81 +0,0 @@
export type LogLevel = 'INFO' | 'WARN' | 'ERROR';
export interface Options {
readonly configFile: string;
readonly extensions: ReadonlyArray<string>;
readonly baseUrl: string | undefined;
readonly silent: boolean;
readonly logLevel: LogLevel;
readonly logInfoToStdOut: boolean;
readonly context: string | undefined;
readonly colors: boolean;
readonly mainFields: string[];
}
type ValidOptions = keyof Options;
const validOptions: ReadonlyArray<ValidOptions> = [
'configFile',
'extensions',
'baseUrl',
'silent',
'logLevel',
'logInfoToStdOut',
'context',
'mainFields',
];
/**
* Takes raw options from the webpack config,
* validates them and adds defaults for missing options
*/
export function getOptions(rawOptions: {}): Options {
validateOptions(rawOptions);
const options = makeOptions(rawOptions);
return options;
}
/**
* Validate the supplied loader options.
* At present this validates the option names only; in future we may look at validating the values too
* @param rawOptions
*/
function validateOptions(rawOptions: {}): void {
const loaderOptionKeys = Object.keys(rawOptions);
for (let i = 0; i < loaderOptionKeys.length; i++) {
const option = loaderOptionKeys[i];
const isUnexpectedOption =
(validOptions as ReadonlyArray<string>).indexOf(option) === -1;
if (isUnexpectedOption) {
throw new Error(`tsconfig-paths-webpack-plugin was supplied with an unexpected loader option: ${option}
Please take a look at the options you are supplying; the following are valid options:
${validOptions.join(' / ')}
`);
}
}
}
function makeOptions(rawOptions: Partial<Options>): Options {
const options: Options = {
...({
configFile: 'tsconfig.json',
extensions: ['.ts', '.tsx'],
baseUrl: undefined,
silent: false,
logLevel: 'WARN',
logInfoToStdOut: false,
context: undefined,
colors: true,
mainFields: ['main'],
} as Options),
...rawOptions,
};
const options2: Options = {
...options,
logLevel: options.logLevel.toUpperCase() as LogLevel,
};
return options2;
}

View File

@ -1,353 +0,0 @@
// Adapted from https://github.com/dividab/tsconfig-paths-webpack-plugin
import * as chalk from 'chalk';
import * as TsconfigPaths from 'tsconfig-paths';
import * as path from 'path';
import * as Options from './tsconfig-paths.options';
import * as Logger from './tsconfig-paths.logger';
import * as fs from 'fs';
import { ResolveContext, ResolveRequest } from 'enhanced-resolve';
type ResolvePluginInstance = any;
type Resolver = any;
type FileSystem = Resolver['fileSystem'];
type TapAsyncCallback = (
request: ResolveRequest,
context: ResolveContext,
callback: TapAsyncInnerCallback
) => void;
type TapAsyncInnerCallback = (
error?: Error | null | false,
result?: null | ResolveRequest
) => void;
export interface LegacyResolverPlugin {
readonly apply: (resolver: LegacyResolver) => void;
}
export interface LegacyResolver {
readonly apply: (plugin: LegacyResolverPlugin) => void;
readonly plugin: (source: string, cb: ResolverCallbackLegacy) => void;
readonly doResolve: doResolveLegacy | doResolve;
readonly join: (relativePath: string, innerRequest: Request) => Request;
readonly fileSystem: LegacyResolverFileSystem;
readonly getHook: (hook: string) => Tapable;
}
export type doResolveLegacy = (
target: string,
req: Request,
desc: string,
callback: Callback
) => void;
export type doResolve = (
hook: Tapable,
req: Request,
message: string,
resolveContext: LegacyResolveContext,
callback: Callback
) => void;
export type ReadJsonCallback = (error: Error | undefined, result?: {}) => void;
export type ReadJson = (path2: string, callback: ReadJsonCallback) => void;
export type LegacyResolverFileSystem = typeof fs & { readJson?: ReadJson };
export interface LegacyResolveContext {
log?: string;
stack?: string;
missing?: string;
}
export interface Tapable {
readonly tapAsync: (
options: TapableOptions,
callback: TapAsyncCallback
) => void;
}
export interface TapableOptions {
readonly name: string;
}
export type ResolverCallbackLegacy = (
request: Request,
callback: Callback
) => void;
export type ResolverCallback = (
request: Request,
resolveContext: LegacyResolveContext,
callback: Callback
) => void;
type CreateInnerCallback = (
callback: Callback,
options: Callback,
message?: string,
messageOptional?: string
) => Callback;
type CreateInnerContext = (
options: {
log?: string;
stack?: string;
missing?: string;
},
message?: string,
messageOptional?: string
) => ResolveContext;
type getInnerRequest = (
resolver: Resolver | LegacyResolver,
request: ResolveRequest | Request
) => string;
export interface Request {
readonly request?: Request | string;
readonly relativePath: string;
readonly path: string;
readonly context: {
readonly issuer: string;
};
}
export interface Callback {
(err?: Error, result?: string): void;
log?: string;
stack?: string;
missing?: string;
}
const getInnerRequest: getInnerRequest = require('enhanced-resolve/lib/getInnerRequest');
export class TsconfigPathsPlugin implements ResolvePluginInstance {
source: string = 'described-resolve';
target: string = 'resolve';
log: Logger.Logger;
baseUrl: string;
absoluteBaseUrl: string;
extensions: ReadonlyArray<string>;
matchPath: TsconfigPaths.MatchPathAsync;
constructor(rawOptions: Partial<Options.Options> = {}) {
const options = Options.getOptions(rawOptions);
this.extensions = options.extensions;
// const colors = new chalk.constructor({ enabled: options.colors });
this.log = Logger.makeLogger(
options,
new chalk.Instance({ level: options.colors ? undefined : 0 })
);
const context = options.context || process.cwd();
const loadFrom = options.configFile || context;
const loadResult = TsconfigPaths.loadConfig(loadFrom);
if (loadResult.resultType === 'failed') {
this.log.logError(`Failed to load ${loadFrom}: ${loadResult.message}`);
} else {
this.log.logInfo(
`tsconfig-paths-webpack-plugin: Using config file at ${loadResult.configFileAbsolutePath}`
);
this.baseUrl = options.baseUrl || loadResult.baseUrl;
this.absoluteBaseUrl = options.baseUrl
? path.resolve(options.baseUrl)
: loadResult.absoluteBaseUrl;
this.matchPath = TsconfigPaths.createMatchPathAsync(
this.absoluteBaseUrl,
loadResult.paths,
options.mainFields
);
}
}
apply(resolver: Resolver): void {
if (!resolver) {
this.log.logWarning(
'tsconfig-paths-webpack-plugin: Found no resolver, not applying tsconfig-paths-webpack-plugin'
);
return;
}
const { baseUrl } = this;
if (!baseUrl) {
// Nothing to do if there is no baseUrl
this.log.logWarning(
'tsconfig-paths-webpack-plugin: Found no baseUrl in tsconfig.json, not applying tsconfig-paths-webpack-plugin'
);
return;
}
// The file system only exists when the plugin is in the resolve context. This means it's also properly placed in the resolve.plugins array.
// If not, we should warn the user that this plugin should be placed in resolve.plugins and not the plugins array of the root config for example.
// This should hopefully prevent issues like: https://github.com/dividab/tsconfig-paths-webpack-plugin/issues/9
if (!('fileSystem' in resolver)) {
this.log.logWarning(
'tsconfig-paths-webpack-plugin: No file system found on resolver.' +
" Please make sure you've placed the plugin in the correct part of the configuration." +
' This plugin is a resolver plugin and should be placed in the resolve part of the Webpack configuration.'
);
return;
}
resolver.getHook(this.source).tapAsync(
{ name: 'TsconfigPathsPlugin' },
// @ts-ignore
createPluginCallback(
this.matchPath,
resolver,
this.absoluteBaseUrl,
// @ts-ignore
resolver.getHook(this.target),
this.extensions
)
);
}
}
function createPluginCallback(
matchPath: TsconfigPaths.MatchPathAsync,
resolver: Resolver,
absoluteBaseUrl: string,
hook: Tapable,
extensions: ReadonlyArray<string>
): TapAsyncCallback {
const fileExistAsync = createFileExistAsync(resolver.fileSystem);
const readJsonAsync = createReadJsonAsync(resolver.fileSystem);
return (
request: ResolveRequest,
resolveContext: ResolveContext,
callback: TapAsyncInnerCallback
) => {
const innerRequest = getInnerRequest(resolver, request);
// innerRequest never starts with '.' in new enhanced-resolve
if (
!innerRequest ||
request?.request?.startsWith('.') ||
request?.request?.startsWith('..')
) {
return callback();
}
matchPath(
innerRequest,
readJsonAsync,
fileExistAsync,
extensions,
(err, foundMatch) => {
if (err) {
return callback(err);
}
if (!foundMatch) {
return callback();
}
const newRequest = {
...request,
request: foundMatch,
path: absoluteBaseUrl,
};
// Only at this point we are sure we are dealing with the latest Webpack version (>= 4.0.0)
// So only now can we require the createInnerContext function.
// (It doesn't exist in legacy versions)
const createInnerContext: CreateInnerContext = require('enhanced-resolve/lib/createInnerContext');
return resolver.doResolve(
hook,
newRequest as never,
`Resolved request '${innerRequest}' to '${foundMatch}' using tsconfig.json paths mapping`,
// tslint:disable-next-line:no-any
createInnerContext({ ...(resolveContext as any) }),
(err2: Error, result2: ResolveRequest): void => {
// Pattern taken from:
// https://github.com/webpack/enhanced-resolve/blob/42ff594140582c3f8f86811f95dea7bf6774a1c8/lib/AliasPlugin.js#L44
if (err2) {
return callback(err2);
}
// Don't allow other aliasing or raw request
if (result2 === undefined) {
return callback(undefined, undefined);
}
// tslint:disable-next-line:no-any
callback(undefined, result2);
}
);
}
);
};
}
function readJson(
fileSystem: FileSystem,
path2: string,
callback: ReadJsonCallback
): void {
if ('readJson' in fileSystem && fileSystem.readJson) {
return fileSystem.readJson(path2, callback);
}
fileSystem.readFile(path2, (err, buf) => {
if (err) {
return callback(err);
}
let data;
try {
// @ts-ignore This will crash if buf is undefined, which I guess it can be...
data = JSON.parse(buf.toString('utf-8'));
} catch (e) {
return callback(e);
}
return callback(undefined, data);
});
}
function createReadJsonAsync(
filesystem: FileSystem
): TsconfigPaths.ReadJsonAsync {
// tslint:disable-next-line:no-any
return (path2: string, callback2: (err?: Error, content?: any) => void) => {
readJson(filesystem, path2, (err, json) => {
// If error assume file does not exist
if (err || !json) {
callback2();
return;
}
callback2(undefined, json);
});
};
}
function createFileExistAsync(
filesystem: FileSystem
): TsconfigPaths.FileExistsAsync {
return (
path2: string,
callback2: (err?: Error, exists?: boolean) => void
) => {
filesystem.stat(path2, (err: Error, stats: fs.Stats) => {
// If error assume file does not exist
if (err) {
callback2(undefined, false);
return;
}
callback2(undefined, stats ? stats.isFile() : false);
});
};
}
export default TsconfigPathsPlugin;

View File

@ -22244,7 +22244,7 @@ tsconfig-paths-webpack-plugin@3.4.1:
enhanced-resolve "^5.7.0"
tsconfig-paths "^3.9.0"
tsconfig-paths-webpack-plugin@^3.3.0:
tsconfig-paths-webpack-plugin@3.5.2, tsconfig-paths-webpack-plugin@^3.3.0:
version "3.5.2"
resolved "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.5.2.tgz"
integrity sha512-EhnfjHbzm5IYI9YPNVIxx1moxMI4bpHD2e0zTXeDNQcwjjRaGepP7IhTHJkyDBG0CAOoxRfe7jCG630Ou+C6Pw==