@storybook/codemod
Version:
A collection of codemod scripts written with JSCodeshift
113 lines (109 loc) • 4.58 kB
JavaScript
import CJS_COMPAT_NODE_URL_9n6kaelwkiq from 'node:url';
import CJS_COMPAT_NODE_PATH_9n6kaelwkiq from 'node:path';
import CJS_COMPAT_NODE_MODULE_9n6kaelwkiq from "node:module";
var __filename = CJS_COMPAT_NODE_URL_9n6kaelwkiq.fileURLToPath(import.meta.url);
var __dirname = CJS_COMPAT_NODE_PATH_9n6kaelwkiq.dirname(__filename);
var require = CJS_COMPAT_NODE_MODULE_9n6kaelwkiq.createRequire(import.meta.url);
// ------------------------------------------------------------
// end of CJS compatibility banner, injected by Storybook's esbuild configuration
// ------------------------------------------------------------
// src/transforms/find-implicit-spies.ts
import { core as babel, types as t } from "storybook/internal/babel";
import { loadCsf } from "storybook/internal/csf-tools";
function findImplicitSpies(path, file, keys) {
path.traverse({
Identifier: (identifier) => {
!keys.includes(identifier.node.name) && /^on[A-Z].*/.test(identifier.node.name) && console.warn(identifier.buildCodeFrameError(`${file} Possible implicit spy found`).message);
}
});
}
function getAnnotationKeys(file, storyName, annotationName) {
let argKeys = [];
return file.path.traverse({
// CSF2 play function Story.args =
AssignmentExpression: (path) => {
let left = path.get("left");
if (!left.isMemberExpression())
return;
let object = left.get("object");
if (!(object.isIdentifier() && object.node.name === storyName))
return;
let property = left.get("property"), right = path.get("right");
property.isIdentifier() && property.node.name === annotationName && right.isObjectExpression() && argKeys.push(
...right.node.properties.flatMap(
(value) => t.isObjectProperty(value) && t.isIdentifier(value.key) ? [value.key.name] : []
)
);
},
// CSF3 const Story = {args: () => {} };
VariableDeclarator: (path) => {
let id = path.get("id"), init = path.get("init");
if (!(id.isIdentifier() && id.node.name === storyName) || !init.isObjectExpression())
return;
let args = init.get("properties").flatMap((it) => it.isObjectProperty() ? [it] : []).find((it) => {
let argKey = it.get("key");
return argKey.isIdentifier() && argKey.node.name === annotationName;
});
if (!args)
return;
let argsValue = args.get("value");
!argsValue || !argsValue.isObjectExpression() || argKeys.push(
...argsValue.node.properties.flatMap(
(value) => t.isObjectProperty(value) && t.isIdentifier(value.key) ? [value.key.name] : []
)
);
}
}), argKeys;
}
var getObjectExpressionKeys = (node) => t.isObjectExpression(node) ? node.properties.flatMap(
(value) => t.isObjectProperty(value) && t.isIdentifier(value.key) ? [value.key.name] : []
) : [];
async function transform(info) {
let csf = loadCsf(info.source, { makeTitle: (title) => title }), fileNode = csf._ast, file = new babel.File(
{ filename: info.path },
{ code: info.source, ast: fileNode }
);
csf.parse();
let metaKeys = [
...getObjectExpressionKeys(csf._metaAnnotations.args),
...getObjectExpressionKeys(csf._metaAnnotations.argTypes)
];
Object.values(csf.stories).forEach(({ name }) => {
if (!name)
return;
let allKeys = [
...metaKeys,
...getAnnotationKeys(file, name, "args"),
...getAnnotationKeys(file, name, "argTypes")
];
file.path.traverse({
// CSF2 play function Story.play =
AssignmentExpression: (path) => {
let left = path.get("left");
if (!left.isMemberExpression())
return;
let object = left.get("object");
if (!(object.isIdentifier() && object.node.name === name))
return;
let property = left.get("property");
property.isIdentifier() && property.node.name === "play" && findImplicitSpies(path, info.path, allKeys);
},
// CSF3 play function: const Story = {play: () => {} };
VariableDeclarator: (path) => {
let id = path.get("id"), init = path.get("init");
if (!(id.isIdentifier() && id.node.name === name) || !init.isObjectExpression())
return;
let play = init.get("properties").flatMap((it) => it.isObjectProperty() ? [it] : []).find((it) => {
let argKey = it.get("key");
return argKey.isIdentifier() && argKey.node.name === "play";
});
play && findImplicitSpies(play, info.path, allKeys);
}
});
});
}
var parser = "tsx";
export {
transform as default,
parser
};