131 lines
3.4 KiB
TypeScript

import {
apply,
chain,
mergeWith,
move,
Rule,
SchematicContext,
SchematicsException,
template,
Tree,
url
} from '@angular-devkit/schematics';
import { findNodes, getProjectConfig } from '@nrwl/workspace';
import {
PropertyAssignment,
PropertyDeclaration,
SyntaxKind
} from 'typescript';
import {
getTsSourceFile,
getDecoratorMetadata,
applyWithSkipExisting
} from '../../utils/ast-utils';
import {
getInputPropertyDeclarations,
getKnobType
} from '../component-story/component-story';
export default function(schema: CreateComponentSpecFileSchema): Rule {
return chain([createComponentSpecFile(schema)]);
}
export interface CreateComponentSpecFileSchema {
projectName: string;
libPath: string;
componentName: string;
componentPath: string;
componentFileName: string;
}
export function createComponentSpecFile({
projectName,
libPath,
componentName,
componentPath,
componentFileName
}: CreateComponentSpecFileSchema): Rule {
return (tree: Tree, context: SchematicContext): Rule => {
const e2eLibIntegrationFolderPath =
getProjectConfig(tree, projectName + '-e2e').sourceRoot + '/integration';
const fullComponentPath =
libPath + '/' + componentPath + '/' + componentFileName + '.ts';
const props = getInputPropertyDeclarations(tree, fullComponentPath).map(
node => {
const decoratorContent = findNodes(
findNodes(node, SyntaxKind.Decorator)[0],
SyntaxKind.StringLiteral
);
const name = decoratorContent.length
? decoratorContent[0].getText().slice(1, -1)
: node.name.getText();
const type = getKnobType(node);
const defaultValue = getKnobDefaultValue(node);
return {
name,
type,
defaultValue
};
}
);
const componentSelector = getComponentSelector(tree, fullComponentPath);
return applyWithSkipExisting(url('./files'), [
template({
projectName,
componentFileName: componentFileName,
componentName: componentName,
componentSelector,
props,
tmpl: ''
}),
move(e2eLibIntegrationFolderPath + '/' + componentPath)
]);
};
}
export function getComponentSelector(tree: Tree, path: string): string {
const file = getTsSourceFile(tree, path);
const componentDecorators = getDecoratorMetadata(
file,
'Component',
'@angular/core'
);
if (componentDecorators.length === 0) {
throw new SchematicsException(`No @NgModule decorator in ${path}`);
}
const componentDecorator = componentDecorators[0];
const selectorNode = <PropertyAssignment>(
findNodes(componentDecorator, SyntaxKind.PropertyAssignment).find(
(node: PropertyAssignment) => node.name.getText() === 'selector'
)
);
if (!selectorNode) {
throw new SchematicsException(
`No selector defined for the component in ${path}`
);
}
return selectorNode.initializer.getText().slice(1, -1);
}
export function getKnobDefaultValue(
property: PropertyDeclaration
): string | undefined {
if (!property.initializer) {
return undefined;
}
switch (property.initializer.kind) {
case SyntaxKind.StringLiteral:
return property.initializer.getText().slice(1, -1);
case SyntaxKind.NumericLiteral:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return property.initializer.getText();
default:
return undefined;
}
}