From c109bd240585631fd9c5dbb866b8ed6fda751596 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Tue, 1 Apr 2025 09:20:57 +0000 Subject: [PATCH] fix(@angular-devkit/schematics): properly resolve relative schematics when executed from a nested directory Ensure that relative schematic paths are correctly resolved when invoked from within a nested directory. Closes: #29978 --- .../tools/node-module-engine-host.ts | 10 +--- .../schematics-collections-relative.ts | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 tests/legacy-cli/e2e/tests/generate/schematics-collections-relative.ts diff --git a/packages/angular_devkit/schematics/tools/node-module-engine-host.ts b/packages/angular_devkit/schematics/tools/node-module-engine-host.ts index c24599cf345e..0bc269840c18 100644 --- a/packages/angular_devkit/schematics/tools/node-module-engine-host.ts +++ b/packages/angular_devkit/schematics/tools/node-module-engine-host.ts @@ -7,7 +7,7 @@ */ import { BaseException } from '@angular-devkit/core'; -import { dirname, join, resolve } from 'node:path'; +import { dirname, resolve } from 'node:path'; import { RuleFactory } from '../src'; import { FileSystemCollectionDesc, FileSystemSchematicDesc } from './description'; import { ExportStringRef } from './export-ref'; @@ -46,20 +46,14 @@ export class NodeModulesEngineHost extends FileSystemEngineHostBase { } } - const relativeBase = requester ? dirname(requester) : process.cwd(); let collectionPath: string | undefined = undefined; - - if (name.startsWith('.')) { - name = resolve(relativeBase, name); - } - const resolveOptions = { paths: requester ? [dirname(requester), ...(this.paths || [])] : this.paths, }; // Try to resolve as a package try { - const packageJsonPath = require.resolve(join(name, 'package.json'), resolveOptions); + const packageJsonPath = require.resolve(`${name}/package.json`, resolveOptions); const { schematics } = require(packageJsonPath); if (!schematics || typeof schematics !== 'string') { diff --git a/tests/legacy-cli/e2e/tests/generate/schematics-collections-relative.ts b/tests/legacy-cli/e2e/tests/generate/schematics-collections-relative.ts new file mode 100644 index 000000000000..f6f583bf0e72 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/generate/schematics-collections-relative.ts @@ -0,0 +1,53 @@ +import assert from 'node:assert'; +import { join } from 'node:path'; +import { ng } from '../../utils/process'; +import { writeMultipleFiles, createDir } from '../../utils/fs'; +import { updateJsonFile } from '../../utils/project'; + +export default async function () { + // setup temp collection + await createDir('./fake-schematics'); + await writeMultipleFiles({ + './fake-schematics/package.json': JSON.stringify({ + 'schematics': './collection.json', + }), + './fake-schematics/collection.json': JSON.stringify({ + 'schematics': { + 'fake': { + 'description': 'Fake schematic', + 'schema': './fake-schema.json', + 'factory': './fake', + }, + }, + }), + './fake-schematics/fake-schema.json': JSON.stringify({ + '$id': 'FakeSchema', + 'title': 'Fake Schema', + 'type': 'object', + }), + './fake-schematics/fake.js': ` + exports.default = () => (host, context) => context.logger.info('fake schematic run.'); + `, + }); + + await updateJsonFile('angular.json', (json) => { + json.cli ??= {}; + json.cli.schematicCollections = ['./fake-schematics']; + }); + + const { stdout: stdout1 } = await ng('generate', '--help'); + assert.match(stdout1, /Fake schematic/); + + const { stdout: stdout2 } = await ng('generate', 'fake'); + assert.match(stdout2, /fake schematic run/); + + // change cwd to a nested directory to validate the relative schematic is resolved correctly + const originalCwd = process.cwd(); + try { + process.chdir(join(originalCwd, 'src/app')); + const { stdout: stdout3 } = await ng('generate', 'fake'); + assert.match(stdout3, /fake schematic run/); + } finally { + process.chdir(originalCwd); + } +}