angular-cli/packages/angular_devkit/schematics/tools/node-module-engine-host.ts
2018-06-05 18:50:06 -07:00

144 lines
4.2 KiB
TypeScript

/**
* @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.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<RuleFactory<{}>>(refString, parentPath);
if (!ref.ref) {
return null;
}
return { ref: ref.ref, path: ref.module };
}
protected _transformCollectionDescription(
name: string,
desc: Partial<FileSystemCollectionDesc>,
): 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>,
): FileSystemSchematicDesc {
if (!desc.factoryFn || !desc.path || !desc.description) {
throw new SchematicMissingFieldsException(name);
}
return desc as FileSystemSchematicDesc;
}
}