@nx/js
Version:
200 lines (199 loc) • 7.88 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.compileSwc = compileSwc;
exports.compileSwcWatch = compileSwcWatch;
const devkit_1 = require("@nx/devkit");
const node_child_process_1 = require("node:child_process");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const async_iterable_1 = require("@nx/devkit/src/utils/async-iterable");
const print_diagnostics_1 = require("../typescript/print-diagnostics");
const run_type_check_1 = require("../typescript/run-type-check");
const path_1 = require("path");
function getSwcCmd({ swcCliOptions: { swcrcPath, destPath, stripLeadingPaths }, root, projectRoot, originalProjectRoot, sourceRoot, inline, }, watch = false) {
const swcCLI = require.resolve('@swc/cli/bin/swc.js');
let inputDir;
// TODO(v21): remove inline feature
if (inline) {
inputDir = originalProjectRoot.split('/')[0];
}
else {
if (sourceRoot) {
inputDir = (0, path_1.relative)(projectRoot, sourceRoot);
}
else {
// If sourceRoot is not provided, check if `src` exists and use that instead.
// This is important for root projects to avoid compiling too many directories.
inputDir = (0, node_fs_1.existsSync)((0, node_path_1.join)(root, projectRoot, 'src')) ? 'src' : '.';
}
}
let swcCmd = `node ${swcCLI} ${inputDir || '.'} -d ${destPath} --config-file=${swcrcPath} ${stripLeadingPaths ? '--strip-leading-paths' : ''}`;
return watch ? swcCmd.concat(' --watch') : swcCmd;
}
function getTypeCheckOptions(normalizedOptions) {
const { sourceRoot, projectRoot, watch, tsConfig, root, outputPath } = normalizedOptions;
const inputDir =
// If `--strip-leading-paths` SWC option is used, we need to transpile from `src` directory.
!normalizedOptions.swcCliOptions.stripLeadingPaths
? projectRoot
: sourceRoot
? sourceRoot
: (0, node_fs_1.existsSync)((0, node_path_1.join)(root, projectRoot, 'src'))
? (0, node_path_1.join)(projectRoot, 'src')
: projectRoot;
const typeCheckOptions = {
mode: 'emitDeclarationOnly',
tsConfigPath: tsConfig,
outDir: outputPath,
workspaceRoot: root,
rootDir: inputDir,
};
if (watch) {
typeCheckOptions.incremental = true;
typeCheckOptions.cacheDir = devkit_1.cacheDir;
}
if (normalizedOptions.isTsSolutionSetup && normalizedOptions.skipTypeCheck) {
typeCheckOptions.ignoreDiagnostics = true;
}
return typeCheckOptions;
}
async function compileSwc(context, normalizedOptions, postCompilationCallback) {
devkit_1.logger.log(`Compiling with SWC for ${context.projectName}...`);
if (normalizedOptions.clean) {
(0, node_fs_1.rmSync)(normalizedOptions.outputPath, { recursive: true, force: true });
}
try {
const swcCmdLog = (0, node_child_process_1.execSync)(getSwcCmd(normalizedOptions), {
encoding: 'utf8',
cwd: normalizedOptions.swcCliOptions.swcCwd,
windowsHide: false,
stdio: 'pipe',
});
devkit_1.logger.log(swcCmdLog.replace(/\n/, ''));
}
catch (error) {
devkit_1.logger.error('SWC compilation failed');
if (error.stderr) {
devkit_1.logger.error(error.stderr.toString());
}
return { success: false };
}
if (normalizedOptions.skipTypeCheck && !normalizedOptions.isTsSolutionSetup) {
await postCompilationCallback();
return { success: true };
}
const { errors, warnings } = await (0, run_type_check_1.runTypeCheck)(getTypeCheckOptions(normalizedOptions));
const hasErrors = errors.length > 0;
const hasWarnings = warnings.length > 0;
if (hasErrors || hasWarnings) {
await (0, print_diagnostics_1.printDiagnostics)(errors, warnings);
}
await postCompilationCallback();
return {
success: !hasErrors,
outfile: normalizedOptions.mainOutputPath,
};
}
async function* compileSwcWatch(context, normalizedOptions, postCompilationCallback) {
const getResult = (success) => ({
success,
outfile: normalizedOptions.mainOutputPath,
});
let typeCheckOptions;
let initialPostCompile = true;
if (normalizedOptions.clean) {
(0, node_fs_1.rmSync)(normalizedOptions.outputPath, { recursive: true, force: true });
}
return yield* (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
let processOnExit;
let stdoutOnData;
let stderrOnData;
let watcherOnExit;
const swcWatcher = (0, node_child_process_1.exec)(getSwcCmd(normalizedOptions, true), {
cwd: normalizedOptions.swcCliOptions.swcCwd,
windowsHide: false,
});
processOnExit = () => {
swcWatcher.kill();
done();
process.off('SIGINT', processOnExit);
process.off('SIGTERM', processOnExit);
process.off('exit', processOnExit);
};
stdoutOnData = async (data) => {
process.stdout.write(data);
if (!data.startsWith('Watching')) {
const swcStatus = data.includes('Successfully');
if (initialPostCompile) {
await postCompilationCallback();
initialPostCompile = false;
}
if (normalizedOptions.skipTypeCheck ||
normalizedOptions.isTsSolutionSetup) {
next(getResult(swcStatus));
return;
}
if (!typeCheckOptions) {
typeCheckOptions = getTypeCheckOptions(normalizedOptions);
}
const delayed = delay(5000);
next(getResult(await Promise.race([
delayed
.start()
.then(() => ({ tscStatus: false, type: 'timeout' })),
(0, run_type_check_1.runTypeCheck)(typeCheckOptions).then(({ errors, warnings }) => {
const hasErrors = errors.length > 0;
if (hasErrors) {
(0, print_diagnostics_1.printDiagnostics)(errors, warnings);
}
return {
tscStatus: !hasErrors,
type: 'tsc',
};
}),
]).then(({ type, tscStatus }) => {
if (type === 'tsc') {
delayed.cancel();
return tscStatus && swcStatus;
}
return swcStatus;
})));
}
};
stderrOnData = (err) => {
process.stderr.write(err);
if (err.includes('Debugger attached.')) {
return;
}
next(getResult(false));
};
watcherOnExit = () => {
done();
swcWatcher.off('exit', watcherOnExit);
};
swcWatcher.stdout.on('data', stdoutOnData);
swcWatcher.stderr.on('data', stderrOnData);
process.on('SIGINT', processOnExit);
process.on('SIGTERM', processOnExit);
process.on('exit', processOnExit);
swcWatcher.on('exit', watcherOnExit);
});
}
function delay(ms) {
let timerId = undefined;
return {
start() {
return new Promise((resolve) => {
timerId = setTimeout(() => {
resolve();
}, ms);
});
},
cancel() {
if (timerId) {
clearTimeout(timerId);
timerId = undefined;
}
},
};
}
;