mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-17 02:54:21 +08:00
fix(@angular-devkit/schematics): resolve external schematics from requesting collection
This change first attempts to resolve a schematic referenced via the external schematic rule from the requesting schematic collection. This allows schematic packages that are direct dependencies of another schematic package to be used with the external schematic rule without manual package resolution code within the schematic. Closes #18098 Closes #11026
This commit is contained in:
parent
99210b203d
commit
5ce621e371
@ -197,7 +197,7 @@ export declare class EmptyTree extends HostTree {
|
||||
export interface Engine<CollectionMetadataT extends object, SchematicMetadataT extends object> {
|
||||
readonly defaultMergeStrategy: MergeStrategy;
|
||||
readonly workflow: Workflow | null;
|
||||
createCollection(name: string): Collection<CollectionMetadataT, SchematicMetadataT>;
|
||||
createCollection(name: string, requester?: Collection<CollectionMetadataT, SchematicMetadataT>): Collection<CollectionMetadataT, SchematicMetadataT>;
|
||||
createContext(schematic: Schematic<CollectionMetadataT, SchematicMetadataT>, parent?: Partial<TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>>, executionOptions?: Partial<ExecutionOptions>): TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>;
|
||||
createSchematic(name: string, collection: Collection<CollectionMetadataT, SchematicMetadataT>): Schematic<CollectionMetadataT, SchematicMetadataT>;
|
||||
createSourceFromUrl(url: Url, context: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): Source;
|
||||
@ -207,7 +207,7 @@ export interface Engine<CollectionMetadataT extends object, SchematicMetadataT e
|
||||
|
||||
export interface EngineHost<CollectionMetadataT extends object, SchematicMetadataT extends object> {
|
||||
readonly defaultMergeStrategy?: MergeStrategy;
|
||||
createCollectionDescription(name: string): CollectionDescription<CollectionMetadataT>;
|
||||
createCollectionDescription(name: string, requester?: CollectionDescription<CollectionMetadataT>): CollectionDescription<CollectionMetadataT>;
|
||||
createSchematicDescription(name: string, collection: CollectionDescription<CollectionMetadataT>): SchematicDescription<CollectionMetadataT, SchematicMetadataT> | null;
|
||||
createSourceFromUrl(url: Url, context: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): Source | null;
|
||||
createTaskExecutor(name: string): Observable<TaskExecutor>;
|
||||
@ -445,7 +445,7 @@ export declare class SchematicEngine<CollectionT extends object, SchematicT exte
|
||||
get defaultMergeStrategy(): MergeStrategy;
|
||||
get workflow(): Workflow | null;
|
||||
constructor(_host: EngineHost<CollectionT, SchematicT>, _workflow?: Workflow | undefined);
|
||||
createCollection(name: string): Collection<CollectionT, SchematicT>;
|
||||
createCollection(name: string, requester?: Collection<CollectionT, SchematicT>): Collection<CollectionT, SchematicT>;
|
||||
createContext(schematic: Schematic<CollectionT, SchematicT>, parent?: Partial<TypedSchematicContext<CollectionT, SchematicT>>, executionOptions?: Partial<ExecutionOptions>): TypedSchematicContext<CollectionT, SchematicT>;
|
||||
createSchematic(name: string, collection: Collection<CollectionT, SchematicT>, allowPrivate?: boolean): Schematic<CollectionT, SchematicT>;
|
||||
createSourceFromUrl(url: Url, context: TypedSchematicContext<CollectionT, SchematicT>): Source;
|
||||
|
@ -53,14 +53,14 @@ export declare class FileSystemEngineHost extends FileSystemEngineHostBase {
|
||||
}
|
||||
|
||||
export declare abstract class FileSystemEngineHostBase implements FileSystemEngineHost {
|
||||
protected abstract _resolveCollectionPath(name: string): string;
|
||||
protected abstract _resolveCollectionPath(name: string, requester?: string): string;
|
||||
protected abstract _resolveReferenceString(name: string, parentPath: string): {
|
||||
ref: RuleFactory<{}>;
|
||||
path: string;
|
||||
} | null;
|
||||
protected abstract _transformCollectionDescription(name: string, desc: Partial<FileSystemCollectionDesc>): FileSystemCollectionDesc;
|
||||
protected abstract _transformSchematicDescription(name: string, collection: FileSystemCollectionDesc, desc: Partial<FileSystemSchematicDesc>): FileSystemSchematicDesc;
|
||||
createCollectionDescription(name: string): FileSystemCollectionDesc;
|
||||
createCollectionDescription(name: string, requester?: FileSystemCollectionDesc): FileSystemCollectionDesc;
|
||||
createSchematicDescription(name: string, collection: FileSystemCollectionDesc): FileSystemSchematicDesc | null;
|
||||
createSourceFromUrl(url: Url): Source | null;
|
||||
createTaskExecutor(name: string): Observable<TaskExecutor>;
|
||||
@ -102,7 +102,7 @@ export declare class InvalidCollectionJsonException extends BaseException {
|
||||
|
||||
export declare class NodeModulesEngineHost extends FileSystemEngineHostBase {
|
||||
constructor(paths?: string[] | undefined);
|
||||
protected _resolveCollectionPath(name: string): string;
|
||||
protected _resolveCollectionPath(name: string, requester?: string): string;
|
||||
protected _resolveReferenceString(refString: string, parentPath: string): {
|
||||
ref: RuleFactory<{}>;
|
||||
path: string;
|
||||
@ -113,7 +113,7 @@ export declare class NodeModulesEngineHost extends FileSystemEngineHostBase {
|
||||
|
||||
export declare class NodeModulesTestEngineHost extends NodeModulesEngineHost {
|
||||
get tasks(): TaskConfiguration<{}>[];
|
||||
protected _resolveCollectionPath(name: string): string;
|
||||
protected _resolveCollectionPath(name: string, requester?: string): string;
|
||||
clearTasks(): void;
|
||||
registerCollection(name: string, path: string): void;
|
||||
transformContext(context: FileSystemSchematicContext): FileSystemSchematicContext;
|
||||
|
@ -164,7 +164,7 @@ export class SchematicEngine<CollectionT extends object, SchematicT extends obje
|
||||
|
||||
private _collectionCache = new Map<string, CollectionImpl<CollectionT, SchematicT>>();
|
||||
private _schematicCache
|
||||
= new Map<string, Map<string, SchematicImpl<CollectionT, SchematicT>>>();
|
||||
= new WeakMap<Collection<CollectionT, SchematicT>, Map<string, SchematicImpl<CollectionT, SchematicT>>>();
|
||||
private _taskSchedulers = new Array<TaskScheduler>();
|
||||
|
||||
constructor(private _host: EngineHost<CollectionT, SchematicT>, protected _workflow?: Workflow) {
|
||||
@ -173,26 +173,30 @@ export class SchematicEngine<CollectionT extends object, SchematicT extends obje
|
||||
get workflow() { return this._workflow || null; }
|
||||
get defaultMergeStrategy() { return this._host.defaultMergeStrategy || MergeStrategy.Default; }
|
||||
|
||||
createCollection(name: string): Collection<CollectionT, SchematicT> {
|
||||
createCollection(
|
||||
name: string,
|
||||
requester?: Collection<CollectionT, SchematicT>,
|
||||
): Collection<CollectionT, SchematicT> {
|
||||
let collection = this._collectionCache.get(name);
|
||||
if (collection) {
|
||||
return collection;
|
||||
}
|
||||
|
||||
const [description, bases] = this._createCollectionDescription(name);
|
||||
const [description, bases] = this._createCollectionDescription(name, requester?.description);
|
||||
|
||||
collection = new CollectionImpl<CollectionT, SchematicT>(description, this, bases);
|
||||
this._collectionCache.set(name, collection);
|
||||
this._schematicCache.set(name, new Map());
|
||||
this._schematicCache.set(collection, new Map());
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
private _createCollectionDescription(
|
||||
name: string,
|
||||
requester?: CollectionDescription<CollectionT>,
|
||||
parentNames?: Set<string>,
|
||||
): [CollectionDescription<CollectionT>, Array<CollectionDescription<CollectionT>>] {
|
||||
const description = this._host.createCollectionDescription(name);
|
||||
const description = this._host.createCollectionDescription(name, requester);
|
||||
if (!description) {
|
||||
throw new UnknownCollectionException(name);
|
||||
}
|
||||
@ -204,7 +208,11 @@ export class SchematicEngine<CollectionT extends object, SchematicT extends obje
|
||||
if (description.extends) {
|
||||
parentNames = (parentNames || new Set<string>()).add(description.name);
|
||||
for (const baseName of description.extends) {
|
||||
const [base, baseBases] = this._createCollectionDescription(baseName, new Set(parentNames));
|
||||
const [base, baseBases] = this._createCollectionDescription(
|
||||
baseName,
|
||||
description,
|
||||
new Set(parentNames),
|
||||
);
|
||||
|
||||
bases.unshift(base, ...baseBases);
|
||||
}
|
||||
@ -277,14 +285,9 @@ export class SchematicEngine<CollectionT extends object, SchematicT extends obje
|
||||
collection: Collection<CollectionT, SchematicT>,
|
||||
allowPrivate = false,
|
||||
): Schematic<CollectionT, SchematicT> {
|
||||
const collectionImpl = this._collectionCache.get(collection.description.name);
|
||||
const schematicMap = this._schematicCache.get(collection.description.name);
|
||||
if (!collectionImpl || !schematicMap || collectionImpl !== collection) {
|
||||
// This is weird, maybe the collection was created by another engine?
|
||||
throw new UnknownCollectionException(collection.description.name);
|
||||
}
|
||||
const schematicMap = this._schematicCache.get(collection);
|
||||
|
||||
let schematic = schematicMap.get(name);
|
||||
let schematic = schematicMap?.get(name);
|
||||
if (schematic) {
|
||||
return schematic;
|
||||
}
|
||||
@ -314,7 +317,7 @@ export class SchematicEngine<CollectionT extends object, SchematicT extends obje
|
||||
const factory = this._host.getSchematicRuleFactory(description, collectionDescription);
|
||||
schematic = new SchematicImpl<CollectionT, SchematicT>(description, factory, collection, this);
|
||||
|
||||
schematicMap.set(name, schematic);
|
||||
schematicMap?.set(name, schematic);
|
||||
|
||||
return schematic;
|
||||
}
|
||||
|
@ -76,7 +76,10 @@ export type SchematicDescription<CollectionMetadataT extends object,
|
||||
* parameters contain additional metadata that you want to store while remaining type-safe.
|
||||
*/
|
||||
export interface EngineHost<CollectionMetadataT extends object, SchematicMetadataT extends object> {
|
||||
createCollectionDescription(name: string): CollectionDescription<CollectionMetadataT>;
|
||||
createCollectionDescription(
|
||||
name: string,
|
||||
requester?: CollectionDescription<CollectionMetadataT>,
|
||||
): CollectionDescription<CollectionMetadataT>;
|
||||
listSchematicNames(collection: CollectionDescription<CollectionMetadataT>): string[];
|
||||
|
||||
createSchematicDescription(
|
||||
@ -116,24 +119,27 @@ export interface EngineHost<CollectionMetadataT extends object, SchematicMetadat
|
||||
* SchematicMetadataT is a type that contains additional typing for the Schematic Description.
|
||||
*/
|
||||
export interface Engine<CollectionMetadataT extends object, SchematicMetadataT extends object> {
|
||||
createCollection(name: string): Collection<CollectionMetadataT, SchematicMetadataT>;
|
||||
createCollection(
|
||||
name: string,
|
||||
requester?: Collection<CollectionMetadataT, SchematicMetadataT>,
|
||||
): Collection<CollectionMetadataT, SchematicMetadataT>;
|
||||
createContext(
|
||||
schematic: Schematic<CollectionMetadataT, SchematicMetadataT>,
|
||||
parent?: Partial<TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>>,
|
||||
executionOptions?: Partial<ExecutionOptions>,
|
||||
): TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>;
|
||||
createSchematic(
|
||||
name: string,
|
||||
collection: Collection<CollectionMetadataT, SchematicMetadataT>,
|
||||
name: string,
|
||||
collection: Collection<CollectionMetadataT, SchematicMetadataT>,
|
||||
): Schematic<CollectionMetadataT, SchematicMetadataT>;
|
||||
createSourceFromUrl(
|
||||
url: Url,
|
||||
context: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>,
|
||||
): Source;
|
||||
transformOptions<OptionT extends object, ResultT extends object>(
|
||||
schematic: Schematic<CollectionMetadataT, SchematicMetadataT>,
|
||||
options: OptionT,
|
||||
context?: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>,
|
||||
schematic: Schematic<CollectionMetadataT, SchematicMetadataT>,
|
||||
options: OptionT,
|
||||
context?: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>,
|
||||
): Observable<ResultT>;
|
||||
executePostTasks(): Observable<void>;
|
||||
|
||||
|
@ -26,7 +26,7 @@ export function externalSchematic<OptionT extends object>(
|
||||
executionOptions?: Partial<ExecutionOptions>,
|
||||
): Rule {
|
||||
return (input: Tree, context: SchematicContext) => {
|
||||
const collection = context.engine.createCollection(collectionName);
|
||||
const collection = context.engine.createCollection(collectionName, context.schematic.collection);
|
||||
const schematic = collection.createSchematic(schematicName);
|
||||
|
||||
return schematic.call(options, observableOf(branch(input)), context, executionOptions);
|
||||
|
@ -47,10 +47,10 @@ export class FallbackEngineHost implements EngineHost<{}, {}> {
|
||||
this._hosts.push(host);
|
||||
}
|
||||
|
||||
createCollectionDescription(name: string): CollectionDescription<FallbackCollectionDescription> {
|
||||
createCollectionDescription(name: string, requester?: CollectionDescription<{}>): CollectionDescription<FallbackCollectionDescription> {
|
||||
for (const host of this._hosts) {
|
||||
try {
|
||||
const description = host.createCollectionDescription(name);
|
||||
const description = host.createCollectionDescription(name, requester);
|
||||
|
||||
return { name, host, description };
|
||||
} catch (_) {
|
||||
|
@ -109,7 +109,7 @@ export class SchematicNameCollisionException extends BaseException {
|
||||
* all other EngineHost provided by the tooling part of the Schematics library.
|
||||
*/
|
||||
export abstract class FileSystemEngineHostBase implements FileSystemEngineHost {
|
||||
protected abstract _resolveCollectionPath(name: string): string;
|
||||
protected abstract _resolveCollectionPath(name: string, requester?: string): string;
|
||||
protected abstract _resolveReferenceString(
|
||||
name: string, parentPath: string): { ref: RuleFactory<{}>, path: string } | null;
|
||||
protected abstract _transformCollectionDescription(
|
||||
@ -158,8 +158,11 @@ export abstract class FileSystemEngineHostBase implements FileSystemEngineHost {
|
||||
* @param name
|
||||
* @return {{path: string}}
|
||||
*/
|
||||
createCollectionDescription(name: string): FileSystemCollectionDesc {
|
||||
const path = this._resolveCollectionPath(name);
|
||||
createCollectionDescription(
|
||||
name: string,
|
||||
requester?: FileSystemCollectionDesc,
|
||||
): FileSystemCollectionDesc {
|
||||
const path = this._resolveCollectionPath(name, requester?.path);
|
||||
const jsonValue = readJsonFile(path);
|
||||
if (!jsonValue || typeof jsonValue != 'object' || Array.isArray(jsonValue)) {
|
||||
throw new InvalidCollectionJsonException(name, path);
|
||||
|
@ -97,8 +97,8 @@ export class NodeModulesEngineHost extends FileSystemEngineHostBase {
|
||||
return collectionPath;
|
||||
}
|
||||
|
||||
protected _resolveCollectionPath(name: string): string {
|
||||
const collectionPath = this.resolve(name);
|
||||
protected _resolveCollectionPath(name: string, requester?: string): string {
|
||||
const collectionPath = this.resolve(name, requester);
|
||||
|
||||
try {
|
||||
readJsonFile(collectionPath);
|
||||
|
@ -37,12 +37,12 @@ export class NodeModulesTestEngineHost extends NodeModulesEngineHost {
|
||||
return context;
|
||||
}
|
||||
|
||||
protected _resolveCollectionPath(name: string): string {
|
||||
protected _resolveCollectionPath(name: string, requester?: string): string {
|
||||
const maybePath = this._collections.get(name);
|
||||
if (maybePath) {
|
||||
return maybePath;
|
||||
}
|
||||
|
||||
return super._resolveCollectionPath(name);
|
||||
return super._resolveCollectionPath(name, requester);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user