From 5cafd6264d53efc947ce46aee7e3c77c4b7c3f1f Mon Sep 17 00:00:00 2001 From: Victor Diez Date: Thu, 28 Nov 2024 18:14:07 +0100 Subject: [PATCH] merge functionality in tools scripts --- tools/helpers.ts | 9 ++- tools/new-rule.mts | 89 ++++++++++++---------- tools/templates/ts/rule.decorated.template | 41 ++++++++++ 3 files changed, 99 insertions(+), 40 deletions(-) create mode 100644 tools/templates/ts/rule.decorated.template diff --git a/tools/helpers.ts b/tools/helpers.ts index c6959d2ed0..a18d7f2bb2 100644 --- a/tools/helpers.ts +++ b/tools/helpers.ts @@ -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}`); } diff --git a/tools/new-rule.mts b/tools/new-rule.mts index 40d7ebd5c1..5ba5ee2e30 100644 --- a/tools/new-rule.mts +++ b/tools/new-rule.mts @@ -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, @@ -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({ @@ -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, @@ -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') { diff --git a/tools/templates/ts/rule.decorated.template b/tools/templates/ts/rule.decorated.template new file mode 100644 index 0000000000..fa5948112a --- /dev/null +++ b/tools/templates/ts/rule.decorated.template @@ -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), + ); + }, +};