@ngneat/spectator
Version:
A powerful tool to simplify your Angular tests
156 lines (134 loc) • 4.38 kB
text/typescript
import { normalize, strings } from '@angular-devkit/core';
import {
apply,
chain,
externalSchematic,
mergeWith,
move,
template,
url,
MergeStrategy,
Rule,
SchematicContext,
Tree,
noop,
} from '@angular-devkit/schematics';
import { buildDefaultPath, getWorkspace } from '@schematics/angular/utility/workspace';
import { parseName } from '@schematics/angular/utility/parse-name';
import { ComponentOptions, DirectiveOptions, ServiceOptions } from './schema';
export function spectatorComponentSchematic(options: ComponentOptions): Rule {
return chain([
externalSchematic('@schematics/angular', 'component', {
...omit(options, ['jest', 'withHost', 'withCustomHost']),
skipTests: true,
}),
async (tree: Tree, _context: SchematicContext): Promise<Rule> => {
if (options.skipTests) {
return noop;
}
await _ensurePath(tree, options);
const movePath = options.flat ? (options.path as string) : normalize(options.path + '/' + strings.dasherize(options.name) || '');
const specTemplateRule = apply(
url(`./files/${options.withHost ? 'component-host' : options.withCustomHost ? 'component-custom-host' : 'component'}`),
[
template({
...strings,
...options,
}),
move(movePath),
],
);
return mergeWith(specTemplateRule, MergeStrategy.Default);
},
]);
}
export function spectatorServiceSchematic(options: ServiceOptions): Rule {
return chain([
externalSchematic('@schematics/angular', 'service', {
...omit(options, ['jest', 'isDataService']),
skipTests: true,
}),
async (tree: Tree, _context: SchematicContext): Promise<Rule> => {
if (options.skipTests) {
return noop;
}
await _ensurePath(tree, options);
const movePath = normalize(options.path || '');
const specTemplateRule = apply(url(`./files/${options.isDataService ? 'data-service' : `service`}`), [
template({
...strings,
...options,
}),
move(movePath),
]);
return mergeWith(specTemplateRule, MergeStrategy.Default);
},
]);
}
export function spectatorDirectiveSchematic(options: DirectiveOptions): Rule {
return chain([
externalSchematic('@schematics/angular', 'directive', {
...omit(options, ['jest']),
skipTests: true,
}),
async (tree: Tree, _context: SchematicContext): Promise<Rule> => {
if (options.skipTests) {
return noop;
}
await _ensurePath(tree, options);
const movePath = normalize(options.path || '');
const specTemplateRule = apply(url(`./files/directive`), [
template({
...strings,
...options,
}),
move(movePath),
]);
return mergeWith(specTemplateRule, MergeStrategy.Default);
},
]);
}
export function spectatorPipeSchematic(options: DirectiveOptions): Rule {
return chain([
externalSchematic('@schematics/angular', 'pipe', {
...omit(options, ['jest']),
skipTests: true,
}),
async (tree: Tree, _context: SchematicContext): Promise<Rule> => {
if (options.skipTests) {
return noop;
}
await _ensurePath(tree, options);
const movePath = normalize(options.path || '');
const specTemplateRule = apply(url(`./files/pipe`), [
template({
...strings,
...options,
}),
move(movePath),
]);
return mergeWith(specTemplateRule, MergeStrategy.Default);
},
]);
}
async function _ensurePath(tree: Tree, options: any): Promise<void> {
const workspace = await getWorkspace(tree);
if (!options.project) {
options.project = workspace.projects.keys().next().value;
}
const project = workspace.projects.get(options.project as string);
if (options.path === undefined && project) {
options.path = buildDefaultPath(project);
}
const parsedPath = parseName(options.path as string, options.name);
options.name = parsedPath.name;
options.path = parsedPath.path;
}
function omit<T extends Record<PropertyKey, any>>(original: T, keys: (keyof T)[]): any {
return Object.keys(original)
.filter((key) => !keys.includes(key))
.reduce((obj: Record<PropertyKey, any>, key) => {
obj[key] = original[key];
return obj;
}, {});
}