feat(js): add swc cli options --strip-leading-paths (#22856)

This commit is contained in:
Jack Hsu 2024-04-17 14:18:10 -04:00 committed by GitHub
parent 82145e7d02
commit 701c513fd7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 106 additions and 31 deletions

View File

@ -116,7 +116,8 @@
"oneOf": [ "oneOf": [
{ "type": "string", "enum": ["all", "none"] }, { "type": "string", "enum": ["all", "none"] },
{ "type": "array", "items": { "type": "string" } } { "type": "array", "items": { "type": "string" } }
] ],
"x-deprecated": "Make sure all dependencies are buildable by running `nx g @nx/js:setup-build`. This option will be removed in Nx 20."
}, },
"externalBuildTargets": { "externalBuildTargets": {
"type": "array", "type": "array",
@ -131,6 +132,11 @@
"x-priority": "internal" "x-priority": "internal"
} }
}, },
"stripLeadingPaths": {
"type": "boolean",
"description": "Remove leading directory from output (e.g. src). See: https://swc.rs/docs/usage/cli#--strip-leading-paths",
"default": false
},
"required": ["main", "outputPath", "tsConfig"], "required": ["main", "outputPath", "tsConfig"],
"definitions": { "definitions": {
"assetPattern": { "assetPattern": {

View File

@ -116,7 +116,8 @@
"oneOf": [ "oneOf": [
{ "type": "string", "enum": ["all", "none"] }, { "type": "string", "enum": ["all", "none"] },
{ "type": "array", "items": { "type": "string" } } { "type": "array", "items": { "type": "string" } }
] ],
"x-deprecated": "Make sure all dependencies are buildable by running `nx g @nx/js:setup-build`. This option will be removed in Nx 20."
}, },
"externalBuildTargets": { "externalBuildTargets": {
"type": "array", "type": "array",

View File

@ -92,4 +92,19 @@ myLib();
}).toString(); }).toString();
expect(result).toContain('x'); expect(result).toContain('x');
}, 240_000); }, 240_000);
it('should support --strip-leading-paths option', () => {
const lib = uniq('lib');
runCLI(`generate @nx/js:lib ${lib} --bundler=swc --no-interactive`);
runCLI(`build ${lib} --stripLeadingPaths`);
checkFilesExist(
`dist/libs/${lib}/package.json`,
`dist/libs/${lib}/index.js`,
`dist/libs/${lib}/lib/${lib}.js`,
`dist/libs/${lib}/index.d.ts`,
`dist/libs/${lib}/lib/${lib}.d.ts`
);
});
}); });

View File

@ -167,6 +167,15 @@
"alwaysAddToPackageJson": false "alwaysAddToPackageJson": false
} }
} }
},
"19.0.0": {
"version": "19.0.0-beta.0",
"packages": {
"@swc/cli": {
"version": "~0.3.12",
"alwaysAddToPackageJson": false
}
}
} }
} }
} }

View File

@ -97,7 +97,8 @@
"type": "string" "type": "string"
} }
} }
] ],
"x-deprecated": "Make sure all dependencies are buildable by running `nx g @nx/js:setup-build`. This option will be removed in Nx 20."
}, },
"externalBuildTargets": { "externalBuildTargets": {
"type": "array", "type": "array",
@ -114,6 +115,11 @@
"x-priority": "internal" "x-priority": "internal"
} }
}, },
"stripLeadingPaths": {
"type": "boolean",
"description": "Remove leading directory from output (e.g. src). See: https://swc.rs/docs/usage/cli#--strip-leading-paths",
"default": false
},
"required": ["main", "outputPath", "tsConfig"], "required": ["main", "outputPath", "tsConfig"],
"definitions": { "definitions": {
"assetPattern": { "assetPattern": {

View File

@ -17,6 +17,7 @@ import {
import { copyPackageJson } from '../../utils/package-json'; import { copyPackageJson } from '../../utils/package-json';
import { import {
NormalizedSwcExecutorOptions, NormalizedSwcExecutorOptions,
SwcCliOptions,
SwcExecutorOptions, SwcExecutorOptions,
} from '../../utils/schema'; } from '../../utils/schema';
import { compileSwc, compileSwcWatch } from '../../utils/swc/compile-swc'; import { compileSwc, compileSwcWatch } from '../../utils/swc/compile-swc';
@ -59,20 +60,16 @@ function normalizeOptions(
outputPath outputPath
); );
const projectRootParts = projectRoot.split('/'); // Always execute from root of project, same as with SWC CLI.
// We pop the last part of the `projectRoot` to pass const swcCwd = join(root, projectRoot);
// the last part (projectDir) and the remainder (projectRootParts) to swc
const projectDir = projectRootParts.pop();
// default to current directory if projectRootParts is [].
// Eg: when a project is at the root level, outside of layout dir
const swcCwd = projectRootParts.join('/') || '.';
const { swcrcPath, tmpSwcrcPath } = getSwcrcPath(options, root, projectRoot); const { swcrcPath, tmpSwcrcPath } = getSwcrcPath(options, root, projectRoot);
const swcCliOptions = { const swcCliOptions = {
srcPath: projectDir, srcPath: projectRoot,
destPath: relative(join(root, swcCwd), outputPath), destPath: relative(swcCwd, outputPath),
swcCwd, swcCwd,
swcrcPath, swcrcPath,
stripLeadingPaths: Boolean(options.stripLeadingPaths),
}; };
return { return {
@ -127,11 +124,16 @@ export async function* swcExecutor(
); );
if (!isInlineGraphEmpty(inlineProjectGraph)) { if (!isInlineGraphEmpty(inlineProjectGraph)) {
if (options.stripLeadingPaths) {
throw new Error(`Cannot use --strip-leading-paths with inlining.`);
}
options.projectRoot = '.'; // set to root of workspace to include other libs for type check options.projectRoot = '.'; // set to root of workspace to include other libs for type check
// remap paths for SWC compilation // remap paths for SWC compilation
options.swcCliOptions.srcPath = options.swcCliOptions.swcCwd; options.inline = true;
options.swcCliOptions.swcCwd = '.'; options.swcCliOptions.swcCwd = '.';
options.swcCliOptions.srcPath = options.swcCliOptions.swcCwd;
options.swcCliOptions.destPath = join( options.swcCliOptions.destPath = join(
options.swcCliOptions.destPath.split(normalize('../')).at(-1), options.swcCliOptions.destPath.split(normalize('../')).at(-1),
options.swcCliOptions.srcPath options.swcCliOptions.srcPath

View File

@ -85,7 +85,8 @@
"type": "string" "type": "string"
} }
} }
] ],
"x-deprecated": "Make sure all dependencies are buildable by running `nx g @nx/js:setup-build`. This option will be removed in Nx 20."
}, },
"externalBuildTargets": { "externalBuildTargets": {
"type": "array", "type": "array",

View File

@ -51,6 +51,7 @@ export interface ExecutorOptions {
external?: 'all' | 'none' | string[]; external?: 'all' | 'none' | string[];
externalBuildTargets?: string[]; externalBuildTargets?: string[];
generateLockfile?: boolean; generateLockfile?: boolean;
stripLeadingPaths?: boolean;
} }
export interface NormalizedExecutorOptions extends ExecutorOptions { export interface NormalizedExecutorOptions extends ExecutorOptions {
@ -75,6 +76,7 @@ export interface SwcCliOptions {
destPath: string; destPath: string;
swcrcPath: string; swcrcPath: string;
swcCwd: string; swcCwd: string;
stripLeadingPaths: boolean;
} }
export interface NormalizedSwcExecutorOptions export interface NormalizedSwcExecutorOptions
@ -84,4 +86,7 @@ export interface NormalizedSwcExecutorOptions
skipTypeCheck: boolean; skipTypeCheck: boolean;
swcCliOptions: SwcCliOptions; swcCliOptions: SwcCliOptions;
tmpSwcrcPath: string; tmpSwcrcPath: string;
sourceRoot?: string;
// TODO(v20): remove inline feature
inline?: boolean;
} }

View File

@ -1,35 +1,66 @@
import { cacheDir, ExecutorContext, logger } from '@nx/devkit'; import { cacheDir, ExecutorContext, logger } from '@nx/devkit';
import { exec, execSync } from 'child_process'; import { exec, execSync } from 'node:child_process';
import { removeSync } from 'fs-extra'; import { join } from 'node:path';
import { existsSync, removeSync } from 'fs-extra';
import { createAsyncIterable } from '@nx/devkit/src/utils/async-iterable'; import { createAsyncIterable } from '@nx/devkit/src/utils/async-iterable';
import { NormalizedSwcExecutorOptions, SwcCliOptions } from '../schema'; import { NormalizedSwcExecutorOptions, SwcCliOptions } from '../schema';
import { printDiagnostics } from '../typescript/print-diagnostics'; import { printDiagnostics } from '../typescript/print-diagnostics';
import { runTypeCheck, TypeCheckOptions } from '../typescript/run-type-check'; import { runTypeCheck, TypeCheckOptions } from '../typescript/run-type-check';
import { relative } from 'path';
function getSwcCmd( function getSwcCmd(
{ swcrcPath, srcPath, destPath }: SwcCliOptions, {
swcCliOptions: { swcrcPath, destPath, stripLeadingPaths },
root,
projectRoot,
originalProjectRoot,
sourceRoot,
inline,
}: NormalizedSwcExecutorOptions,
watch = false watch = false
) { ) {
const swcCLI = require.resolve('@swc/cli/bin/swc.js'); const swcCLI = require.resolve('@swc/cli/bin/swc.js');
let inputDir: string;
// TODO(v20): remove inline feature
if (inline) {
inputDir = originalProjectRoot.split('/')[0];
} else {
if (sourceRoot) {
inputDir = 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 = existsSync(join(root, projectRoot, 'src')) ? 'src' : '.';
}
}
let swcCmd = `node ${swcCLI} ${ let swcCmd = `node ${swcCLI} ${
// TODO(jack): clean this up when we remove inline module support inputDir || '.'
// Handle root project } -d ${destPath} --config-file=${swcrcPath} ${
srcPath === '.' ? 'src' : srcPath stripLeadingPaths ? '--strip-leading-paths' : ''
} -d ${ }`;
srcPath === '.' ? `${destPath}/src` : destPath
} --config-file=${swcrcPath}`;
return watch ? swcCmd.concat(' --watch') : swcCmd; return watch ? swcCmd.concat(' --watch') : swcCmd;
} }
function getTypeCheckOptions(normalizedOptions: NormalizedSwcExecutorOptions) { function getTypeCheckOptions(normalizedOptions: NormalizedSwcExecutorOptions) {
const { projectRoot, watch, tsConfig, root, outputPath } = 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
: existsSync(join(root, projectRoot, 'src'))
? join(projectRoot, 'src')
: projectRoot;
const typeCheckOptions: TypeCheckOptions = { const typeCheckOptions: TypeCheckOptions = {
mode: 'emitDeclarationOnly', mode: 'emitDeclarationOnly',
tsConfigPath: tsConfig, tsConfigPath: tsConfig,
outDir: outputPath, outDir: outputPath,
workspaceRoot: root, workspaceRoot: root,
rootDir: projectRoot, rootDir: inputDir,
}; };
if (watch) { if (watch) {
@ -51,7 +82,7 @@ export async function compileSwc(
removeSync(normalizedOptions.outputPath); removeSync(normalizedOptions.outputPath);
} }
const swcCmdLog = execSync(getSwcCmd(normalizedOptions.swcCliOptions), { const swcCmdLog = execSync(getSwcCmd(normalizedOptions), {
encoding: 'utf8', encoding: 'utf8',
cwd: normalizedOptions.swcCliOptions.swcCwd, cwd: normalizedOptions.swcCliOptions.swcCwd,
}); });
@ -104,10 +135,9 @@ export async function* compileSwcWatch(
let stderrOnData: () => void; let stderrOnData: () => void;
let watcherOnExit: () => void; let watcherOnExit: () => void;
const swcWatcher = exec( const swcWatcher = exec(getSwcCmd(normalizedOptions, true), {
getSwcCmd(normalizedOptions.swcCliOptions, true), cwd: normalizedOptions.swcCliOptions.swcCwd,
{ cwd: normalizedOptions.swcCliOptions.swcCwd } });
);
processOnExit = () => { processOnExit = () => {
swcWatcher.kill(); swcWatcher.kill();

View File

@ -2,7 +2,7 @@ export const nxVersion = require('../../package.json').version;
export const esbuildVersion = '^0.19.2'; export const esbuildVersion = '^0.19.2';
export const prettierVersion = '^2.6.2'; export const prettierVersion = '^2.6.2';
export const swcCliVersion = '~0.1.62'; export const swcCliVersion = '~0.3.12';
export const swcCoreVersion = '~1.3.85'; export const swcCoreVersion = '~1.3.85';
export const swcHelpersVersion = '~0.5.2'; export const swcHelpersVersion = '~0.5.2';
export const swcNodeVersion = '~1.8.0'; export const swcNodeVersion = '~1.8.0';