156 lines
3.6 KiB
TypeScript

import {
convertNxGenerator,
formatFiles,
generateFiles,
getProjects,
joinPathFragments,
normalizePath,
Tree,
} from '@nrwl/devkit';
import type * as ts from 'typescript';
import {
findExportDeclarationsForJsx,
getComponentNode,
} from '../../utils/ast-utils';
import { getDefaultsForComponent } from '../../utils/component-props';
import { ensureTypescript } from '@nrwl/js/src/utils/typescript/ensure-typescript';
let tsModule: typeof import('typescript');
export interface CreateComponentStoriesFileSchema {
project: string;
componentPath: string;
skipFormat?: boolean;
}
export function createComponentStoriesFile(
host: Tree,
{ project, componentPath }: CreateComponentStoriesFileSchema
) {
if (!tsModule) {
tsModule = ensureTypescript();
}
const proj = getProjects(host).get(project);
const sourceRoot = proj.sourceRoot;
const componentFilePath = joinPathFragments(sourceRoot, componentPath);
const componentDirectory = componentFilePath.replace(
componentFilePath.slice(componentFilePath.lastIndexOf('/')),
''
);
const isPlainJs =
componentFilePath.endsWith('.jsx') || componentFilePath.endsWith('.js');
let fileExt = 'tsx';
if (componentFilePath.endsWith('.jsx')) {
fileExt = 'jsx';
} else if (componentFilePath.endsWith('.js')) {
fileExt = 'js';
}
const componentFileName = componentFilePath
.slice(componentFilePath.lastIndexOf('/') + 1)
.replace('.tsx', '')
.replace('.jsx', '')
.replace('.js', '');
const name = componentFileName;
const contents = host.read(componentFilePath, 'utf-8');
if (contents === null) {
throw new Error(`Failed to read ${componentFilePath}`);
}
const sourceFile = tsModule.createSourceFile(
componentFilePath,
contents,
tsModule.ScriptTarget.Latest,
true
);
const cmpDeclaration = getComponentNode(sourceFile);
if (!cmpDeclaration) {
const componentNodes = findExportDeclarationsForJsx(sourceFile);
if (componentNodes?.length) {
componentNodes.forEach((declaration) => {
findPropsAndGenerateFile(
host,
sourceFile,
declaration,
componentDirectory,
name,
isPlainJs,
fileExt,
componentNodes.length > 1
);
});
} else {
throw new Error(
`Could not find any React component in file ${componentFilePath}`
);
}
} else {
findPropsAndGenerateFile(
host,
sourceFile,
cmpDeclaration,
componentDirectory,
name,
isPlainJs,
fileExt
);
}
}
export function findPropsAndGenerateFile(
host: Tree,
sourceFile: ts.SourceFile,
cmpDeclaration: ts.Node,
componentDirectory: string,
name: string,
isPlainJs: boolean,
fileExt: string,
fromNodeArray?: boolean
) {
const { propsTypeName, props, argTypes } = getDefaultsForComponent(
sourceFile,
cmpDeclaration
);
generateFiles(
host,
joinPathFragments(__dirname, './files'),
normalizePath(componentDirectory),
{
componentFileName: fromNodeArray
? `${name}--${(cmpDeclaration as any).name.text}`
: name,
componentImportFileName: name,
propsTypeName,
props,
argTypes,
componentName: (cmpDeclaration as any).name.text,
isPlainJs,
fileExt,
}
);
}
export async function componentStoryGenerator(
host: Tree,
schema: CreateComponentStoriesFileSchema
) {
createComponentStoriesFile(host, schema);
if (!schema.skipFormat) {
await formatFiles(host);
}
}
export default componentStoryGenerator;
export const componentStorySchematic = convertNxGenerator(
componentStoryGenerator
);