Skip to content

Commit

Permalink
merge functionality in tools scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
vdiez committed Nov 28, 2024
1 parent bd3e776 commit 5cafd62
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 40 deletions.
9 changes: 8 additions & 1 deletion tools/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,20 @@ export async function inflateTemplateToFile(

export async function generateMetaForRule(sonarKey: string) {
const rspecFile = join(METADATA_FOLDER, `${sonarKey}.json`);
const ruleRspecMeta: rspecMeta = (await exists(rspecFile))
const rspecFileExists = await exists(rspecFile);
const ruleRspecMeta: rspecMeta = rspecFileExists
? JSON.parse(await readFile(rspecFile, 'utf-8'))
: {
title: 'Description',
tags: [],
};

if (!rspecFileExists) {
console.log(
`RSPEC metadata not found for rule ${sonarKey}. Creating dummy "generated-meta.ts"`,
);
}

if (!typeMatrix[ruleRspecMeta.type]) {
console.log(`Type not found for rule ${sonarKey}`);
}
Expand Down
89 changes: 50 additions & 39 deletions tools/new-rule.mts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
*/
import { writeFile, readFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
import { input, select } from '@inquirer/prompts';
import {
DIRNAME,
generateMetaForRule,
inflateTemplateToFile,
JAVA_TEMPLATES_FOLDER,
javaChecksPath,
Expand All @@ -30,17 +32,6 @@ import {

const header = await readFile(join(DIRNAME, 'header.ts'), 'utf8');

const ruleTemplate = join(TS_TEMPLATES_FOLDER, 'rule.template');
const ruleTestTemplate = join(TS_TEMPLATES_FOLDER, 'rule.cbtest.template');
const metaTemplate = join(TS_TEMPLATES_FOLDER, 'meta.template');

const javaMainRuleTemplate = join(JAVA_TEMPLATES_FOLDER, 'rule.main.template');
const javaTestRuleTemplate = join(JAVA_TEMPLATES_FOLDER, 'rule.test.template');
const javaTestTemplate = join(JAVA_TEMPLATES_FOLDER, 'ruletest.template');
const properties = await readFile(join(JAVA_TEMPLATES_FOLDER, 'properties'), 'utf8');

import { input, select } from '@inquirer/prompts';

const sonarKey = await input({ message: 'Enter the Sonar key for the new rule (SXXXX)' });
const eslintId = await input({ message: 'Enter the ESLint ID for the rule' });
const ruleTarget = await select({
Expand Down Expand Up @@ -79,58 +70,77 @@ const ruleFolder = join(RULES_FOLDER, sonarKey);

await mkdir(ruleFolder, { recursive: true });

// index.ts
await writeFile(join(ruleFolder, `index.ts`), `${header}\nexport { rule } from './rule';\n`);
if (implementation !== 'external') {
// index.ts
await writeFile(join(ruleFolder, `index.ts`), `${header}\nexport { rule } from './rule';\n`);
// rule.ts
await inflateTemplateToFile(
join(
TS_TEMPLATES_FOLDER,
implementation === 'original' ? 'rule.template' : 'rule.decorated.template',
),
join(ruleFolder, `rule.ts`),
{
___HEADER___: header,
___RULE_KEY___: sonarKey,
},
);

// rule.ts
await inflateTemplateToFile(ruleTemplate, join(ruleFolder, `rule.ts`), {
___HEADER___: header,
___RULE_KEY___: sonarKey,
});
// cb.test.ts
await inflateTemplateToFile(
join(TS_TEMPLATES_FOLDER, 'rule.cbtest.template'),
join(ruleFolder, `cb.test.ts`),
{
___HEADER___: header,
},
);

// cb.test.ts
await inflateTemplateToFile(ruleTestTemplate, join(ruleFolder, `cb.test.ts`), {
___HEADER___: header,
});
// empty cb.fixture.ts
await writeFile(join(ruleFolder, `cb.fixture.ts`), '');
} else {
// index.ts
await writeFile(
join(ruleFolder, `index.ts`),
`${header}\nimport { rules } from 'external-plugin';\nexport const rule = rules('rule-name');\n`,
);
}

// meta.ts
let extra = '';
if (implementation === 'decorated') {
extra = `export const externalRules = [\n { externalPlugin: 'plugin-name', externalRule: '${eslintId}' },\n];`;
} else if (implementation === 'external') {
extra = `export const externalPlugin = '${eslintId}';`;
extra = `export const externalPlugin = 'plugin-name';`;
}
await inflateTemplateToFile(metaTemplate, join(ruleFolder, `meta.ts`), {
___HEADER___: header,
___IMPLEMENTATION___: implementation,
___ESLINT_ID___: eslintId,
___EXTRA___: extra,
});
await inflateTemplateToFile(
join(TS_TEMPLATES_FOLDER, 'meta.template'),
join(ruleFolder, `meta.ts`),
{
___HEADER___: header,
___IMPLEMENTATION___: implementation,
___ESLINT_ID___: eslintId,
___EXTRA___: extra,
},
);

// preliminary generated-meta.ts
await inflateTemplateToFile(metaTemplate, join(ruleFolder, `generated-meta.ts`), {
___HEADER___: header,
___RULE_KEY___: sonarKey,
});

// empty cb.fixture.ts
await writeFile(join(ruleFolder, `cb.fixture.ts`), '');
await generateMetaForRule(sonarKey);

// Create rule java source from template
await inflateTemplateToFile(
ruleTarget === 'MAIN' ? javaMainRuleTemplate : javaTestRuleTemplate,
join(JAVA_TEMPLATES_FOLDER, ruleTarget === 'MAIN' ? 'rule.main.template' : 'rule.test.template'),
join(javaChecksPath('main'), `${sonarKey}.java`),
{
___JAVA_RULE_CLASS_NAME___: sonarKey,
___RULE_KEY___: sonarKey,
___PROPERTIES___: properties,
___PROPERTIES___: await readFile(join(JAVA_TEMPLATES_FOLDER, 'properties'), 'utf8'),
___HEADER___: header,
},
);

// Create rule java test from template
await inflateTemplateToFile(
javaTestTemplate,
join(JAVA_TEMPLATES_FOLDER, 'ruletest.template'),
join(javaChecksPath('test'), `${sonarKey}Test.java`),
{
___JAVA_RULE_CLASS_NAME___: sonarKey,
Expand All @@ -142,6 +152,7 @@ console.log(`
STEPS
1. If your rule accepts parameters, please add the JSON Schema to "sonar-plugin/javascript-checks/src/main/resources/org/sonar/l10n/javascript/rules/javascript/schemas"
2. After RSPEC for the new rule has been generated, run 'npm run generate-meta'
`);

if (implementation === 'decorated') {
Expand Down
41 changes: 41 additions & 0 deletions tools/templates/ts/rule.decorated.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
___HEADER___
// https://sonarsource.github.io/rspec/#/rspec/___RULE_KEY___/javascript

import { Rule } from 'eslint';
import { rules } from 'external-plugin';
import {
generateMeta,
interceptReport,
mergeRules,
} from '../../../packages/jsts/src/rules/helpers/index.js';
import { meta } from '../../../packages/jsts/src/rules/S131/meta.js';

// you can return the decoratedRule
export const rule: Rule.RuleModule = interceptReport(
rules['external-rule'],
function (context: Rule.RuleContext, descriptor: Rule.ReportDescriptor) {
const node = 'node' in descriptor && descriptor.node;
if (node) {
// ...
context.report({ ...descriptor });
}
},
);

// or return the merger of two or more rules together
export const rule: Rule.RuleModule = {
meta: generateMeta(meta as Rule.RuleMetaData, {
hasSuggestions: true,
messages: {
...rule1.meta?.messages,
...rule2.meta?.messages,
},
schema: rule1.schema,
}),
create(context: Rule.RuleContext) {
return mergeRules(
rule1.create(context),
rule1.create(context),
);
},
};

0 comments on commit 5cafd62

Please sign in to comment.