@nestjs/cli
Version:
Nest - modern, fast, powerful node.js web framework (@cli)
194 lines (193 loc) • 8.06 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.SwcCompiler = void 0;
const ansis_1 = require("ansis");
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 ?? 'undefined',
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, (0, ansis_1.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 watcher = chokidar.watch(dir, {
ignored: (file, stats) => (stats?.isFile() &&
!(file.endsWith('.js') || file.endsWith('.mjs'))),
ignoreInitial: true,
awaitWriteFinish: {
stabilityThreshold: 50,
pollInterval: 10,
},
});
for (const type of ['add', 'change']) {
watcher.on(type, async () => onChange());
}
}
}
exports.SwcCompiler = SwcCompiler;
;