Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 6 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading