/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { BaseException } from '@angular-devkit/core'; import * as core from '@angular-devkit/core/node'; import { dirname, join, resolve as resolvePath } from 'path'; import { RuleFactory } from '../src'; import { FileSystemCollectionDesc, FileSystemSchematicDesc, } from './description'; import { ExportStringRef } from './export-ref'; import { CollectionCannotBeResolvedException, CollectionMissingSchematicsMapException, FileSystemEngineHostBase, SchematicMissingFieldsException, } from './file-system-engine-host-base'; import { readJsonFile } from './file-system-utility'; export class NodePackageDoesNotSupportSchematics extends BaseException { constructor(name: string) { super(`Package ${JSON.stringify(name)} was found but does not support schematics.`); } } /** * A simple EngineHost that uses NodeModules to resolve collections. */ export class NodeModulesEngineHost extends FileSystemEngineHostBase { constructor() { super(); } protected _resolvePackageJson(name: string, basedir = process.cwd()) { return core.resolve(name, { basedir, checkLocal: true, checkGlobal: true, resolvePackageJson: true, }); } protected _resolvePath(name: string, basedir = process.cwd()) { // Allow relative / absolute paths. if (name.startsWith('.') || name.startsWith('/')) { return resolvePath(basedir, name); } else { // If it's a file inside a package, resolve the package then return the file... if (name.split('/').length > (name[0] == '@' ? 2 : 1)) { const rest = name.split('/'); const packageName = rest.shift() + (name[0] == '@' ? '/' + rest.shift() : ''); return resolvePath(core.resolve(packageName, { basedir, checkLocal: true, checkGlobal: true, resolvePackageJson: true, }), '..', ...rest); } return core.resolve(name, { basedir, checkLocal: true, checkGlobal: true, }); } } protected _resolveCollectionPath(name: string): string { let collectionPath: string | undefined = undefined; if (name.replace(/\\/, '/').split('/').length > (name[0] == '@' ? 2 : 1)) { try { collectionPath = this._resolvePath(name, process.cwd()); } catch (_) { } } if (!collectionPath) { let packageJsonPath = this._resolvePackageJson(name, process.cwd()); // If it's a file, use it as is. Otherwise append package.json to it. if (!core.fs.isFile(packageJsonPath)) { packageJsonPath = join(packageJsonPath, 'package.json'); } const pkgJsonSchematics = require(packageJsonPath)['schematics']; if (!pkgJsonSchematics || typeof pkgJsonSchematics != 'string') { throw new NodePackageDoesNotSupportSchematics(name); } collectionPath = this._resolvePath(pkgJsonSchematics, dirname(packageJsonPath)); } try { if (collectionPath) { readJsonFile(collectionPath); return collectionPath; } } catch (e) { } throw new CollectionCannotBeResolvedException(name); } protected _resolveReferenceString(refString: string, parentPath: string) { const ref = new ExportStringRef>(refString, parentPath); if (!ref.ref) { return null; } return { ref: ref.ref, path: ref.module }; } protected _transformCollectionDescription( name: string, desc: Partial, ): FileSystemCollectionDesc { if (!desc.schematics || typeof desc.schematics != 'object') { throw new CollectionMissingSchematicsMapException(name); } return { ...desc, name, } as FileSystemCollectionDesc; } protected _transformSchematicDescription( name: string, _collection: FileSystemCollectionDesc, desc: Partial, ): FileSystemSchematicDesc { if (!desc.factoryFn || !desc.path || !desc.description) { throw new SchematicMissingFieldsException(name); } return desc as FileSystemSchematicDesc; } }