UNPKG

11.5 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright Google LLC All Rights Reserved.
5 *
6 * Use of this source code is governed by an MIT-style license that can be
7 * found in the LICENSE file at https://angular.io/license
8 */
9Object.defineProperty(exports, "__esModule", { value: true });
10exports.FileSystemEngineHostBase = exports.SchematicNameCollisionException = exports.SchematicMissingDescriptionException = exports.SchematicMissingFieldsException = exports.CollectionMissingFieldsException = exports.CollectionMissingSchematicsMapException = exports.FactoryCannotBeResolvedException = exports.SchematicMissingFactoryException = exports.InvalidCollectionJsonException = exports.CollectionCannotBeResolvedException = void 0;
11const core_1 = require("@angular-devkit/core");
12const node_1 = require("@angular-devkit/core/node");
13const fs_1 = require("fs");
14const path_1 = require("path");
15const rxjs_1 = require("rxjs");
16const src_1 = require("../src");
17const file_system_utility_1 = require("./file-system-utility");
18class CollectionCannotBeResolvedException extends core_1.BaseException {
19 constructor(name) {
20 super(`Collection ${JSON.stringify(name)} cannot be resolved.`);
21 }
22}
23exports.CollectionCannotBeResolvedException = CollectionCannotBeResolvedException;
24class InvalidCollectionJsonException extends core_1.BaseException {
25 constructor(_name, path, jsonException) {
26 let msg = `Collection JSON at path ${JSON.stringify(path)} is invalid.`;
27 if (jsonException) {
28 msg = `${msg} ${jsonException.message}`;
29 }
30 super(msg);
31 }
32}
33exports.InvalidCollectionJsonException = InvalidCollectionJsonException;
34class SchematicMissingFactoryException extends core_1.BaseException {
35 constructor(name) {
36 super(`Schematic ${JSON.stringify(name)} is missing a factory.`);
37 }
38}
39exports.SchematicMissingFactoryException = SchematicMissingFactoryException;
40class FactoryCannotBeResolvedException extends core_1.BaseException {
41 constructor(name) {
42 super(`Schematic ${JSON.stringify(name)} cannot resolve the factory.`);
43 }
44}
45exports.FactoryCannotBeResolvedException = FactoryCannotBeResolvedException;
46class CollectionMissingSchematicsMapException extends core_1.BaseException {
47 constructor(name) {
48 super(`Collection "${name}" does not have a schematics map.`);
49 }
50}
51exports.CollectionMissingSchematicsMapException = CollectionMissingSchematicsMapException;
52class CollectionMissingFieldsException extends core_1.BaseException {
53 constructor(name) {
54 super(`Collection "${name}" is missing fields.`);
55 }
56}
57exports.CollectionMissingFieldsException = CollectionMissingFieldsException;
58class SchematicMissingFieldsException extends core_1.BaseException {
59 constructor(name) {
60 super(`Schematic "${name}" is missing fields.`);
61 }
62}
63exports.SchematicMissingFieldsException = SchematicMissingFieldsException;
64class SchematicMissingDescriptionException extends core_1.BaseException {
65 constructor(name) {
66 super(`Schematics "${name}" does not have a description.`);
67 }
68}
69exports.SchematicMissingDescriptionException = SchematicMissingDescriptionException;
70class SchematicNameCollisionException extends core_1.BaseException {
71 constructor(name) {
72 super(`Schematics/alias ${JSON.stringify(name)} collides with another alias or schematic` +
73 ' name.');
74 }
75}
76exports.SchematicNameCollisionException = SchematicNameCollisionException;
77/**
78 * A EngineHost base class that uses the file system to resolve collections. This is the base of
79 * all other EngineHost provided by the tooling part of the Schematics library.
80 */
81class FileSystemEngineHostBase {
82 constructor() {
83 // eslint-disable-next-line @typescript-eslint/no-explicit-any
84 this._transforms = [];
85 this._contextTransforms = [];
86 this._taskFactories = new Map();
87 }
88 listSchematicNames(collection, includeHidden) {
89 const schematics = [];
90 for (const key of Object.keys(collection.schematics)) {
91 const schematic = collection.schematics[key];
92 if ((schematic.hidden && !includeHidden) || schematic.private) {
93 continue;
94 }
95 // If extends is present without a factory it is an alias, do not return it
96 // unless it is from another collection.
97 if (!schematic.extends || schematic.factory) {
98 schematics.push(key);
99 }
100 else if (schematic.extends && schematic.extends.indexOf(':') !== -1) {
101 schematics.push(key);
102 }
103 }
104 return schematics;
105 }
106 registerOptionsTransform(t) {
107 this._transforms.push(t);
108 }
109 registerContextTransform(t) {
110 this._contextTransforms.push(t);
111 }
112 /**
113 *
114 * @param name
115 * @return {{path: string}}
116 */
117 createCollectionDescription(name, requester) {
118 const path = this._resolveCollectionPath(name, requester === null || requester === void 0 ? void 0 : requester.path);
119 const jsonValue = (0, file_system_utility_1.readJsonFile)(path);
120 if (!jsonValue || typeof jsonValue != 'object' || Array.isArray(jsonValue)) {
121 throw new InvalidCollectionJsonException(name, path);
122 }
123 // normalize extends property to an array
124 if (typeof jsonValue['extends'] === 'string') {
125 jsonValue['extends'] = [jsonValue['extends']];
126 }
127 const description = this._transformCollectionDescription(name, {
128 ...jsonValue,
129 path,
130 });
131 if (!description || !description.name) {
132 throw new InvalidCollectionJsonException(name, path);
133 }
134 // Validate aliases.
135 const allNames = Object.keys(description.schematics);
136 for (const schematicName of Object.keys(description.schematics)) {
137 const aliases = description.schematics[schematicName].aliases || [];
138 for (const alias of aliases) {
139 if (allNames.indexOf(alias) != -1) {
140 throw new SchematicNameCollisionException(alias);
141 }
142 }
143 allNames.push(...aliases);
144 }
145 return description;
146 }
147 createSchematicDescription(name, collection) {
148 // Resolve aliases first.
149 for (const schematicName of Object.keys(collection.schematics)) {
150 const schematicDescription = collection.schematics[schematicName];
151 if (schematicDescription.aliases && schematicDescription.aliases.indexOf(name) != -1) {
152 name = schematicName;
153 break;
154 }
155 }
156 if (!(name in collection.schematics)) {
157 return null;
158 }
159 const collectionPath = (0, path_1.dirname)(collection.path);
160 const partialDesc = collection.schematics[name];
161 if (!partialDesc) {
162 return null;
163 }
164 if (partialDesc.extends) {
165 const index = partialDesc.extends.indexOf(':');
166 const collectionName = index !== -1 ? partialDesc.extends.slice(0, index) : null;
167 const schematicName = index === -1 ? partialDesc.extends : partialDesc.extends.slice(index + 1);
168 if (collectionName !== null) {
169 const extendCollection = this.createCollectionDescription(collectionName);
170 return this.createSchematicDescription(schematicName, extendCollection);
171 }
172 else {
173 return this.createSchematicDescription(schematicName, collection);
174 }
175 }
176 // Use any on this ref as we don't have the OptionT here, but we don't need it (we only need
177 // the path).
178 if (!partialDesc.factory) {
179 throw new SchematicMissingFactoryException(name);
180 }
181 const resolvedRef = this._resolveReferenceString(partialDesc.factory, collectionPath, collection);
182 if (!resolvedRef) {
183 throw new FactoryCannotBeResolvedException(name);
184 }
185 let schema = partialDesc.schema;
186 let schemaJson = undefined;
187 if (schema) {
188 if (!(0, path_1.isAbsolute)(schema)) {
189 schema = (0, path_1.join)(collectionPath, schema);
190 }
191 schemaJson = (0, file_system_utility_1.readJsonFile)(schema);
192 }
193 // The schematic path is used to resolve URLs.
194 // We should be able to just do `dirname(resolvedRef.path)` but for compatibility with
195 // Bazel under Windows this directory needs to be resolved from the collection instead.
196 // This is needed because on Bazel under Windows the data files (such as the collection or
197 // url files) are not in the same place as the compiled JS.
198 const maybePath = (0, path_1.join)(collectionPath, partialDesc.factory);
199 const path = (0, fs_1.existsSync)(maybePath) && (0, fs_1.statSync)(maybePath).isDirectory() ? maybePath : (0, path_1.dirname)(maybePath);
200 return this._transformSchematicDescription(name, collection, {
201 ...partialDesc,
202 schema,
203 schemaJson,
204 name,
205 path,
206 factoryFn: resolvedRef.ref,
207 collection,
208 });
209 }
210 createSourceFromUrl(url) {
211 switch (url.protocol) {
212 case null:
213 case 'file:':
214 return (context) => {
215 // Check if context has necessary FileSystemSchematicContext path property
216 const fileDescription = context.schematic.description;
217 if (fileDescription.path === undefined) {
218 throw new Error('Unsupported schematic context. Expected a FileSystemSchematicContext.');
219 }
220 // Resolve all file:///a/b/c/d from the schematic's own path, and not the current
221 // path.
222 const root = (0, core_1.normalize)((0, path_1.resolve)(fileDescription.path, url.path || ''));
223 return new src_1.HostCreateTree(new core_1.virtualFs.ScopedHost(new node_1.NodeJsSyncHost(), root));
224 };
225 }
226 return null;
227 }
228 transformOptions(schematic, options, context) {
229 const transform = async () => {
230 let transformedOptions = options;
231 for (const transformer of this._transforms) {
232 const transformerResult = transformer(schematic, transformedOptions, context);
233 transformedOptions = await ((0, rxjs_1.isObservable)(transformerResult)
234 ? transformerResult.toPromise()
235 : transformerResult);
236 }
237 return transformedOptions;
238 };
239 return (0, rxjs_1.from)(transform());
240 }
241 transformContext(context) {
242 return this._contextTransforms.reduce((acc, curr) => curr(acc), context);
243 }
244 getSchematicRuleFactory(schematic, _collection) {
245 return schematic.factoryFn;
246 }
247 registerTaskExecutor(factory, options) {
248 this._taskFactories.set(factory.name, () => (0, rxjs_1.from)(factory.create(options)));
249 }
250 createTaskExecutor(name) {
251 const factory = this._taskFactories.get(name);
252 if (factory) {
253 return factory();
254 }
255 return (0, rxjs_1.throwError)(new src_1.UnregisteredTaskException(name));
256 }
257 hasTaskExecutor(name) {
258 return this._taskFactories.has(name);
259 }
260}
261exports.FileSystemEngineHostBase = FileSystemEngineHostBase;