Skip to content

Commit

Permalink
chore(refactor): Refactor rule structure to prevent merge conflicts (#…
Browse files Browse the repository at this point in the history
…565)

* chore(refactor): Refactor rule structure to prevent merge conflicts

* chore(refactor): update generators to use ts & esm

* chore(refactor): fix testing and rule curation

* chore(refactor): fix readme generation

* fix(generator): fix tsx generator to use same input and output content

* chore(refactor): fix build bugs and address other misc issues
  • Loading branch information
wise-king-sullyman authored Feb 15, 2024
1 parent 88b58b0 commit 1bb4265
Show file tree
Hide file tree
Showing 29 changed files with 5,042 additions and 3,694 deletions.
File renamed without changes.
7 changes: 7 additions & 0 deletions generators/src/plop-interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface Answers {
componentName: string;
propName: string;
ruleName: string;
referencePR: string;
message?: string;
}
60 changes: 60 additions & 0 deletions generators/src/write-readme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { join } from "path";
import { outputFile } from "fs-extra";
import { camelCase } from "case-anything";
import { Answers } from "./plop-interfaces";

async function baseReadme({ referencePR, ruleName, message }: Answers) {
const camelCaseRuleName = camelCase(ruleName);
const readMePath = join(
require
.resolve("@patternfly/eslint-plugin-pf-codemods")
.replace(
/dist\/(js|esm)\/index\.js/,
`src/rules/v6/${camelCaseRuleName}`
),
`${ruleName}.md`
);

const readMeContent = `### ${ruleName} [(#${referencePR})](https://github.com/patternfly/patternfly-react/pull/${referencePR})
${message}
#### Examples
In:
\`\`\`jsx
%inputExample%
\`\`\`
Out:
\`\`\`jsx
%outputExample%
\`\`\`
`;

await outputFile(readMePath, readMeContent);
}

export async function genericReadme(answers: Answers) {
const message = "Default message";
await baseReadme({ message, ...answers });
}

export async function addEventCBReadme(answers: Answers) {
const { componentName, propName } = answers;

const message = `We've updated the \`${propName}\` prop for ${componentName} so that the \`event\` parameter is the first parameter. Handlers may require an update.`;

await baseReadme({ message, ...answers });
}

export async function swapCBReadme(answers: Answers) {
const { componentName, propName } = answers;

const message = `We've updated the \`${propName}\` prop for ${componentName} so that the \`event\` parameter is the first parameter. Handlers may require an update.`;

await baseReadme({ message, ...answers });
}
94 changes: 94 additions & 0 deletions generators/src/write-rule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { join } from "path";
import { outputFile } from "fs-extra";
import { camelCase } from "case-anything";
import { Answers } from "./plop-interfaces";

async function baseRule(ruleName: string, fileContent: string) {
const camelCaseRuleName = camelCase(ruleName);
const destination = join(
require
.resolve("@patternfly/eslint-plugin-pf-codemods")
.replace(
/dist\/(js|esm)\/index\.js/,
`src/rules/v6/${camelCaseRuleName}`
),
`${ruleName}.ts`
);

await outputFile(destination, fileContent);
}

export async function genericRule({
componentName,
propName,
ruleName,
referencePR,
message,
}: Answers) {
// the formatting for content here looks weird, but that's to preserve indentation in the written file
const content = `import { getFromPackage } from '../../helpers';
// https://github.com/patternfly/patternfly-react/pull/${referencePR}
module.exports = {
meta: { fixable: 'code' },
create: function(context: { report: (arg0: { node: any; message: string; fix(fixer: any): any; }) => void; }) {
const {imports, exports} = getFromPackage(context, '@patternfly/react-core')
const componentImports = imports.filter((specifier: { imported: { name: string; }; }) => specifier.imported.name === '${componentName}');
const componentExports = exports.filter((specifier: { imported: { name: string; }; }) => specifier.imported.name === '${componentName}');
return !componentImports.length && !componentExports.length ? {} : {
JSXOpeningElement(node: { name: { name: any; }; attributes: any[]; }) {
if (componentImports.map((imp: { local: { name: any; }; }) => imp.local.name).includes(node.name.name)) {
const attribute = node.attributes.find((attr: { name: { name: string; }; }) => attr.name?.name === '${propName}');
if (attribute) {
context.report({
node,
message: '${message}',
fix(fixer: { replaceText: (arg0: any, arg1: string) => any; }) {
return fixer.replaceText(attribute, '');
}
});
}
}
}
};
}
};
`;
baseRule(ruleName, content);
}

export async function addEventCBRule({
componentName,
propName,
ruleName,
referencePR,
}: Answers) {
const content = `const { addCallbackParam } = require("../../helpers");
// https://github.com/patternfly/patternfly-react/pull/${referencePR}
module.exports = {
meta: { fixable: "code" },
create: addCallbackParam(["${componentName}"], { ${propName}: "_event" }),
};
`;
baseRule(ruleName, content);
}

export async function swapCBRule({
componentName,
propName,
ruleName,
referencePR,
}: Answers) {
const content = `const { addCallbackParam } = require("../../helpers");
// https://github.com/patternfly/patternfly-react/pull/${referencePR}
module.exports = {
meta: { fixable: "code" },
create: addCallbackParam(["${componentName}"], { ${propName}: { defaultParamName: "_event", previousParamIndex: 1, otherMatchers: /^_?(ev\\w*|e$)/ } }),
};
`;
baseRule(ruleName, content);
}
67 changes: 67 additions & 0 deletions generators/src/write-test-single.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { join } from "path";
import { outputFile } from "fs-extra";
import { camelCase, pascalCase } from "case-anything";
import { Answers } from "./plop-interfaces";

async function baseTestSingle(
{ componentName, ruleName }: Answers,
componentUsage: string
) {
const camelCaseRuleName = camelCase(ruleName);
const pascalCaseRuleName = pascalCase(ruleName);

const ruleDir = require
.resolve("@patternfly/eslint-plugin-pf-codemods")
.replace(/dist\/(js|esm)\/index\.js/, `src/rules/v6/${camelCaseRuleName}`);

const testInputPath = join(ruleDir, `${camelCaseRuleName}Input.tsx`);
const testOutputPath = join(ruleDir, `${camelCaseRuleName}Output.tsx`);

const testInputContent = `import { ${componentName} } from "@patternfly/react-core";
export const ${pascalCaseRuleName}Input = () => ${componentUsage}`;

await outputFile(testInputPath, testInputContent);
await outputFile(testOutputPath, testInputContent);
}
export async function genericTestSingle(answers: Answers) {
const { componentName, propName } = answers;

baseTestSingle(answers, `<${componentName} ${propName} />`);
}

export async function addEventCBTestSingle(answers: Answers) {
const { componentName, propName } = answers;

baseTestSingle(
answers,
`{
function handler1(foo) {}
return (
<>
<${componentName} ${propName}={handler1} />
<${componentName} ${propName}={(foo) => handler(foo)} />
</>
);
}
`
);
}

export async function swapCBTestSingle(answers: Answers) {
const { componentName, propName } = answers;

baseTestSingle(
answers,
`{
function handler1(foo, event) {}
return (
<>
<${componentName} ${propName}={handler1} />
<${componentName} ${propName}={(foo, event) => handler(foo)} />
</>
);
}
`
);
}
50 changes: 31 additions & 19 deletions generators/write-test.js → generators/src/write-test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
const path = require("path");
const fs = require("fs");
import { join } from "path";
import { outputFile } from "fs-extra";
import { camelCase } from "case-anything";
import { Answers } from "./plop-interfaces";

function baseTest(ruleName, fileContent) {
const destination = path.join(
async function baseTest(ruleName: string, fileContent: string) {
const camelCaseRuleName = camelCase(ruleName);
const destination = join(
require
.resolve("@patternfly/eslint-plugin-pf-codemods")
.replace("index.js", ""),
"test/rules/v5",
`${ruleName}.js`
.replace(
/dist\/(js|esm)\/index\.js/,
`src/rules/v6/${camelCaseRuleName}`
),
`${ruleName}.test.ts`
);

fs.writeFileSync(destination, fileContent);
await outputFile(destination, fileContent);
}

function genericTest({ componentName, propName, ruleName, message }) {
export async function genericTest({
componentName,
propName,
ruleName,
message,
}: Answers) {
// the formatting for content here looks weird, but that's to preserve indentation in the written file
const content = `const ruleTester = require('../../ruletester');
const rule = require('../../../lib/rules/v5/${ruleName}');
import * as rule from './${ruleName}';
ruleTester.run("${ruleName}", rule, {
valid: [
Expand All @@ -27,7 +37,7 @@ ruleTester.run("${ruleName}", rule, {
invalid: [
{
code: \`import { ${componentName} } from '@patternfly/react-core'; <${componentName} ${propName} />\`,
output: \`import { ${componentName} } from '@patternfly/react-core'; <${componentName} ${propName} />\`,
output: \`import { ${componentName} } from '@patternfly/react-core'; <${componentName} />\`,
errors: [{
message: \`${message}\`,
type: "JSXOpeningElement",
Expand All @@ -39,7 +49,11 @@ ruleTester.run("${ruleName}", rule, {
baseTest(ruleName, content);
}

function addEventCBTest({ componentName, propName, ruleName }) {
export async function addEventCBTest({
componentName,
propName,
ruleName,
}: Answers) {
const content = `const { addCallbackParamTester } = require("../../testHelpers");
addCallbackParamTester('${ruleName}', '${componentName}', '${propName}')
Expand All @@ -48,17 +62,15 @@ addCallbackParamTester('${ruleName}', '${componentName}', '${propName}')
baseTest(ruleName, content);
}

function swapCBTest({ componentName, propName, ruleName }) {
export async function swapCBTest({
componentName,
propName,
ruleName,
}: Answers) {
const content = `const { swapCallbackParamTester } = require("../../testHelpers");
swapCallbackParamTester('${ruleName}', '${componentName}', '${propName}', 1)
`;
baseTest(ruleName, content);
}

module.exports = {
genericTest,
addEventCBTest,
swapCBTest,
};
9 changes: 9 additions & 0 deletions generators/tsconfig.cjs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "./dist/js"
},
"include": ["./src/**/*"],
"exclude": ["./dist"]
}
Loading

0 comments on commit 1bb4265

Please sign in to comment.