New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

react-ui-codemod

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-ui-codemod - npm Package Compare versions

Comparing version 1.3.0-beta.5 to 1.3.0-beta.6

82

creevey/skipsArrays.ts
/* eslint-disable import/no-default-export */
import { API, FileInfo } from 'jscodeshift';
import { API, FileInfo, ObjectExpression } from 'jscodeshift';
import { Collection } from 'jscodeshift/src/Collection';
/**
* Transform skips to new format
* @see https://github.com/creevey/creevey/pull/206
*/
const transformSkips = (api: API, collection: Collection<any>): boolean => {

@@ -10,39 +15,52 @@ const j = api.jscodeshift;

collection
// { creevey: ... }
.find(j.ObjectProperty, (node) => node.key.name === 'creevey')
.forEach((path) => {
const skip = path.node.value.properties.find((p) => p.key.name === 'skip');
if (skip) {
const { value } = skip;
if (path.node.value.type === "ObjectExpression") {
// { creevey: {...} }
const skip = path.node.value.properties.find((p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === 'skip');
if (skip && skip.type === "ObjectProperty") {
// { creevey: { skip: ... } }
const { value } = skip;
if (value.type === 'ArrayExpression') {
const { elements } = value;
if (value.type === 'ArrayExpression') {
// { creevey: { skip: [...] } }
const { elements } = value;
if (elements.length === 1 && elements[0].type === 'BooleanLiteral') {
skip.value = elements[0];
modified = true;
} else if (elements.length !== 0) {
const props = [];
elements.forEach((e, i) => {
let reason = '';
if (e.type === 'ObjectExpression') {
const reasonProp = e.properties.find((p) => p.key.name === 'reason');
if (reasonProp) {
reason = reasonProp.value.value;
}
// j(reasonProp).remove();
}
if (!reason) {
let defaultExport = path.parent;
while (defaultExport) {
if (defaultExport.value.type === 'ExportDefaultDeclaration') {
break;
if (elements.length === 1 && elements[0] && elements[0].type === 'BooleanLiteral') {
// { creevey: { skip: [true|false] } }
skip.value = elements[0];
modified = true;
} else if (elements.length !== 0) {
// { creevey: { skip: [..., ...] } }
const props: ObjectExpression['properties'] = [];
elements.forEach((e, i) => {
let reason = '';
if (e) {
if (e.type === 'ObjectExpression') {
// { creevey: { skip: [{ ... }, ] } }
const reasonProp = e.properties.find((p) => p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === 'reason');
// { creevey: { skip: [{ reason: ..., }, ] } }
if (reasonProp && reasonProp.type === "ObjectProperty" && reasonProp.value.type === "StringLiteral") {
// { creevey: { skip: [{ reason: "...", }, ] } }
reason = reasonProp.value.value;
}
// j(reasonProp).remove();
}
defaultExport = defaultExport.parent;
if (!reason) {
let defaultExport = path.parent;
while (defaultExport) {
if (defaultExport.value.type === 'ExportDefaultDeclaration') {
break;
}
defaultExport = defaultExport.parent;
}
reason = (defaultExport ? 'kind' : 'story') + '-skip-' + i;
}
props.push(j.property('init', j.literal(reason), e as any));
}
reason = (defaultExport ? 'kind' : 'story') + '-skip-' + i;
}
props.push(j.property('init', j.literal(reason), e));
});
skip.value = j.objectExpression(props);
modified = true;
});
skip.value = j.objectExpression(props);
modified = true;
}
}

@@ -49,0 +67,0 @@ }

/* eslint-disable import/no-default-export */
import path from 'path';
import { writeFileSync, mkdirSync, existsSync } from 'fs';
import { exec } from 'child_process';
import { API, FileInfo } from 'jscodeshift';
import { API, FileInfo, ObjectExpression, VariableDeclarator } from 'jscodeshift';
import { Collection } from 'jscodeshift/src/Collection';
import { CreeveyStoryParams } from 'creevey';
import prettier from 'prettier';
import type { CreeveyStoryParams } from 'creevey';
type Stories = Record<string, CreeveyStoryParams>;
type StringifiedTests = Record<string, string>;
type StringifiedCreeveyParams = Record<keyof CreeveyStoryParams, string>;
type ExtractedCreeveyParams = Partial<Omit<StringifiedCreeveyParams, 'tests'> & { tests: StringifiedTests }>;
type ExtractedStories = Record<string, ExtractedCreeveyParams>;
type GlobalVariables = Record<string, unknown>;
let vars = {};
/**
* Remove variable declaration from code by its name
*/
const removeVariable = (api: API, c: Collection<any>, name: string): void => {

@@ -19,23 +24,66 @@ const j = api.jscodeshift;

const processVariable = (api: API, c: Collection<any>, name: string): void => {
/**
* Extract properties from a variable and store them globally
*/
const processVariable = (api: API, c: Collection<any>, name: string, vars: GlobalVariables): void => {
const properties = getPropertiesFromVarible(api, c, name);
if (properties.length) {
vars[name] = propertiesToStringsObj(api, properties, c);
vars[name] = stringifyTests(api, properties, c, vars);
}
};
const propertiesToStringsObj = (api: API, properties: Collection<any>, collection: Collection<any>): any => {
/**
* Convert tests defined as object properties to strings.
*
* E.g., properties of
* {
* testA: () => { void 0 },
* testA: function(){ void 0 },
* ['testC']() { void 0 },
* ...varX
* }
*
* results in
*
* {
* testA: 'void 0',
* testB: 'void 0',
* testC: 'void 0',
* varX: 'varX'
* }
*/
const stringifyTests = (api: API, properties: ObjectExpression['properties'], collection: Collection<any>, globalVars: GlobalVariables): StringifiedTests => {
const j = api.jscodeshift;
const result = {};
const result: StringifiedTests = {};
properties.forEach((p) => {
if (p.type === 'ObjectMethod') {
const testName = p.key.name || p.key.value;
if (p.type === 'ObjectMethod' || p.type === 'ObjectProperty') {
// { a() {} } or { a: ... }
let testName = '';
let testSource: string | string[] = '';
if (p.key.type === "Identifier") {
// { a() {} } or { a: ... }
testName = p.key.name;
} else if (p.key.type === "StringLiteral") {
// { ['a']() {} } or { ['a']: ... }
testName = p.key.value;
}
const testSource = j(p.body.body).toSource();
result[testName] = Array.isArray(testSource) ? testSource.join(' ') : testSource;
} else if (p.type === 'SpreadElement') {
if (testName) {
if (p.type === "ObjectMethod") {
// { a() {} }
testSource = j(p.body.body).toSource();
} else if ( p.value.type === "ArrowFunctionExpression" || p.value.type === "FunctionExpression") {
// { a: () => {} } or { a: function() {} }
testSource = p.value.body.type === "BlockStatement" ? j(p.value.body.body).toSource() : j(p.value.body).toSource();
}
}
if (testSource) {
result[testName] = Array.isArray(testSource) ? testSource.join(' ') : testSource;
}
} else if (p.type === 'SpreadElement' && p.argument.type === "Identifier" ) {
// { ...varA }
const n = p.argument.name;
result[n] = n;
processVariable(api, collection, n);
processVariable(api, collection, n, globalVars);
}

@@ -47,17 +95,25 @@ });

const getPropertiesFromVarible = (api: API, c: Collection<any>, name: string): any[] => {
/**
* Extract properties of object defined as variable
*/
const getPropertiesFromVarible = (api: API, c: Collection<any>, name: string): ObjectExpression['properties'] => {
const j = api.jscodeshift;
// const name = ...
const vs = c.find(j.VariableDeclarator, (node) => node.id.name === name);
if (vs.size() === 1) {
const v = vs.get(0).node;
if (v.init.type !== 'ObjectExpression') {
console.log(`VAR (${name}) IS NOT AN OBJECT!`, v.init);
} else {
return v.init.properties;
const v: VariableDeclarator = vs.get(0).node;
if (v.init) {
if (v.init.type !== 'ObjectExpression') {
// const name = ??
console.log(`The type of the "${name}" is not supported. Expected an ObjectExpression, but got ${'ObjectExpression'}.`, v.init);
} else {
// const name = {...}
return v.init.properties;
}
}
} else {
console.log(`NUMBER OF VARS (${name}) IS UNEXPECTED (${vs.size()})!`);
// either found no vars or too many of them
console.log(`Can't get the definition: "${name}". Found: ${vs.size()}.`);
}

@@ -68,9 +124,14 @@

const extractTests = (api: API, collection: Collection<any>, vars: any): { kind: string; stories: Stories } => {
/**
* Extract creevey tests from stories to separate files
*/
const extractTests = (api: API, collection: Collection<any>): { kind: string; stories: ExtractedStories, vars: GlobalVariables } => {
const j = api.jscodeshift;
let kind = '';
let kindName = '';
let kindTests = {};
const stories: Stories = {};
const stories: ExtractedStories = {};
const vars: GlobalVariables = {};
// extract kind's title and creevey params from default export
collection

@@ -82,37 +143,48 @@ .find(j.ExportDefaultDeclaration)

if (node.key.name === 'title') {
kind = node.value.value;
}
if (node.key.name === 'creevey') {
let testsIndex = -1;
node.value.properties.forEach((p, i) => {
if (p.key.name === 'tests') {
testsIndex = i;
if (node.key.type === "Identifier") {
if (node.key.name === 'title' && node.value.type === 'StringLiteral') {
// export default { title: '...' }
kindName = node.value.value;
}
if (node.key.name === 'creevey' && node.value.type === "ObjectExpression") {
// export default { creevey: { ... } }
let testsIndex = -1;
node.value.properties.forEach((p, i) => {
if (p.type === "ObjectProperty" && p.key.type === "Identifier" && p.key.name === 'tests') {
// export default { creevey: { tests: { ... } } }
testsIndex = i;
if (p.value.type === 'Identifier') {
const variableName = p.value.name;
kindTests = {
[variableName]: variableName,
};
processVariable(api, collection, variableName);
} else if (p.value.type === 'ObjectExpression') {
kindTests = propertiesToStringsObj(api, p.value.properties, collection);
} else {
console.log(kind + ': TESTS IS SOMETHING UNKNOWN: ', p.value.type);
if (p.value.type === 'Identifier') {
// export default { creevey: { tests: variableName } }
const variableName = p.value.name;
kindTests = {
[variableName]: variableName,
};
processVariable(api, collection, variableName, vars);
} else if (p.value.type === 'ObjectExpression') {
// export default { creevey: { tests: {...} } }
kindTests = stringifyTests(api, p.value.properties, collection, vars);
} else {
console.log(`${kindName}: tests are not an ObjectExpression (${p.value.type}`);
}
}
});
if (testsIndex > -1) {
// remove tests from kind params
// but remain the other creevey params
node.value.properties.splice(testsIndex, 1);
}
});
if (testsIndex > -1) {
node.value.properties.splice(testsIndex, 1);
}
}
});
if (kind) {
// find named exports and init stories hash
if (kindName) {
collection
.find(j.ExportNamedDeclaration)
.find(j.VariableDeclarator)
.find(j.Identifier)
.forEach((path) => {
stories[path.node.id.name] = null;
stories[path.node.name] = {};
});

@@ -124,38 +196,50 @@ }

Object.keys(stories).forEach((story) => {
stories[story] = stories[story] || {};
const creeveyObj = code
// find assignment of story parameters: Story.parameters = ...
.find(j.ExpressionStatement, (node) =>
node.expression.left && node.expression.left.object ? node.expression.left.object.name === story : false,
)
// find creevey parameters: Story.parameters = { creevey: ... }
.find(j.ObjectProperty, (node) => node.key.name === 'creevey')
.forEach((path) => {
let hasTests = false;
path.node.value.properties.forEach((p) => {
const paramName = p.key.name;
if (paramName === 'tests') {
let props = [];
hasTests = true;
if (p.value.type === 'Identifier') {
const variableName = p.value.name;
stories[story].tests = {
[variableName]: variableName,
};
processVariable(api, collection, variableName);
} else if (p.value.type === 'ObjectExpression') {
props = p.value.properties;
} else {
hasTests = false;
console.log(kind + ': TESTS IS SOMETHING UNKNOWN: ', p.value.type);
}
if (path.node.value.type === "ObjectExpression") {
// Story.parameters = { creevey: { ... } }
path.node.value.properties.forEach((p) => {
if (p.type === 'ObjectProperty' && p.key.type === "Identifier") {
const paramName = p.key.name as keyof ExtractedCreeveyParams;
if (paramName === 'tests') {
// Story.parameters = { creevey: { tests: ... } }
let props: ObjectExpression['properties'] = [];
hasTests = true;
if (props.length) {
stories[story].tests = propertiesToStringsObj(api, props, collection);
if (p.value.type === 'Identifier') {
// Story.parameters = { creevey: { tests: variableName } }
const variableName = p.value.name;
stories[story].tests = {
[variableName]: variableName,
};
processVariable(api, collection, variableName, vars);
} else if (p.value.type === 'ObjectExpression') {
// Story.parameters = { creevey: { tests: {...} } }
props = p.value.properties;
} else {
hasTests = false;
console.log(`${kindName}.${story}: tests are not an ObjectExpression or a variable (${p.value.type}.`);
}
if (props.length) {
stories[story].tests = stringifyTests(api, props, collection, vars);
}
} else {
// Story.parameters = { creevey: { paramName: ... } }
stories[story][paramName] = j(p.value).toSource();
}
}
} else {
stories[story][paramName] = j(p.value).toSource();
}
});
});
}
if (hasTests) {
// remove extracted creevey parameters from story
path.replace();

@@ -165,2 +249,4 @@ }

// if a story doesn't have any more parameters
// then remove the whole parameters assignment
if (

@@ -184,2 +270,5 @@ creeveyObj.size() &&

// if there are some kind-level tests
// add them to every story as a variable
// to unwrap later
if (Object.keys(kindTests).length) {

@@ -193,2 +282,4 @@ vars['kindTests'] = kindTests;

// if there is no tests in extracted creevey params
// then ignore these params and leave them in a story file
Object.keys(stories).forEach((key) => {

@@ -200,2 +291,4 @@ if (!stories[key].tests) {

// remove variables that used to contain tests
Object.keys(vars).forEach((name) => {

@@ -205,15 +298,19 @@ removeVariable(api, collection, name);

return { kind, stories };
return { kind: kindName, stories, vars };
};
/**
* Generate a file with extracted tests
*/
const createTestFile = (
filePath: string,
kind: string,
stories: Stories,
vars: any,
{ testsPath = '../__creevey__/', prettier: usePrettier = true }: TransformOptions,
stories: ExtractedStories,
vars: GlobalVariables,
{ testsPath = '../__creevey__/', prettier = true }: TransformOptions,
): void => {
const mainTemplate = (kind, stories, vars) => `
const mainTemplate = (kind: string, stories: ExtractedStories, vars: GlobalVariables) => `
import { story, kind, test } from 'creevey';
${/* if there are variables containing tests, defined them at the top */''}
${Object.keys(vars).reduce(

@@ -223,3 +320,3 @@ (r, v) => `

const ${v} = () => {
${generateTests(vars[v])}
${generateTests(vars[v] as StringifiedTests)}
}

@@ -235,7 +332,7 @@ `,

const generateStories = (stories, vars) => {
const storyTemplate = (story, params, vars) => {
const generateStories = (stories: ExtractedStories, vars: GlobalVariables) => {
const storyTemplate = (story: string, params: ExtractedCreeveyParams, vars: GlobalVariables) => {
const { tests, ...rest } = params;
const restParams = Object.keys(rest);
const storyParams = restParams.length ? `{ ${restParams.map((key) => `${key}: ${rest[key]}`).join(', ')} }` : ``;
const storyParams = restParams.length ? `{ ${restParams.map((key) => `${key}: ${rest[key as keyof typeof rest]}`).join(', ')} }` : ``;
return `

@@ -245,3 +342,3 @@ story("${story}", (${storyParams ? `{ setStoryParameters }` : ``}) => {

${generateTests(tests, vars)}
${tests ? generateTests(tests, vars) : ``}
});

@@ -252,5 +349,5 @@ `;

return Object.keys(stories).reduce(
(result, story) => `
(result, storyName) => `
${result}
${storyTemplate(story, stories[story], vars)}
${storyTemplate(storyName, stories[storyName], vars)}
`,

@@ -261,4 +358,7 @@ ``,

const generateTests = (tests, vars?) => {
const testsTemplate = (test, fn, vars) => {
const generateTests = (tests: StringifiedTests, vars?: GlobalVariables): string => {
if (!tests) {
return '';
}
const testsTemplate = (test: string, fn: string, vars?: GlobalVariables) => {
if (vars && vars[test]) {

@@ -278,5 +378,5 @@ return `

return Object.keys(tests).reduce(
(result, test) => `
(result, testName) => `
${result}
${testsTemplate(test, tests[test], vars)}
${testsTemplate(testName, tests[testName], vars)}
`,

@@ -287,10 +387,4 @@ ``,

const fileText = mainTemplate(kind, stories, vars);
const file = mainTemplate(kind, stories, vars);
const file = usePrettier
? prettier.format(fileText, {
parser: 'typescript',
})
: fileText;
const dir = path.isAbsolute(testsPath)

@@ -306,2 +400,4 @@ ? path.normalize(testsPath + '/')

// handle possible stories hierarchy by taking only the last kind's subtitle
// "KindTitle/Subtitle_1/Subtitle_2" => "Subtitle_2"
const testFilePath = path.join(dir + kind.split('/').pop() + '.creevey' + ext);

@@ -312,2 +408,6 @@

writeFileSync(testFilePath, file);
if (prettier) {
exec(`prettier --write ${testFilePath}`);
}
};

@@ -329,9 +429,6 @@

export default function transform(file: FileInfo, api: API, options: TransformOptions) {
const { testsPath } = options;
const j = api.jscodeshift;
const result = j(file.source);
vars = {};
const { kind, stories } = extractTests(api, result, vars);
const { kind, stories, vars } = extractTests(api, result);

@@ -342,3 +439,6 @@ if (!Object.keys(stories).length) {

createTestFile(file.path, kind, stories, vars, options);
if (file.path) {
// file.path is undefined in tests, so prevent the error
createTestFile(file.path, kind, stories, vars, options);
}

@@ -345,0 +445,0 @@ return result.toSource();

{
"name": "react-ui-codemod",
"version": "1.3.0-beta.5",
"version": "1.3.0-beta.6",
"main": "index.js",

@@ -11,3 +11,3 @@ "license": "MIT",

"ast-types": "0.13.2",
"jscodeshift": "^0.7.0",
"jscodeshift": "^0.11.0",
"prettier": "^2"

@@ -14,0 +14,0 @@ },

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc