131 lines
3.4 KiB
TypeScript
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;
|
|
}
|
|
}
|