Skip to content

Commit 2e8c47a

Browse files
os-zhuangclaude
andcommitted
feat(lint): guard examples against bare metadata literals (#2035)
Adds an AST-only no-restricted-syntax rule that flags exported consts in examples/** annotated with a spec domain type (simple `Page` or qualified `UI.Page`) instead of being wrapped in the defineX factory. Turns the authoring convention into an invariant so the "pick by history" pattern cannot creep back into the reference corpus AI learns from — it already caught 15 unmigrated authoring files in this change. No type info required. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a0293ae commit 2e8c47a

1 file changed

Lines changed: 41 additions & 0 deletions

File tree

eslint.config.mjs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ const SUBPATH_RULE_MESSAGE =
3131
're-exports were removed because Node ESM cannot tree-shake them — see ' +
3232
'packages/spec/src/index.ts.';
3333

34+
// issue #2035 — the 16 writable domains that now have a `defineX` factory. In
35+
// example/app metadata files these must be authored through the factory, never a
36+
// bare `: DomainType` / `: DomainTypeInput` literal: the factory validates at
37+
// `.parse()` time and is a *value* import that fails loudly on a broken import
38+
// instead of silently degrading to `any` (the #2023 failure mode).
39+
const DOMAIN_TYPES = [
40+
'Datasource', 'Connector', 'Policy', 'SharingRule', 'Role', 'PermissionSet',
41+
'EmailTemplateDefinition', 'Report', 'Webhook', 'ObjectExtension', 'Cube',
42+
'Mapping', 'Theme', 'TranslationBundle', 'Page', 'Action',
43+
].flatMap((t) => [t, t + 'Input']).join('|');
44+
45+
const DOMAIN_RULE_MESSAGE =
46+
'Author this metadata through its defineX factory (e.g. `definePage({ ... })`) ' +
47+
'instead of a bare `: Type` literal. The factory validates at parse time and a ' +
48+
'broken value import fails loudly instead of degrading to `any` — see issue #2035.';
49+
3450
export default [
3551
{
3652
files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'],
@@ -62,4 +78,29 @@ export default [
6278
}],
6379
},
6480
},
81+
// issue #2035 — authoring-entry guard. Flags exported consts in example
82+
// metadata files that are annotated with a spec domain type (simple `Page`
83+
// or qualified `UI.Page`) instead of being wrapped in the `defineX` factory.
84+
// AST-only (no type info): matches the declaration shape, not local vars or
85+
// function params. Scoped to examples — the reference corpus AI learns from.
86+
{
87+
files: ['examples/**/*.{ts,tsx,mts,cts}'],
88+
ignores: ['**/node_modules/**', '**/dist/**'],
89+
languageOptions: {
90+
parser: tsParser,
91+
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
92+
},
93+
rules: {
94+
'no-restricted-syntax': ['error',
95+
{
96+
selector: `ExportNamedDeclaration VariableDeclarator[id.typeAnnotation.typeAnnotation.typeName.name=/^(${DOMAIN_TYPES})$/]`,
97+
message: DOMAIN_RULE_MESSAGE,
98+
},
99+
{
100+
selector: `ExportNamedDeclaration VariableDeclarator[id.typeAnnotation.typeAnnotation.typeName.right.name=/^(${DOMAIN_TYPES})$/]`,
101+
message: DOMAIN_RULE_MESSAGE,
102+
},
103+
],
104+
},
105+
},
65106
];

0 commit comments

Comments
 (0)