"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.convertStorybookFiles = convertStorybookFiles; const fs_1 = require("fs"); const compiler_1 = require("../typescript/compiler"); const parser_1 = require("../react/parser"); const project_1 = require("../connect/project"); const logging_1 = require("../common/logging"); const label_language_mapping_1 = require("../connect/label_language_mapping"); const typescript_1 = __importDefault(require("typescript")); const minimatch_1 = require("minimatch"); const intrinsics_1 = require("../connect/intrinsics"); const parser_common_1 = require("../connect/parser_common"); /** * Converts all Storyboook files in a directory into Code Connect objects. If a file * cannot be converted (e.g. unsupported syntax), it is ignored and an error is * logged. * * @param args * @returns An array of Code Connect objects */ async function convertStorybookFiles({ projectInfo, storiesGlob = '**/*.stories.{tsx,ts,jsx,js}', isForMigration = false, }) { const { remoteUrl, config, files, tsProgram } = projectInfo; const storyFiles = files.filter((file) => (0, minimatch_1.minimatch)(file, storiesGlob, { matchBase: true })); logging_1.logger.debug(`Story files found:\n${storyFiles.map((f) => `- ${f}`).join('\n')}`); return Promise.all(storyFiles.map((path) => convertStorybookFile({ path, tsProgram, config, remoteUrl, absPath: projectInfo.absPath, isForMigration, }))) .then((f) => f.filter((x) => Boolean(x))) .then((f) => f.flat()); } async function convertStorybookFile({ path, tsProgram, remoteUrl, config, absPath, isForMigration = false, }) { const checker = tsProgram.getTypeChecker(); const sourceFile = tsProgram.getSourceFile(path); if (!sourceFile) { throw new parser_common_1.InternalError(`Source file not found: ${path}`); } const parserContext = { checker, config, sourceFile, absPath, resolvedImports: (0, parser_1.findAndResolveImports)(tsProgram, sourceFile), }; let source = (0, fs_1.readFileSync)(path).toString(); // Replace backticks with ' as csf-tools can't parse dynamic titles source = source.replace(/title: `(.*)`/g, (_, title) => { return `title: '${title}'`; }); logging_1.logger.debug(`Parsing story ${path}`); try { // We need to get the default export, which contains the story file meta, // from the TS Program rather than using `babelNodeToTsSourceFile(csf._metaNode)`, // because we need access to the full Program to parse it for prop types etc. const storyFileMetaNode = (0, compiler_1.getDefaultExport)(sourceFile); if (!storyFileMetaNode) { return; } const parseResult = parseStoryMetadata(storyFileMetaNode, parserContext); if (!parseResult) { logging_1.logger.debug(`Could not parse story metadata for ${path}`); return; } const { figmaStoryMetadata, componentDeclaration, propMappings, examples, imports, links } = parseResult; const componentMetadata = await (0, parser_1.parseComponentMetadata)(componentDeclaration, parserContext); const codeConnectObjects = []; const baseCodeConnect = { figmaNode: figmaStoryMetadata.url, source: config?.storybook?.url ? (0, project_1.getStorybookUrl)(componentMetadata.source, config.storybook.url) : (0, project_1.getRemoteFileUrl)(componentMetadata.source, remoteUrl, config?.defaultBranch), sourceLocation: { line: componentMetadata.line }, template: '', templateData: { props: propMappings, imports, }, links, component: componentMetadata.component, label: label_language_mapping_1.CodeConnectLabel.Storybook, language: label_language_mapping_1.SyntaxHighlightLanguage.TypeScript, metadata: { cliVersion: require('../../package.json').version, }, }; // If there are no examples, just return a default Code Connect object if (!examples) { codeConnectObjects.push({ ...baseCodeConnect, template: (0, parser_1.getDefaultTemplate)(componentMetadata), }); return codeConnectObjects; } for (const statement of sourceFile.statements) { // Find any exported function or variable declarations, which correspond to stories if (!(typescript_1.default.isFunctionDeclaration(statement) || typescript_1.default.isVariableStatement(statement))) { continue; } const name = typescript_1.default.isFunctionDeclaration(statement) ? statement.name?.text : statement.declarationList.declarations?.[0].name.getText(sourceFile); const example = examples?.find((example) => example.example === name); // This story is not in the examples array, so skip it if (examples && !example) { continue; } let statementToParse; if (typescript_1.default.isFunctionDeclaration(statement)) { statementToParse = statement; } else { const initializer = statement.declarationList.declarations[0].initializer; if (initializer && typescript_1.default.isArrowFunction(initializer)) { statementToParse = initializer; } else if (initializer && typescript_1.default.isObjectLiteralExpression(initializer)) { // Handle stories like `export const Primary = { render: () =>