import { sanitize } from '@storybook/csf'; const STORIES_OF = 'storiesOf'; function pushParts(source, parts, from, to) { const start = source.slice(from, to); parts.push(start); const end = source.slice(to); parts.push(end); } export function patchNode(node) { if (node.range && node.range.length === 2 && node.start === undefined && node.end === undefined) { const [start, end] = node.range; // eslint-disable-next-line no-param-reassign node.start = start; // eslint-disable-next-line no-param-reassign node.end = end; } if (!node.range && node.start !== undefined && node.end !== undefined) { // eslint-disable-next-line no-param-reassign node.range = [node.start, node.end]; } return node; } function findTemplate(templateName, program) { let template = null; program.body.find((node) => { let declarations = null; if (node.type === 'VariableDeclaration') { declarations = node.declarations; } else if ( node.type === 'ExportNamedDeclaration' && node.declaration?.type === 'VariableDeclaration' ) { declarations = node.declaration.declarations; } return ( declarations && declarations.find((decl) => { if ( decl.type === 'VariableDeclarator' && decl.id.type === 'Identifier' && decl.id.name === templateName ) { template = decl.init; return true; // stop looking } return false; }) ); }); return template; } function expandBindExpression(node, parent) { if (node.type === 'CallExpression') { const { callee, arguments: bindArguments } = node; if ( parent.type === 'Program' && callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.property.type === 'Identifier' && callee.property.name === 'bind' && (bindArguments.length === 0 || (bindArguments.length === 1 && bindArguments[0].type === 'ObjectExpression' && bindArguments[0].properties.length === 0)) ) { const boundIdentifier = callee.object.name; const template = findTemplate(boundIdentifier, parent); if (template) { return template; } } } return node; } export function handleExportedName(storyName, originalNode, parent) { const node = expandBindExpression(originalNode, parent); const startLoc = { col: node.loc.start.column, line: node.loc.start.line, }; const endLoc = { col: node.loc.end.column, line: node.loc.end.line, }; return { [storyName]: { startLoc, endLoc, startBody: startLoc, endBody: endLoc, }, }; } export function handleADD(node, parent, storiesOfIdentifiers) { if (!node.property || !node.property.name || node.property.name !== 'add') { return {}; } const addArgs = parent.arguments; if (!addArgs || addArgs.length < 2) { return {}; } let tmp = node.object; while (tmp.callee && tmp.callee.object) { tmp = tmp.callee.object; } const framework = tmp.callee && tmp.callee.name && storiesOfIdentifiers[tmp.callee.name]; const storyName = addArgs[0]; const body = addArgs[1]; const lastArg = addArgs[addArgs.length - 1]; if (storyName.type !== 'Literal' && storyName.type !== 'StringLiteral') { // if story name is not literal, it's much harder to extract it return {}; } if (storyName.value && typeof storyName.value === 'string') { const key = sanitize(storyName.value); let idToFramework; if (key && framework) { idToFramework = { [key]: framework }; } return { toAdd: { [key]: { // Debug: code: source.slice(storyName.start, lastArg.end), startLoc: { col: storyName.loc.start.column, line: storyName.loc.start.line, }, endLoc: { col: lastArg.loc.end.column, line: lastArg.loc.end.line, }, startBody: { col: body.loc.start.column, line: body.loc.start.line, }, endBody: { col: body.loc.end.column, line: body.loc.end.line, }, }, }, idToFramework, }; } return {}; } export function handleSTORYOF(node, parts, source, lastIndex) { if (!node.callee || !node.callee.name || node.callee.name !== STORIES_OF) { return lastIndex; } parts.pop(); pushParts(source, parts, lastIndex, node.end); return node.end; } export function asImport(node) { return node.source.value; }