fix(js): resolve asset paths relative to workspace root instead of cwd (#31664)
## Description This PR fixes an issue where asset files copied during a build using the `@nx/js:tsc` executor are placed in the wrong directory depending on the current working directory from which the `nx` command is executed. This behavior becomes particularly problematic in scenarios like release workflows that rely on `preVersionCommand` to run E2E tests. For instance, when using tools like Jest from the root of an E2E project, scripts like `start-local-registry` may trigger a build and run the `preVersionCommand`. However, instead of placing assets in the expected `dist` folder of the project, they are incorrectly copied relative to the E2E folder’s location. ## Reproduction Steps 1. Create a new Nx workspace: ```bash npx --yes create-nx-workspace assets-issue --preset=ts --no-interactive cd assets-issue ``` 2. Add the Nx Plugin package: ```bash nx add @nx/plugin ``` 3. Generate a new plugin: ```bash nx g @nx/plugin:plugin packages/my-plugin --linter eslint --unitTestRunner jest ``` 4. Add a generator to the plugin: ```bash nx g @nx/plugin:generator packages/my-plugin/src/generators/my-generator ``` 5. Build the plugin from the workspace root: ```bash nx build my-plugin ``` ✅ Assets are copied correctly: ``` dist/packages/my-plugin/generators/files/src/index.ts.template dist/packages/my-plugin/generators/schema.json dist/packages/my-plugin/generators/schema.d.ts ``` 6. Now build the same project from a nested folder: ```bash mkdir e2e && cd e2e nx build my-plugin --skip-nx-cache ``` ❌ Assets are copied relative to the current folder: ``` e2e/packages/my-plugin/dist/generators/files/src/index.ts.template e2e/packages/my-plugin/dist/generators/schema.json e2e/packages/my-plugin/dist/generators/schema.d.ts ``` ## Expected Behavior The build output—especially copied assets—should always respect the project’s `outputPath` configuration regardless of where the `nx` command is invoked from. The behavior should be consistent and **not influenced by `process.cwd()`**.
This commit is contained in:
parent
57e70d0e91
commit
fd31fa633d
@ -66,8 +66,12 @@ describe('AssetInputOutputHandler', () => {
|
||||
let projectDir: string;
|
||||
let outputDir: string;
|
||||
let callback: jest.SpyInstance;
|
||||
let originalCwd: string;
|
||||
|
||||
beforeEach(() => {
|
||||
// Store original cwd to restore later
|
||||
originalCwd = process.cwd();
|
||||
|
||||
// Resolve to real paths to avoid symlink discrepancies with watcher.
|
||||
const tmp = fs.realpathSync(path.join(os.tmpdir()));
|
||||
|
||||
@ -102,6 +106,11 @@ describe('AssetInputOutputHandler', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original cwd
|
||||
process.chdir(originalCwd);
|
||||
});
|
||||
|
||||
test('watchAndProcessOnAssetChange', async () => {
|
||||
const dispose = await sut.watchAndProcessOnAssetChange();
|
||||
|
||||
@ -219,6 +228,61 @@ describe('AssetInputOutputHandler', () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('should copy assets to correct location when running from nested directory', async () => {
|
||||
// Create a nested directory structure to simulate running from a subdirectory
|
||||
const nestedDir = path.join(rootDir, 'e2e', 'integration-tests');
|
||||
fs.mkdirSync(nestedDir, { recursive: true });
|
||||
|
||||
// Change to nested directory to simulate running nx command from there
|
||||
process.chdir(nestedDir);
|
||||
|
||||
// Create test files
|
||||
fs.writeFileSync(path.join(rootDir, 'LICENSE'), 'license');
|
||||
fs.writeFileSync(path.join(projectDir, 'README.md'), 'readme');
|
||||
fs.writeFileSync(path.join(projectDir, 'docs/test1.md'), 'test');
|
||||
|
||||
// Create CopyAssetsHandler with relative outputDir (this is where the bug manifests)
|
||||
const nestedSut = new CopyAssetsHandler({
|
||||
rootDir,
|
||||
projectDir,
|
||||
outputDir: 'dist/mylib', // relative path - this triggers the bug
|
||||
callback: callback as any,
|
||||
assets: [
|
||||
'mylib/*.md',
|
||||
{
|
||||
input: 'mylib/docs',
|
||||
glob: '**/*.md',
|
||||
output: 'docs',
|
||||
},
|
||||
'LICENSE',
|
||||
],
|
||||
});
|
||||
|
||||
await nestedSut.processAllAssetsOnce();
|
||||
|
||||
expect(callback).toHaveBeenCalledWith([
|
||||
{
|
||||
type: 'create',
|
||||
src: path.join(rootDir, 'LICENSE'),
|
||||
dest: path.join(rootDir, 'dist/mylib/LICENSE'),
|
||||
},
|
||||
]);
|
||||
expect(callback).toHaveBeenCalledWith([
|
||||
{
|
||||
type: 'create',
|
||||
src: path.join(rootDir, 'mylib/README.md'),
|
||||
dest: path.join(rootDir, 'dist/mylib/README.md'),
|
||||
},
|
||||
]);
|
||||
expect(callback).toHaveBeenCalledWith([
|
||||
{
|
||||
type: 'create',
|
||||
src: path.join(rootDir, 'mylib/docs/test1.md'),
|
||||
dest: path.join(rootDir, 'dist/mylib/docs/test1.md'),
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
function wait(ms: number) {
|
||||
|
||||
@ -89,16 +89,21 @@ export class CopyAssetsHandler {
|
||||
let input: string;
|
||||
let output: string;
|
||||
let ignore: string[] | null = null;
|
||||
|
||||
const resolvedOutputDir = path.isAbsolute(opts.outputDir)
|
||||
? opts.outputDir
|
||||
: path.resolve(opts.rootDir, opts.outputDir);
|
||||
|
||||
if (typeof f === 'string') {
|
||||
pattern = f;
|
||||
input = path.relative(opts.rootDir, opts.projectDir);
|
||||
output = path.relative(opts.rootDir, opts.outputDir);
|
||||
output = path.relative(opts.rootDir, resolvedOutputDir);
|
||||
} else {
|
||||
isGlob = true;
|
||||
pattern = pathPosix.join(f.input, f.glob);
|
||||
input = f.input;
|
||||
output = pathPosix.join(
|
||||
path.relative(opts.rootDir, opts.outputDir),
|
||||
path.relative(opts.rootDir, resolvedOutputDir),
|
||||
f.output
|
||||
);
|
||||
if (f.ignore)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user