diff --git a/eslint-plugin-report-name-utils/index.mjs b/eslint-plugin-report-name-utils/index.mjs new file mode 100644 index 0000000000000..78bbc70504e0f --- /dev/null +++ b/eslint-plugin-report-name-utils/index.mjs @@ -0,0 +1,30 @@ +/** + * ESLint plugin that enforces architectural constraints on ReportNameUtils.ts. + * + * `getReportName` must remain a pure read-only function — it reads from + * pre-computed `reportAttributesDerivedValue` and must never call other + * functions. All computation belongs in `computeReportName`. + */ + +const noFunctionCallInGetReportName = { + meta: { + type: 'problem', + docs: {description: 'getReportName must be a pure read-only function. Move any computation to computeReportName instead.'}, + messages: {noFunctionCall: 'getReportName must be a pure read-only function. Move any computation to computeReportName instead.'}, + schema: [], + }, + create(context) { + return { + 'FunctionDeclaration[id.name="getReportName"] CallExpression': function (node) { + context.report({node, messageId: 'noFunctionCall'}); + }, + }; + }, +}; + +export default { + meta: {name: 'eslint-plugin-report-name-utils'}, + rules: { + 'no-function-call-in-get-report-name': noFunctionCallInGetReportName, + }, +}; diff --git a/eslint.changed.config.mjs b/eslint.changed.config.mjs index 79114b59a65eb..87417ae72a2ab 100644 --- a/eslint.changed.config.mjs +++ b/eslint.changed.config.mjs @@ -1,4 +1,5 @@ import {defineConfig} from 'eslint/config'; +import reportNameUtilsPlugin from './eslint-plugin-report-name-utils/index.mjs'; import mainConfig from './eslint.config.mjs'; const restrictedIconImportPaths = [ @@ -106,6 +107,12 @@ const config = defineConfig([ ], }, }, + + { + files: ['src/libs/ReportNameUtils.ts'], + plugins: {'report-name-utils': reportNameUtilsPlugin}, + rules: {'report-name-utils/no-function-call-in-get-report-name': 'error'}, + }, ]); export default config; diff --git a/eslint.config.mjs b/eslint.config.mjs index 361e2b63b90ae..6051af243bcde 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -14,6 +14,7 @@ import path from 'node:path'; import {fileURLToPath} from 'node:url'; import typescriptEslint from 'typescript-eslint'; import reactCompilerCompat from './eslint-plugin-react-compiler-compat/index.mjs'; +import reportNameUtilsPlugin from './eslint-plugin-report-name-utils/index.mjs'; const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); @@ -592,6 +593,12 @@ const config = defineConfig([ }, }, + { + files: ['src/libs/ReportNameUtils.ts'], + plugins: {'report-name-utils': reportNameUtilsPlugin}, + rules: {'report-name-utils/no-function-call-in-get-report-name': 'error'}, + }, + { files: ['src/**/*'], ignores: ['src/languages/**', 'src/CONST/index.ts', 'src/NAICS.ts'],