@ttt-productions/repo-guards 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"text-field-declarations.d.ts","sourceRoot":"","sources":["../../../src/modules/core-definitions/text-field-declarations.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAIL,KAAK,MAAM,EAEZ,MAAM,wBAAwB,CAAC;AAGhC,KAAK,cAAc,GAAG,aAAa,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AACH,KAAK,WAAW,GAAG;IAAE,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,CAAC;AAE3F,MAAM,WAAW,yBAAyB;IACxC,qEAAqE;IACrE,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,qFAAqF;IACrF,gBAAgB,EAAE,MAAM,CAAC;IACzB,kGAAkG;IAClG,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,8GAA8G;IAC9G,WAAW,EAAE,cAAc,CAAC;IAC5B,sGAAsG;IACtG,kBAAkB,EAAE,cAAc,CAAC;IACnC,qEAAqE;IACrE,WAAW,EAAE,aAAa,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACxD,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,WAAW,CAAC,CAAC;CAC1D;AAiCD,UAAU,IAAI;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,CAyBxD;AAED,+GAA+G;AAC/G,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,GAC5C,IAAI,EAAE,CAiCR;AAqDD,eAAO,MAAM,0BAA0B,EAAE,sBAAsB,CAAC,yBAAyB,CAkKxF,CAAC"}
1
+ {"version":3,"file":"text-field-declarations.d.ts","sourceRoot":"","sources":["../../../src/modules/core-definitions/text-field-declarations.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAIL,KAAK,MAAM,EAEZ,MAAM,wBAAwB,CAAC;AAGhC,KAAK,cAAc,GAAG,aAAa,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AACH,KAAK,WAAW,GAAG;IAAE,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,CAAC;AAE3F,MAAM,WAAW,yBAAyB;IACxC,qEAAqE;IACrE,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,qFAAqF;IACrF,gBAAgB,EAAE,MAAM,CAAC;IACzB,kGAAkG;IAClG,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,8GAA8G;IAC9G,WAAW,EAAE,cAAc,CAAC;IAC5B,sGAAsG;IACtG,kBAAkB,EAAE,cAAc,CAAC;IACnC,qEAAqE;IACrE,WAAW,EAAE,aAAa,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjE,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACxD,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,WAAW,CAAC,CAAC;CAC1D;AAiCD,UAAU,IAAI;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,CAyBxD;AAED,+GAA+G;AAC/G,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,GAC5C,IAAI,EAAE,CAiCR;AA2DD,eAAO,MAAM,0BAA0B,EAAE,sBAAsB,CAAC,yBAAyB,CAmKxF,CAAC"}
@@ -114,6 +114,11 @@ function compareReviewed(sites, reviewed, what) {
114
114
  }
115
115
  return findings;
116
116
  }
117
+ /**
118
+ * input-format-core's format for free text — a description, a message, a chapter — which holds its
119
+ * spacing as typed; every other format holds a run of spaces to one.
120
+ */
121
+ const FREE_TEXT_FORMAT = 'none';
117
122
  const textDeclarations = (exports) => Object.entries(exports).filter((e) => typeof e[1] === 'object' && e[1] !== null && 'format' in e[1]);
118
123
  const reviewedCountProblems = (value, what, problems) => reviewedProblems(value, what, problems, {
119
124
  key: 'file',
@@ -231,10 +236,10 @@ export const textFieldDeclarationsGuard = {
231
236
  },
232
237
  },
233
238
  {
234
- title: 'every declared text field trims, keeps one space for each run of spaces, and a required one refuses blank text',
239
+ title: 'every declared text field trims, one that holds words keeps one space for each run of spaces, and a required one refuses blank text',
235
240
  description: "Each text field's wire schema, built from its declaration, refuses whitespace-only text when the field requires " +
236
- 'text (min of 1 or more), and trims the text of every field that does not keep it as typed; a field that accepts ' +
237
- 'a space between words keeps one space for a run of them — so the form, the wire, and the writer agree, and a ' +
241
+ 'text (min of 1 or more), and trims the text of every field that does not keep it as typed; a field whose format ' +
242
+ "holds words (every format but free text, 'none') keeps one space for a run of them — so the form, the wire, and the writer agree, and a " +
238
243
  'name spaced twice is never stored, searched, or reserved apart from the same name spaced once.',
239
244
  async run(context) {
240
245
  const findings = [];
@@ -260,6 +265,8 @@ export const textFieldDeclarationsGuard = {
260
265
  const parsed = schema.safeParse(` ${sample} `);
261
266
  if (!parsed.success || parsed.data !== sample)
262
267
  findings.push({ file: `(core) ${name}`, detail: 'does not trim its text' });
268
+ if (declaration.format === FREE_TEXT_FORMAT)
269
+ continue;
263
270
  const word = 'a'.repeat(Math.max(min - 2, 1));
264
271
  if (!schema.safeParse(`${word} a`).success)
265
272
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"text-field-declarations.js","sourceRoot":"","sources":["../../../src/modules/core-definitions/text-field-declarations.ts"],"names":[],"mappings":"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","sourcesContent":["// Text-field declarations: every text field a person types is declared once in the core's\n// declarations module (its input format, min, and max, through input-format-core's\n// `defineInputFormat`) and every wire schema reads it through the core's field-schema builder, so\n// the trim, the non-blank rule, the cap, and the characters are the same at the form, the wire, and\n// the writer.\n\nimport ts from 'typescript';\nimport type { Finding, GuardContext } from '../../guard.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { FileView } from '../../reader/view.js';\nimport { lineOfNode } from '../../support/ast.js';\nimport {\n namesProblem,\n reviewedProblems,\n loaderProblem,\n type Loader,\n textProblem,\n} from '../../support/facts.js';\nimport { CORE_CODE, coreBuild, filesIn, isCount, topLevelDeclaration } from './shared.js';\n\ntype ReviewedCounts = ReadonlyArray<{\n file: string;\n declaration: string;\n count: number;\n why: string;\n}>;\ntype FieldSchema = { safeParse: (value: unknown) => { success: boolean; data?: unknown } };\n\nexport interface TextFieldDeclarationFacts {\n /** The core build folders that hold wire schemas (repo-relative). */\n wireFolders: readonly string[];\n /** The core build module that holds every text-field declaration (repo-relative). */\n declarationsFile: string;\n /** Export-name endings that mark a wire input declaration (`InputSchema`, `RequestSchema`, …). */\n wireInputNameSuffixes: readonly string[];\n /** Wire `z.string()` chains that trim or bound themselves, per `module#declaration`: text no person types. */\n selfBounded: ReviewedCounts;\n /** Free-text-named wire `z.string()` properties that are not typed text, per `module#declaration`. */\n undeclaredFreeText: ReviewedCounts;\n /** The declarations that keep their text as typed, each with why. */\n keepAsTyped: ReadonlyArray<{ declaration: string; why: string }>;\n /** The declarations module's exports. */\n declarations: Loader<Readonly<Record<string, unknown>>>;\n /** The core's builder of a wire schema from one declaration. */\n fieldSchema: Loader<(declaration: never) => FieldSchema>;\n}\n\nconst isZString = (node: ts.Node): node is ts.CallExpression =>\n ts.isCallExpression(node) &&\n ts.isPropertyAccessExpression(node.expression) &&\n node.expression.name.text === 'string' &&\n ts.isIdentifier(node.expression.expression) &&\n node.expression.expression.text === 'z';\n\n/** A length constant that bounds text — an id's length (`*_ID_LENGTH`) bounds an id, not text. */\nconst TEXT_LENGTH_CONSTANT = /^(MAX|MIN)_(?![A-Z0-9_]*_ID_LENGTH$)[A-Z0-9_]*LENGTH$/;\nconst FREE_TEXT_PROPERTY =\n /reason|message|text|note|comment|title|description|name|subject|caption|summary|statement|facts|letter|label|heading|body|query|content|suggestion|region|country|value|intro|points|strings|term/i;\nconst BUILT_IN_FORMAT_METHODS = new Set(['regex', 'url', 'email', 'uuid']);\n\n/** The method calls chained onto a `z.string()` call, outermost last. */\nfunction chainedCalls(root: ts.CallExpression): ts.CallExpression[] {\n const calls: ts.CallExpression[] = [];\n let node: ts.Node = root;\n while (\n ts.isPropertyAccessExpression(node.parent) &&\n node.parent.expression === node &&\n ts.isCallExpression(node.parent.parent)\n ) {\n calls.push(node.parent.parent);\n node = node.parent.parent;\n }\n return calls;\n}\n\nconst methodOf = (call: ts.CallExpression) =>\n (call.expression as ts.PropertyAccessExpression).name.text;\n\ninterface Site {\n key: string;\n line: number;\n}\n\n/** Every `z.string()` chain that trims itself or bounds itself by a text-length constant. */\nexport function selfBoundedChains(file: FileView): Site[] {\n const sites: Site[] = [];\n const visit = (node: ts.Node): void => {\n if (isZString(node)) {\n const bounded = chainedCalls(node).some((call) => {\n const method = methodOf(call);\n if (method === 'trim') return true;\n if (method !== 'min' && method !== 'max') return false;\n const [argument] = call.arguments;\n return (\n argument !== undefined &&\n ts.isIdentifier(argument) &&\n TEXT_LENGTH_CONSTANT.test(argument.text)\n );\n });\n if (bounded)\n sites.push({\n key: `${file.path}#${topLevelDeclaration(node)}`,\n line: lineOfNode(file, node),\n });\n }\n ts.forEachChild(node, visit);\n };\n visit(file.sourceFile);\n return sites;\n}\n\n/** Every free-text-named `z.string()` property of a wire input declaration that carries no built-in format. */\nexport function undeclaredFreeText(\n file: FileView,\n isWireInput: (declaration: string) => boolean,\n): Site[] {\n const sites: Site[] = [];\n const visit = (node: ts.Node): void => {\n if (isZString(node)) {\n const declaration = topLevelDeclaration(node);\n const calls = chainedCalls(node);\n const outer: ts.Node = calls.length ? calls[calls.length - 1] : node;\n const parent = outer.parent;\n let property: string | null = null;\n if (parent && ts.isPropertyAssignment(parent))\n property = parent.name.getText(file.sourceFile);\n else if (\n parent &&\n ts.isCallExpression(parent) &&\n parent.parent &&\n ts.isPropertyAssignment(parent.parent)\n ) {\n property = parent.parent.name.getText(file.sourceFile);\n }\n const builtIn = calls.some((call) => BUILT_IN_FORMAT_METHODS.has(methodOf(call)));\n if (\n property !== null &&\n isWireInput(declaration) &&\n FREE_TEXT_PROPERTY.test(property) &&\n !builtIn\n ) {\n sites.push({ key: `${file.path}#${declaration}`, line: lineOfNode(file, node) });\n }\n }\n ts.forEachChild(node, visit);\n };\n visit(file.sourceFile);\n return sites;\n}\n\n/** Sites against reviewed `module#declaration` counts: an excess site fails, a short count is stale. */\nfunction compareReviewed(\n sites: readonly Site[],\n reviewed: ReviewedCounts,\n what: string,\n): Finding[] {\n const findings: Finding[] = [];\n const byKey = new Map<string, Site[]>();\n for (const site of sites) byKey.set(site.key, [...(byKey.get(site.key) ?? []), site]);\n const allowed = new Map(reviewed.map((e) => [`${e.file}#${e.declaration}`, e.count]));\n for (const [key, found] of byKey) {\n const count = allowed.get(key) ?? 0;\n const file = key.slice(0, key.indexOf('#'));\n for (const site of found.slice(count))\n findings.push({\n file,\n line: site.line,\n detail: `${what} in ${key.slice(key.indexOf('#') + 1)}${count ? ` beyond the ${count} reviewed` : ''}`,\n });\n }\n for (const [key, count] of allowed) {\n const found = byKey.get(key)?.length ?? 0;\n if (found < count)\n findings.push({\n file: key.slice(0, key.indexOf('#')),\n detail: `stale reviewed count for ${key}: reviewed ${count}, found ${found}`,\n });\n }\n return findings;\n}\n\ntype Declaration = { format?: unknown; min?: number; keepAsTyped?: unknown };\nconst textDeclarations = (exports: Readonly<Record<string, unknown>>) =>\n Object.entries(exports).filter(\n (e): e is [string, Declaration] =>\n typeof e[1] === 'object' && e[1] !== null && 'format' in e[1],\n );\n\nconst reviewedCountProblems = (value: unknown, what: string, problems: string[]) =>\n reviewedProblems(value, what, problems, {\n key: 'file',\n id: (e) =>\n typeof e.declaration === 'string' && e.declaration.trim()\n ? `${e.file}#${e.declaration}`\n : undefined,\n idNames: 'declaration',\n extra(entry, label) {\n if (!isCount(entry.count)) problems.push(`${label}'s count must be a positive whole number`);\n },\n });\n\nexport const textFieldDeclarationsGuard: GenericGuardDefinition<TextFieldDeclarationFacts> = {\n id: 'textFieldDeclarations',\n absentWhen: \"the app's core declares no text field through input-format-core\",\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.wireFolders, 'wireFolders', problems);\n textProblem(facts?.declarationsFile, 'declarationsFile', problems);\n namesProblem(facts?.wireInputNameSuffixes, 'wireInputNameSuffixes', problems);\n reviewedCountProblems(facts?.selfBounded, 'selfBounded', problems);\n reviewedCountProblems(facts?.undeclaredFreeText, 'undeclaredFreeText', problems);\n reviewedProblems(facts?.keepAsTyped, 'keepAsTyped', problems, { key: 'declaration' });\n loaderProblem(facts?.declarations, 'declarations', problems);\n loaderProblem(facts?.fieldSchema, 'fieldSchema', problems);\n return problems;\n },\n build(facts) {\n const isWireInput = (name: string) => facts.wireInputNameSuffixes.some((s) => name.endsWith(s));\n const wireFiles = (context: GuardContext, findings: Finding[]) =>\n filesIn(context.files(CORE_CODE), facts.wireFolders, findings);\n return [\n {\n title: 'every free-text property of a core wire input is a declared text field',\n description:\n 'A wire input property that names free text (reason, message, title, …) reads its declared text field through ' +\n \"the core's field-schema builder, or carries a built-in format (.regex / .url / .email / .uuid). Wire text no \" +\n 'person types is a reviewed module#declaration count with why; a count that no longer matches fails.',\n timeoutMs: 120_000,\n run(context) {\n const findings: Finding[] = [];\n const sites = wireFiles(context, findings).flatMap((f) =>\n undeclaredFreeText(f, isWireInput),\n );\n return [\n ...findings,\n ...compareReviewed(\n sites,\n facts.undeclaredFreeText,\n 'an undeclared free-text z.string() property',\n ),\n ];\n },\n },\n {\n title: 'no core wire schema trims or bounds a text field itself',\n description:\n 'A wire z.string() chain never trims itself or bounds itself by a text-length constant: that is a second ' +\n \"declaration of the field. Read the field's declaration through the core's field-schema builder. Wire text no \" +\n 'person types (stored values, fixed strings, ids, addresses) is a reviewed module#declaration count with why.',\n timeoutMs: 120_000,\n run(context) {\n const findings: Finding[] = [];\n const sites = wireFiles(context, findings).flatMap(selfBoundedChains);\n return [\n ...findings,\n ...compareReviewed(sites, facts.selfBounded, 'a self-bounded z.string() chain'),\n ];\n },\n },\n {\n title: 'text fields are declared only in the core declarations module',\n description:\n 'defineInputFormat is called, and keepAsTyped is set, only in the one module that declares every text field, ' +\n 'so each field has one declaration. Move the declaration there and import it.',\n timeoutMs: 120_000,\n run(context) {\n const findings: Finding[] = [];\n const files = context.files(CORE_CODE);\n if (!files.some((f) => f.path === facts.declarationsFile)) {\n findings.push({\n file: facts.declarationsFile,\n detail: 'the declarations module is not in the core build',\n });\n }\n for (const file of files) {\n if (file.path === facts.declarationsFile) continue;\n for (const m of file.code.matchAll(/\\bdefineInputFormat\\s*\\(|\\bkeepAsTyped\\b/g)) {\n findings.push({\n file: file.path,\n line: file.lineOf(m.index ?? 0),\n detail: `${m[0].replace(/\\s*\\($/, '')} outside the declarations module`,\n });\n }\n }\n return findings;\n },\n },\n {\n title: 'only the reviewed text fields keep their text as typed',\n description:\n 'Every declared text field is trimmed unless it is a reviewed keep-as-typed declaration with why (a legal or ' +\n 'statutory text whose exact characters matter). A declaration that newly keeps its text fails until reviewed, ' +\n 'and a reviewed one that no longer does is stale.',\n async run(context) {\n const findings: Finding[] = [];\n coreBuild(context, findings);\n const kept = new Set(\n textDeclarations(await facts.declarations())\n .filter(([, d]) => 'keepAsTyped' in d)\n .map(([n]) => n),\n );\n const reviewed = new Set(facts.keepAsTyped.map((e) => e.declaration));\n for (const name of [...kept].sort())\n if (!reviewed.has(name))\n findings.push({\n file: `(core) ${name}`,\n detail: 'keeps its text as typed without review',\n });\n for (const name of reviewed)\n if (!kept.has(name))\n findings.push({\n file: `(core) ${name}`,\n detail:\n 'stale keep-as-typed entry: the declaration no longer keeps its text, or is gone',\n });\n return findings;\n },\n },\n {\n title:\n 'every declared text field trims, keeps one space for each run of spaces, and a required one refuses blank text',\n description:\n \"Each text field's wire schema, built from its declaration, refuses whitespace-only text when the field requires \" +\n 'text (min of 1 or more), and trims the text of every field that does not keep it as typed; a field that accepts ' +\n 'a space between words keeps one space for a run of them — so the form, the wire, and the writer agree, and a ' +\n 'name spaced twice is never stored, searched, or reserved apart from the same name spaced once.',\n async run(context) {\n const findings: Finding[] = [];\n coreBuild(context, findings);\n const declarations = textDeclarations(await facts.declarations());\n const schemaOf = await facts.fieldSchema();\n if (!declarations.length)\n findings.push({\n file: facts.declarationsFile,\n detail: 'no text-field declaration was loaded',\n });\n for (const [name, declaration] of declarations) {\n const schema = schemaOf(declaration as never);\n const min = typeof declaration.min === 'number' ? declaration.min : 0;\n if (min >= 1 && schema.safeParse(' \\n\\t ').success)\n findings.push({\n file: `(core) ${name}`,\n detail: 'accepts whitespace-only text although the field requires text',\n });\n if ('keepAsTyped' in declaration) continue;\n const sample = 'a'.repeat(Math.max(min, 1));\n const parsed = schema.safeParse(` ${sample} `);\n if (!parsed.success || parsed.data !== sample)\n findings.push({ file: `(core) ${name}`, detail: 'does not trim its text' });\n const word = 'a'.repeat(Math.max(min - 2, 1));\n if (!schema.safeParse(`${word} a`).success) continue;\n const run = schema.safeParse(`${word} a`);\n if (!run.success || run.data !== `${word} a`)\n findings.push({\n file: `(core) ${name}`,\n detail: 'keeps a run of spaces instead of one space',\n });\n }\n return findings;\n },\n },\n ];\n },\n};\n"]}
1
+ {"version":3,"file":"text-field-declarations.js","sourceRoot":"","sources":["../../../src/modules/core-definitions/text-field-declarations.ts"],"names":[],"mappings":"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","sourcesContent":["// Text-field declarations: every text field a person types is declared once in the core's\n// declarations module (its input format, min, and max, through input-format-core's\n// `defineInputFormat`) and every wire schema reads it through the core's field-schema builder, so\n// the trim, the non-blank rule, the cap, and the characters are the same at the form, the wire, and\n// the writer.\n\nimport ts from 'typescript';\nimport type { Finding, GuardContext } from '../../guard.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport type { FileView } from '../../reader/view.js';\nimport { lineOfNode } from '../../support/ast.js';\nimport {\n namesProblem,\n reviewedProblems,\n loaderProblem,\n type Loader,\n textProblem,\n} from '../../support/facts.js';\nimport { CORE_CODE, coreBuild, filesIn, isCount, topLevelDeclaration } from './shared.js';\n\ntype ReviewedCounts = ReadonlyArray<{\n file: string;\n declaration: string;\n count: number;\n why: string;\n}>;\ntype FieldSchema = { safeParse: (value: unknown) => { success: boolean; data?: unknown } };\n\nexport interface TextFieldDeclarationFacts {\n /** The core build folders that hold wire schemas (repo-relative). */\n wireFolders: readonly string[];\n /** The core build module that holds every text-field declaration (repo-relative). */\n declarationsFile: string;\n /** Export-name endings that mark a wire input declaration (`InputSchema`, `RequestSchema`, …). */\n wireInputNameSuffixes: readonly string[];\n /** Wire `z.string()` chains that trim or bound themselves, per `module#declaration`: text no person types. */\n selfBounded: ReviewedCounts;\n /** Free-text-named wire `z.string()` properties that are not typed text, per `module#declaration`. */\n undeclaredFreeText: ReviewedCounts;\n /** The declarations that keep their text as typed, each with why. */\n keepAsTyped: ReadonlyArray<{ declaration: string; why: string }>;\n /** The declarations module's exports. */\n declarations: Loader<Readonly<Record<string, unknown>>>;\n /** The core's builder of a wire schema from one declaration. */\n fieldSchema: Loader<(declaration: never) => FieldSchema>;\n}\n\nconst isZString = (node: ts.Node): node is ts.CallExpression =>\n ts.isCallExpression(node) &&\n ts.isPropertyAccessExpression(node.expression) &&\n node.expression.name.text === 'string' &&\n ts.isIdentifier(node.expression.expression) &&\n node.expression.expression.text === 'z';\n\n/** A length constant that bounds text — an id's length (`*_ID_LENGTH`) bounds an id, not text. */\nconst TEXT_LENGTH_CONSTANT = /^(MAX|MIN)_(?![A-Z0-9_]*_ID_LENGTH$)[A-Z0-9_]*LENGTH$/;\nconst FREE_TEXT_PROPERTY =\n /reason|message|text|note|comment|title|description|name|subject|caption|summary|statement|facts|letter|label|heading|body|query|content|suggestion|region|country|value|intro|points|strings|term/i;\nconst BUILT_IN_FORMAT_METHODS = new Set(['regex', 'url', 'email', 'uuid']);\n\n/** The method calls chained onto a `z.string()` call, outermost last. */\nfunction chainedCalls(root: ts.CallExpression): ts.CallExpression[] {\n const calls: ts.CallExpression[] = [];\n let node: ts.Node = root;\n while (\n ts.isPropertyAccessExpression(node.parent) &&\n node.parent.expression === node &&\n ts.isCallExpression(node.parent.parent)\n ) {\n calls.push(node.parent.parent);\n node = node.parent.parent;\n }\n return calls;\n}\n\nconst methodOf = (call: ts.CallExpression) =>\n (call.expression as ts.PropertyAccessExpression).name.text;\n\ninterface Site {\n key: string;\n line: number;\n}\n\n/** Every `z.string()` chain that trims itself or bounds itself by a text-length constant. */\nexport function selfBoundedChains(file: FileView): Site[] {\n const sites: Site[] = [];\n const visit = (node: ts.Node): void => {\n if (isZString(node)) {\n const bounded = chainedCalls(node).some((call) => {\n const method = methodOf(call);\n if (method === 'trim') return true;\n if (method !== 'min' && method !== 'max') return false;\n const [argument] = call.arguments;\n return (\n argument !== undefined &&\n ts.isIdentifier(argument) &&\n TEXT_LENGTH_CONSTANT.test(argument.text)\n );\n });\n if (bounded)\n sites.push({\n key: `${file.path}#${topLevelDeclaration(node)}`,\n line: lineOfNode(file, node),\n });\n }\n ts.forEachChild(node, visit);\n };\n visit(file.sourceFile);\n return sites;\n}\n\n/** Every free-text-named `z.string()` property of a wire input declaration that carries no built-in format. */\nexport function undeclaredFreeText(\n file: FileView,\n isWireInput: (declaration: string) => boolean,\n): Site[] {\n const sites: Site[] = [];\n const visit = (node: ts.Node): void => {\n if (isZString(node)) {\n const declaration = topLevelDeclaration(node);\n const calls = chainedCalls(node);\n const outer: ts.Node = calls.length ? calls[calls.length - 1] : node;\n const parent = outer.parent;\n let property: string | null = null;\n if (parent && ts.isPropertyAssignment(parent))\n property = parent.name.getText(file.sourceFile);\n else if (\n parent &&\n ts.isCallExpression(parent) &&\n parent.parent &&\n ts.isPropertyAssignment(parent.parent)\n ) {\n property = parent.parent.name.getText(file.sourceFile);\n }\n const builtIn = calls.some((call) => BUILT_IN_FORMAT_METHODS.has(methodOf(call)));\n if (\n property !== null &&\n isWireInput(declaration) &&\n FREE_TEXT_PROPERTY.test(property) &&\n !builtIn\n ) {\n sites.push({ key: `${file.path}#${declaration}`, line: lineOfNode(file, node) });\n }\n }\n ts.forEachChild(node, visit);\n };\n visit(file.sourceFile);\n return sites;\n}\n\n/** Sites against reviewed `module#declaration` counts: an excess site fails, a short count is stale. */\nfunction compareReviewed(\n sites: readonly Site[],\n reviewed: ReviewedCounts,\n what: string,\n): Finding[] {\n const findings: Finding[] = [];\n const byKey = new Map<string, Site[]>();\n for (const site of sites) byKey.set(site.key, [...(byKey.get(site.key) ?? []), site]);\n const allowed = new Map(reviewed.map((e) => [`${e.file}#${e.declaration}`, e.count]));\n for (const [key, found] of byKey) {\n const count = allowed.get(key) ?? 0;\n const file = key.slice(0, key.indexOf('#'));\n for (const site of found.slice(count))\n findings.push({\n file,\n line: site.line,\n detail: `${what} in ${key.slice(key.indexOf('#') + 1)}${count ? ` beyond the ${count} reviewed` : ''}`,\n });\n }\n for (const [key, count] of allowed) {\n const found = byKey.get(key)?.length ?? 0;\n if (found < count)\n findings.push({\n file: key.slice(0, key.indexOf('#')),\n detail: `stale reviewed count for ${key}: reviewed ${count}, found ${found}`,\n });\n }\n return findings;\n}\n\ntype Declaration = { format?: unknown; min?: number; keepAsTyped?: unknown };\n\n/**\n * input-format-core's format for free text — a description, a message, a chapter — which holds its\n * spacing as typed; every other format holds a run of spaces to one.\n */\nconst FREE_TEXT_FORMAT = 'none';\nconst textDeclarations = (exports: Readonly<Record<string, unknown>>) =>\n Object.entries(exports).filter(\n (e): e is [string, Declaration] =>\n typeof e[1] === 'object' && e[1] !== null && 'format' in e[1],\n );\n\nconst reviewedCountProblems = (value: unknown, what: string, problems: string[]) =>\n reviewedProblems(value, what, problems, {\n key: 'file',\n id: (e) =>\n typeof e.declaration === 'string' && e.declaration.trim()\n ? `${e.file}#${e.declaration}`\n : undefined,\n idNames: 'declaration',\n extra(entry, label) {\n if (!isCount(entry.count)) problems.push(`${label}'s count must be a positive whole number`);\n },\n });\n\nexport const textFieldDeclarationsGuard: GenericGuardDefinition<TextFieldDeclarationFacts> = {\n id: 'textFieldDeclarations',\n absentWhen: \"the app's core declares no text field through input-format-core\",\n factProblems(facts) {\n const problems: string[] = [];\n namesProblem(facts?.wireFolders, 'wireFolders', problems);\n textProblem(facts?.declarationsFile, 'declarationsFile', problems);\n namesProblem(facts?.wireInputNameSuffixes, 'wireInputNameSuffixes', problems);\n reviewedCountProblems(facts?.selfBounded, 'selfBounded', problems);\n reviewedCountProblems(facts?.undeclaredFreeText, 'undeclaredFreeText', problems);\n reviewedProblems(facts?.keepAsTyped, 'keepAsTyped', problems, { key: 'declaration' });\n loaderProblem(facts?.declarations, 'declarations', problems);\n loaderProblem(facts?.fieldSchema, 'fieldSchema', problems);\n return problems;\n },\n build(facts) {\n const isWireInput = (name: string) => facts.wireInputNameSuffixes.some((s) => name.endsWith(s));\n const wireFiles = (context: GuardContext, findings: Finding[]) =>\n filesIn(context.files(CORE_CODE), facts.wireFolders, findings);\n return [\n {\n title: 'every free-text property of a core wire input is a declared text field',\n description:\n 'A wire input property that names free text (reason, message, title, …) reads its declared text field through ' +\n \"the core's field-schema builder, or carries a built-in format (.regex / .url / .email / .uuid). Wire text no \" +\n 'person types is a reviewed module#declaration count with why; a count that no longer matches fails.',\n timeoutMs: 120_000,\n run(context) {\n const findings: Finding[] = [];\n const sites = wireFiles(context, findings).flatMap((f) =>\n undeclaredFreeText(f, isWireInput),\n );\n return [\n ...findings,\n ...compareReviewed(\n sites,\n facts.undeclaredFreeText,\n 'an undeclared free-text z.string() property',\n ),\n ];\n },\n },\n {\n title: 'no core wire schema trims or bounds a text field itself',\n description:\n 'A wire z.string() chain never trims itself or bounds itself by a text-length constant: that is a second ' +\n \"declaration of the field. Read the field's declaration through the core's field-schema builder. Wire text no \" +\n 'person types (stored values, fixed strings, ids, addresses) is a reviewed module#declaration count with why.',\n timeoutMs: 120_000,\n run(context) {\n const findings: Finding[] = [];\n const sites = wireFiles(context, findings).flatMap(selfBoundedChains);\n return [\n ...findings,\n ...compareReviewed(sites, facts.selfBounded, 'a self-bounded z.string() chain'),\n ];\n },\n },\n {\n title: 'text fields are declared only in the core declarations module',\n description:\n 'defineInputFormat is called, and keepAsTyped is set, only in the one module that declares every text field, ' +\n 'so each field has one declaration. Move the declaration there and import it.',\n timeoutMs: 120_000,\n run(context) {\n const findings: Finding[] = [];\n const files = context.files(CORE_CODE);\n if (!files.some((f) => f.path === facts.declarationsFile)) {\n findings.push({\n file: facts.declarationsFile,\n detail: 'the declarations module is not in the core build',\n });\n }\n for (const file of files) {\n if (file.path === facts.declarationsFile) continue;\n for (const m of file.code.matchAll(/\\bdefineInputFormat\\s*\\(|\\bkeepAsTyped\\b/g)) {\n findings.push({\n file: file.path,\n line: file.lineOf(m.index ?? 0),\n detail: `${m[0].replace(/\\s*\\($/, '')} outside the declarations module`,\n });\n }\n }\n return findings;\n },\n },\n {\n title: 'only the reviewed text fields keep their text as typed',\n description:\n 'Every declared text field is trimmed unless it is a reviewed keep-as-typed declaration with why (a legal or ' +\n 'statutory text whose exact characters matter). A declaration that newly keeps its text fails until reviewed, ' +\n 'and a reviewed one that no longer does is stale.',\n async run(context) {\n const findings: Finding[] = [];\n coreBuild(context, findings);\n const kept = new Set(\n textDeclarations(await facts.declarations())\n .filter(([, d]) => 'keepAsTyped' in d)\n .map(([n]) => n),\n );\n const reviewed = new Set(facts.keepAsTyped.map((e) => e.declaration));\n for (const name of [...kept].sort())\n if (!reviewed.has(name))\n findings.push({\n file: `(core) ${name}`,\n detail: 'keeps its text as typed without review',\n });\n for (const name of reviewed)\n if (!kept.has(name))\n findings.push({\n file: `(core) ${name}`,\n detail:\n 'stale keep-as-typed entry: the declaration no longer keeps its text, or is gone',\n });\n return findings;\n },\n },\n {\n title:\n 'every declared text field trims, one that holds words keeps one space for each run of spaces, and a required one refuses blank text',\n description:\n \"Each text field's wire schema, built from its declaration, refuses whitespace-only text when the field requires \" +\n 'text (min of 1 or more), and trims the text of every field that does not keep it as typed; a field whose format ' +\n \"holds words (every format but free text, 'none') keeps one space for a run of them — so the form, the wire, and the writer agree, and a \" +\n 'name spaced twice is never stored, searched, or reserved apart from the same name spaced once.',\n async run(context) {\n const findings: Finding[] = [];\n coreBuild(context, findings);\n const declarations = textDeclarations(await facts.declarations());\n const schemaOf = await facts.fieldSchema();\n if (!declarations.length)\n findings.push({\n file: facts.declarationsFile,\n detail: 'no text-field declaration was loaded',\n });\n for (const [name, declaration] of declarations) {\n const schema = schemaOf(declaration as never);\n const min = typeof declaration.min === 'number' ? declaration.min : 0;\n if (min >= 1 && schema.safeParse(' \\n\\t ').success)\n findings.push({\n file: `(core) ${name}`,\n detail: 'accepts whitespace-only text although the field requires text',\n });\n if ('keepAsTyped' in declaration) continue;\n const sample = 'a'.repeat(Math.max(min, 1));\n const parsed = schema.safeParse(` ${sample} `);\n if (!parsed.success || parsed.data !== sample)\n findings.push({ file: `(core) ${name}`, detail: 'does not trim its text' });\n if (declaration.format === FREE_TEXT_FORMAT) continue;\n const word = 'a'.repeat(Math.max(min - 2, 1));\n if (!schema.safeParse(`${word} a`).success) continue;\n const run = schema.safeParse(`${word} a`);\n if (!run.success || run.data !== `${word} a`)\n findings.push({\n file: `(core) ${name}`,\n detail: 'keeps a run of spaces instead of one space',\n });\n }\n return findings;\n },\n },\n ];\n },\n};\n"]}
@@ -3,12 +3,14 @@ import { type ApplicationDataFacts } from './application-data.js';
3
3
  import { type EntryPurityFacts } from './entry-purity.js';
4
4
  import { type ImportPathFacts } from './guard.js';
5
5
  import { type PackageEntryFacts, type PackageEntryImportFacts } from './package-entries.js';
6
+ import { type PackageWildcardImportFacts } from './package-wildcard-imports.js';
6
7
  import { type PackageImportFacts } from './package-imports.js';
7
8
  /** The facts each guard in this area needs, by its id. */
8
9
  export interface ImportPathGuardFacts {
9
10
  importPaths: ImportPathFacts;
10
11
  packageEntries: PackageEntryFacts;
11
12
  packageEntryImports: PackageEntryImportFacts;
13
+ packageWildcardImports: PackageWildcardImportFacts;
12
14
  packageImports: PackageImportFacts;
13
15
  packageImportCycles: PackageImportFacts;
14
16
  entryPurity: EntryPurityFacts;
@@ -1 +1 @@
1
- {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../../src/modules/import-paths/guards.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAA+B,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAE9B,0DAA0D;AAC1D,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,eAAe,CAAC;IAC7B,cAAc,EAAE,iBAAiB,CAAC;IAClC,mBAAmB,EAAE,uBAAuB,CAAC;IAC7C,cAAc,EAAE,kBAAkB,CAAC;IACnC,mBAAmB,EAAE,kBAAkB,CAAC;IACxC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,sBAAsB,EAAE,oBAAoB,CAAC;CAC9C;AAED,eAAO,MAAM,kBAAkB,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAQ1B,CAAC"}
1
+ {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../../src/modules/import-paths/guards.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAA+B,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAE9B,0DAA0D;AAC1D,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,eAAe,CAAC;IAC7B,cAAc,EAAE,iBAAiB,CAAC;IAClC,mBAAmB,EAAE,uBAAuB,CAAC;IAC7C,sBAAsB,EAAE,0BAA0B,CAAC;IACnD,cAAc,EAAE,kBAAkB,CAAC;IACnC,mBAAmB,EAAE,kBAAkB,CAAC;IACxC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,sBAAsB,EAAE,oBAAoB,CAAC;CAC9C;AAED,eAAO,MAAM,kBAAkB,EAAE,aAAa,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAS1B,CAAC"}
@@ -3,11 +3,13 @@ import { applicationDataImportsGuard } from './application-data.js';
3
3
  import { entryPurityGuard } from './entry-purity.js';
4
4
  import { importPathGuard } from './guard.js';
5
5
  import { packageEntriesGuard, packageEntryImportsGuard, } from './package-entries.js';
6
+ import { packageWildcardImportsGuard, } from './package-wildcard-imports.js';
6
7
  import { packageImportCyclesGuard, packageImportsGuard, } from './package-imports.js';
7
8
  export const IMPORT_PATH_GUARDS = Object.freeze([
8
9
  importPathGuard,
9
10
  packageEntriesGuard,
10
11
  packageEntryImportsGuard,
12
+ packageWildcardImportsGuard,
11
13
  packageImportsGuard,
12
14
  packageImportCyclesGuard,
13
15
  entryPurityGuard,
@@ -1 +1 @@
1
- {"version":3,"file":"guards.js","sourceRoot":"","sources":["../../../src/modules/import-paths/guards.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAGvE,OAAO,EAAE,2BAA2B,EAA6B,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAyB,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAwB,MAAM,YAAY,CAAC;AACnE,OAAO,EACL,mBAAmB,EACnB,wBAAwB,GAGzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,wBAAwB,EACxB,mBAAmB,GAEpB,MAAM,sBAAsB,CAAC;AAa9B,MAAM,CAAC,MAAM,kBAAkB,GAAiD,MAAM,CAAC,MAAM,CAAC;IAC5F,eAAe;IACf,mBAAmB;IACnB,wBAAwB;IACxB,mBAAmB;IACnB,wBAAwB;IACxB,gBAAgB;IAChB,2BAA2B;CACoB,CAAC,CAAC","sourcesContent":["// The import-path guard: every definition has exactly one import path.\n\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport { applicationDataImportsGuard, type ApplicationDataFacts } from './application-data.js';\nimport { entryPurityGuard, type EntryPurityFacts } from './entry-purity.js';\nimport { importPathGuard, type ImportPathFacts } from './guard.js';\nimport {\n packageEntriesGuard,\n packageEntryImportsGuard,\n type PackageEntryFacts,\n type PackageEntryImportFacts,\n} from './package-entries.js';\nimport {\n packageImportCyclesGuard,\n packageImportsGuard,\n type PackageImportFacts,\n} from './package-imports.js';\n\n/** The facts each guard in this area needs, by its id. */\nexport interface ImportPathGuardFacts {\n importPaths: ImportPathFacts;\n packageEntries: PackageEntryFacts;\n packageEntryImports: PackageEntryImportFacts;\n packageImports: PackageImportFacts;\n packageImportCycles: PackageImportFacts;\n entryPurity: EntryPurityFacts;\n applicationDataImports: ApplicationDataFacts;\n}\n\nexport const IMPORT_PATH_GUARDS: ReadonlyArray<GenericGuardDefinition<never>> = Object.freeze([\n importPathGuard,\n packageEntriesGuard,\n packageEntryImportsGuard,\n packageImportsGuard,\n packageImportCyclesGuard,\n entryPurityGuard,\n applicationDataImportsGuard,\n] as ReadonlyArray<GenericGuardDefinition<never>>);\n"]}
1
+ {"version":3,"file":"guards.js","sourceRoot":"","sources":["../../../src/modules/import-paths/guards.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAGvE,OAAO,EAAE,2BAA2B,EAA6B,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAyB,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAwB,MAAM,YAAY,CAAC;AACnE,OAAO,EACL,mBAAmB,EACnB,wBAAwB,GAGzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,2BAA2B,GAE5B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,wBAAwB,EACxB,mBAAmB,GAEpB,MAAM,sBAAsB,CAAC;AAc9B,MAAM,CAAC,MAAM,kBAAkB,GAAiD,MAAM,CAAC,MAAM,CAAC;IAC5F,eAAe;IACf,mBAAmB;IACnB,wBAAwB;IACxB,2BAA2B;IAC3B,mBAAmB;IACnB,wBAAwB;IACxB,gBAAgB;IAChB,2BAA2B;CACoB,CAAC,CAAC","sourcesContent":["// The import-path guard: every definition has exactly one import path.\n\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport { applicationDataImportsGuard, type ApplicationDataFacts } from './application-data.js';\nimport { entryPurityGuard, type EntryPurityFacts } from './entry-purity.js';\nimport { importPathGuard, type ImportPathFacts } from './guard.js';\nimport {\n packageEntriesGuard,\n packageEntryImportsGuard,\n type PackageEntryFacts,\n type PackageEntryImportFacts,\n} from './package-entries.js';\nimport {\n packageWildcardImportsGuard,\n type PackageWildcardImportFacts,\n} from './package-wildcard-imports.js';\nimport {\n packageImportCyclesGuard,\n packageImportsGuard,\n type PackageImportFacts,\n} from './package-imports.js';\n\n/** The facts each guard in this area needs, by its id. */\nexport interface ImportPathGuardFacts {\n importPaths: ImportPathFacts;\n packageEntries: PackageEntryFacts;\n packageEntryImports: PackageEntryImportFacts;\n packageWildcardImports: PackageWildcardImportFacts;\n packageImports: PackageImportFacts;\n packageImportCycles: PackageImportFacts;\n entryPurity: EntryPurityFacts;\n applicationDataImports: ApplicationDataFacts;\n}\n\nexport const IMPORT_PATH_GUARDS: ReadonlyArray<GenericGuardDefinition<never>> = Object.freeze([\n importPathGuard,\n packageEntriesGuard,\n packageEntryImportsGuard,\n packageWildcardImportsGuard,\n packageImportsGuard,\n packageImportCyclesGuard,\n entryPurityGuard,\n applicationDataImportsGuard,\n] as ReadonlyArray<GenericGuardDefinition<never>>);\n"]}
@@ -0,0 +1,7 @@
1
+ import type { GenericGuardDefinition } from '../../node/suite/config.js';
2
+ export interface PackageWildcardImportFacts {
3
+ /** The npm scope whose packages the rule holds (`@ttt-productions`). */
4
+ scope: string;
5
+ }
6
+ export declare const packageWildcardImportsGuard: GenericGuardDefinition<PackageWildcardImportFacts>;
7
+ //# sourceMappingURL=package-wildcard-imports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-wildcard-imports.d.ts","sourceRoot":"","sources":["../../../src/modules/import-paths/package-wildcard-imports.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAGzE,MAAM,WAAW,0BAA0B;IACzC,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;CACf;AASD,eAAO,MAAM,2BAA2B,EAAE,sBAAsB,CAAC,0BAA0B,CA6C1F,CAAC"}
@@ -0,0 +1,56 @@
1
+ // No wildcard import of a scoped package. A consumer that takes a package's whole module (`import *
2
+ // as X`, `export * as X from`, `export * from`) names no definition, so nothing traces a name it uses
3
+ // to the entry that owns it, and a test's mock of one name stands in for every other name the
4
+ // wildcard reaches. Each name is imported by name instead.
5
+ import { textProblem } from '../../support/facts.js';
6
+ const SCOPE = {
7
+ roots: ['app', 'functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],
8
+ kinds: ['code'],
9
+ tests: true,
10
+ optionalRoots: ['functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],
11
+ };
12
+ export const packageWildcardImportsGuard = {
13
+ id: 'packageWildcardImports',
14
+ absentWhen: false,
15
+ factProblems(facts) {
16
+ const problems = [];
17
+ textProblem(facts?.scope, 'scope', problems);
18
+ if (typeof facts?.scope === 'string' && !/^@[a-z0-9][a-z0-9._-]*$/.test(facts.scope))
19
+ problems.push('scope must be an npm scope such as @acme');
20
+ return problems;
21
+ },
22
+ build(facts) {
23
+ const prefix = `${facts.scope}/`;
24
+ return [
25
+ {
26
+ title: 'no code takes a scoped package whole through a wildcard import',
27
+ description: "No code imports or re-exports a scoped package's whole module (`import * as X`, `export * as X from`, " +
28
+ '`export * from`): a wildcard names no definition, so no name it uses is traced to the entry that owns it, and a ' +
29
+ "test's mock of one name stands in for the rest. Import each name the code uses, by name.",
30
+ run(context) {
31
+ const findings = [];
32
+ for (const file of context.files(SCOPE)) {
33
+ for (const imported of file.imports) {
34
+ if (imported.kind !== 'import' && imported.kind !== 'export')
35
+ continue;
36
+ if (!imported.star || !imported.spec.startsWith(prefix))
37
+ continue;
38
+ const form = imported.kind === 'import'
39
+ ? `import * as ${imported.namespace}`
40
+ : imported.namespace
41
+ ? `export * as ${imported.namespace}`
42
+ : 'export *';
43
+ findings.push({
44
+ file: file.path,
45
+ line: file.lineOf(imported.node.getStart(file.sourceFile)),
46
+ detail: `${form} from '${imported.spec}' takes the whole module; import each name it uses`,
47
+ });
48
+ }
49
+ }
50
+ return findings.sort((a, b) => a.file === b.file ? (a.line ?? 0) - (b.line ?? 0) : a.file < b.file ? -1 : 1);
51
+ },
52
+ },
53
+ ];
54
+ },
55
+ };
56
+ //# sourceMappingURL=package-wildcard-imports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-wildcard-imports.js","sourceRoot":"","sources":["../../../src/modules/import-paths/package-wildcard-imports.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,sGAAsG;AACtG,8FAA8F;AAC9F,2DAA2D;AAK3D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAOrD,MAAM,KAAK,GAAU;IACnB,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC;IACrE,KAAK,EAAE,CAAC,MAAM,CAAC;IACf,KAAK,EAAE,IAAI;IACX,aAAa,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,CAAC;CACvE,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAuD;IAC7F,EAAE,EAAE,wBAAwB;IAC5B,UAAU,EAAE,KAAK;IACjB,YAAY,CAAC,KAAK;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC7C,IAAI,OAAO,KAAK,EAAE,KAAK,KAAK,QAAQ,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YAClF,QAAQ,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;QAC5D,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,KAAK,CAAC,KAAK;QACT,MAAM,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC;QACjC,OAAO;YACL;gBACE,KAAK,EAAE,gEAAgE;gBACvE,WAAW,EACT,wGAAwG;oBACxG,kHAAkH;oBAClH,0FAA0F;gBAC5F,GAAG,CAAC,OAAqB;oBACvB,MAAM,QAAQ,GAAc,EAAE,CAAC;oBAC/B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;wBACxC,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;4BACpC,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ;gCAAE,SAAS;4BACvE,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;gCAAE,SAAS;4BAClE,MAAM,IAAI,GACR,QAAQ,CAAC,IAAI,KAAK,QAAQ;gCACxB,CAAC,CAAC,eAAe,QAAQ,CAAC,SAAS,EAAE;gCACrC,CAAC,CAAC,QAAQ,CAAC,SAAS;oCAClB,CAAC,CAAC,eAAe,QAAQ,CAAC,SAAS,EAAE;oCACrC,CAAC,CAAC,UAAU,CAAC;4BACnB,QAAQ,CAAC,IAAI,CAAC;gCACZ,IAAI,EAAE,IAAI,CAAC,IAAI;gCACf,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gCAC1D,MAAM,EAAE,GAAG,IAAI,UAAU,QAAQ,CAAC,IAAI,oDAAoD;6BAC3F,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;oBACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC5B,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC7E,CAAC;gBACJ,CAAC;aACF;SACF,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["// No wildcard import of a scoped package. A consumer that takes a package's whole module (`import *\n// as X`, `export * as X from`, `export * from`) names no definition, so nothing traces a name it uses\n// to the entry that owns it, and a test's mock of one name stands in for every other name the\n// wildcard reaches. Each name is imported by name instead.\n\nimport type { Finding, GuardContext } from '../../guard.js';\nimport type { Scope } from '../../node/reader.js';\nimport type { GenericGuardDefinition } from '../../node/suite/config.js';\nimport { textProblem } from '../../support/facts.js';\n\nexport interface PackageWildcardImportFacts {\n /** The npm scope whose packages the rule holds (`@ttt-productions`). */\n scope: string;\n}\n\nconst SCOPE: Scope = {\n roots: ['app', 'functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],\n kinds: ['code'],\n tests: true,\n optionalRoots: ['functions', 'e2e', 'hostedE2e', 'scripts', 'workers'],\n};\n\nexport const packageWildcardImportsGuard: GenericGuardDefinition<PackageWildcardImportFacts> = {\n id: 'packageWildcardImports',\n absentWhen: false,\n factProblems(facts) {\n const problems: string[] = [];\n textProblem(facts?.scope, 'scope', problems);\n if (typeof facts?.scope === 'string' && !/^@[a-z0-9][a-z0-9._-]*$/.test(facts.scope))\n problems.push('scope must be an npm scope such as @acme');\n return problems;\n },\n build(facts) {\n const prefix = `${facts.scope}/`;\n return [\n {\n title: 'no code takes a scoped package whole through a wildcard import',\n description:\n \"No code imports or re-exports a scoped package's whole module (`import * as X`, `export * as X from`, \" +\n '`export * from`): a wildcard names no definition, so no name it uses is traced to the entry that owns it, and a ' +\n \"test's mock of one name stands in for the rest. Import each name the code uses, by name.\",\n run(context: GuardContext): Finding[] {\n const findings: Finding[] = [];\n for (const file of context.files(SCOPE)) {\n for (const imported of file.imports) {\n if (imported.kind !== 'import' && imported.kind !== 'export') continue;\n if (!imported.star || !imported.spec.startsWith(prefix)) continue;\n const form =\n imported.kind === 'import'\n ? `import * as ${imported.namespace}`\n : imported.namespace\n ? `export * as ${imported.namespace}`\n : 'export *';\n findings.push({\n file: file.path,\n line: file.lineOf(imported.node.getStart(file.sourceFile)),\n detail: `${form} from '${imported.spec}' takes the whole module; import each name it uses`,\n });\n }\n }\n return findings.sort((a, b) =>\n a.file === b.file ? (a.line ?? 0) - (b.line ?? 0) : a.file < b.file ? -1 : 1,\n );\n },\n },\n ];\n },\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/repo-guards",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ttt-productions/ttt-packages.git",