UNPKG

nx

Version:

Smart, Fast and Extensible Build System

895 lines • 41.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getLogger = exports.invokeNew = exports.wrapAngularDevkitSchematic = exports.mockSchematicsForTesting = exports.overrideCollectionResolutionForTesting = exports.runMigration = exports.generate = exports.NxScopeHostUsedForWrappedSchematics = exports.NxScopedHost = exports.scheduleTarget = void 0; const tslib_1 = require("tslib"); /* eslint-disable no-restricted-imports */ const core_1 = require("@angular-devkit/core"); const chalk = require("chalk"); const node_1 = require("@angular-devkit/core/node"); const package_manager_1 = require("../utils/package-manager"); const workspaces_1 = require("../config/workspaces"); const path_1 = require("path"); const rxjs_1 = require("rxjs"); const operators_1 = require("rxjs/operators"); const logger_1 = require("../utils/logger"); const fileutils_1 = require("../utils/fileutils"); const json_1 = require("../utils/json"); const project_configuration_1 = require("../generators/utils/project-configuration"); const package_json_1 = require("../utils/package-json"); function scheduleTarget(root, opts, verbose) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const { Architect } = require('@angular-devkit/architect'); const { WorkspaceNodeModulesArchitectHost, } = require('@angular-devkit/architect/node'); const logger = getTargetLogger(opts.executor, verbose); const fsHost = new NxScopedHost(root); const { workspace } = yield core_1.workspaces.readWorkspace((0, workspaces_1.workspaceConfigName)(root), core_1.workspaces.createWorkspaceHost(fsHost)); const registry = new core_1.schema.CoreSchemaRegistry(); registry.addPostTransform(core_1.schema.transforms.addUndefinedDefaults); registry.addSmartDefaultProvider('unparsed', () => { // This happens when context.scheduleTarget is used to run a target using nx:run-commands return []; }); const architectHost = new WorkspaceNodeModulesArchitectHost(workspace, root); const architect = new Architect(architectHost, registry); const run = yield architect.scheduleTarget({ project: opts.project, target: opts.target, configuration: opts.configuration, }, opts.runOptions, { logger }); let lastOutputError; return run.output.pipe((0, operators_1.tap)((output) => (lastOutputError = !output.success ? output.error : undefined), (error) => { }, // do nothing, this could be an intentional error () => { lastOutputError ? logger.error(lastOutputError) : 0; })); }); } exports.scheduleTarget = scheduleTarget; function createWorkflow(fsHost, root, opts) { const NodeWorkflow = require('@angular-devkit/schematics/tools').NodeWorkflow; const workflow = new NodeWorkflow(fsHost, { force: false, dryRun: opts.dryRun, packageManager: (0, package_manager_1.detectPackageManager)(), root: (0, core_1.normalize)(root), registry: new core_1.schema.CoreSchemaRegistry(require('@angular-devkit/schematics').formats.standardFormats), resolvePaths: [process.cwd(), root], }); workflow.registry.addPostTransform(core_1.schema.transforms.addUndefinedDefaults); workflow.engineHost.registerOptionsTransform(require('@angular-devkit/schematics/tools').validateOptionsWithSchema(workflow.registry)); if (opts.interactive) { workflow.registry.usePromptProvider(createPromptProvider()); } return workflow; } function getCollection(workflow, name) { const collection = workflow.engine.createCollection(name); if (!collection) throw new Error(`Cannot find collection '${name}'`); return collection; } function createRecorder(host, record, logger) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const actualConfigName = yield host.workspaceConfigName(); return (event) => { let eventPath = event.path.startsWith('/') ? event.path.slice(1) : event.path; if (eventPath === 'workspace.json' || eventPath === 'angular.json') { eventPath = actualConfigName; } if (event.kind === 'error') { record.error = true; logger.warn(`ERROR! ${eventPath} ${event.description == 'alreadyExist' ? 'already exists' : 'does not exist.'}.`); } else if (event.kind === 'update') { record.loggingQueue.push(core_1.tags.oneLine `${chalk.white('UPDATE')} ${eventPath}`); } else if (event.kind === 'create') { record.loggingQueue.push(core_1.tags.oneLine `${chalk.green('CREATE')} ${eventPath}`); } else if (event.kind === 'delete') { record.loggingQueue.push(`${chalk.yellow('DELETE')} ${eventPath}`); } else if (event.kind === 'rename') { record.loggingQueue.push(`${chalk.blue('RENAME')} ${eventPath} => ${event.to}`); } }; }); } function runSchematic(host, root, workflow, logger, opts, schematic, printDryRunMessage = true, recorder = null) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const record = { loggingQueue: [], error: false }; workflow.reporter.subscribe(recorder || (yield createRecorder(host, record, logger))); try { yield workflow .execute({ collection: opts.collectionName, schematic: opts.generatorName, options: opts.generatorOptions, debug: false, logger, }) .toPromise(); } catch (e) { console.log(e); throw e; } if (!record.error) { record.loggingQueue.forEach((log) => logger.info(log)); } if (opts.dryRun && printDryRunMessage) { logger.warn(`\nNOTE: The "dryRun" flag means no changes were made.`); } return { status: 0, loggingQueue: record.loggingQueue }; }); } class NxScopedHost extends core_1.virtualFs.ScopedHost { constructor(root) { super(new node_1.NodeJsSyncHost(), (0, core_1.normalize)(root)); this.root = root; this.__readWorkspaceConfiguration = (configFileName, overrides) => { const readJsonFile = (path) => super .read(path) .pipe((0, operators_1.map)((data) => JSON.parse(Buffer.from(data).toString()))); const readWorkspaceJsonFile = (nxJson) => { if (overrides === null || overrides === void 0 ? void 0 : overrides.workspace) { return overrides.workspace; } else if (this.__nxInMemoryWorkspace) { return (0, rxjs_1.of)(this.__nxInMemoryWorkspace); } else { if (configFileName) { return super .read(configFileName) .pipe((0, operators_1.map)((data) => (0, json_1.parseJson)(Buffer.from(data).toString()))); } else { const staticProjects = (0, workspaces_1.globForProjectFiles)(this.root); this.__nxInMemoryWorkspace = (0, workspaces_1.buildWorkspaceConfigurationFromGlobs)(nxJson, staticProjects.filter((x) => (0, path_1.basename)(x) !== 'package.json')); Object.entries(this.__nxInMemoryWorkspace.projects).forEach(([project, config]) => { this.__nxInMemoryWorkspace.projects[project] = config.root; }); return (0, rxjs_1.of)(this.__nxInMemoryWorkspace); } } }; const readNxJsonFile = () => { let nxJson = (overrides === null || overrides === void 0 ? void 0 : overrides.nx) ? overrides.nx : readJsonFile('nx.json'); return nxJson.pipe((0, operators_1.map)((json) => { if (json.extends) { return Object.assign(Object.assign({}, require(json.extends)), json); } else { return json; } })); }; return super.exists('nx.json').pipe((0, operators_1.switchMap)((nxJsonExists) => { let nxJsonObservable = rxjs_1.NEVER; if (nxJsonExists) { nxJsonObservable = readNxJsonFile(); } else { nxJsonObservable = (0, rxjs_1.of)({}); } const workspaceJsonObservable = nxJsonObservable.pipe((0, operators_1.switchMap)((x) => readWorkspaceJsonFile(x))); return (0, rxjs_1.forkJoin)([nxJsonObservable, workspaceJsonObservable]); }), (0, operators_1.switchMap)(([nxJson, workspaceJson]) => { try { // resolve inline configurations and downlevel format return this.resolveInlineProjectConfigurations(workspaceJson).pipe((0, operators_1.map)((x) => { var _a, _b, _c; const angularJson = x; // assign props ng cli expects from nx json, if it exists (_a = angularJson.cli) !== null && _a !== void 0 ? _a : (angularJson.cli = nxJson === null || nxJson === void 0 ? void 0 : nxJson.cli); (_b = angularJson.generators) !== null && _b !== void 0 ? _b : (angularJson.generators = nxJson === null || nxJson === void 0 ? void 0 : nxJson.generators); (_c = angularJson.defaultProject) !== null && _c !== void 0 ? _c : (angularJson.defaultProject = nxJson === null || nxJson === void 0 ? void 0 : nxJson.defaultProject); if (workspaceJson.version === 2) { const formatted = (0, workspaces_1.toOldFormatOrNull)(workspaceJson); return formatted ? Buffer.from((0, json_1.serializeJson)(formatted)) : Buffer.from((0, json_1.serializeJson)(x)); } return Buffer.from((0, json_1.serializeJson)(x)); })); } catch (_a) { return (0, rxjs_1.of)(Buffer.from((0, json_1.serializeJson)(workspaceJson))); } })); }; } read(path) { return this.context(path).pipe((0, operators_1.switchMap)((r) => { if (r.isWorkspaceConfig) { return this.__readWorkspaceConfiguration(r.actualConfigFileName); } else { return super.read(path); } })); } write(path, content) { return this.context(path).pipe((0, operators_1.switchMap)((r) => { if (r.isWorkspaceConfig) { return this.writeWorkspaceConfiguration(r, content); } else { return super.write(path, content); } })); } isFile(path) { return this.context(path).pipe((0, operators_1.switchMap)((r) => { if (r.isWorkspaceConfig) { return (0, rxjs_1.of)(true); // isWorkspaceConfig means its a file } else { return super.isFile(path); } })); } exists(path) { return this.context(path).pipe((0, operators_1.switchMap)((r) => { if (r.isWorkspaceConfig) { return (0, rxjs_1.of)(true); } else { return super.exists(path); } })); } workspaceConfigName() { return super .exists('/angular.json') .pipe((0, operators_1.map)((hasAngularJson) => hasAngularJson ? 'angular.json' : 'workspace.json')) .toPromise(); } context(path) { if (isWorkspaceConfigPath(path)) { return (0, rxjs_1.forkJoin)([ super.exists('/angular.json'), super.exists('/workspace.json'), ]).pipe((0, operators_1.switchMap)(([isAngularJson, isWorkspaceJson]) => { if (!isAngularJson && !isWorkspaceJson) { return (0, rxjs_1.of)({ isWorkspaceConfig: true, actualConfigFileName: null, // AngularJson / WorkspaceJson v2 is always used for standalone project config isNewFormat: true, }); } const actualConfigFileName = isAngularJson ? '/angular.json' : '/workspace.json'; return super.read(actualConfigFileName).pipe((0, operators_1.map)((r) => { try { const w = (0, json_1.parseJson)(Buffer.from(r).toString()); return { isWorkspaceConfig: true, actualConfigFileName, isNewFormat: w.version === 2, }; } catch (_a) { return { isWorkspaceConfig: true, actualConfigFileName, isNewFormat: false, }; } })); })); } else { return (0, rxjs_1.of)({ isWorkspaceConfig: false, actualConfigFileName: null, isNewFormat: false, }); } } writeWorkspaceConfiguration(context, content) { const config = (0, json_1.parseJson)(Buffer.from(content).toString()); if (context.isNewFormat) { try { const w = (0, json_1.parseJson)(Buffer.from(content).toString()); const formatted = (0, workspaces_1.toNewFormatOrNull)(w); if (formatted) { const { cli, generators, defaultProject } = formatted, workspaceJson = tslib_1.__rest(formatted, ["cli", "generators", "defaultProject"]); cli === null || cli === void 0 ? true : delete cli.schematicCollections; return (0, rxjs_1.merge)(this.writeWorkspaceConfigFiles(context, workspaceJson), cli || generators || defaultProject ? this.__saveNxJsonProps({ cli, generators, defaultProject }) : (0, rxjs_1.of)(null)); } else { const { cli, schematics, generators, defaultProject } = w, angularJson = tslib_1.__rest(w, ["cli", "schematics", "generators", "defaultProject"]); cli === null || cli === void 0 ? true : delete cli.schematicCollections; return (0, rxjs_1.merge)(this.writeWorkspaceConfigFiles(context, angularJson), cli || schematics ? this.__saveNxJsonProps({ cli, defaultProject, generators: schematics || generators, }) : (0, rxjs_1.of)(null)); } } catch (e) { } } const { cli, schematics, generators, defaultProject } = config, angularJson = tslib_1.__rest(config, ["cli", "schematics", "generators", "defaultProject"]); cli === null || cli === void 0 ? true : delete cli.schematicCollections; return (0, rxjs_1.merge)(this.writeWorkspaceConfigFiles(context, angularJson), this.__saveNxJsonProps({ cli, defaultProject, generators: schematics || generators, })); } __saveNxJsonProps(props) { const nxJsonPath = 'nx.json'; return super.read(nxJsonPath).pipe((0, operators_1.switchMap)((buf) => { const nxJson = (0, json_1.parseJson)(Buffer.from(buf).toString()); Object.assign(nxJson, props); return super.write(nxJsonPath, Buffer.from((0, json_1.serializeJson)(nxJson))); })); } writeWorkspaceConfigFiles({ actualConfigFileName: workspaceFileName, isNewFormat }, config) { // copy to avoid removing inlined config files. const writeObservables = []; const configToWrite = Object.assign(Object.assign({}, config), { projects: Object.assign({}, config.projects) }); const projects = Object.entries(configToWrite.projects); for (const [project, projectConfig] of projects) { if (projectConfig.configFilePath) { if (workspaceFileName && !isNewFormat) { throw new Error('Attempted to write standalone project configuration into a v1 workspace'); } // project was read from a project.json file const configPath = projectConfig.configFilePath; const fileConfigObject = Object.assign({}, projectConfig); delete fileConfigObject.root; // remove the root before writing delete fileConfigObject.configFilePath; // remove the configFilePath before writing const projectJsonWrite = super.write(configPath, Buffer.from((0, json_1.serializeJson)(fileConfigObject))); // write back to the project.json file writeObservables.push(projectJsonWrite); configToWrite.projects[project] = (0, core_1.normalize)((0, path_1.dirname)(configPath)); // update the config object to point to the written file. } } if (workspaceFileName) { const workspaceJsonWrite = super.write(workspaceFileName, Buffer.from((0, json_1.serializeJson)(configToWrite))); writeObservables.push(workspaceJsonWrite); } return (0, rxjs_1.merge)(...writeObservables); } resolveInlineProjectConfigurations(config) { var _a; // Creates an observable where each emission is a project configuration // that is not listed inside workspace.json. Each time it encounters a // standalone config, observable is updated by concatenating the new // config read operation. const observables = []; Object.entries((_a = config.projects) !== null && _a !== void 0 ? _a : {}).forEach(([project, projectConfig]) => { if (typeof projectConfig === 'string') { // configFilePath is not written to files, but is stored on the config object // so that we know where to save the project's configuration if it was updated // by another angular schematic. const configFilePath = (0, path_1.join)(projectConfig, 'project.json'); const next = this.read(configFilePath).pipe((0, operators_1.map)((x) => ({ project, projectConfig: Object.assign(Object.assign({ root: (0, path_1.dirname)(configFilePath) }, (0, json_1.parseJson)(Buffer.from(x).toString())), { configFilePath }), }))); observables.push(next); } }); return (0, rxjs_1.merge)(...observables).pipe((0, operators_1.toArray)(), (0, operators_1.map)((configs) => { configs.forEach(({ project, projectConfig }) => { config.projects[project] = projectConfig; }); return config; })); } } exports.NxScopedHost = NxScopedHost; class NxScopeHostUsedForWrappedSchematics extends NxScopedHost { constructor(root, host) { super(root); this.host = host; } read(path) { if (isWorkspaceConfigPath(path)) { const nxJsonChange = findMatchingFileChange(this.host, 'nx.json'); const match = findWorkspaceConfigFileChange(this.host); let workspaceJsonOverride; let actualConfigFileName = [ '/workspace.json', '/angular.json', ].filter((f) => this.host.exists(f))[0]; if (actualConfigFileName) { if (match) { workspaceJsonOverride = (0, rxjs_1.of)((0, json_1.parseJson)(match.content.toString())); } } else if (!this.__nxInMemoryWorkspace) { // if we've already dealt with this, let NxScopedHost read the cache... // projects created inside a generator will not be visible // to glob when it runs in nx/shared/workspace, so // we have to add them into the file. const createdProjectFiles = findCreatedProjects(this.host); const deletedProjectFiles = findDeletedProjects(this.host); const nxJsonInTree = (0, project_configuration_1.readNxJson)(this.host); const readJsonWithHost = (file) => (Object.assign({ root: (0, path_1.dirname)(file) }, (0, json_1.parseJson)(this.host.read(file).toString()))); const staticProjects = (0, workspaces_1.buildWorkspaceConfigurationFromGlobs)(nxJsonInTree, (0, workspaces_1.globForProjectFiles)(this.host.root).filter((x) => (0, path_1.basename)(x) !== 'package.json'), readJsonWithHost); const createdProjects = (0, workspaces_1.buildWorkspaceConfigurationFromGlobs)(nxJsonInTree, createdProjectFiles.map((x) => x.path), readJsonWithHost).projects; deletedProjectFiles.forEach((file) => { const matchingStaticProject = Object.entries(staticProjects.projects).find(([, config]) => config.root === (0, path_1.dirname)(file.path)); if (matchingStaticProject) { delete staticProjects.projects[matchingStaticProject[0]]; } }); const workspace = Object.assign(Object.assign({}, staticProjects), { projects: Object.assign(Object.assign({}, staticProjects.projects), createdProjects) }); workspaceJsonOverride = (0, rxjs_1.of)(Object.assign(Object.assign({}, workspace), { // all projects **must** be standalone if workspace.json doesn't exist // since the NxScopedHost already handles the standalone config case, // lets pass them as standalone. projects: Object.fromEntries(Object.entries(workspace.projects).map(([project, config]) => [ project, config.root, ])) })); } // no match, default to existing behavior if (!workspaceJsonOverride && !nxJsonChange) { return super.read(path); } // we try to format it, if it changes, return it, otherwise return the original change try { return this.__readWorkspaceConfiguration(actualConfigFileName, { // we are overriding workspaceJson + nxJson, workspace: workspaceJsonOverride, nx: nxJsonChange ? (0, rxjs_1.of)((0, json_1.parseJson)(nxJsonChange.content.toString())) : null, }); } catch (e) { return super.read(path); } } else { const match = findMatchingFileChange(this.host, path); if (match) { // found a matching change in the host return (0, rxjs_1.of)(Buffer.from(match.content)); } else if ( // found a change to workspace config, and reading a project config file (0, path_1.basename)(path) === 'project.json' && findWorkspaceConfigFileChange(this.host)) { return (0, rxjs_1.of)(this.host.read(path)); } else { // found neither, use default read method return super.read(path); } } } exists(path) { if (isWorkspaceConfigPath(path)) { return findWorkspaceConfigFileChange(this.host) ? (0, rxjs_1.of)(true) : super.exists(path); } else { return findMatchingFileChange(this.host, path) ? (0, rxjs_1.of)(true) : super.exists(path); } } isDirectory(path) { return super.isDirectory(path).pipe((0, operators_1.catchError)(() => (0, rxjs_1.of)(false)), (0, operators_1.switchMap)((isDirectory) => isDirectory ? (0, rxjs_1.of)(true) : (0, rxjs_1.of)(this.host.exists(path) && !this.host.isFile(path)))); } isFile(path) { if (isWorkspaceConfigPath(path)) { return findWorkspaceConfigFileChange(this.host) ? (0, rxjs_1.of)(true) : super.isFile(path); } else { return findMatchingFileChange(this.host, path) ? (0, rxjs_1.of)(true) : super.isFile(path); } } list(path) { const fragments = this.host.children(path).map((child) => (0, core_1.fragment)(child)); return (0, rxjs_1.of)(fragments); } } exports.NxScopeHostUsedForWrappedSchematics = NxScopeHostUsedForWrappedSchematics; function findWorkspaceConfigFileChange(host) { return host .listChanges() .find((f) => f.path == 'workspace.json' || f.path == 'angular.json'); } function findCreatedProjects(host) { return host .listChanges() .filter((f) => f.type === 'CREATE' && ((0, path_1.basename)(f.path) === 'project.json' || (0, path_1.basename)(f.path) === 'package.json')); } function findDeletedProjects(host) { return host .listChanges() .filter((f) => f.type === 'DELETE' && (0, path_1.basename)(f.path) === 'project.json'); } function findMatchingFileChange(host, path) { const targetPath = (0, core_1.normalize)(path.startsWith('/') ? path.substring(1) : path.toString()); return host .listChanges() .find((f) => f.type !== 'DELETE' && (0, core_1.normalize)(f.path) === targetPath); } function isWorkspaceConfigPath(p) { return (p === 'angular.json' || p === '/angular.json' || p === 'workspace.json' || p === '/workspace.json'); } function generate(root, opts, verbose) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const logger = (0, exports.getLogger)(verbose); const fsHost = new NxScopedHost(root); const workflow = createWorkflow(fsHost, root, opts); const collection = getCollection(workflow, opts.collectionName); const schematic = collection.createSchematic(opts.generatorName, true); return (yield runSchematic(fsHost, root, workflow, logger, Object.assign(Object.assign({}, opts), { generatorName: schematic.description.name }), schematic)).status; }); } exports.generate = generate; function createPromptProvider() { return (definitions) => { const questions = definitions.map((definition) => { const question = { name: definition.id, message: definition.message, }; if (definition.default) { question.initial = definition.default; } const validator = definition.validator; if (validator) { question.validate = (input) => validator(input); } switch (definition.type) { case 'string': case 'input': return Object.assign(Object.assign({}, question), { type: 'input' }); case 'boolean': case 'confirmation': case 'confirm': return Object.assign(Object.assign({}, question), { type: 'confirm' }); case 'number': case 'numeral': return Object.assign(Object.assign({}, question), { type: 'numeral' }); case 'list': return Object.assign(Object.assign({}, question), { type: !!definition.multiselect ? 'multiselect' : 'select', choices: definition.items && definition.items.map((item) => { if (typeof item == 'string') { return item; } else { return { message: item.label, name: item.value, }; } }) }); default: return Object.assign(Object.assign({}, question), { type: definition.type }); } }); return require('enquirer').prompt(questions); }; } function runMigration(root, packageName, migrationName, isVerbose) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const logger = (0, exports.getLogger)(isVerbose); const fsHost = new NxScopedHost(root); const workflow = createWorkflow(fsHost, root, {}); const collection = resolveMigrationsCollection(packageName); const record = { loggingQueue: [], error: false }; workflow.reporter.subscribe(yield createRecorder(fsHost, record, logger)); yield workflow .execute({ collection, schematic: migrationName, options: {}, debug: false, logger: logger, }) .toPromise(); return { loggingQueue: record.loggingQueue, madeChanges: record.loggingQueue.length > 0, }; }); } exports.runMigration = runMigration; function resolveMigrationsCollection(name) { var _a; let collectionPath = undefined; if (name.startsWith('.') || name.startsWith('/')) { name = (0, path_1.resolve)(name); } if ((0, path_1.extname)(name)) { collectionPath = require.resolve(name); } else { const { path: packageJsonPath, packageJson } = (0, package_json_1.readModulePackageJson)(name, [ process.cwd(), ]); let pkgJsonSchematics = (_a = packageJson['nx-migrations']) !== null && _a !== void 0 ? _a : packageJson['ng-update']; if (!pkgJsonSchematics) { throw new Error(`Could not find migrations in package: "${name}"`); } if (typeof pkgJsonSchematics != 'string') { pkgJsonSchematics = pkgJsonSchematics.migrations; } collectionPath = require.resolve(pkgJsonSchematics, { paths: [(0, path_1.dirname)(packageJsonPath)], }); } try { if (collectionPath) { (0, fileutils_1.readJsonFile)(collectionPath); return collectionPath; } } catch (_b) { throw new Error(`Invalid migration file in package: "${name}"`); } throw new Error(`Collection cannot be resolved: "${name}"`); } function convertEventTypeToHandleMultipleConfigNames(host, eventPath, content) { const actualConfigName = host.exists('/angular.json') ? 'angular.json' : 'workspace.json'; const isWorkspaceConfig = eventPath === 'angular.json' || eventPath === 'workspace.json'; if (isWorkspaceConfig) { let isNewFormat = true; try { isNewFormat = (0, json_1.parseJson)(host.read(actualConfigName, 'utf-8')).version === 2; } catch (e) { } if (content && isNewFormat) { const formatted = (0, workspaces_1.toNewFormat)((0, json_1.parseJson)(content.toString())); if (formatted) { return { eventPath: actualConfigName, content: Buffer.from((0, json_1.serializeJson)(formatted)), }; } else { return { eventPath: actualConfigName, content }; } } else { return { eventPath: actualConfigName, content }; } } else { return { eventPath, content }; } } let collectionResolutionOverrides = null; let mockedSchematics = null; /** * By default, Angular Devkit schematic collections will be resolved using the Node resolution. * This doesn't work if you are testing schematics that refer to other schematics in the * same repo. * * This function can can be used to override the resolution behaviour. * * Example: * * ```typescript * overrideCollectionResolutionForTesting({ * '@nrwl/workspace': path.join(__dirname, '../../../../workspace/generators.json'), * '@nrwl/angular': path.join(__dirname, '../../../../angular/generators.json'), * '@nrwl/linter': path.join(__dirname, '../../../../linter/generators.json') * }); * * ``` */ function overrideCollectionResolutionForTesting(collections) { collectionResolutionOverrides = collections; } exports.overrideCollectionResolutionForTesting = overrideCollectionResolutionForTesting; /** * If you have an Nx Devkit generator invoking the wrapped Angular Devkit schematic, * and you don't want the Angular Devkit schematic to run, you can mock it up using this function. * * Unfortunately, there are some edge cases in the Nx-Angular devkit integration that * can be seen in the unit tests context. This function is useful for handling that as well. * * In this case, you can mock it up. * * Example: * * ```typescript * mockSchematicsForTesting({ * 'mycollection:myschematic': (tree, params) => { * tree.write('README.md'); * } * }); * * ``` */ function mockSchematicsForTesting(schematics) { mockedSchematics = schematics; } exports.mockSchematicsForTesting = mockSchematicsForTesting; function wrapAngularDevkitSchematic(collectionName, generatorName) { // This is idempotent, if it happens to get called // multiple times its no big deal. It ensures that some // patches are applied to @angular-devkit code which // are necessary. For the most part, our wrapped host hits // the necessary areas, but for some things it wouldn't make // sense for the adapter to be 100% accurate. // // e.g. Angular warns about tags, but some angular CLI schematics // were written with Nx in mind, and may care about tags. require('./compat'); return (host, generatorOptions) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (mockedSchematics && mockedSchematics[`${collectionName}:${generatorName}`]) { return yield mockedSchematics[`${collectionName}:${generatorName}`](host, generatorOptions); } const emptyLogger = { log: (e) => { }, info: (e) => { }, warn: (e) => { }, debug: () => { }, error: (e) => { }, fatal: (e) => { }, }; emptyLogger.createChild = () => emptyLogger; const recorder = (event) => { let eventPath = event.path.startsWith('/') ? event.path.slice(1) : event.path; const r = convertEventTypeToHandleMultipleConfigNames(host, eventPath, event.content); if (event.kind === 'error') { } else if (event.kind === 'update') { if (r.eventPath === 'angular.json' || r.eventPath === 'workspace.json') { saveWorkspaceConfigurationInWrappedSchematic(host, r); } else { host.write(r.eventPath, r.content); } } else if (event.kind === 'create') { host.write(r.eventPath, r.content); } else if (event.kind === 'delete') { host.delete(r.eventPath); } else if (event.kind === 'rename') { host.rename(r.eventPath, event.to); } }; const fsHost = new NxScopeHostUsedForWrappedSchematics(host.root, host); const options = { generatorOptions, dryRun: true, interactive: false, help: false, debug: false, collectionName, generatorName, force: false, defaults: false, }; const workflow = createWorkflow(fsHost, host.root, options); // used for testing if (collectionResolutionOverrides) { const r = workflow.engineHost.resolve; workflow.engineHost.resolve = (collection, b, c) => { if (collectionResolutionOverrides[collection]) { return collectionResolutionOverrides[collection]; } else { return r.apply(workflow.engineHost, [collection, b, c]); } }; } const collection = getCollection(workflow, collectionName); const schematic = collection.createSchematic(generatorName, true); const res = yield runSchematic(fsHost, host.root, workflow, emptyLogger, options, schematic, false, recorder); if (res.status !== 0) { throw new Error(res.loggingQueue.join('\n')); } }); } exports.wrapAngularDevkitSchematic = wrapAngularDevkitSchematic; function invokeNew(root, opts, verbose) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const logger = (0, exports.getLogger)(verbose); const fsHost = new NxScopedHost(root); const workflow = createWorkflow(fsHost, root, opts); const collection = getCollection(workflow, opts.collectionName); const schematic = collection.createSchematic('new', true); return (yield runSchematic(fsHost, root, workflow, logger, Object.assign(Object.assign({}, opts), { generatorName: schematic.description.name }), schematic)).status; }); } exports.invokeNew = invokeNew; let logger; const loggerColors = { warn: (s) => chalk.bold(chalk.yellow(s)), error: (s) => { if (s.startsWith('NX ')) { return `\n${logger_1.NX_ERROR} ${chalk.bold(chalk.red(s.slice(3)))}\n`; } return chalk.bold(chalk.red(s)); }, info: (s) => { if (s.startsWith('NX ')) { return `\n${logger_1.NX_PREFIX} ${chalk.bold(s.slice(3))}\n`; } return chalk.white(s); }, }; const getLogger = (isVerbose = false) => { if (!logger) { logger = (0, node_1.createConsoleLogger)(isVerbose, process.stdout, process.stderr, loggerColors); } return logger; }; exports.getLogger = getLogger; const getTargetLogger = (executor, isVerbose = false) => { if (executor !== '@angular-devkit/build-angular:tslint') { return (0, exports.getLogger)(isVerbose); } const tslintExecutorLogger = (0, node_1.createConsoleLogger)(isVerbose, process.stdout, process.stderr, Object.assign(Object.assign({}, loggerColors), { warn: (s) => { if (s.startsWith(`TSLint's support is discontinued and we're deprecating its support in Angular CLI.`)) { s = `TSLint's support is discontinued and the @angular-devkit/build-angular:tslint executor is deprecated.\n` + 'To start using a modern linter tool, please consider replacing TSLint with ESLint. ' + 'You can use the "@nrwl/angular:convert-tslint-to-eslint" generator to automatically convert your projects.\n' + 'For more info, visit https://nx.dev/packages/angular/generators/convert-tslint-to-eslint.'; } return chalk.bold(chalk.yellow(s)); } })); return tslintExecutorLogger; }; function saveWorkspaceConfigurationInWrappedSchematic(host, r) { const workspaceJsonExists = host.exists(r.eventPath); const workspace = (0, json_1.parseJson)(r.content.toString()); for (const [project, config] of Object.entries(workspace.projects)) { if (typeof config === 'object' && (!workspaceJsonExists || config.configFilePath)) { const path = config.configFilePath || (0, path_1.join)(config.root, 'project.json'); workspace.projects[project] = (0, core_1.normalize)((0, path_1.dirname)(path)); delete config.root; // remove the root before writing delete config.configFilePath; host.write(path, (0, json_1.serializeJson)(config)); } } const nxJson = (0, json_1.parseJson)(host.read('nx.json').toString()); nxJson.generators = workspace.generators || workspace.schematics; nxJson.cli = workspace.cli || nxJson.cli; nxJson.defaultProject = workspace.defaultProject; delete workspace.cli; delete workspace.generators; delete workspace.schematics; if (workspaceJsonExists) { r.content = Buffer.from((0, json_1.serializeJson)(workspace)); host.write(r.eventPath, r.content); } } //# sourceMappingURL=ngcli-adapter.js.map