🎯 MapView v2.0 - Global Deployment Ready

 MAJOR FEATURES:
• Auto-zoom intelligence với smart bounds fitting
• Enhanced 3D GPS markers với pulsing effects
• Professional route display với 6-layer rendering
• Status-based parking icons với availability indicators
• Production-ready build optimizations

🗺️ AUTO-ZOOM FEATURES:
• Smart bounds fitting cho GPS + selected parking
• Adaptive padding (50px) cho visual balance
• Max zoom control (level 16) để tránh quá gần
• Dynamic centering khi không có selection

🎨 ENHANCED VISUALS:
• 3D GPS marker với multi-layer pulse effects
• Advanced parking icons với status colors
• Selection highlighting với animation
• Dimming system cho non-selected items

🛣️ ROUTE SYSTEM:
• OpenRouteService API integration
• Multi-layer route rendering (glow, shadow, main, animated)
• Real-time distance & duration calculation
• Visual route info trong popup

📱 PRODUCTION READY:
• SSR safe với dynamic imports
• Build errors resolved
• Global deployment via Vercel
• Optimized performance

🌍 DEPLOYMENT:
• Vercel: https://whatever-ctk2auuxr-phong12hexdockworks-projects.vercel.app
• Bundle size: 22.8 kB optimized
• Global CDN distribution
• HTTPS enabled

💾 VERSION CONTROL:
• MapView-v2.0.tsx backup created
• MAPVIEW_VERSIONS.md documentation
• Full version history tracking
This commit is contained in:
2025-07-20 19:52:16 +07:00
parent 3203463a6a
commit c65cc97a33
64624 changed files with 7199453 additions and 6462 deletions

View File

@@ -0,0 +1,15 @@
import { Configuration } from '../configuration';
export declare class AssetsManager {
private watchAssetsKeyValue;
private watchers;
private actionInProgress;
/**
* Using on `nest build` to close file watch or the build process will not end
* Interval like process
* If no action has been taken recently close watchers
* If action has been taken recently flag and try again
*/
closeWatchers(): void;
copyAssets(configuration: Required<Configuration>, appName: string, outDir: string, watchAssetsMode: boolean): void;
private actionOnFile;
}

View File

@@ -0,0 +1,126 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AssetsManager = void 0;
const chokidar = require("chokidar");
const fs_1 = require("fs");
const glob_1 = require("glob");
const path_1 = require("path");
const copy_path_resolve_1 = require("./helpers/copy-path-resolve");
const get_value_or_default_1 = require("./helpers/get-value-or-default");
class AssetsManager {
constructor() {
this.watchAssetsKeyValue = {};
this.watchers = [];
this.actionInProgress = false;
}
/**
* Using on `nest build` to close file watch or the build process will not end
* Interval like process
* If no action has been taken recently close watchers
* If action has been taken recently flag and try again
*/
closeWatchers() {
// Consider adjusting this for larger files
const timeoutMs = 500;
const closeFn = () => {
if (this.actionInProgress) {
this.actionInProgress = false;
setTimeout(closeFn, timeoutMs);
}
else {
this.watchers.forEach((watcher) => watcher.close());
}
};
setTimeout(closeFn, timeoutMs);
}
copyAssets(configuration, appName, outDir, watchAssetsMode) {
const assets = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.assets', appName) || [];
if (assets.length <= 0) {
return;
}
try {
let sourceRoot = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'sourceRoot', appName);
sourceRoot = (0, path_1.join)(process.cwd(), sourceRoot);
const filesToCopy = assets.map((item) => {
let includePath = typeof item === 'string' ? item : item.include;
let excludePath = typeof item !== 'string' && item.exclude ? item.exclude : undefined;
includePath = (0, path_1.join)(sourceRoot, includePath).replace(/\\/g, '/');
excludePath = excludePath
? (0, path_1.join)(sourceRoot, excludePath).replace(/\\/g, '/')
: undefined;
return {
outDir: typeof item !== 'string' ? item.outDir || outDir : outDir,
glob: includePath,
exclude: excludePath,
flat: typeof item !== 'string' ? item.flat : undefined, // deprecated field
watchAssets: typeof item !== 'string' ? item.watchAssets : undefined,
};
});
const isWatchEnabled = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.watchAssets', appName) || watchAssetsMode;
for (const item of filesToCopy) {
const option = {
action: 'change',
item,
path: '',
sourceRoot,
watchAssetsMode: isWatchEnabled,
};
if (isWatchEnabled || item.watchAssets) {
// prettier-ignore
const watcher = chokidar
.watch(item.glob, { ignored: item.exclude })
.on('add', (path) => this.actionOnFile({ ...option, path, action: 'change' }))
.on('change', (path) => this.actionOnFile({ ...option, path, action: 'change' }))
.on('unlink', (path) => this.actionOnFile({ ...option, path, action: 'unlink' }));
this.watchers.push(watcher);
}
else {
const matchedPaths = (0, glob_1.sync)(item.glob, {
ignore: item.exclude,
dot: true,
});
const files = item.glob.endsWith('*')
? matchedPaths.filter((matched) => (0, fs_1.statSync)(matched).isFile())
: matchedPaths.flatMap((matched) => {
if ((0, fs_1.statSync)(matched).isDirectory()) {
return (0, glob_1.sync)(`${matched}/**/*`, {
ignore: item.exclude,
}).filter((file) => (0, fs_1.statSync)(file).isFile());
}
return matched;
});
for (const path of files) {
this.actionOnFile({ ...option, path, action: 'change' });
}
}
}
}
catch (err) {
throw new Error(`An error occurred during the assets copying process. ${err.message}`);
}
}
actionOnFile(option) {
const { action, item, path, sourceRoot, watchAssetsMode } = option;
const isWatchEnabled = watchAssetsMode || item.watchAssets;
const assetCheckKey = path + (item.outDir ?? '');
// Allow to do action for the first time before check watchMode
if (!isWatchEnabled && this.watchAssetsKeyValue[assetCheckKey]) {
return;
}
// Set path value to true for watching the first time
this.watchAssetsKeyValue[assetCheckKey] = true;
// Set action to true to avoid watches getting cutoff
this.actionInProgress = true;
const dest = (0, copy_path_resolve_1.copyPathResolve)(path, item.outDir, sourceRoot.split(path_1.sep).length);
// Copy to output dir if file is changed or added
if (action === 'change') {
(0, fs_1.mkdirSync)((0, path_1.dirname)(dest), { recursive: true });
(0, fs_1.copyFileSync)(path, dest);
}
else if (action === 'unlink') {
// Remove from output dir if file is deleted
(0, fs_1.rmSync)(dest, { force: true });
}
}
}
exports.AssetsManager = AssetsManager;

View File

@@ -0,0 +1,9 @@
import { Configuration } from '../configuration';
import { PluginsLoader } from './plugins/plugins-loader';
export declare abstract class BaseCompiler<T = Record<string, any>> {
private readonly pluginsLoader;
constructor(pluginsLoader: PluginsLoader);
abstract run(configuration: Required<Configuration>, tsConfigPath: string, appName: string | undefined, extras?: T, onSuccess?: () => void): any;
loadPlugins(configuration: Required<Configuration>, tsConfigPath: string, appName: string | undefined): import("./plugins/plugins-loader").MultiNestCompilerPlugins;
getPathToSource(configuration: Required<Configuration>, tsConfigPath: string, appName: string | undefined): string;
}

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseCompiler = void 0;
const path_1 = require("path");
const get_value_or_default_1 = require("./helpers/get-value-or-default");
class BaseCompiler {
constructor(pluginsLoader) {
this.pluginsLoader = pluginsLoader;
}
loadPlugins(configuration, tsConfigPath, appName) {
const pluginsConfig = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.plugins', appName);
const pathToSource = this.getPathToSource(configuration, tsConfigPath, appName);
const plugins = this.pluginsLoader.load(pluginsConfig, { pathToSource });
return plugins;
}
getPathToSource(configuration, tsConfigPath, appName) {
const sourceRoot = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'sourceRoot', appName, 'sourceRoot');
const cwd = process.cwd();
const relativeRootPath = (0, path_1.dirname)((0, path_1.relative)(cwd, tsConfigPath));
const pathToSource = (0, path_1.normalize)(sourceRoot).indexOf((0, path_1.normalize)(relativeRootPath)) >= 0
? (0, path_1.join)(cwd, sourceRoot)
: (0, path_1.join)(cwd, relativeRootPath, sourceRoot);
return pathToSource;
}
}
exports.BaseCompiler = BaseCompiler;

View File

@@ -0,0 +1,12 @@
import { Configuration } from '../configuration';
import { BaseCompiler } from './base-compiler';
import { TsConfigProvider } from './helpers/tsconfig-provider';
import { PluginsLoader } from './plugins/plugins-loader';
import { TypeScriptBinaryLoader } from './typescript-loader';
export declare class Compiler extends BaseCompiler {
private readonly tsConfigProvider;
private readonly typescriptLoader;
constructor(pluginsLoader: PluginsLoader, tsConfigProvider: TsConfigProvider, typescriptLoader: TypeScriptBinaryLoader);
run(configuration: Required<Configuration>, tsConfigPath: string, appName: string, _extras: any, onSuccess?: () => void): void;
private reportAfterCompilationDiagnostic;
}

View File

@@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Compiler = void 0;
const ts = require("typescript");
const base_compiler_1 = require("./base-compiler");
const tsconfig_paths_hook_1 = require("./hooks/tsconfig-paths.hook");
class Compiler extends base_compiler_1.BaseCompiler {
constructor(pluginsLoader, tsConfigProvider, typescriptLoader) {
super(pluginsLoader);
this.tsConfigProvider = tsConfigProvider;
this.typescriptLoader = typescriptLoader;
}
run(configuration, tsConfigPath, appName, _extras, onSuccess) {
const tsBinary = this.typescriptLoader.load();
const formatHost = {
getCanonicalFileName: (path) => path,
getCurrentDirectory: tsBinary.sys.getCurrentDirectory,
getNewLine: () => tsBinary.sys.newLine,
};
const { options, fileNames, projectReferences } = this.tsConfigProvider.getByConfigFilename(tsConfigPath);
const createProgram = tsBinary.createIncrementalProgram || tsBinary.createProgram;
const program = createProgram.call(ts, {
rootNames: fileNames,
projectReferences,
options,
});
const plugins = this.loadPlugins(configuration, tsConfigPath, appName);
const tsconfigPathsPlugin = (0, tsconfig_paths_hook_1.tsconfigPathsBeforeHookFactory)(options);
const programRef = program.getProgram
? program.getProgram()
: program;
const before = plugins.beforeHooks.map((hook) => hook(programRef));
const after = plugins.afterHooks.map((hook) => hook(programRef));
const afterDeclarations = plugins.afterDeclarationsHooks.map((hook) => hook(programRef));
const emitResult = program.emit(undefined, undefined, undefined, undefined, {
before: tsconfigPathsPlugin
? before.concat(tsconfigPathsPlugin)
: before,
after,
afterDeclarations,
});
const errorsCount = this.reportAfterCompilationDiagnostic(program, emitResult, tsBinary, formatHost);
if (errorsCount) {
process.exit(1);
}
else if (!errorsCount && onSuccess) {
onSuccess();
}
}
reportAfterCompilationDiagnostic(program, emitResult, tsBinary, formatHost) {
const diagnostics = tsBinary
.getPreEmitDiagnostics(program)
.concat(emitResult.diagnostics);
if (diagnostics.length > 0) {
console.error(tsBinary.formatDiagnosticsWithColorAndContext(diagnostics, formatHost));
console.info(`Found ${diagnostics.length} error(s).` + tsBinary.sys.newLine);
}
return diagnostics.length;
}
}
exports.Compiler = Compiler;

View File

@@ -0,0 +1,61 @@
import * as ts from 'typescript';
import { Configuration } from '../../configuration';
export declare const swcDefaultsFactory: (tsOptions?: ts.CompilerOptions, configuration?: Configuration) => {
swcOptions: {
sourceMaps: string | boolean | undefined;
module: {
type: string;
};
jsc: {
target: string;
parser: {
syntax: string;
decorators: boolean;
dynamicImport: boolean;
};
transform: {
legacyDecorator: boolean;
decoratorMetadata: boolean;
useDefineForClassFields: boolean;
};
keepClassNames: boolean;
baseUrl: string | undefined;
paths: ts.MapLike<string[]> | undefined;
};
minify: boolean;
swcrc: boolean;
};
cliOptions: {
outDir: string;
filenames: string[];
sync: boolean;
extensions: string[];
copyFiles: boolean;
includeDotfiles: boolean;
quiet: boolean;
watch: boolean;
stripLeadingPaths: boolean;
} | {
swcrcPath?: string;
outDir: string;
filenames: string[];
sync: boolean;
extensions: string[];
copyFiles: boolean;
includeDotfiles: boolean;
quiet: boolean;
watch: boolean;
stripLeadingPaths: boolean;
} | {
configPath?: string;
outDir: string;
filenames: string[];
sync: boolean;
extensions: string[];
copyFiles: boolean;
includeDotfiles: boolean;
quiet: boolean;
watch: boolean;
stripLeadingPaths: boolean;
};
};

View File

@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.swcDefaultsFactory = void 0;
const swcDefaultsFactory = (tsOptions, configuration) => {
const builderOptions = typeof configuration?.compilerOptions?.builder !== 'string'
? configuration?.compilerOptions?.builder?.options
: {};
return {
swcOptions: {
sourceMaps: tsOptions?.sourceMap || (tsOptions?.inlineSourceMap && 'inline'),
module: {
type: 'commonjs',
},
jsc: {
target: 'es2021',
parser: {
syntax: 'typescript',
decorators: true,
dynamicImport: true,
},
transform: {
legacyDecorator: true,
decoratorMetadata: true,
useDefineForClassFields: false,
},
keepClassNames: true,
baseUrl: tsOptions?.baseUrl,
paths: tsOptions?.paths,
},
minify: false,
swcrc: true,
},
cliOptions: {
outDir: tsOptions?.outDir ? convertPath(tsOptions.outDir) : 'dist',
filenames: [configuration?.sourceRoot ?? 'src'],
sync: false,
extensions: ['.js', '.ts'],
copyFiles: false,
includeDotfiles: false,
quiet: false,
watch: false,
stripLeadingPaths: true,
...builderOptions,
},
};
};
exports.swcDefaultsFactory = swcDefaultsFactory;
/**
* Converts Windows specific file paths to posix
* @param windowsPath
*/
function convertPath(windowsPath) {
return windowsPath
.replace(/^\\\\\?\\/, '')
.replace(/\\/g, '/')
.replace(/\/\/+/g, '/');
}

View File

@@ -0,0 +1,3 @@
import { MultiNestCompilerPlugins } from '../plugins/plugins-loader';
import webpack = require('webpack');
export declare const webpackDefaultsFactory: (sourceRoot: string, relativeSourceRoot: string, entryFilename: string, isDebugEnabled: boolean | undefined, tsConfigFile: string | undefined, plugins: MultiNestCompilerPlugins) => webpack.Configuration;

View File

@@ -0,0 +1,104 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.webpackDefaultsFactory = void 0;
const path_1 = require("path");
const tsconfig_paths_webpack_plugin_1 = require("tsconfig-paths-webpack-plugin");
const defaults_1 = require("../../configuration/defaults");
const append_extension_1 = require("../helpers/append-extension");
const webpack = require("webpack");
const nodeExternals = require("webpack-node-externals");
const webpackDefaultsFactory = (sourceRoot, relativeSourceRoot, entryFilename, isDebugEnabled = false, tsConfigFile = defaults_1.defaultTsconfigFilename, plugins) => {
const isPluginRegistered = isAnyPluginRegistered(plugins);
const webpackConfiguration = {
entry: (0, append_extension_1.appendTsExtension)((0, path_1.join)(sourceRoot, entryFilename)),
devtool: isDebugEnabled ? 'inline-source-map' : false,
target: 'node',
output: {
filename: (0, path_1.join)(relativeSourceRoot, `${entryFilename}.js`),
},
ignoreWarnings: [/^(?!CriticalDependenciesWarning$)/],
externals: [nodeExternals()],
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 tsconfig_paths_webpack_plugin_1.TsconfigPathsPlugin({
configFile: tsConfigFile,
}),
],
},
mode: 'none',
optimization: {
nodeEnv: false,
},
node: {
__filename: false,
__dirname: false,
},
plugins: [
new webpack.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 (err) {
return true;
}
return false;
},
}),
],
};
if (!isPluginRegistered) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
webpackConfiguration.plugins.push(new ForkTsCheckerWebpackPlugin({
typescript: {
configFile: tsConfigFile,
},
}));
}
return webpackConfiguration;
};
exports.webpackDefaultsFactory = webpackDefaultsFactory;
function isAnyPluginRegistered(plugins) {
return ((plugins.afterHooks && plugins.afterHooks.length > 0) ||
(plugins.beforeHooks && plugins.beforeHooks.length > 0) ||
(plugins.afterDeclarationsHooks &&
plugins.afterDeclarationsHooks.length > 0));
}

View File

@@ -0,0 +1 @@
export declare function appendTsExtension(path: string): string;

View File

@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.appendTsExtension = appendTsExtension;
const path_1 = require("path");
function appendTsExtension(path) {
return (0, path_1.extname)(path) === '.ts' ? path : path + '.ts';
}

View File

@@ -0,0 +1,7 @@
/**
* Helper function for returning a copy destination filename
*
* @description used in `assets-manager.ts` (copy from `copyfiles`)
* @see https://github.com/calvinmetcalf/copyfiles/blob/master/index.js#L22
*/
export declare function copyPathResolve(filePath: string, outDir: string, up: number): string;

View File

@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.copyPathResolve = copyPathResolve;
const path = require("path");
/**
* Helper function for returning a copy destination filename
*
* @description used in `assets-manager.ts` (copy from `copyfiles`)
* @see https://github.com/calvinmetcalf/copyfiles/blob/master/index.js#L22
*/
function copyPathResolve(filePath, outDir, up) {
return path.join(outDir, dealWith(filePath, up));
}
function dealWith(inPath, up) {
if (!up) {
return inPath;
}
if (depth(inPath) < up - 1) {
throw new Error('Path outside of project folder is not allowed');
}
return path.join(...path.normalize(inPath).split(path.sep).slice(up));
}
function depth(string) {
return path.normalize(string).split(path.sep).length - 1;
}

View File

@@ -0,0 +1,2 @@
import { Configuration } from '../../configuration';
export declare function deleteOutDirIfEnabled(configuration: Required<Configuration>, appName: string, dirPath: string): Promise<void>;

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deleteOutDirIfEnabled = deleteOutDirIfEnabled;
const promises_1 = require("fs/promises");
const get_value_or_default_1 = require("./get-value-or-default");
async function deleteOutDirIfEnabled(configuration, appName, dirPath) {
const isDeleteEnabled = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.deleteOutDir', appName);
if (!isDeleteEnabled) {
return;
}
await (0, promises_1.rm)(dirPath, { recursive: true, force: true });
}

View File

@@ -0,0 +1,21 @@
import { Input } from '../../../commands';
import { Configuration } from '../../configuration';
/**
* Returns the builder to use for the given application.
* @param configuration Configuration object.
* @param cmdOptions Command line options.
* @param appName Application name.
* @returns The builder to use.
*/
export declare function getBuilder(configuration: Required<Configuration>, cmdOptions: Input[], appName: string): {
type: "webpack";
options?: import("../../configuration").WebpackBuilderOptions;
} | {
type: "swc";
options?: import("../../configuration").SwcBuilderOptions;
} | {
type: "tsc";
options?: import("../../configuration").TscBuilderOptions;
} | {
type: import("../../configuration").BuilderVariant;
};

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBuilder = getBuilder;
const get_value_or_default_1 = require("./get-value-or-default");
/**
* Returns the builder to use for the given application.
* @param configuration Configuration object.
* @param cmdOptions Command line options.
* @param appName Application name.
* @returns The builder to use.
*/
function getBuilder(configuration, cmdOptions, appName) {
const builderValue = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.builder', appName, 'builder', cmdOptions, 'tsc');
return typeof builderValue === 'string'
? {
type: builderValue,
}
: builderValue;
}

View File

@@ -0,0 +1,10 @@
import { Input } from '../../../commands';
import { Configuration } from '../../configuration';
/**
* Returns the path to the tsc configuration file to use for the given application.
* @param configuration Configuration object.
* @param cmdOptions Command line options.
* @param appName Application name.
* @returns The path to the tsc configuration file to use.
*/
export declare function getTscConfigPath(configuration: Required<Configuration>, cmdOptions: Input[], appName: string): string;

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getTscConfigPath = getTscConfigPath;
const get_default_tsconfig_path_1 = require("../../utils/get-default-tsconfig-path");
const get_value_or_default_1 = require("./get-value-or-default");
/**
* Returns the path to the tsc configuration file to use for the given application.
* @param configuration Configuration object.
* @param cmdOptions Command line options.
* @param appName Application name.
* @returns The path to the tsc configuration file to use.
*/
function getTscConfigPath(configuration, cmdOptions, appName) {
let tsconfigPath = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.tsConfigPath', appName, 'path', cmdOptions);
if (tsconfigPath) {
return tsconfigPath;
}
const builder = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.builder', appName);
tsconfigPath =
typeof builder === 'object' && builder?.type === 'tsc'
? builder.options?.configPath
: undefined;
return tsconfigPath ?? (0, get_default_tsconfig_path_1.getDefaultTsconfigPath)();
}

View File

@@ -0,0 +1,4 @@
import { Input } from '../../../commands';
import { Configuration } from '../../configuration';
export declare function getValueOrDefault<T = any>(configuration: Required<Configuration>, propertyPath: string, appName: string | undefined, key?: 'path' | 'webpack' | 'webpackPath' | 'entryFile' | 'sourceRoot' | 'exec' | 'builder' | 'typeCheck', options?: Input[], defaultValue?: T): T;
export declare function getValueOfPath<T = any>(object: Record<string, any>, propertyPath: string): T;

View File

@@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getValueOrDefault = getValueOrDefault;
exports.getValueOfPath = getValueOfPath;
function getValueOrDefault(configuration, propertyPath, appName, key, options = [], defaultValue) {
const item = options.find((option) => option.name === key);
const origValue = item && item.value;
if (origValue !== undefined && origValue !== null) {
return origValue;
}
if (configuration.projects && configuration.projects[appName]) {
// Wrap the application name in double-quotes to prevent splitting it
// into separate chunks
appName = appName && !appName.includes('"') ? `"${appName}"` : appName;
const perAppValue = getValueOfPath(configuration, `projects.${appName}.`.concat(propertyPath));
if (perAppValue !== undefined) {
return perAppValue;
}
}
let value = getValueOfPath(configuration, propertyPath);
if (value === undefined) {
value = defaultValue;
}
return value;
}
function getValueOfPath(object, propertyPath) {
const fragments = propertyPath.split('.');
let propertyValue = object;
let isConcatInProgress = false;
let path = '';
for (const fragment of fragments) {
if (!propertyValue) {
break;
}
/**
* When path is escaped with "" double quotes,
* concatenate the property path.
* Reference: https://github.com/nestjs/nest-cli/issues/947
*/
if (fragment.startsWith('"') && fragment.endsWith('"')) {
path = stripDoubleQuotes(fragment);
}
else if (fragment.startsWith('"')) {
path += stripDoubleQuotes(fragment) + '.';
isConcatInProgress = true;
continue;
}
else if (isConcatInProgress && !fragment.endsWith('"')) {
path += fragment + '.';
continue;
}
else if (fragment.endsWith('"')) {
path += stripDoubleQuotes(fragment);
isConcatInProgress = false;
}
else {
path = fragment;
}
propertyValue = propertyValue[path];
path = '';
}
return propertyValue;
}
function stripDoubleQuotes(text) {
return text.replace(/"/g, '');
}

View File

@@ -0,0 +1,10 @@
import { Input } from '../../../commands';
import { Configuration } from '../../configuration';
/**
* Returns the path to the webpack configuration file to use for the given application.
* @param configuration Configuration object.
* @param cmdOptions Command line options.
* @param appName Application name.
* @returns The path to the webpack configuration file to use.
*/
export declare function getWebpackConfigPath(configuration: Required<Configuration>, cmdOptions: Input[], appName: string): string | undefined;

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getWebpackConfigPath = getWebpackConfigPath;
const get_value_or_default_1 = require("./get-value-or-default");
/**
* Returns the path to the webpack configuration file to use for the given application.
* @param configuration Configuration object.
* @param cmdOptions Command line options.
* @param appName Application name.
* @returns The path to the webpack configuration file to use.
*/
function getWebpackConfigPath(configuration, cmdOptions, appName) {
let webpackPath = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.webpackConfigPath', appName, 'webpackPath', cmdOptions);
if (webpackPath) {
return webpackPath;
}
const builder = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.builder', appName);
webpackPath =
typeof builder === 'object' && builder?.type === 'webpack'
? builder.options?.configPath
: undefined;
return webpackPath;
}

View File

@@ -0,0 +1,2 @@
export declare function listenForManualRestart(callback: () => void): void;
export declare function displayManualRestartTip(): void;

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.listenForManualRestart = listenForManualRestart;
exports.displayManualRestartTip = displayManualRestartTip;
const chalk = require("chalk");
function listenForManualRestart(callback) {
const stdinListener = (data) => {
if (data.toString().trim() === 'rs') {
process.stdin.removeListener('data', stdinListener);
callback();
}
};
process.stdin.on('data', stdinListener);
}
function displayManualRestartTip() {
console.log(`To restart at any time, enter ${chalk.gray('rs')}.\n`);
}

View File

@@ -0,0 +1,11 @@
import * as ts from 'typescript';
import { TypeScriptBinaryLoader } from '../typescript-loader';
export declare class TsConfigProvider {
private readonly typescriptLoader;
constructor(typescriptLoader: TypeScriptBinaryLoader);
getByConfigFilename(configFilename: string): {
options: ts.CompilerOptions;
fileNames: string[];
projectReferences: readonly ts.ProjectReference[] | undefined;
};
}

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TsConfigProvider = void 0;
const fs_1 = require("fs");
const path_1 = require("path");
const ui_1 = require("../../ui");
class TsConfigProvider {
constructor(typescriptLoader) {
this.typescriptLoader = typescriptLoader;
}
getByConfigFilename(configFilename) {
const configPath = (0, path_1.join)(process.cwd(), configFilename);
if (!(0, fs_1.existsSync)(configPath)) {
throw new Error(ui_1.CLI_ERRORS.MISSING_TYPESCRIPT(configFilename));
}
const tsBinary = this.typescriptLoader.load();
const parsedCmd = tsBinary.getParsedCommandLineOfConfigFile(configPath, undefined, tsBinary.sys);
const { options, fileNames, projectReferences } = parsedCmd;
return { options, fileNames, projectReferences };
}
}
exports.TsConfigProvider = TsConfigProvider;

View File

@@ -0,0 +1,2 @@
import * as ts from 'typescript';
export declare function tsconfigPathsBeforeHookFactory(compilerOptions: ts.CompilerOptions): (ctx: ts.TransformationContext) => ts.Transformer<any>;

View File

@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tsconfigPathsBeforeHookFactory = tsconfigPathsBeforeHookFactory;
const os = require("os");
const path_1 = require("path");
const typescript_loader_1 = require("../typescript-loader");
const tsPaths = require("tsconfig-paths");
function tsconfigPathsBeforeHookFactory(compilerOptions) {
const tsBinary = new typescript_loader_1.TypeScriptBinaryLoader().load();
const { paths = {}, baseUrl = './' } = compilerOptions;
const matcher = tsPaths.createMatchPath(baseUrl, paths, ['main']);
return (ctx) => {
return (sf) => {
const visitNode = (node) => {
if (tsBinary.isImportDeclaration(node) ||
(tsBinary.isExportDeclaration(node) && node.moduleSpecifier)) {
try {
const importPathWithQuotes = node.moduleSpecifier && node.moduleSpecifier.getText();
if (!importPathWithQuotes) {
return node;
}
const text = importPathWithQuotes.substring(1, importPathWithQuotes.length - 1);
const result = getNotAliasedPath(sf, matcher, text);
if (!result) {
return node;
}
const moduleSpecifier = tsBinary.factory.createStringLiteral(result);
moduleSpecifier.parent = node.moduleSpecifier.parent;
if (tsBinary.isImportDeclaration(node)) {
const updatedNode = tsBinary.factory.updateImportDeclaration(node, node.modifiers, node.importClause, moduleSpecifier, node.assertClause);
updatedNode.flags = node.flags;
return updatedNode;
}
else {
const updatedNode = tsBinary.factory.updateExportDeclaration(node, node.modifiers, node.isTypeOnly, node.exportClause, moduleSpecifier, node.assertClause);
updatedNode.flags = node.flags;
return updatedNode;
}
}
catch {
return node;
}
}
return tsBinary.visitEachChild(node, visitNode, ctx);
};
return tsBinary.visitNode(sf, visitNode);
};
};
}
function getNotAliasedPath(sf, matcher, text) {
let result = matcher(text, undefined, undefined, [
'.ts',
'.tsx',
'.js',
'.jsx',
]);
if (!result) {
return;
}
if (os.platform() === 'win32') {
result = result.replace(/\\/g, '/');
}
try {
// Installed packages (node modules) should take precedence over root files with the same name.
// Ref: https://github.com/nestjs/nest-cli/issues/838
const packagePath = require.resolve(text, {
paths: [process.cwd(), ...module.paths],
});
if (packagePath) {
return text;
}
}
catch { }
const resolvedPath = path_1.posix.relative((0, path_1.dirname)(sf.fileName), result) || './';
return resolvedPath[0] === '.' ? resolvedPath : './' + resolvedPath;
}

View File

@@ -0,0 +1,10 @@
import * as ts from 'typescript';
export type DeepPluginMeta = ts.ObjectLiteralExpression | {
[key: string]: DeepPluginMeta;
};
export interface ReadonlyVisitor {
key: string;
typeImports: Record<string, string>;
visit(program: unknown, sf: unknown): unknown;
collect(): Record<string, Array<unknown>>;
}

View File

@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@@ -0,0 +1,58 @@
import * as ts from 'typescript';
import { ReadonlyVisitor } from '../interfaces/readonly-visitor.interface';
export interface PluginMetadataGenerateOptions {
/**
* The visitors to use to generate the metadata.
*/
visitors: ReadonlyVisitor[];
/**
* The output directory to write the metadata to.
*/
outputDir: string;
/**
* Whether to watch the project for changes.
*/
watch?: boolean;
/**
* The path to the tsconfig file.
* Relative to the current working directory (process.cwd()).
*/
tsconfigPath?: string;
/**
* The filename to write the metadata to.
*/
filename?: string;
/**
* A reference to an existing ts.Program instance.
*/
tsProgramRef?: ts.Program;
/**
* Whether to print diagnostics to the console.
* @default true
*/
printDiagnostics?: boolean;
}
/**
* Generates plugins metadata by traversing the AST of the project.
* @example
* ```ts
* const generator = new PluginMetadataGenerator();
* generator.generate({
* visitors: [
* new ReadonlyVisitor({ introspectComments: true, pathToSource: __dirname }),
* ],
* outputDir: __dirname,
* watch: true,
* tsconfigPath: 'tsconfig.build.json',
* });
* ```
*/
export declare class PluginMetadataGenerator {
private readonly pluginMetadataPrinter;
private readonly typeCheckerHost;
private readonly typescriptLoader;
private readonly tsBinary;
constructor();
generate(options: PluginMetadataGenerateOptions): void;
private traverseAndPrintMetadata;
}

View File

@@ -0,0 +1,84 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluginMetadataGenerator = void 0;
const constants_1 = require("../swc/constants");
const type_checker_host_1 = require("../swc/type-checker-host");
const typescript_loader_1 = require("../typescript-loader");
const plugin_metadata_printer_1 = require("./plugin-metadata-printer");
/**
* Generates plugins metadata by traversing the AST of the project.
* @example
* ```ts
* const generator = new PluginMetadataGenerator();
* generator.generate({
* visitors: [
* new ReadonlyVisitor({ introspectComments: true, pathToSource: __dirname }),
* ],
* outputDir: __dirname,
* watch: true,
* tsconfigPath: 'tsconfig.build.json',
* });
* ```
*/
class PluginMetadataGenerator {
constructor() {
this.pluginMetadataPrinter = new plugin_metadata_printer_1.PluginMetadataPrinter();
this.typeCheckerHost = new type_checker_host_1.TypeCheckerHost();
this.typescriptLoader = new typescript_loader_1.TypeScriptBinaryLoader();
this.tsBinary = this.typescriptLoader.load();
}
generate(options) {
const { tsconfigPath, visitors, tsProgramRef, outputDir, watch, filename, printDiagnostics = true, } = options;
if (visitors.length === 0) {
return;
}
if (tsProgramRef) {
return this.traverseAndPrintMetadata(tsProgramRef, visitors, outputDir, filename);
}
const onTypeCheckOrProgramInit = (program) => {
this.traverseAndPrintMetadata(program, visitors, outputDir, filename);
if (printDiagnostics) {
const tsBinary = this.typescriptLoader.load();
const diagnostics = tsBinary.getPreEmitDiagnostics(program);
if (diagnostics.length > 0) {
const formatDiagnosticsHost = {
getCanonicalFileName: (path) => path,
getCurrentDirectory: tsBinary.sys.getCurrentDirectory,
getNewLine: () => tsBinary.sys.newLine,
};
console.log();
console.log(tsBinary.formatDiagnosticsWithColorAndContext(diagnostics, formatDiagnosticsHost));
}
else {
console.log(constants_1.FOUND_NO_ISSUES_GENERATING_METADATA);
}
}
};
this.typeCheckerHost.run(tsconfigPath, {
watch,
onTypeCheck: onTypeCheckOrProgramInit,
onProgramInit: onTypeCheckOrProgramInit,
});
}
traverseAndPrintMetadata(programRef, visitors, outputDir, filename) {
for (const sourceFile of programRef.getSourceFiles()) {
if (!sourceFile.isDeclarationFile) {
visitors.forEach((visitor) => visitor.visit(programRef, sourceFile));
}
}
let typeImports = {};
const collectedMetadata = {};
visitors.forEach((visitor) => {
collectedMetadata[visitor.key] = visitor.collect();
typeImports = {
...typeImports,
...visitor.typeImports,
};
});
this.pluginMetadataPrinter.print(collectedMetadata, typeImports, {
outputDir,
filename,
}, this.tsBinary);
}
}
exports.PluginMetadataGenerator = PluginMetadataGenerator;

View File

@@ -0,0 +1,17 @@
import * as ts from 'typescript';
import { DeepPluginMeta } from '../interfaces/readonly-visitor.interface';
export interface PluginMetadataPrintOptions {
outputDir: string;
filename?: string;
}
type ComposedPluginMeta = Record<string, Record<string, Array<[ts.CallExpression, DeepPluginMeta]>>>;
/**
* Prints the metadata to a file.
*/
export declare class PluginMetadataPrinter {
print(metadata: ComposedPluginMeta, typeImports: Record<string, string>, options: PluginMetadataPrintOptions, tsBinary: typeof ts): void;
private recursivelyCreatePropertyAssignment;
private createTypeImportVariableStatement;
private createPropertyAssignment;
}
export {};

View File

@@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluginMetadataPrinter = void 0;
const fs_1 = require("fs");
const path_1 = require("path");
const SERIALIZED_METADATA_FILENAME = 'metadata.ts';
const TYPE_IMPORT_VARIABLE_NAME = 't';
/**
* Prints the metadata to a file.
*/
class PluginMetadataPrinter {
print(metadata, typeImports, options, tsBinary) {
const objectLiteralExpr = tsBinary.factory.createObjectLiteralExpression(Object.keys(metadata).map((key) => this.recursivelyCreatePropertyAssignment(key, metadata[key], tsBinary)));
const exportAssignment = tsBinary.factory.createExportAssignment(undefined, undefined, tsBinary.factory.createArrowFunction([tsBinary.factory.createToken(tsBinary.SyntaxKind.AsyncKeyword)], undefined, [], undefined, tsBinary.factory.createToken(tsBinary.SyntaxKind.EqualsGreaterThanToken), tsBinary.factory.createBlock([
this.createTypeImportVariableStatement(typeImports, tsBinary),
tsBinary.factory.createReturnStatement(objectLiteralExpr),
], true)));
const printer = tsBinary.createPrinter({
newLine: tsBinary.NewLineKind.LineFeed,
});
const resultFile = tsBinary.createSourceFile('file.ts', '', tsBinary.ScriptTarget.Latest,
/*setParentNodes*/ false, tsBinary.ScriptKind.TS);
const filename = (0, path_1.join)(options.outputDir, options.filename ?? SERIALIZED_METADATA_FILENAME);
const eslintPrefix = `/* eslint-disable */\n`;
(0, fs_1.writeFileSync)(filename, eslintPrefix +
printer.printNode(tsBinary.EmitHint.Unspecified, exportAssignment, resultFile));
}
recursivelyCreatePropertyAssignment(identifier, meta, tsBinary) {
if (Array.isArray(meta)) {
return tsBinary.factory.createPropertyAssignment(tsBinary.factory.createStringLiteral(identifier), tsBinary.factory.createArrayLiteralExpression(meta.map(([importExpr, meta]) => tsBinary.factory.createArrayLiteralExpression([
importExpr,
tsBinary.factory.createObjectLiteralExpression(Object.keys(meta).map((key) => this.recursivelyCreatePropertyAssignment(key, meta[key], tsBinary))),
]))));
}
return tsBinary.factory.createPropertyAssignment(tsBinary.factory.createStringLiteral(identifier), tsBinary.isObjectLiteralExpression(meta)
? meta
: tsBinary.factory.createObjectLiteralExpression(Object.keys(meta).map((key) => this.recursivelyCreatePropertyAssignment(key, meta[key], tsBinary))));
}
createTypeImportVariableStatement(typeImports, tsBinary) {
return tsBinary.factory.createVariableStatement(undefined, tsBinary.factory.createVariableDeclarationList([
tsBinary.factory.createVariableDeclaration(tsBinary.factory.createIdentifier(TYPE_IMPORT_VARIABLE_NAME), undefined, undefined, tsBinary.factory.createObjectLiteralExpression(Object.keys(typeImports).map((ti) => this.createPropertyAssignment(ti, typeImports[ti], tsBinary)), true)),
], tsBinary.NodeFlags.Const |
tsBinary.NodeFlags.AwaitContext |
tsBinary.NodeFlags.ContextFlags |
tsBinary.NodeFlags.TypeExcludesFlags));
}
createPropertyAssignment(identifier, target, tsBinary) {
return tsBinary.factory.createPropertyAssignment(tsBinary.factory.createComputedPropertyName(tsBinary.factory.createStringLiteral(identifier)), tsBinary.factory.createIdentifier(target));
}
}
exports.PluginMetadataPrinter = PluginMetadataPrinter;

View File

@@ -0,0 +1,30 @@
import * as ts from 'typescript';
import { ReadonlyVisitor } from '../interfaces/readonly-visitor.interface';
type Transformer = ts.TransformerFactory<any> | ts.CustomTransformerFactory;
type PluginEntry = string | PluginAndOptions;
type PluginOptions = Record<string, any>;
interface PluginAndOptions {
name: 'string';
options: PluginOptions;
}
export interface NestCompilerPlugin {
before?: (options?: PluginOptions, program?: ts.Program) => Transformer;
after?: (options?: PluginOptions, program?: ts.Program) => Transformer;
afterDeclarations?: (options?: PluginOptions, program?: ts.Program) => Transformer;
ReadonlyVisitor?: {
new (options: PluginOptions): ReadonlyVisitor;
};
}
export interface MultiNestCompilerPlugins {
beforeHooks: Array<(program?: ts.Program) => Transformer>;
afterHooks: Array<(program?: ts.Program) => Transformer>;
afterDeclarationsHooks: Array<(program?: ts.Program) => Transformer>;
readonlyVisitors: Array<ReadonlyVisitor>;
}
export declare class PluginsLoader {
load(plugins?: PluginEntry[], extras?: {
pathToSource?: string;
}): MultiNestCompilerPlugins;
private resolvePluginReferences;
}
export {};

View File

@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluginsLoader = void 0;
const path_1 = require("path");
const ui_1 = require("../../ui");
const PLUGIN_ENTRY_FILENAME = 'plugin';
class PluginsLoader {
load(plugins = [], extras = {}) {
const pluginNames = plugins.map((entry) => typeof entry === 'object'
? entry.name
: entry);
const pluginRefs = this.resolvePluginReferences(pluginNames);
const multiCompilerPlugins = {
afterHooks: [],
afterDeclarationsHooks: [],
beforeHooks: [],
readonlyVisitors: [],
};
pluginRefs.forEach((plugin, index) => {
if (!plugin.before && !plugin.after && !plugin.afterDeclarations) {
throw new Error(ui_1.CLI_ERRORS.WRONG_PLUGIN(pluginNames[index]));
}
const options = typeof plugins[index] === 'object'
? plugins[index].options || {}
: {};
if (plugin.before) {
multiCompilerPlugins.beforeHooks.push(plugin.before.bind(plugin.before, options));
}
if (plugin.after) {
multiCompilerPlugins.afterHooks.push(plugin.after.bind(plugin.after, options));
}
if (plugin.afterDeclarations) {
multiCompilerPlugins.afterDeclarationsHooks.push(plugin.afterDeclarations.bind(plugin.afterDeclarations, options));
}
if (plugin.ReadonlyVisitor) {
const instance = new plugin.ReadonlyVisitor({
...options,
...extras,
readonly: true,
});
instance.key = pluginNames[index];
multiCompilerPlugins.readonlyVisitors.push(instance);
}
});
return multiCompilerPlugins;
}
resolvePluginReferences(pluginNames) {
const nodeModulePaths = [
(0, path_1.join)(process.cwd(), 'node_modules'),
...module.paths,
];
return pluginNames.map((item) => {
try {
try {
const binaryPath = require.resolve((0, path_1.join)(item, PLUGIN_ENTRY_FILENAME), {
paths: nodeModulePaths,
});
return require(binaryPath);
}
catch { }
const binaryPath = require.resolve(item, { paths: nodeModulePaths });
return require(binaryPath);
}
catch (e) {
throw new Error(`"${item}" plugin is not installed.`);
}
});
}
}
exports.PluginsLoader = PluginsLoader;

View File

@@ -0,0 +1,9 @@
export declare const TSC_NO_ERRORS_MESSAGE = "Found 0 errors. Watching for file changes.";
export declare const TSC_COMPILATION_STARTED_MESSAGE = "Starting compilation in watch mode...";
export declare const SWC_LOG_PREFIX: string;
export declare const TSC_LOG_PREFIX: string;
export declare const TSC_LOG_ERROR_PREFIX: string;
export declare const TSC_LOG_SUCCESS_PREFIX: string;
export declare const INITIALIZING_TYPE_CHECKER: string;
export declare const FOUND_NO_ISSUES_METADATA_GENERATION_SKIPPED: string;
export declare const FOUND_NO_ISSUES_GENERATING_METADATA: string;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FOUND_NO_ISSUES_GENERATING_METADATA = exports.FOUND_NO_ISSUES_METADATA_GENERATION_SKIPPED = exports.INITIALIZING_TYPE_CHECKER = exports.TSC_LOG_SUCCESS_PREFIX = exports.TSC_LOG_ERROR_PREFIX = exports.TSC_LOG_PREFIX = exports.SWC_LOG_PREFIX = exports.TSC_COMPILATION_STARTED_MESSAGE = exports.TSC_NO_ERRORS_MESSAGE = void 0;
const chalk = require("chalk");
exports.TSC_NO_ERRORS_MESSAGE = 'Found 0 errors. Watching for file changes.';
exports.TSC_COMPILATION_STARTED_MESSAGE = 'Starting compilation in watch mode...';
exports.SWC_LOG_PREFIX = chalk.cyan('> ') + chalk.bgCyan.bold(' SWC ');
exports.TSC_LOG_PREFIX = chalk.cyan('> ') + chalk.bgCyan.bold(' TSC ');
exports.TSC_LOG_ERROR_PREFIX = chalk.red('> ') + chalk.bgRed.bold(' TSC ');
exports.TSC_LOG_SUCCESS_PREFIX = chalk.green('> ') + chalk.bgGreen.bold(' TSC ');
exports.INITIALIZING_TYPE_CHECKER = chalk.bgCyan.bold(' TSC ') + chalk.cyan(' Initializing type checker...');
exports.FOUND_NO_ISSUES_METADATA_GENERATION_SKIPPED = exports.TSC_LOG_SUCCESS_PREFIX + chalk.green(' Found 0 issues.');
exports.FOUND_NO_ISSUES_GENERATING_METADATA = exports.TSC_LOG_SUCCESS_PREFIX +
chalk.green(' Found 0 issues. Generating metadata...');

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ui_1 = require("../../ui");
const base_compiler_1 = require("../base-compiler");
const plugin_metadata_generator_1 = require("../plugins/plugin-metadata-generator");
const plugins_loader_1 = require("../plugins/plugins-loader");
const constants_1 = require("./constants");
const type_checker_host_1 = require("./type-checker-host");
const [tsConfigPath, appName, sourceRoot, plugins] = process.argv.slice(2);
class ForkedTypeChecker extends base_compiler_1.BaseCompiler {
constructor() {
super(...arguments);
this.pluginMetadataGenerator = new plugin_metadata_generator_1.PluginMetadataGenerator();
this.typeCheckerHost = new type_checker_host_1.TypeCheckerHost();
}
async run(configuration, tsConfigPath, appName, extras) {
const { readonlyVisitors } = this.loadPlugins(configuration, tsConfigPath, appName);
const outputDir = this.getPathToSource(configuration, tsConfigPath, appName);
try {
const onTypeCheckOrProgramInit = (program) => {
if (readonlyVisitors.length > 0) {
console.log(constants_1.FOUND_NO_ISSUES_GENERATING_METADATA);
this.pluginMetadataGenerator.generate({
outputDir,
visitors: readonlyVisitors,
tsProgramRef: program,
});
}
else {
console.log(constants_1.FOUND_NO_ISSUES_METADATA_GENERATION_SKIPPED);
}
};
this.typeCheckerHost.run(tsConfigPath, {
watch: extras.watch,
onTypeCheck: onTypeCheckOrProgramInit,
onProgramInit: onTypeCheckOrProgramInit,
});
}
catch (err) {
console.log(ui_1.ERROR_PREFIX, err.message);
}
}
}
const pluginsLoader = new plugins_loader_1.PluginsLoader();
const forkedTypeChecker = new ForkedTypeChecker(pluginsLoader);
const applicationName = appName === 'undefined' ? '' : appName;
const options = {
sourceRoot,
};
if (applicationName) {
options.projects = {};
options.projects[applicationName] = {
compilerOptions: {
plugins: JSON.parse(plugins),
},
};
}
else {
options.compilerOptions = {
plugins: JSON.parse(plugins),
};
}
forkedTypeChecker.run(options, tsConfigPath, applicationName, { watch: true, typeCheck: true });

View File

@@ -0,0 +1,24 @@
import * as ts from 'typescript';
import { Configuration } from '../../configuration';
import { AssetsManager } from '../assets-manager';
import { BaseCompiler } from '../base-compiler';
import { PluginsLoader } from '../plugins/plugins-loader';
export type SwcCompilerExtras = {
watch: boolean;
typeCheck: boolean;
assetsManager: AssetsManager;
tsOptions: ts.CompilerOptions;
};
export declare class SwcCompiler extends BaseCompiler {
private readonly pluginMetadataGenerator;
private readonly typeCheckerHost;
constructor(pluginsLoader: PluginsLoader);
run(configuration: Required<Configuration>, tsConfigPath: string, appName: string, extras: SwcCompilerExtras, onSuccess?: () => void): Promise<void>;
private runTypeChecker;
private runSwc;
private loadSwcCliBinary;
private getSwcRcFileContentIfExists;
private deepMerge;
private debounce;
private watchFilesInOutDir;
}

View File

@@ -0,0 +1,192 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SwcCompiler = void 0;
const chalk = require("chalk");
const child_process_1 = require("child_process");
const chokidar = require("chokidar");
const fs_1 = require("fs");
const path = require("path");
const path_1 = require("path");
const ui_1 = require("../../ui");
const tree_kill_1 = require("../../utils/tree-kill");
const base_compiler_1 = require("../base-compiler");
const swc_defaults_1 = require("../defaults/swc-defaults");
const get_value_or_default_1 = require("../helpers/get-value-or-default");
const plugin_metadata_generator_1 = require("../plugins/plugin-metadata-generator");
const constants_1 = require("./constants");
const type_checker_host_1 = require("./type-checker-host");
class SwcCompiler extends base_compiler_1.BaseCompiler {
constructor(pluginsLoader) {
super(pluginsLoader);
this.pluginMetadataGenerator = new plugin_metadata_generator_1.PluginMetadataGenerator();
this.typeCheckerHost = new type_checker_host_1.TypeCheckerHost();
}
async run(configuration, tsConfigPath, appName, extras, onSuccess) {
const swcOptions = (0, swc_defaults_1.swcDefaultsFactory)(extras.tsOptions, configuration);
const swcrcFilePath = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.builder.options.swcrcPath', appName);
if (extras.watch) {
if (extras.typeCheck) {
this.runTypeChecker(configuration, tsConfigPath, appName, extras);
}
await this.runSwc(swcOptions, extras, swcrcFilePath);
if (onSuccess) {
onSuccess();
const debounceTime = 150;
const callback = this.debounce(onSuccess, debounceTime);
this.watchFilesInOutDir(swcOptions, callback);
}
}
else {
if (extras.typeCheck) {
await this.runTypeChecker(configuration, tsConfigPath, appName, extras);
}
await this.runSwc(swcOptions, extras, swcrcFilePath);
if (onSuccess) {
onSuccess();
}
extras.assetsManager?.closeWatchers();
}
}
runTypeChecker(configuration, tsConfigPath, appName, extras) {
if (extras.watch) {
const args = [
tsConfigPath,
appName,
configuration.sourceRoot ?? 'src',
JSON.stringify(configuration.compilerOptions.plugins ?? []),
];
const childProcessRef = (0, child_process_1.fork)((0, path_1.join)(__dirname, 'forked-type-checker.js'), args, {
cwd: process.cwd(),
});
process.on('exit', () => childProcessRef && (0, tree_kill_1.treeKillSync)(childProcessRef.pid));
}
else {
const { readonlyVisitors } = this.loadPlugins(configuration, tsConfigPath, appName);
const outputDir = this.getPathToSource(configuration, tsConfigPath, appName);
let fulfilled = false;
return new Promise((resolve, reject) => {
try {
this.typeCheckerHost.run(tsConfigPath, {
watch: extras.watch,
onTypeCheck: (program) => {
if (!fulfilled) {
fulfilled = true;
resolve();
}
if (readonlyVisitors.length > 0) {
process.nextTick(() => console.log(constants_1.FOUND_NO_ISSUES_GENERATING_METADATA));
this.pluginMetadataGenerator.generate({
outputDir,
visitors: readonlyVisitors,
tsProgramRef: program,
});
}
else {
process.nextTick(() => console.log(constants_1.FOUND_NO_ISSUES_METADATA_GENERATION_SKIPPED));
}
},
});
}
catch (err) {
if (!fulfilled) {
fulfilled = true;
reject(err);
}
}
});
}
}
async runSwc(options, extras, swcrcFilePath) {
process.nextTick(() => console.log(constants_1.SWC_LOG_PREFIX, chalk.cyan('Running...')));
const swcCli = this.loadSwcCliBinary();
const swcRcFile = await this.getSwcRcFileContentIfExists(swcrcFilePath);
const swcOptions = this.deepMerge(options.swcOptions, swcRcFile);
if (swcOptions?.jsc?.baseUrl && !(0, path_1.isAbsolute)(swcOptions?.jsc?.baseUrl)) {
// jsc.baseUrl should be resolved by the caller, if it's passed as an object.
// https://github.com/swc-project/swc/pull/7827
const rootDir = process.cwd();
swcOptions.jsc.baseUrl = path.join(rootDir, swcOptions.jsc.baseUrl);
}
await swcCli.default({
...options,
swcOptions,
cliOptions: {
...options.cliOptions,
watch: extras.watch,
},
});
}
loadSwcCliBinary() {
try {
return require('@swc/cli/lib/swc/dir');
}
catch (err) {
console.error(ui_1.ERROR_PREFIX +
' Failed to load "@swc/cli" and/or "@swc/core" required packages. Please, make sure to install them as development dependencies.');
process.exit(1);
}
}
getSwcRcFileContentIfExists(swcrcFilePath) {
try {
return JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(process.cwd(), swcrcFilePath ?? '.swcrc'), 'utf8'));
}
catch (err) {
if (swcrcFilePath !== undefined) {
console.error(ui_1.ERROR_PREFIX +
` Failed to load "${swcrcFilePath}". Please, check if the file exists and is valid JSON.`);
process.exit(1);
}
return {};
}
}
deepMerge(target, source) {
if (typeof target !== 'object' ||
target === null ||
typeof source !== 'object' ||
source === null) {
return source;
}
if (Array.isArray(target) && Array.isArray(source)) {
return source.reduce((acc, value, index) => {
acc[index] = this.deepMerge(target[index], value);
return acc;
}, target);
}
const merged = { ...target };
for (const key in source) {
if (source.hasOwnProperty(key)) {
if (key in target) {
merged[key] = this.deepMerge(target[key], source[key]);
}
else {
merged[key] = source[key];
}
}
}
return merged;
}
debounce(callback, wait) {
let timeout;
return () => {
clearTimeout(timeout);
timeout = setTimeout(callback, wait);
};
}
watchFilesInOutDir(options, onChange) {
const dir = (0, path_1.isAbsolute)(options.cliOptions.outDir)
? options.cliOptions.outDir
: (0, path_1.join)(process.cwd(), options.cliOptions.outDir);
const paths = (0, path_1.join)(dir, '**/*.js');
const watcher = chokidar.watch(paths, {
ignoreInitial: true,
awaitWriteFinish: {
stabilityThreshold: 50,
pollInterval: 10,
},
});
for (const type of ['add', 'change']) {
watcher.on(type, async () => onChange());
}
}
}
exports.SwcCompiler = SwcCompiler;

View File

@@ -0,0 +1,14 @@
import * as ts from 'typescript';
export interface TypeCheckerHostRunOptions {
watch?: boolean;
onTypeCheck?: (program: ts.Program) => void;
onProgramInit?: (program: ts.Program) => void;
}
export declare class TypeCheckerHost {
private readonly typescriptLoader;
private readonly tsConfigProvider;
run(tsconfigPath: string | undefined, options: TypeCheckerHostRunOptions): void;
private runInWatchMode;
private runOnce;
private createWatchCompilerHost;
}

View File

@@ -0,0 +1,90 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeCheckerHost = void 0;
const chalk = require("chalk");
const ora = require("ora");
const ts = require("typescript");
const tsconfig_provider_1 = require("../helpers/tsconfig-provider");
const typescript_loader_1 = require("../typescript-loader");
const constants_1 = require("./constants");
class TypeCheckerHost {
constructor() {
this.typescriptLoader = new typescript_loader_1.TypeScriptBinaryLoader();
this.tsConfigProvider = new tsconfig_provider_1.TsConfigProvider(this.typescriptLoader);
}
run(tsconfigPath, options) {
if (!tsconfigPath) {
throw new Error('"tsconfigPath" is required when "tsProgramRef" is not provided.');
}
const tsBinary = this.typescriptLoader.load();
const spinner = ora({
text: constants_1.INITIALIZING_TYPE_CHECKER,
});
if (options.watch) {
console.log();
spinner.start();
this.runInWatchMode(tsconfigPath, tsBinary, options);
spinner.succeed();
return;
}
spinner.start();
this.runOnce(tsconfigPath, tsBinary, options);
spinner.succeed();
}
runInWatchMode(tsconfigPath, tsBinary, options) {
const { options: tsOptions } = this.tsConfigProvider.getByConfigFilename(tsconfigPath);
let builderProgram = undefined;
const reportWatchStatusCallback = (diagnostic) => {
if (diagnostic.messageText !== constants_1.TSC_NO_ERRORS_MESSAGE) {
if (diagnostic.messageText?.includes('Found')) {
console.log(constants_1.TSC_LOG_ERROR_PREFIX, chalk.red(diagnostic.messageText));
}
return;
}
if (!builderProgram) {
return;
}
const tsProgram = builderProgram.getProgram().getProgram();
options.onTypeCheck?.(tsProgram);
};
const host = this.createWatchCompilerHost(tsBinary, tsconfigPath, tsOptions, reportWatchStatusCallback);
builderProgram = tsBinary.createWatchProgram(host);
process.nextTick(() => {
options.onProgramInit?.(builderProgram.getProgram().getProgram());
});
}
runOnce(tsconfigPath, tsBinary, options) {
const { options: tsOptions, fileNames, projectReferences, } = this.tsConfigProvider.getByConfigFilename(tsconfigPath);
const createProgram = tsBinary.createIncrementalProgram ?? tsBinary.createProgram;
const program = createProgram.call(ts, {
rootNames: fileNames,
projectReferences,
options: tsOptions,
});
const programRef = program.getProgram
? program.getProgram()
: program;
const diagnostics = tsBinary.getPreEmitDiagnostics(programRef);
if (diagnostics.length > 0) {
const formatDiagnosticsHost = {
getCanonicalFileName: (path) => path,
getCurrentDirectory: tsBinary.sys.getCurrentDirectory,
getNewLine: () => tsBinary.sys.newLine,
};
console.log();
console.log(tsBinary.formatDiagnosticsWithColorAndContext(diagnostics, formatDiagnosticsHost));
process.exit(1);
}
options.onTypeCheck?.(programRef);
}
createWatchCompilerHost(tsBinary, tsConfigPath, options, reportWatchStatusCallback) {
const origDiagnosticReporter = tsBinary.createDiagnosticReporter(tsBinary.sys, true);
const tsOptions = {
...options,
preserveWatchOutput: true,
noEmit: true,
};
return tsBinary.createWatchCompilerHost(tsConfigPath, tsOptions, tsBinary.sys, undefined, origDiagnosticReporter, reportWatchStatusCallback);
}
}
exports.TypeCheckerHost = TypeCheckerHost;

View File

@@ -0,0 +1,6 @@
import * as ts from 'typescript';
export declare class TypeScriptBinaryLoader {
private tsBinary?;
load(): typeof ts;
getModulePaths(): string[];
}

View File

@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeScriptBinaryLoader = void 0;
class TypeScriptBinaryLoader {
load() {
if (this.tsBinary) {
return this.tsBinary;
}
try {
const tsBinaryPath = require.resolve('typescript', {
paths: [process.cwd(), ...this.getModulePaths()],
});
// eslint-disable-next-line @typescript-eslint/no-var-requires
const tsBinary = require(tsBinaryPath);
this.tsBinary = tsBinary;
return tsBinary;
}
catch {
throw new Error('TypeScript could not be found! Please, install "typescript" package.');
}
}
getModulePaths() {
const modulePaths = module.paths.slice(2, module.paths.length);
const packageDeps = modulePaths.slice(0, 3);
return [
...packageDeps.reverse(),
...modulePaths.slice(3, modulePaths.length).reverse(),
];
}
}
exports.TypeScriptBinaryLoader = TypeScriptBinaryLoader;

View File

@@ -0,0 +1,22 @@
import { Configuration } from '../configuration';
import { BaseCompiler } from './base-compiler';
import { TsConfigProvider } from './helpers/tsconfig-provider';
import { PluginsLoader } from './plugins/plugins-loader';
import { TypeScriptBinaryLoader } from './typescript-loader';
type TypescriptWatchCompilerExtras = {
/**
* If `undefined`, the value of 'preserveWatchOutput' option from tsconfig
* file will be used instead.
*/
preserveWatchOutput: boolean | undefined;
};
export declare class WatchCompiler extends BaseCompiler<TypescriptWatchCompilerExtras> {
private readonly tsConfigProvider;
private readonly typescriptLoader;
constructor(pluginsLoader: PluginsLoader, tsConfigProvider: TsConfigProvider, typescriptLoader: TypeScriptBinaryLoader);
run(configuration: Required<Configuration>, tsConfigPath: string, appName: string, extras: TypescriptWatchCompilerExtras, onSuccess?: () => void): void;
private overrideCreateProgramFn;
private createDiagnosticReporter;
private createWatchStatusChanged;
}
export {};

View File

@@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WatchCompiler = void 0;
const errors_1 = require("../ui/errors");
const base_compiler_1 = require("./base-compiler");
const get_value_or_default_1 = require("./helpers/get-value-or-default");
const manual_restart_1 = require("./helpers/manual-restart");
const tsconfig_paths_hook_1 = require("./hooks/tsconfig-paths.hook");
class WatchCompiler extends base_compiler_1.BaseCompiler {
constructor(pluginsLoader, tsConfigProvider, typescriptLoader) {
super(pluginsLoader);
this.tsConfigProvider = tsConfigProvider;
this.typescriptLoader = typescriptLoader;
}
run(configuration, tsConfigPath, appName, extras, onSuccess) {
const tsBin = this.typescriptLoader.load();
const configPath = tsBin.findConfigFile(process.cwd(), tsBin.sys.fileExists, tsConfigPath);
if (!configPath) {
throw new Error(errors_1.CLI_ERRORS.MISSING_TYPESCRIPT(tsConfigPath));
}
const { options, projectReferences } = this.tsConfigProvider.getByConfigFilename(tsConfigPath);
const createProgram = tsBin.createEmitAndSemanticDiagnosticsBuilderProgram;
const origDiagnosticReporter = tsBin.createDiagnosticReporter(tsBin.sys, true);
const origWatchStatusReporter = tsBin.createWatchStatusReporter(tsBin.sys, true);
const host = tsBin.createWatchCompilerHost(configPath, {
...options,
preserveWatchOutput: extras.preserveWatchOutput ?? options.preserveWatchOutput,
}, tsBin.sys, createProgram, this.createDiagnosticReporter(origDiagnosticReporter), this.createWatchStatusChanged(origWatchStatusReporter, onSuccess));
const manualRestart = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.manualRestart', appName);
const plugins = this.loadPlugins(configuration, tsConfigPath, appName);
this.overrideCreateProgramFn(host, manualRestart, projectReferences, plugins);
const watchProgram = tsBin.createWatchProgram(host);
if (manualRestart) {
(0, manual_restart_1.listenForManualRestart)(() => {
watchProgram.close();
this.run(configuration, tsConfigPath, appName, extras, onSuccess);
});
}
}
overrideCreateProgramFn(host, manualRestart, projectReferences, plugins) {
const origCreateProgram = host.createProgram;
host.createProgram = (rootNames, options,
// tslint:disable-next-line:no-shadowed-variable
host, oldProgram) => {
if (manualRestart) {
(0, manual_restart_1.displayManualRestartTip)();
}
const tsconfigPathsPlugin = options
? (0, tsconfig_paths_hook_1.tsconfigPathsBeforeHookFactory)(options)
: null;
const program = origCreateProgram(rootNames, options, host, oldProgram, undefined, projectReferences);
const origProgramEmit = program.emit;
program.emit = (targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers) => {
let transforms = customTransformers;
transforms = typeof transforms !== 'object' ? {} : transforms;
const before = plugins.beforeHooks.map((hook) => hook(program.getProgram()));
const after = plugins.afterHooks.map((hook) => hook(program.getProgram()));
const afterDeclarations = plugins.afterDeclarationsHooks.map((hook) => hook(program.getProgram()));
if (tsconfigPathsPlugin) {
before.unshift(tsconfigPathsPlugin);
}
transforms.before = before.concat(transforms.before || []);
transforms.after = after.concat(transforms.after || []);
transforms.afterDeclarations = afterDeclarations.concat(transforms.afterDeclarations || []);
return origProgramEmit(targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, transforms);
};
return program;
};
}
createDiagnosticReporter(diagnosticReporter) {
return function (diagnostic, ...args) {
return diagnosticReporter.call(this, diagnostic, ...args);
};
}
createWatchStatusChanged(watchStatusReporter, onSuccess) {
return function (diagnostic, ...args) {
const messageText = diagnostic && diagnostic.messageText;
const noErrorsMessage = '0 errors';
if (messageText &&
messageText.includes &&
messageText.includes(noErrorsMessage) &&
onSuccess) {
onSuccess();
}
return watchStatusReporter.call(this, diagnostic, ...args);
};
}
}
exports.WatchCompiler = WatchCompiler;

View File

@@ -0,0 +1,21 @@
import { Input } from '../../commands';
import { Configuration } from '../configuration';
import { AssetsManager } from './assets-manager';
import { BaseCompiler } from './base-compiler';
import { PluginsLoader } from './plugins/plugins-loader';
import webpack = require('webpack');
type WebpackConfigFactory = (config: webpack.Configuration, webpackRef: typeof webpack) => webpack.Configuration;
type WebpackConfigFactoryOrConfig = WebpackConfigFactory | webpack.Configuration;
type WebpackCompilerExtras = {
inputs: Input[];
assetsManager: AssetsManager;
webpackConfigFactoryOrConfig: WebpackConfigFactoryOrConfig | WebpackConfigFactoryOrConfig[];
debug?: boolean;
watchMode?: boolean;
};
export declare class WebpackCompiler extends BaseCompiler<WebpackCompilerExtras> {
constructor(pluginsLoader: PluginsLoader);
run(configuration: Required<Configuration>, tsConfigPath: string, appName: string, extras: WebpackCompilerExtras, onSuccess?: () => void): void;
private createAfterCallback;
}
export {};

View File

@@ -0,0 +1,99 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebpackCompiler = void 0;
const fs_1 = require("fs");
const path_1 = require("path");
const ui_1 = require("../ui");
const base_compiler_1 = require("./base-compiler");
const webpack_defaults_1 = require("./defaults/webpack-defaults");
const get_value_or_default_1 = require("./helpers/get-value-or-default");
const webpack = require("webpack");
class WebpackCompiler extends base_compiler_1.BaseCompiler {
constructor(pluginsLoader) {
super(pluginsLoader);
}
run(configuration, tsConfigPath, appName, extras, onSuccess) {
const cwd = process.cwd();
const configPath = (0, path_1.join)(cwd, tsConfigPath);
if (!(0, fs_1.existsSync)(configPath)) {
throw new Error(`Could not find TypeScript configuration file "${tsConfigPath}".`);
}
const plugins = this.loadPlugins(configuration, tsConfigPath, appName);
const pathToSource = this.getPathToSource(configuration, tsConfigPath, appName);
const entryFile = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'entryFile', appName, 'entryFile', extras.inputs);
const entryFileRoot = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'root', appName) || '';
const defaultOptions = (0, webpack_defaults_1.webpackDefaultsFactory)(pathToSource, entryFileRoot, entryFile, extras.debug ?? false, tsConfigPath, plugins);
let compiler;
let watchOptions;
let watch;
if (Array.isArray(extras.webpackConfigFactoryOrConfig)) {
const webpackConfigurations = extras.webpackConfigFactoryOrConfig.map((configOrFactory) => {
const unwrappedConfig = typeof configOrFactory !== 'function'
? configOrFactory
: configOrFactory(defaultOptions, webpack);
return {
...defaultOptions,
mode: extras.watchMode ? 'development' : defaultOptions.mode,
...unwrappedConfig,
};
});
compiler = webpack(webpackConfigurations);
watchOptions = webpackConfigurations.map((config) => config.watchOptions || {});
watch = webpackConfigurations.some((config) => config.watch);
}
else {
const projectWebpackOptions = typeof extras.webpackConfigFactoryOrConfig !== 'function'
? extras.webpackConfigFactoryOrConfig
: extras.webpackConfigFactoryOrConfig(defaultOptions, webpack);
const webpackConfiguration = {
...defaultOptions,
mode: extras.watchMode ? 'development' : defaultOptions.mode,
...projectWebpackOptions,
};
compiler = webpack(webpackConfiguration);
watchOptions = webpackConfiguration.watchOptions;
watch = webpackConfiguration.watch;
}
const afterCallback = this.createAfterCallback(onSuccess, extras.assetsManager, extras.watchMode ?? false, watch);
if (extras.watchMode || watch) {
compiler.hooks.watchRun.tapAsync('Rebuild info', (params, callback) => {
console.log(`\n${ui_1.INFO_PREFIX} Webpack is building your sources...\n`);
callback();
});
compiler.watch(watchOptions || {}, afterCallback);
}
else {
compiler.run(afterCallback);
}
}
createAfterCallback(onSuccess, assetsManager, watchMode, watch) {
return (err, stats) => {
if (err && stats === undefined) {
// Could not complete the compilation
// The error caught is most likely thrown by underlying tasks
console.log(err);
return process.exit(1);
}
const statsOutput = stats.toString({
chunks: false,
colors: true,
modules: false,
assets: false,
});
if (!err && !stats.hasErrors()) {
if (!onSuccess) {
assetsManager.closeWatchers();
}
else {
onSuccess();
}
}
else if (!watchMode && !watch) {
console.log(statsOutput);
return process.exit(1);
}
console.log(statsOutput);
};
}
}
exports.WebpackCompiler = WebpackCompiler;