@estiva-app/ui 0.26.0 → 0.28.0
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.
- package/dist/gates/app-checks.d.ts.map +1 -1
- package/dist/gates/{chunk-GTQZEHPC.js → chunk-I7CJLUPM.js} +13 -1
- package/dist/gates/chunk-I7CJLUPM.js.map +7 -0
- package/dist/gates/{chunk-EA33NP5B.js → chunk-XE6HC5MJ.js} +8 -12
- package/dist/gates/chunk-XE6HC5MJ.js.map +7 -0
- package/dist/gates/cli.js +1 -1
- package/dist/gates/create-app.d.ts.map +1 -1
- package/dist/gates/create-app.js +1 -1
- package/dist/gates/gate-config.d.ts +1 -1
- package/dist/gates/gate-config.d.ts.map +1 -1
- package/dist/gates/index.d.ts +2 -1
- package/dist/gates/index.d.ts.map +1 -1
- package/dist/gates/index.js +36 -6
- package/dist/gates/index.js.map +2 -2
- package/dist/gates/status.d.ts +2 -0
- package/dist/gates/status.d.ts.map +1 -1
- package/dist/gates/token-lint.d.ts +27 -1
- package/dist/gates/token-lint.d.ts.map +1 -1
- package/dist/registry/{app-DQI6WLHR.js → app-FXN4EI34.js} +2 -2
- package/dist/registry/app.d.ts.map +1 -1
- package/dist/registry/{chunk-W2B2G7OE.js → chunk-6OH3TWGF.js} +59 -1
- package/dist/registry/chunk-6OH3TWGF.js.map +7 -0
- package/dist/registry/{chunk-WMFF3MPP.js → chunk-XCBN2EKW.js} +3 -2
- package/dist/registry/{chunk-WMFF3MPP.js.map → chunk-XCBN2EKW.js.map} +2 -2
- package/dist/registry/cli.js +69 -8
- package/dist/registry/cli.js.map +3 -3
- package/dist/registry/contract.d.ts +29 -0
- package/dist/registry/contract.d.ts.map +1 -0
- package/dist/registry/gated-5CPIUQAW.js +230 -0
- package/dist/registry/gated-5CPIUQAW.js.map +7 -0
- package/dist/registry/gated.d.ts +31 -0
- package/dist/registry/gated.d.ts.map +1 -0
- package/dist/registry/index.d.ts +1 -0
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +15 -3
- package/package.json +1 -1
- package/registry.json +1 -1
- package/src/CollapsibleSection.stories.tsx +6 -0
- package/src/ListColumn.stories.tsx +6 -0
- package/src/MenuItem.stories.tsx +151 -135
- package/src/Person.stories.tsx +7 -0
- package/src/PersonTrigger.stories.tsx +7 -0
- package/src/SectionHeader.stories.tsx +5 -0
- package/src/gates/app-checks.ts +19 -2
- package/src/gates/create-app.ts +7 -11
- package/src/gates/gate-config.ts +3 -2
- package/src/gates/gates.test.ts +35 -1
- package/src/gates/index.ts +2 -1
- package/src/gates/status.ts +15 -0
- package/src/gates/token-lint.ts +47 -2
- package/src/registry/app.test.ts +14 -1
- package/src/registry/app.ts +5 -1
- package/src/registry/cli.ts +79 -7
- package/src/registry/contract.test.ts +158 -0
- package/src/registry/contract.ts +108 -0
- package/src/registry/gated.test.ts +98 -0
- package/src/registry/gated.ts +308 -0
- package/src/registry/index.ts +1 -0
- package/dist/gates/chunk-EA33NP5B.js.map +0 -7
- package/dist/gates/chunk-GTQZEHPC.js.map +0 -7
- package/dist/registry/chunk-W2B2G7OE.js.map +0 -7
- /package/dist/registry/{app-DQI6WLHR.js.map → app-FXN4EI34.js.map} +0 -0
package/dist/gates/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/gates/token-lint.ts", "../../src/gates/gate-config.ts", "../../src/gates/app-checks.ts"],
|
|
4
|
-
"sourcesContent": ["/// <reference types=\"node\" />\n/**\n * The token contract as lint settings, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 this block was pasted into every repo: `eslint.config.js` here,\n * `eslint.tokens.js` in Peek, `eslint.config.js` in Ship. Katerina ruled on 17 September that a\n * gate piece ships in the package and an app imports it, so this is that one\n * copy. Peek and Ship move onto it in UIG-32.\n *\n * Two configs, as the copies had:\n *\n * - `tokenLint` \u2014 a class Tailwind does not generate (the doubled token name,\n * `bg-bg-surface`), Tailwind's own type ramp or palette, a raw colour, an\n * opacity modifier on a token colour.\n * - `tokenValues` \u2014 UIG-28: type, corners and shadows written by hand, and an\n * inline style that sets a colour (errors); heights, spacing and border\n * widths written by hand (warnings, never blocking).\n *\n * The patterns are the same for everyone. Only the words differ: an app is told\n * to use \"the package\", the package is told to add the token to `tokens.css`.\n * Measured on 17 September: the UIG-28 block was byte for byte the same in Peek\n * and Ship, and this repository's had the same patterns with seven messages\n * worded for the package.\n *\n * Where the plugin looks: every `className`, every `cn()` / `clsx()` argument,\n * and the values of a class map whose name ends in `Styles` / `_STYLES` or\n * `Classes` / `_CLASSES`. A class map named any other way is invisible to it.\n *\n * Tests are out of `tokenValues` (ruling of 13 September): a test mounts markup\n * to test it. A kept hand-written value says why:\n * `// eslint-disable-next-line <rule> -- @estiva-escape: <reason>` (A2).\n */\nimport { createRequire } from 'node:module'\nimport { fileURLToPath } from 'node:url'\nimport type { Linter } from 'eslint'\nimport betterTailwindcss from 'eslint-plugin-better-tailwindcss'\nimport { getDefaultSelectors } from 'eslint-plugin-better-tailwindcss/defaults'\nimport { parser as typescriptParser } from 'typescript-eslint'\n\n/** Who reads the messages. */\nexport type TokenAudience = 'app' | 'package'\n\nexport interface TokenLintOptions {\n /** The Tailwind config the unknown-class rule reads, relative to where ESLint runs. */\n tailwindConfig?: string\n /** `app` (the default) names the package; `package` names `tokens.css` and the preset. */\n audience?: TokenAudience\n}\n\n/** What the token lint never reads: build output. */\nexport const TOKEN_LINT_IGNORES = ['dist/', 'storybook-static/', 'node_modules/']\n\nconst CLASS_MAP_SELECTOR = {\n kind: 'variable',\n name: '^(?:[A-Z][A-Z0-9_]*_(?:STYLES|CLASSES)|[a-z][a-zA-Z0-9]*(?:Styles|Classes))$',\n match: [{ type: 'strings' }, { type: 'objectValues' }],\n}\n\nconst WORDS = {\n app: {\n ramp: \"Tailwind's type ramp is not a token. Use the ramp from the package: text-body-2, text-caption, text-h3, text-btn-default...\",\n palette: \"Tailwind's palette is not a token. Use a colour from the package (bg-bg-surface, text-text-primary, border-border-subtle...); a colour with no token is a missing token.\",\n raw: 'A raw colour is a missing token: add it to the package (tokens.css in every theme block, and the preset), or say why not in an eslint-disable comment.',\n opacity: 'An opacity modifier on a token colour compiles to nothing. A transparent colour is a token of its own (D16): add it to the package.',\n type: '$0 is type written by hand. A token from the package sets the size, line height, letter spacing and weight together (text-body-2, text-caption, text-h5...); if none fits, add one to the package, or say why not in an eslint-disable comment.',\n corner: '$0 is a corner written by hand, and no token has it. Use a corner from the package (rounded-sm 4px, rounded-md 6px, rounded-lg 8px...), or say why not in an eslint-disable comment.',\n shadow: '$0 is a shadow written by hand. Use a shadow from the package (shadow-sm, shadow-md, shadow-focus-ring, drop-shadow-glow-success...); a shadow with no token is a missing token.',\n },\n package: {\n ramp: \"Tailwind's type ramp is not a token. Use the ramp in tailwind-preset.js: text-body-2, text-caption, text-h3, text-btn-default...\",\n palette: \"Tailwind's palette is not a token. Use a colour from tailwind-preset.js (bg-bg-surface, text-text-primary, border-border-subtle...); a colour with no token is a missing token.\",\n raw: 'A raw colour is a missing token: add it to tokens.css in every theme block and to the preset, or say why not in an eslint-disable comment.',\n opacity: 'An opacity modifier on a token colour compiles to nothing. A transparent colour is a token of its own (D16): add it to tokens.css in every theme block and to the preset.',\n type: '$0 is type written by hand. A token from tailwind-preset.js sets the size, line height, letter spacing and weight together (text-body-2, text-caption, text-h5...); if none fits, add one to the preset and to cn.ts, or say why not in an eslint-disable comment.',\n corner: '$0 is a corner written by hand, and no token has it. Use a corner from tailwind-preset.js (rounded-sm 4px, rounded-md 6px, rounded-lg 8px...), or say why not in an eslint-disable comment.',\n shadow: '$0 is a shadow written by hand. Use a shadow from tailwind-preset.js (shadow-sm, shadow-md, shadow-focus-ring, drop-shadow-glow-success...); a shadow with no token is a missing token.',\n },\n} satisfies Record<TokenAudience, Record<string, string>>\n\n/**\n * The class rules. `no-unknown-classes` reads the app's own Tailwind config, so\n * it knows exactly what the preset and the app generate.\n */\nexport function tokenLint({ tailwindConfig = 'tailwind.config.js', audience = 'app' }: TokenLintOptions = {}): Linter.Config {\n const words = WORDS[audience]\n return {\n files: ['**/*.{ts,tsx}'],\n languageOptions: {\n parser: typescriptParser,\n parserOptions: { ecmaFeatures: { jsx: true } },\n },\n plugins: { 'better-tailwindcss': betterTailwindcss },\n settings: {\n 'better-tailwindcss': {\n tailwindConfig,\n selectors: [...getDefaultSelectors(), CLASS_MAP_SELECTOR],\n },\n },\n rules: {\n 'better-tailwindcss/no-unknown-classes': 'error',\n 'better-tailwindcss/no-restricted-classes': [\n 'error',\n {\n restrict: [\n { pattern: '^(?:[a-z0-9-]+:)*text-(?:xs|sm|base|lg|xl|[2-9]xl)$', message: words.ramp },\n {\n pattern:\n '^(?:[a-z0-9-]+:)*(?:text|bg|border|ring|outline|fill|stroke|decoration|divide|placeholder|from|via|to|accent|caret|shadow)-(?:black|white|slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)(?:-[0-9]{2,3})?(?:/[0-9]{1,3})?$',\n message: words.palette,\n },\n { pattern: '^(?:[a-z0-9-]+:)*[a-z-]+-\\\\[[^\\\\]]*(?:#[0-9a-fA-F]{3}|rgba?\\\\(|hsla?\\\\(|oklch\\\\()', message: words.raw },\n {\n pattern: '^(?:[a-z0-9-]+:)*(?:text|bg|border|ring|outline|fill|stroke|decoration|divide|placeholder|from|via|to|accent|caret|shadow)-(?:bg|text|border|accent|info|warning|success|error)-[a-z-]+/[0-9]{1,3}$',\n message: words.opacity,\n },\n ],\n },\n ],\n },\n }\n}\n\n// UIG-28's patterns. The variant part reads arbitrary variants too\n// (`[&_pre]:`, `data-[state]:`, `group-hover/row:`). A colour inside `text-[\u2026]`\n// is left to the raw-colour rule of `tokenLint`.\nconst V = '^(?:(?:[^:\\\\[\\\\]\\\\s]|\\\\[[^\\\\]]*\\\\])+:)*!?'\nconst NOT_COLOUR = '(?!color:|#|rgba?\\\\(|hsla?\\\\(|oklch\\\\(|var\\\\()'\nconst HAND_WRITTEN = {\n type: `${V}(?:text-\\\\[${NOT_COLOUR}|text-\\\\[length:|leading-\\\\[|tracking-\\\\[)[^\\\\]]+\\\\](?:/\\\\S*)?$`,\n corner: `${V}rounded(?:-(?:t|r|b|l|s|e|tl|tr|br|bl|ss|se|es|ee))?-\\\\[[^\\\\]]+\\\\]$`,\n shadow: `${V}(?:shadow|drop-shadow)-\\\\[(?!color:)[^\\\\]]+\\\\]$`,\n spacing: `${V}-?(?:w|h|size|min-w|min-h|max-w|max-h|p[xytrblse]?|m[xytrblse]?|gap(?:-[xy])?|space-[xy]|inset(?:-[xy])?|top|right|bottom|left|translate-[xy]|basis|indent|scroll-[mp][xytrblse]?|border-spacing(?:-[xy])?)-\\\\[[^\\\\]]+\\\\]$`,\n width: `${V}(?:border(?:-[xytrblse])?|divide-[xy]|outline|ring|ring-offset)-\\\\[(?:length:)?-?[0-9.]+(?:px|rem|em)?\\\\]$`,\n}\n\nconst INLINE_STYLE_TOKEN_PROPERTIES =\n '/^(color|background|backgroundColor|backgroundImage|fill|stroke|fontSize|border|borderTop|borderRight|borderBottom|borderLeft|borderBlock|borderInline|borderStyle|boxShadow|textShadow)$|Color$/'\n\ninterface Preset {\n theme: { extend: { fontSize: Record<string, string | [string, unknown]>; borderRadius: Record<string, string> } }\n}\n\n/**\n * The preset, loaded the way Tailwind loads it: it imports `tailwindcss/plugin`,\n * which a plain `import` cannot resolve. Read from beside this module, so an app\n * and this repository read the one preset the package ships.\n */\nfunction loadPreset(): Preset {\n const require = createRequire(import.meta.url)\n const loadConfig = require('tailwindcss/loadConfig') as (path: string) => Preset\n return loadConfig(fileURLToPath(new URL('../../tailwind-preset.js', import.meta.url)))\n}\n\n/**\n * UIG-28: the values the tokens already name, written by hand (Katerina's\n * rulings B11, B12, C3 of 13 September and R2 of 15 September). Register it\n * after `tokenLint`, whose parser and plugin settings it borrows. Under its own\n * rule names, `token-values` and `token-spacing` (the same plugin twice, so one\n * runs as errors and one as warnings): an escape for a hand-written size then\n * cannot also silence a raw colour on the same line.\n */\nexport function tokenValues({ audience = 'app' }: Pick<TokenLintOptions, 'audience'> = {}): Linter.Config {\n const words = WORDS[audience]\n const preset = loadPreset()\n // `text-[14px]` names the token of that size, read from the preset, so the\n // message stays true when the ramp changes.\n const tokensBySize: Record<string, string[]> = {}\n for (const [name, value] of Object.entries(preset.theme.extend.fontSize)) {\n const size = Array.isArray(value) ? value[0] : value\n ;(tokensBySize[size] ??= []).push(`text-${name}`)\n }\n const cornersBySize: Record<string, string[]> = {}\n for (const [name, size] of Object.entries(preset.theme.extend.borderRadius)) (cornersBySize[size] ??= []).push(name === 'DEFAULT' ? 'rounded' : `rounded-${name}`)\n\n return {\n files: ['**/*.{ts,tsx}'],\n ignores: ['**/*.test.ts', '**/*.test.tsx'],\n plugins: { 'token-values': betterTailwindcss, 'token-spacing': betterTailwindcss },\n rules: {\n 'token-values/no-restricted-classes': [\n 'error',\n {\n restrict: [\n ...Object.entries(tokensBySize).map(([size, names]) => ({\n pattern: `${V}text-\\\\[${size.replace('.', '\\\\.')}\\\\](?:/\\\\S*)?$`,\n message: `$0 is the type ramp written by hand. Use ${names.join(' or ')}.`,\n })),\n {\n pattern: HAND_WRITTEN.type,\n message: words.type,\n },\n ...Object.entries(cornersBySize).map(([size, names]) => ({\n pattern: `${V}rounded(?:-(?:t|r|b|l|s|e|tl|tr|br|bl|ss|se|es|ee))?-\\\\[${size.replace('.', '\\\\.')}\\\\]$`,\n message: `$0 is a corner written by hand. Use ${names.join(' or ')} (with the same side, if it has one).`,\n })),\n {\n pattern: HAND_WRITTEN.corner,\n message: words.corner,\n },\n {\n pattern: HAND_WRITTEN.shadow,\n message: words.shadow,\n },\n ],\n },\n ],\n 'token-spacing/no-restricted-classes': [\n 'warn',\n {\n restrict: [\n {\n pattern: HAND_WRITTEN.spacing,\n message: '$0 is a size or a space written by hand. Use a step of the spacing scale (p-3, h-9, gap-2...) if one fits. Reported, not blocked.',\n },\n {\n pattern: HAND_WRITTEN.width,\n message: '$0 is a border or ring width written by hand. Use border, border-2, ring-1... if one fits. Reported, not blocked.',\n },\n ],\n },\n ],\n 'no-restricted-syntax': [\n 'error',\n {\n selector: `JSXAttribute[name.name=\"style\"] > JSXExpressionContainer > ObjectExpression > Property[key.name=${INLINE_STYLE_TOKEN_PROPERTIES}]`,\n message: 'An inline style that sets a colour, a font size, a border or a shadow is outside the token contract: use a token class. If the value is computed (a palette, a size from a prop), say why in an eslint-disable comment. Width, height and transforms are fine.',\n },\n ],\n },\n }\n}\n", "/// <reference types=\"node\" />\n/**\n * Where the UI Guardrails' rules apply, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 every repo wrote this itself: `eslint.gates.js` (the rules and\n * where they apply) and `eslint.gates.config.js` (the gate: those rules alone)\n * in Peek, in Ship's `web/`, and here pointed inward. The rules were always the\n * package's; these two files were copies. Now they are two functions.\n *\n * Where: every `.ts` and `.tsx` under `src`, stories included (a story is what\n * people read and copy), tests not (a test mounts markup to test it) \u2014\n * Katerina's rulings of 13 September; `.ts` since UIG-8. This package's own\n * inward set reads `.tsx` only, as it always has.\n *\n * A place that keeps something says why, on the line above:\n * `// @estiva-escape: <reason>`, or `{/* @estiva-escape: <reason> *\\/}` as a\n * JSX child. Never `eslint-disable`: the count refuses it.\n */\nimport type { ESLint, Linter } from 'eslint'\nimport betterTailwindcss from 'eslint-plugin-better-tailwindcss'\nimport { parser as typescriptParser } from 'typescript-eslint'\nimport estiva from '../eslint/index'\nimport { TOKEN_LINT_IGNORES } from './token-lint'\n\nexport interface GateConfigOptions {\n /** `app` (the default) runs `configs.recommended`; `package` runs this package's inward set. */\n audience?: 'app' | 'package'\n /**\n * Plugins whose `eslint-disable` directives appear in the source, registered\n * here with every rule off. ESLint refuses a directive for a rule it does not\n * know, so without them the gate would fail on another lint's comments.\n * The token lint's names are registered already.\n */\n quiet?: Record<string, ESLint.Plugin>\n /** Folders the gate never reads, beside build output. */\n ignores?: string[]\n}\n\n/**\n * The rules and where they apply \u2014 one config object, to add beside\n * everything else an app's `eslint.config.js` runs, so an editor shows a\n * refusal where the element is typed.\n */\nexport function gateLint({ audience = 'app' }: Pick<GateConfigOptions, 'audience'> = {}): Linter.Config {\n return {\n ...(audience === 'package' ? estiva.configs.package : estiva.configs.recommended),\n files: audience === 'package' ? ['src/**/*.tsx'] : ['src/**/*.{ts,tsx}'],\n ignores: ['**/*.test.ts', '**/*.test.tsx'],\n languageOptions: {\n parser: typescriptParser,\n parserOptions: { ecmaFeatures: { jsx: true } },\n },\n }\n}\n\n/**\n * The gate: the rules on their own \u2014 `npm run lint:rules`, CI's job `gate`, and\n * the config the editor hook lints a proposed write with. It fails on a gate\n * rule and on nothing else, so a backlog in the full lint never blocks it.\n */\nexport function gateConfig({ audience = 'app', quiet = {}, ignores = [] }: GateConfigOptions = {}): Linter.Config[] {\n return [\n { ignores: [...TOKEN_LINT_IGNORES, ...ignores] },\n {\n plugins: {\n ...quiet,\n 'better-tailwindcss': betterTailwindcss,\n 'token-values': betterTailwindcss,\n 'token-spacing': betterTailwindcss,\n },\n // Every other config's directives are unused here, because their rules are\n // off here. Reporting them would be this config complaining about the\n // others' business.\n linterOptions: { reportUnusedDisableDirectives: 'off' },\n },\n gateLint({ audience }),\n ]\n}\n", "/// <reference types=\"node\" />\n/**\n * The gate checks every app runs, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 Peek and Ship each wrote them into their own\n * `scripts/gates-checks.mjs`. 65 different checks for finished tickets, counted\n * on 17 September: 28 were identical in both, 21 were the same check written two\n * ways, 2 read each app's real page, and 14 read code only one app has. This is\n * the first three groups as one list. The fourth stays in the app's own checks\n * file, beside this list.\n *\n * Where Peek and Ship wrote one check two ways, the check here runs both, so it\n * passes wherever either did: the raw-link probe with both of their texts, the\n * token probes against each token config the app has.\n *\n * `scripts/gates-compare.mjs` holds this list to Peek's and Ship's: every check\n * of theirs must be here, or be about code only that app has, or the comparison\n * fails and names it.\n *\n * A check for a ticket not built yet is a first guess at the evidence, taken\n * from the ticket's own text; the ticket that builds it changes it here.\n */\nimport type { CheckResult, GateCheck, GateHelpers, GateTicket } from './status'\n\nexport interface AppCheckOptions {\n /** Where the app is inside the repository: `web` in Ship, `.` elsewhere. */\n app?: string\n /** A real page of the app, relative to the app, full of elements that are not controls. */\n page: string\n /** The ticket the gate chain is listed under, and whether this repo owns it: UIG-3 in Peek, UIG-4 in Ship. */\n chain?: { ref: string; owner: boolean }\n /** The token lint configs the app has, relative to the app. */\n tokenConfigs?: string[]\n /** The name of the app's header component a hand-made header row should name (UIG-22's first guess). */\n headers?: string[]\n}\n\nexport const APP_TICKET_TITLES: Record<string, string> = {\n 'UIG-2': 'The tracking rails \u2014 GATES.md, the status script, the guide committed',\n 'UIG-3': 'Tracer bullet \u2014 one rule, end to end, blocking in Peek',\n 'UIG-4': 'The same chain, blocking in Ship',\n 'UIG-6': 'Branch protection \u2014 the backstop, all three repos',\n 'UIG-7': 'Lint rule \u2014 every remaining raw element',\n 'UIG-8': 'Lint rule \u2014 forbid the reach',\n 'UIG-9': 'Lint rule \u2014 the className allow-list',\n 'UIG-10': 'create-app \u2014 a command that makes a new Estiva app that runs',\n 'UIG-13': \"The registry widens to Peek's 115 and Ship's 74, with classification\",\n 'UIG-19': 'Lock the contract in CI, and make the three Storybooks one search',\n 'UIG-20': 'The Claude skill, reading the registry',\n 'UIG-21': 'CLAUDE.md becomes an index, not a lecture',\n 'UIG-22': 'Fingerprint \u2014 a hand-made header row',\n 'UIG-23': 'Fingerprint \u2014 a hand-made empty state',\n 'UIG-24': 'Fingerprint \u2014 a browser tooltip where ours belongs',\n 'UIG-25': 'Fingerprint \u2014 a component copied out by hand',\n 'UIG-27': 'The components the apps had to build themselves \u2014 Link, ProgressBar, EmptyState padding',\n 'UIG-28': 'Close the two holes in the token contract \u2014 arbitrary values, and inline style',\n 'UIG-30': 'RichText \u2014 one component that draws a message\\'s text, for both apps',\n 'UIG-32': 'Peek and Ship take their gate pieces from the package',\n}\n\n/** Run several checks as one: the first that does not pass is the answer. */\nfunction all(h: GateHelpers, steps: (() => CheckResult | Promise<CheckResult>)[], label: string): () => Promise<CheckResult> {\n return async () => {\n for (const step of steps) {\n const r = await step()\n if (r.result !== 'pass') return r\n }\n return h.PASS(label)\n }\n}\n\nexport function appChecks(h: GateHelpers, { app = '.', page, chain = { ref: 'UIG-3', owner: false }, tokenConfigs = ['eslint.tokens.config.js', 'eslint.config.js'], headers = ['ContainerHeader', 'SectionHeader'] }: AppCheckOptions): GateTicket[] {\n const WEB = app === '.' ? '' : `${app.replace(/\\/$/, '')}/`\n const cwd = app === '.' ? undefined : app\n const PROBE = 'src/components/__gates_probe__.tsx'\n const GATES = 'eslint.gates.config.js'\n const at = (rel: string) => `${WEB}${rel}`\n const probe = (code: string, expect: 'error' | 'warning' | 'none', mentions?: string, file = PROBE, config = GATES) => () =>\n h.lint({ ...(cwd ? { cwd } : {}), config, file, code, expect, mentions })\n const component = (body: string) => `export function Probe() {\\n return ${body}\\n}\\n`\n const installed = at('node_modules/@estiva-app/ui/dist/index.d.ts')\n const realPage = (): Promise<CheckResult> => h.lint({ ...(cwd ? { cwd } : {}), config: GATES, file: page, code: h.read(at(page)), expect: 'none' })\n const button = component('<button type=\"button\">x</button>')\n const ticket = (ref: string, checks: GateCheck[], owner = false): GateTicket => ({ ref, title: APP_TICKET_TITLES[ref], owner, checks })\n\n const tokenProbe = (code: string, expect: 'error' | 'warning' | 'none', mentions?: string, file = PROBE) =>\n all(h, tokenConfigs.map((config) => probe(code, expect, mentions, file, config)), `${tokenConfigs.join(' and ')}: as expected`)\n\n return [\n ticket('UIG-2', [\n { what: 'npm run gates:status is wired', run: () => h.script('package.json', 'gates:status') },\n ]),\n ticket(chain.ref, [\n { what: 'the gate lint config loads', run: () => h.loads(at(GATES)) },\n { what: `${at('package.json')} has lint:rules`, run: () => h.script(at('package.json'), 'lint:rules') },\n { what: 'CI runs lint:rules', run: () => h.ci('lint:rules') },\n { what: 'the committed hook runs the gate lint', run: () => h.hook('.claude/settings.json', 'gates') },\n { what: '.gates-count.json is committed and parses', run: () => h.json(h.exists('.gates-count.json') ? '.gates-count.json' : at('.gates-count.json')) },\n { what: 'a raw <button> is an error naming Button', run: probe(button, 'error', 'Button') },\n { what: 'the same in a story', run: probe(button, 'error', 'Button', 'src/stories/__gates_probe__.stories.tsx') },\n { what: 'a test file is not checked, while source is', run: async () => {\n const source = await probe(button, 'error', 'Button')()\n if (source.result !== 'pass') return h.FAIL(`source is not checked yet, so this proves nothing: ${source.detail}`)\n return probe(button, 'none', undefined, 'src/components/__gates_probe__.test.tsx')()\n } },\n { what: 'the debt list exists', run: () => h.file('docs/GATES-DEBT.md') },\n ], chain.owner),\n ticket('UIG-6', [\n { what: 'GitHub requires the check gate to merge into main', run: () => h.protectedBranch(/^gate$/) },\n { what: \"CI's job gate runs lint:rules\", run: () => h.ciJob('gate', 'lint:rules') },\n ]),\n ticket('UIG-7', [\n { what: 'a raw <input> is an error naming TextInput', run: probe(component('<input />'), 'error', 'TextInput') },\n { what: 'a raw <a> is an error naming Link', run: all(h, [probe(component('<a href=\"/topics\">Topics</a>'), 'error', 'Link'), probe(component('<a href=\"/issues\">Issues</a>'), 'error', 'Link')], 'a raw <a> is an error naming Link') },\n { what: 'a raw <form> is an error naming Form, and a file input one naming FilePicker', run: async () => {\n const form = await probe(component('<form />'), 'error', '`Form`')()\n if (form.result !== 'pass') return form\n return probe(component('<input type=\"file\" />'), 'error', 'FilePicker')()\n } },\n { what: '<textarea>, <select>, <dialog> and <label> name Textarea, Select, DialogShell and Field', run: async () => {\n for (const [element, part] of [['textarea', 'Textarea'], ['select', 'Select'], ['dialog', 'DialogShell'], ['label', 'Field']]) {\n const one = await probe(component(`<${element} />`), 'error', `\\`${part}\\``)()\n if (one.result !== 'pass') return one\n }\n return h.PASS('all four are errors, each naming its part')\n } },\n { what: 'an element the package has no part for is an error that says to ask for one', run: probe(component('<iframe title=\"Map\" />'), 'error', 'ask Katerina') },\n { what: 'an escaped element is not an error, while the same element is', run: async () => {\n const raw = await probe(component('<form />'), 'error')()\n if (raw.result !== 'pass') return h.FAIL(`a raw <form> is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe('export function Probe() {\\n return (\\n // @estiva-escape: a probe that keeps its element on purpose\\n <form />\\n )\\n}\\n', 'none')()\n } },\n { what: 'a real page, full of elements that are not controls, gets no error', run: realPage },\n ]),\n ticket('UIG-8', [\n { what: 'a Base UI import is an error naming the part built on it', run: probe(\"import { Popover } from '@base-ui/react/popover'\\nexport const Probe = Popover\\n\", 'error', '`Popover`') },\n { what: 'createPortal is an error naming the floating parts', run: probe(\"import { createPortal } from 'react-dom'\\nexport const Probe = (to: HTMLElement) => createPortal(<span />, to)\\n\", 'error', '`DialogShell`') },\n { what: 'a key listener on the whole page is an error, in a .ts file too', run: probe(\"export function listen() {\\n window.addEventListener('keydown', () => {})\\n}\\n\", 'error', '`DialogShell`', 'src/lib/__gates_probe__.ts') },\n { what: 'arrow keys by hand are an error; Enter in a field is not', run: async () => {\n const arrows = await probe(\"export const onKeyDown = (e: KeyboardEvent) => e.key === 'ArrowDown'\\n\", 'error', '`Menu`', 'src/lib/__gates_probe__.ts')()\n if (arrows.result !== 'pass') return arrows\n return probe(\"export const onKeyDown = (e: KeyboardEvent) => e.key === 'Enter'\\n\", 'none', undefined, 'src/lib/__gates_probe__.ts')()\n } },\n { what: 'a hand-written role=\"option\" is an error naming Select', run: probe(component('<div role=\"option\" />'), 'error', '`Select`') },\n { what: 'tabIndex={0} on a div is an error; tabIndex={-1} is not', run: async () => {\n const zero = await probe(component('<div tabIndex={0} />'), 'error', '`Button`')()\n if (zero.result !== 'pass') return h.FAIL(`tabIndex={0} is not caught yet, so this proves nothing: ${zero.detail}`)\n return probe(component('<div tabIndex={-1} />'), 'none')()\n } },\n { what: 'overflow-y-auto is an error naming ScrollArea', run: probe(component('<div className=\"h-64 overflow-y-auto\" />'), 'error', '`ScrollArea`') },\n { what: 'an escaped listener is not an error, while the same listener is', run: async () => {\n const raw = await probe(\"export function listen() {\\n document.addEventListener('mousedown', () => {})\\n}\\n\", 'error', undefined, 'src/lib/__gates_probe__.ts')()\n if (raw.result !== 'pass') return h.FAIL(`a page listener is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe(\"export function listen() {\\n // @estiva-escape: a probe that keeps its listener on purpose\\n document.addEventListener('mousedown', () => {})\\n}\\n\", 'none', undefined, 'src/lib/__gates_probe__.ts')()\n } },\n { what: 'a real page gets no error', run: realPage },\n ]),\n ticket('UIG-9', [\n { what: 'a border passed into Button is an error naming Button', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button className=\"border\">x</Button>')}`, 'error', 'on `Button` changes how it looks') },\n { what: 'placement passed into Button is not', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button className=\"mt-2 w-full flex-1 relative\">x</Button>')}`, 'none') },\n { what: 'an escaped look is not an error, while the same look is', run: async () => {\n const raw = await probe(`import { Link } from '@estiva-app/ui'\\n${component('<Link href=\"/x\" className=\"after:absolute after:inset-0\">x</Link>')}`, 'error', '`Link`')()\n if (raw.result !== 'pass') return h.FAIL(`a restyled part is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe(`import { Link } from '@estiva-app/ui'\\nexport function Probe() {\\n return (\\n // @estiva-escape: a probe that keeps its row link on purpose\\n <Link href=\"/x\" className=\"after:absolute after:inset-0\">x</Link>\\n )\\n}\\n`, 'none')()\n } },\n { what: 'a real page gets no error', run: realPage },\n ]),\n // UIG-10, reopened 18 September: a tab holds one relay client, the way Peek and Ship hold\n // theirs and a made app is written to. Measured on both mains that day: one each, and no\n // socket opened any other way. A second holder, or a socket of the app's own, is a second\n // connection the relay signs in and budgets separately.\n ticket('UIG-10', [\n { what: 'the app holds one relay client, at module level, and opens no socket of its own', run: () => {\n const sources = h.listFiles(at('src'), (n) => /\\.tsx?$/.test(n) && !/\\.(test|stories)\\.tsx?$/.test(n))\n const own = sources.filter((f) => /\\bcreateLiveClient\\(|\\bcreateLiveRelay\\(|\\bnew WebSocket\\(/.test(h.read(f)))\n if (own.length) return h.FAIL(`opens a socket of its own (${own.join(', ')}): hold one client from createLiveClientHolder()`)\n const holders = sources.filter((f) => /^\\s*(?:export\\s+)?const\\s+\\w+\\s*=\\s*createLiveClientHolder\\(\\)/m.test(h.read(f)))\n if (holders.length !== 1) return h.FAIL(holders.length ? `${holders.length} client holders (${holders.join(', ')}): a tab holds one` : 'no module-level createLiveClientHolder(): the app has no relay client')\n return h.PASS(`one client for the tab, held in ${holders[0]}`)\n } },\n ]),\n // Built by UIG-13, 18 September. An app's catalogue is built fresh from its code\n // and never committed: the package is public and the apps are private (Katerina).\n ticket('UIG-13', [\n { what: \"npm run ui:find is the package's search, over the package and this app\", run: () => h.contains(at('package.json'), /\"ui:find\":\\s*\"estiva-ui find\\b/, \"ui:find runs the package's estiva-ui find\") },\n { what: \"npm run registry:check is the package's check\", run: () => h.contains(at('package.json'), /\"registry:check\":\\s*\"estiva-ui check\\b/, 'registry:check runs estiva-ui check') },\n { what: \"CI's job gate refuses a part with no description\", run: () => h.ciJob('gate', 'registry:check') },\n // In the .gitignore of the app's own folder, where `estiva-ui build` would write it.\n { what: 'the catalogue is never committed: git ignores registry.json', run: () => h.contains(at('.gitignore'), /^\\/?registry\\.json\\r?$/m, `${at('.gitignore')} ignores registry.json`) },\n { what: 'every part is described and sorted, and every file is accounted for', run: () => h.catalogue(at('.')) },\n ]),\n ticket('UIG-19', [\n { what: 'the usage-page contract runs in CI', run: () => h.ci(/[\\w:-]*contract[\\w:-]*/) },\n ]),\n ticket('UIG-20', [\n { what: 'a committed skill runs ui:find', run: () => (h.listFiles('.claude/skills', (n) => n === 'SKILL.md').some((f) => h.read(f).includes('ui:find')) ? h.PASS('a skill runs ui:find') : h.FAIL('no skill in .claude/skills runs ui:find')) },\n ]),\n ticket('UIG-21', [\n { what: 'path-scoped instructions exist', run: () => (h.listFiles('.claude/rules', (n) => n.endsWith('.md')).some((f) => /^paths:/m.test(h.read(f))) ? h.PASS('.claude/rules has paths: instructions') : h.FAIL('no .claude/rules file with paths:')) },\n ]),\n ticket('UIG-22', [\n { what: 'a hand-made header row is an error naming the header part', run: all(h, headers.map((name) => probe(component(`<section className=\"flex flex-col\"><div className=\"flex items-center px-3 py-2\"><span>${name === 'SectionHeader' ? 'Issues' : 'Folders'}</span></div><div /></section>`), 'error', name)), 'a hand-made header row is an error naming the header part') },\n ]),\n ticket('UIG-23', [\n { what: 'a hand-made empty line is an error naming EmptyState', run: probe(\"export function Probe({ items }: { items: string[] }) {\\n return <div>{items.length === 0 ? <p className=\\\"text-text-secondary\\\">Nothing here</p> : items.map((i) => <span key={i}>{i}</span>)}</div>\\n}\\n\", 'error', 'EmptyState') },\n ]),\n ticket('UIG-24', [\n { what: 'a title= is an error naming WithTooltip', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button title=\"Delete\">x</Button>')}`, 'error', 'WithTooltip') },\n ]),\n ticket('UIG-25', [\n { what: \"SectionLabel's class list, typed by hand, is a warning\", run: probe(component('<span className=\"text-[10px] uppercase tracking-wide text-text-muted\">Label</span>'), 'warning', 'SectionLabel') },\n ]),\n ticket('UIG-27', [\n { what: 'the installed package has Link', run: () => h.contains(installed, /\\bLink\\b/, 'the installed @estiva-app/ui exports Link') },\n { what: 'the installed package has ProgressBar', run: () => h.contains(installed, /\\bProgressBar\\b/, 'the installed @estiva-app/ui exports ProgressBar') },\n ]),\n ticket('UIG-28', [\n { what: 'text-[14px] is an error', run: tokenProbe(component('<div className=\"text-[14px]\">x</div>'), 'error') },\n { what: 'a colour in an inline style is an error', run: tokenProbe(component(\"<div style={{ backgroundColor: '#ff0000' }}>x</div>\"), 'error') },\n { what: 'h-[240px] is a warning', run: tokenProbe(component('<div className=\"h-[240px]\">x</div>'), 'warning') },\n { what: 'h-[240px] is not an error', run: tokenProbe(component('<div className=\"h-[240px]\">x</div>'), 'none') },\n { what: 'a hand-written line height behind an arbitrary variant is an error', run: tokenProbe(component('<div className=\"[&_p]:leading-[1.4]\">x</div>'), 'error') },\n { what: 'a raw colour in a class is an error (the pattern that matched nothing, R1)', run: tokenProbe(component('<div className=\"bg-[#5c69dc]\">x</div>'), 'error', 'raw colour') },\n { what: 'a width and height from a prop pass', run: tokenProbe(\"export function Probe({ size }: { size: number }) {\\n return <div style={{ width: size, height: size }}>x</div>\\n}\\n\", 'none') },\n { what: 'a test file is not checked for hand-written values, while source is', run: all(h, tokenConfigs.map((config) => async () => {\n const code = component(\"<div className=\\\"text-[14px]\\\" style={{ color: 'red' }}>x</div>\")\n const source = await probe(code, 'error', undefined, PROBE, config)()\n if (source.result !== 'pass') return h.FAIL(`source is not checked yet, so this proves nothing: ${source.detail}`)\n return probe(code, 'none', undefined, 'src/components/__gates_probe__.test.tsx', config)()\n }), 'a test file is not checked for hand-written values, while source is') },\n ]),\n ticket('UIG-30', [\n { what: 'the installed package has RichText', run: () => h.contains(installed, /\\bRichText\\b/, 'the installed @estiva-app/ui exports RichText') },\n ]),\n // Katerina's ruling of 17 September (docs/GATES.md \u00A723): one copy, in the package. An app made\n // by create-estiva-app passes this from its first commit; Peek and Ship do once UIG-32 lands.\n ticket('UIG-32', [\n { what: 'the gate pieces come from the package, and no repo keeps a copy', run: all(h, [\n () => h.contains(at('eslint.gates.config.js'), /from '@estiva-app\\/ui\\/gates'/, 'the gate config imports @estiva-app/ui/gates'),\n () => h.hook('.claude/settings.json', '@estiva-app/ui/dist/gates/cli.js'),\n // `estiva-gates status` where npm can find the command; by path where it cannot,\n // because the install is in the app's folder and the script is in the repo's top one (Ship).\n () => h.contains('package.json', /\"gates:status\":\\s*\"[^\"]*(?:estiva-gates|@estiva-app\\/ui\\/dist\\/gates\\/cli\\.js)[^\"]*status/, \"gates:status runs the package's engine\"),\n () => (h.exists('scripts/gates-status.mjs') ? h.FAIL('scripts/gates-status.mjs is a copy of the status engine: run estiva-gates status instead') : h.PASS('no copy of the status engine')),\n ], 'the gate config, the hook and gates:status are the package\\'s; no copy of the engine') },\n ]),\n ]\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/// <reference types=\"node\" />\n/**\n * The token contract as lint settings, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 this block was pasted into every repo: `eslint.config.js` here,\n * `eslint.tokens.js` in Peek, `eslint.config.js` in Ship. Katerina ruled on 17 September that a\n * gate piece ships in the package and an app imports it, so this is that one\n * copy. Peek and Ship move onto it in UIG-32.\n *\n * Two configs, as the copies had:\n *\n * - `tokenLint` \u2014 a class Tailwind does not generate (the doubled token name,\n * `bg-bg-surface`), Tailwind's own type ramp or palette, a raw colour, an\n * opacity modifier on a token colour.\n * - `tokenValues` \u2014 UIG-28: type, corners and shadows written by hand, and an\n * inline style that sets a colour (errors); heights, spacing and border\n * widths written by hand (warnings, never blocking).\n *\n * And `tokenConfig`, the two on their own as an app's `lint:tokens` runs them\n * (UIG-37), with TypeScript's rule names known so its directives pass.\n *\n * The patterns are the same for everyone. Only the words differ: an app is told\n * to use \"the package\", the package is told to add the token to `tokens.css`.\n * Measured on 17 September: the UIG-28 block was byte for byte the same in Peek\n * and Ship, and this repository's had the same patterns with seven messages\n * worded for the package.\n *\n * Where the plugin looks: every `className`, every `cn()` / `clsx()` argument,\n * and the values of a class map whose name ends in `Styles` / `_STYLES` or\n * `Classes` / `_CLASSES`. A class map named any other way is invisible to it.\n *\n * Tests are out of `tokenValues` (ruling of 13 September): a test mounts markup\n * to test it. A kept hand-written value says why:\n * `// eslint-disable-next-line <rule> -- @estiva-escape: <reason>` (A2).\n */\nimport { createRequire } from 'node:module'\nimport { fileURLToPath } from 'node:url'\nimport type { ESLint, Linter } from 'eslint'\nimport betterTailwindcss from 'eslint-plugin-better-tailwindcss'\nimport { getDefaultSelectors } from 'eslint-plugin-better-tailwindcss/defaults'\nimport { parser as typescriptParser, plugin as typescriptPlugin } from 'typescript-eslint'\n\n/** Who reads the messages. */\nexport type TokenAudience = 'app' | 'package'\n\nexport interface TokenLintOptions {\n /** The Tailwind config the unknown-class rule reads, relative to where ESLint runs. */\n tailwindConfig?: string\n /** `app` (the default) names the package; `package` names `tokens.css` and the preset. */\n audience?: TokenAudience\n}\n\nexport interface TokenConfigOptions extends TokenLintOptions {\n /**\n * Plugins whose `eslint-disable` directives appear in the source, registered\n * with every rule off. ESLint refuses a directive for a rule it does not\n * know. TypeScript's are registered already.\n */\n quiet?: Record<string, ESLint.Plugin>\n /** Folders the token lint never reads, beside build output. */\n ignores?: string[]\n}\n\n/** What the token lint never reads: build output. */\nexport const TOKEN_LINT_IGNORES = ['dist/', 'storybook-static/', 'node_modules/']\n\n/**\n * Plugins a check that runs on its own registers, every rule off, so the\n * directives another lint writes do not fail it (UIG-37). TypeScript's lint is\n * in every app and writes `eslint-disable-next-line @typescript-eslint/...`;\n * Peek PR #324 failed the token lint on one. Only the checks that run alone\n * take this \u2014 a full lint that loads TypeScript's rules from its own copy would\n * refuse a second plugin under the same name.\n */\nexport const KNOWN_PLUGINS: Record<string, ESLint.Plugin> = { '@typescript-eslint': typescriptPlugin as ESLint.Plugin }\n\nconst CLASS_MAP_SELECTOR = {\n kind: 'variable',\n name: '^(?:[A-Z][A-Z0-9_]*_(?:STYLES|CLASSES)|[a-z][a-zA-Z0-9]*(?:Styles|Classes))$',\n match: [{ type: 'strings' }, { type: 'objectValues' }],\n}\n\nconst WORDS = {\n app: {\n ramp: \"Tailwind's type ramp is not a token. Use the ramp from the package: text-body-2, text-caption, text-h3, text-btn-default...\",\n palette: \"Tailwind's palette is not a token. Use a colour from the package (bg-bg-surface, text-text-primary, border-border-subtle...); a colour with no token is a missing token.\",\n raw: 'A raw colour is a missing token: add it to the package (tokens.css in every theme block, and the preset), or say why not in an eslint-disable comment.',\n opacity: 'An opacity modifier on a token colour compiles to nothing. A transparent colour is a token of its own (D16): add it to the package.',\n type: '$0 is type written by hand. A token from the package sets the size, line height, letter spacing and weight together (text-body-2, text-caption, text-h5...); if none fits, add one to the package, or say why not in an eslint-disable comment.',\n corner: '$0 is a corner written by hand, and no token has it. Use a corner from the package (rounded-sm 4px, rounded-md 6px, rounded-lg 8px...), or say why not in an eslint-disable comment.',\n shadow: '$0 is a shadow written by hand. Use a shadow from the package (shadow-sm, shadow-md, shadow-focus-ring, drop-shadow-glow-success...); a shadow with no token is a missing token.',\n },\n package: {\n ramp: \"Tailwind's type ramp is not a token. Use the ramp in tailwind-preset.js: text-body-2, text-caption, text-h3, text-btn-default...\",\n palette: \"Tailwind's palette is not a token. Use a colour from tailwind-preset.js (bg-bg-surface, text-text-primary, border-border-subtle...); a colour with no token is a missing token.\",\n raw: 'A raw colour is a missing token: add it to tokens.css in every theme block and to the preset, or say why not in an eslint-disable comment.',\n opacity: 'An opacity modifier on a token colour compiles to nothing. A transparent colour is a token of its own (D16): add it to tokens.css in every theme block and to the preset.',\n type: '$0 is type written by hand. A token from tailwind-preset.js sets the size, line height, letter spacing and weight together (text-body-2, text-caption, text-h5...); if none fits, add one to the preset and to cn.ts, or say why not in an eslint-disable comment.',\n corner: '$0 is a corner written by hand, and no token has it. Use a corner from tailwind-preset.js (rounded-sm 4px, rounded-md 6px, rounded-lg 8px...), or say why not in an eslint-disable comment.',\n shadow: '$0 is a shadow written by hand. Use a shadow from tailwind-preset.js (shadow-sm, shadow-md, shadow-focus-ring, drop-shadow-glow-success...); a shadow with no token is a missing token.',\n },\n} satisfies Record<TokenAudience, Record<string, string>>\n\n/**\n * The class rules. `no-unknown-classes` reads the app's own Tailwind config, so\n * it knows exactly what the preset and the app generate.\n */\nexport function tokenLint({ tailwindConfig = 'tailwind.config.js', audience = 'app' }: TokenLintOptions = {}): Linter.Config {\n const words = WORDS[audience]\n return {\n files: ['**/*.{ts,tsx}'],\n languageOptions: {\n parser: typescriptParser,\n parserOptions: { ecmaFeatures: { jsx: true } },\n },\n plugins: { 'better-tailwindcss': betterTailwindcss },\n settings: {\n 'better-tailwindcss': {\n tailwindConfig,\n selectors: [...getDefaultSelectors(), CLASS_MAP_SELECTOR],\n },\n },\n rules: {\n 'better-tailwindcss/no-unknown-classes': 'error',\n 'better-tailwindcss/no-restricted-classes': [\n 'error',\n {\n restrict: [\n { pattern: '^(?:[a-z0-9-]+:)*text-(?:xs|sm|base|lg|xl|[2-9]xl)$', message: words.ramp },\n {\n pattern:\n '^(?:[a-z0-9-]+:)*(?:text|bg|border|ring|outline|fill|stroke|decoration|divide|placeholder|from|via|to|accent|caret|shadow)-(?:black|white|slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)(?:-[0-9]{2,3})?(?:/[0-9]{1,3})?$',\n message: words.palette,\n },\n { pattern: '^(?:[a-z0-9-]+:)*[a-z-]+-\\\\[[^\\\\]]*(?:#[0-9a-fA-F]{3}|rgba?\\\\(|hsla?\\\\(|oklch\\\\()', message: words.raw },\n {\n pattern: '^(?:[a-z0-9-]+:)*(?:text|bg|border|ring|outline|fill|stroke|decoration|divide|placeholder|from|via|to|accent|caret|shadow)-(?:bg|text|border|accent|info|warning|success|error)-[a-z-]+/[0-9]{1,3}$',\n message: words.opacity,\n },\n ],\n },\n ],\n },\n }\n}\n\n// UIG-28's patterns. The variant part reads arbitrary variants too\n// (`[&_pre]:`, `data-[state]:`, `group-hover/row:`). A colour inside `text-[\u2026]`\n// is left to the raw-colour rule of `tokenLint`.\nconst V = '^(?:(?:[^:\\\\[\\\\]\\\\s]|\\\\[[^\\\\]]*\\\\])+:)*!?'\nconst NOT_COLOUR = '(?!color:|#|rgba?\\\\(|hsla?\\\\(|oklch\\\\(|var\\\\()'\nconst HAND_WRITTEN = {\n type: `${V}(?:text-\\\\[${NOT_COLOUR}|text-\\\\[length:|leading-\\\\[|tracking-\\\\[)[^\\\\]]+\\\\](?:/\\\\S*)?$`,\n corner: `${V}rounded(?:-(?:t|r|b|l|s|e|tl|tr|br|bl|ss|se|es|ee))?-\\\\[[^\\\\]]+\\\\]$`,\n shadow: `${V}(?:shadow|drop-shadow)-\\\\[(?!color:)[^\\\\]]+\\\\]$`,\n spacing: `${V}-?(?:w|h|size|min-w|min-h|max-w|max-h|p[xytrblse]?|m[xytrblse]?|gap(?:-[xy])?|space-[xy]|inset(?:-[xy])?|top|right|bottom|left|translate-[xy]|basis|indent|scroll-[mp][xytrblse]?|border-spacing(?:-[xy])?)-\\\\[[^\\\\]]+\\\\]$`,\n width: `${V}(?:border(?:-[xytrblse])?|divide-[xy]|outline|ring|ring-offset)-\\\\[(?:length:)?-?[0-9.]+(?:px|rem|em)?\\\\]$`,\n}\n\nconst INLINE_STYLE_TOKEN_PROPERTIES =\n '/^(color|background|backgroundColor|backgroundImage|fill|stroke|fontSize|border|borderTop|borderRight|borderBottom|borderLeft|borderBlock|borderInline|borderStyle|boxShadow|textShadow)$|Color$/'\n\ninterface Preset {\n theme: { extend: { fontSize: Record<string, string | [string, unknown]>; borderRadius: Record<string, string> } }\n}\n\n/**\n * The preset, loaded the way Tailwind loads it: it imports `tailwindcss/plugin`,\n * which a plain `import` cannot resolve. Read from beside this module, so an app\n * and this repository read the one preset the package ships.\n */\nfunction loadPreset(): Preset {\n const require = createRequire(import.meta.url)\n const loadConfig = require('tailwindcss/loadConfig') as (path: string) => Preset\n return loadConfig(fileURLToPath(new URL('../../tailwind-preset.js', import.meta.url)))\n}\n\n/**\n * UIG-28: the values the tokens already name, written by hand (Katerina's\n * rulings B11, B12, C3 of 13 September and R2 of 15 September). Register it\n * after `tokenLint`, whose parser and plugin settings it borrows. Under its own\n * rule names, `token-values` and `token-spacing` (the same plugin twice, so one\n * runs as errors and one as warnings): an escape for a hand-written size then\n * cannot also silence a raw colour on the same line.\n */\nexport function tokenValues({ audience = 'app' }: Pick<TokenLintOptions, 'audience'> = {}): Linter.Config {\n const words = WORDS[audience]\n const preset = loadPreset()\n // `text-[14px]` names the token of that size, read from the preset, so the\n // message stays true when the ramp changes.\n const tokensBySize: Record<string, string[]> = {}\n for (const [name, value] of Object.entries(preset.theme.extend.fontSize)) {\n const size = Array.isArray(value) ? value[0] : value\n ;(tokensBySize[size] ??= []).push(`text-${name}`)\n }\n const cornersBySize: Record<string, string[]> = {}\n for (const [name, size] of Object.entries(preset.theme.extend.borderRadius)) (cornersBySize[size] ??= []).push(name === 'DEFAULT' ? 'rounded' : `rounded-${name}`)\n\n return {\n files: ['**/*.{ts,tsx}'],\n ignores: ['**/*.test.ts', '**/*.test.tsx'],\n plugins: { 'token-values': betterTailwindcss, 'token-spacing': betterTailwindcss },\n rules: {\n 'token-values/no-restricted-classes': [\n 'error',\n {\n restrict: [\n ...Object.entries(tokensBySize).map(([size, names]) => ({\n pattern: `${V}text-\\\\[${size.replace('.', '\\\\.')}\\\\](?:/\\\\S*)?$`,\n message: `$0 is the type ramp written by hand. Use ${names.join(' or ')}.`,\n })),\n {\n pattern: HAND_WRITTEN.type,\n message: words.type,\n },\n ...Object.entries(cornersBySize).map(([size, names]) => ({\n pattern: `${V}rounded(?:-(?:t|r|b|l|s|e|tl|tr|br|bl|ss|se|es|ee))?-\\\\[${size.replace('.', '\\\\.')}\\\\]$`,\n message: `$0 is a corner written by hand. Use ${names.join(' or ')} (with the same side, if it has one).`,\n })),\n {\n pattern: HAND_WRITTEN.corner,\n message: words.corner,\n },\n {\n pattern: HAND_WRITTEN.shadow,\n message: words.shadow,\n },\n ],\n },\n ],\n 'token-spacing/no-restricted-classes': [\n 'warn',\n {\n restrict: [\n {\n pattern: HAND_WRITTEN.spacing,\n message: '$0 is a size or a space written by hand. Use a step of the spacing scale (p-3, h-9, gap-2...) if one fits. Reported, not blocked.',\n },\n {\n pattern: HAND_WRITTEN.width,\n message: '$0 is a border or ring width written by hand. Use border, border-2, ring-1... if one fits. Reported, not blocked.',\n },\n ],\n },\n ],\n 'no-restricted-syntax': [\n 'error',\n {\n selector: `JSXAttribute[name.name=\"style\"] > JSXExpressionContainer > ObjectExpression > Property[key.name=${INLINE_STYLE_TOKEN_PROPERTIES}]`,\n message: 'An inline style that sets a colour, a font size, a border or a shadow is outside the token contract: use a token class. If the value is computed (a palette, a size from a prop), say why in an eslint-disable comment. Width, height and transforms are fine.',\n },\n ],\n },\n }\n}\n\n/**\n * The token lint on its own \u2014 an app's `eslint.tokens.config.js`, `npm run\n * lint:tokens`, the CI step. It fails on the token contract and on nothing\n * else, so a backlog in the full lint never blocks it. Peek built this file by\n * hand until UIG-37.\n */\nexport function tokenConfig({ quiet = {}, ignores = [], ...options }: TokenConfigOptions = {}): Linter.Config[] {\n return [\n { ignores: [...TOKEN_LINT_IGNORES, ...ignores] },\n {\n plugins: { ...KNOWN_PLUGINS, ...quiet },\n // Every other lint's directives are unused here, because their rules are\n // off here. Reporting them would be this config complaining about the\n // others' business.\n linterOptions: { reportUnusedDisableDirectives: 'off' },\n },\n tokenLint(options),\n tokenValues({ audience: options.audience }),\n ]\n}\n", "/// <reference types=\"node\" />\n/**\n * Where the UI Guardrails' rules apply, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 every repo wrote this itself: `eslint.gates.js` (the rules and\n * where they apply) and `eslint.gates.config.js` (the gate: those rules alone)\n * in Peek, in Ship's `web/`, and here pointed inward. The rules were always the\n * package's; these two files were copies. Now they are two functions.\n *\n * Where: every `.ts` and `.tsx` under `src`, stories included (a story is what\n * people read and copy), tests not (a test mounts markup to test it) \u2014\n * Katerina's rulings of 13 September; `.ts` since UIG-8. This package's own\n * inward set reads `.tsx` only, as it always has.\n *\n * A place that keeps something says why, on the line above:\n * `// @estiva-escape: <reason>`, or `{/* @estiva-escape: <reason> *\\/}` as a\n * JSX child. Never `eslint-disable`: the count refuses it.\n */\nimport type { ESLint, Linter } from 'eslint'\nimport betterTailwindcss from 'eslint-plugin-better-tailwindcss'\nimport { parser as typescriptParser } from 'typescript-eslint'\nimport estiva from '../eslint/index'\nimport { KNOWN_PLUGINS, TOKEN_LINT_IGNORES } from './token-lint'\n\nexport interface GateConfigOptions {\n /** `app` (the default) runs `configs.recommended`; `package` runs this package's inward set. */\n audience?: 'app' | 'package'\n /**\n * Plugins whose `eslint-disable` directives appear in the source, registered\n * here with every rule off. ESLint refuses a directive for a rule it does not\n * know, so without them the gate would fail on another lint's comments.\n * The token lint's names and TypeScript's are registered already (UIG-37).\n */\n quiet?: Record<string, ESLint.Plugin>\n /** Folders the gate never reads, beside build output. */\n ignores?: string[]\n}\n\n/**\n * The rules and where they apply \u2014 one config object, to add beside\n * everything else an app's `eslint.config.js` runs, so an editor shows a\n * refusal where the element is typed.\n */\nexport function gateLint({ audience = 'app' }: Pick<GateConfigOptions, 'audience'> = {}): Linter.Config {\n return {\n ...(audience === 'package' ? estiva.configs.package : estiva.configs.recommended),\n files: audience === 'package' ? ['src/**/*.tsx'] : ['src/**/*.{ts,tsx}'],\n ignores: ['**/*.test.ts', '**/*.test.tsx'],\n languageOptions: {\n parser: typescriptParser,\n parserOptions: { ecmaFeatures: { jsx: true } },\n },\n }\n}\n\n/**\n * The gate: the rules on their own \u2014 `npm run lint:rules`, CI's job `gate`, and\n * the config the editor hook lints a proposed write with. It fails on a gate\n * rule and on nothing else, so a backlog in the full lint never blocks it.\n */\nexport function gateConfig({ audience = 'app', quiet = {}, ignores = [] }: GateConfigOptions = {}): Linter.Config[] {\n return [\n { ignores: [...TOKEN_LINT_IGNORES, ...ignores] },\n {\n plugins: {\n ...KNOWN_PLUGINS,\n ...quiet,\n 'better-tailwindcss': betterTailwindcss,\n 'token-values': betterTailwindcss,\n 'token-spacing': betterTailwindcss,\n },\n // Every other config's directives are unused here, because their rules are\n // off here. Reporting them would be this config complaining about the\n // others' business.\n linterOptions: { reportUnusedDisableDirectives: 'off' },\n },\n gateLint({ audience }),\n ]\n}\n", "/// <reference types=\"node\" />\n/**\n * The gate checks every app runs, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 Peek and Ship each wrote them into their own\n * `scripts/gates-checks.mjs`. 65 different checks for finished tickets, counted\n * on 17 September: 28 were identical in both, 21 were the same check written two\n * ways, 2 read each app's real page, and 14 read code only one app has. This is\n * the first three groups as one list. The fourth stays in the app's own checks\n * file, beside this list.\n *\n * Where Peek and Ship wrote one check two ways, the check here runs both, so it\n * passes wherever either did: the raw-link probe with both of their texts, the\n * token probes against each token config the app has.\n *\n * `scripts/gates-compare.mjs` holds this list to Peek's and Ship's: every check\n * of theirs must be here, or be about code only that app has, or the comparison\n * fails and names it.\n *\n * A check for a ticket not built yet is a first guess at the evidence, taken\n * from the ticket's own text; the ticket that builds it changes it here.\n */\nimport type { CheckResult, GateCheck, GateHelpers, GateTicket } from './status'\n\nexport interface AppCheckOptions {\n /** Where the app is inside the repository: `web` in Ship, `.` elsewhere. */\n app?: string\n /** A real page of the app, relative to the app, full of elements that are not controls. */\n page: string\n /** The ticket the gate chain is listed under, and whether this repo owns it: UIG-3 in Peek, UIG-4 in Ship. */\n chain?: { ref: string; owner: boolean }\n /** The token lint configs the app has, relative to the app. */\n tokenConfigs?: string[]\n /** The name of the app's header component a hand-made header row should name (UIG-22's first guess). */\n headers?: string[]\n}\n\nexport const APP_TICKET_TITLES: Record<string, string> = {\n 'UIG-2': 'The tracking rails \u2014 GATES.md, the status script, the guide committed',\n 'UIG-3': 'Tracer bullet \u2014 one rule, end to end, blocking in Peek',\n 'UIG-4': 'The same chain, blocking in Ship',\n 'UIG-6': 'Branch protection \u2014 the backstop, all three repos',\n 'UIG-7': 'Lint rule \u2014 every remaining raw element',\n 'UIG-8': 'Lint rule \u2014 forbid the reach',\n 'UIG-9': 'Lint rule \u2014 the className allow-list',\n 'UIG-10': 'create-app \u2014 a command that makes a new Estiva app that runs',\n 'UIG-13': \"The registry widens to Peek's 115 and Ship's 74, with classification\",\n 'UIG-19': 'Lock the contract in CI',\n 'UIG-20': 'The Claude skill, reading the registry',\n 'UIG-21': 'CLAUDE.md becomes an index, not a lecture',\n 'UIG-22': 'Fingerprint \u2014 a hand-made header row',\n 'UIG-23': 'Fingerprint \u2014 a hand-made empty state',\n 'UIG-24': 'Fingerprint \u2014 a browser tooltip where ours belongs',\n 'UIG-25': 'Fingerprint \u2014 a component copied out by hand',\n 'UIG-27': 'The components the apps had to build themselves \u2014 Link, ProgressBar, EmptyState padding',\n 'UIG-28': 'Close the two holes in the token contract \u2014 arbitrary values, and inline style',\n 'UIG-30': 'RichText \u2014 one component that draws a message\\'s text, for both apps',\n 'UIG-32': 'Peek and Ship take their gate pieces from the package',\n 'UIG-37': 'The gates refuse a TypeScript eslint-disable comment \u2014 know the rule names, rules off',\n}\n\n/** Run several checks as one: the first that does not pass is the answer. */\nfunction all(h: GateHelpers, steps: (() => CheckResult | Promise<CheckResult>)[], label: string): () => Promise<CheckResult> {\n return async () => {\n for (const step of steps) {\n const r = await step()\n if (r.result !== 'pass') return r\n }\n return h.PASS(label)\n }\n}\n\nexport function appChecks(h: GateHelpers, { app = '.', page, chain = { ref: 'UIG-3', owner: false }, tokenConfigs = ['eslint.tokens.config.js', 'eslint.config.js'], headers = ['ContainerHeader', 'SectionHeader'] }: AppCheckOptions): GateTicket[] {\n const WEB = app === '.' ? '' : `${app.replace(/\\/$/, '')}/`\n const cwd = app === '.' ? undefined : app\n const PROBE = 'src/components/__gates_probe__.tsx'\n const GATES = 'eslint.gates.config.js'\n const at = (rel: string) => `${WEB}${rel}`\n const probe = (code: string, expect: 'error' | 'warning' | 'none', mentions?: string, file = PROBE, config = GATES) => () =>\n h.lint({ ...(cwd ? { cwd } : {}), config, file, code, expect, mentions })\n const component = (body: string) => `export function Probe() {\\n return ${body}\\n}\\n`\n const installed = at('node_modules/@estiva-app/ui/dist/index.d.ts')\n const realPage = (): Promise<CheckResult> => h.lint({ ...(cwd ? { cwd } : {}), config: GATES, file: page, code: h.read(at(page)), expect: 'none' })\n const button = component('<button type=\"button\">x</button>')\n /** A line TypeScript's own lint writes; ESLint refuses it where TypeScript's plugin is unknown (UIG-37). */\n const typescriptDirective = '// eslint-disable-next-line @typescript-eslint/no-explicit-any\\nexport const probe: any = 1\\n'\n const ticket = (ref: string, checks: GateCheck[], owner = false): GateTicket => ({ ref, title: APP_TICKET_TITLES[ref], owner, checks })\n\n const tokenProbe = (code: string, expect: 'error' | 'warning' | 'none', mentions?: string, file = PROBE) =>\n all(h, tokenConfigs.map((config) => probe(code, expect, mentions, file, config)), `${tokenConfigs.join(' and ')}: as expected`)\n\n return [\n ticket('UIG-2', [\n { what: 'npm run gates:status is wired', run: () => h.script('package.json', 'gates:status') },\n ]),\n ticket(chain.ref, [\n { what: 'the gate lint config loads', run: () => h.loads(at(GATES)) },\n { what: `${at('package.json')} has lint:rules`, run: () => h.script(at('package.json'), 'lint:rules') },\n { what: 'CI runs lint:rules', run: () => h.ci('lint:rules') },\n { what: 'the committed hook runs the gate lint', run: () => h.hook('.claude/settings.json', 'gates') },\n { what: '.gates-count.json is committed and parses', run: () => h.json(h.exists('.gates-count.json') ? '.gates-count.json' : at('.gates-count.json')) },\n { what: 'a raw <button> is an error naming Button', run: probe(button, 'error', 'Button') },\n { what: 'the same in a story', run: probe(button, 'error', 'Button', 'src/stories/__gates_probe__.stories.tsx') },\n { what: 'a test file is not checked, while source is', run: async () => {\n const source = await probe(button, 'error', 'Button')()\n if (source.result !== 'pass') return h.FAIL(`source is not checked yet, so this proves nothing: ${source.detail}`)\n return probe(button, 'none', undefined, 'src/components/__gates_probe__.test.tsx')()\n } },\n { what: 'the debt list exists', run: () => h.file('docs/GATES-DEBT.md') },\n ], chain.owner),\n ticket('UIG-6', [\n { what: 'GitHub requires the check gate to merge into main', run: () => h.protectedBranch(/^gate$/) },\n { what: \"CI's job gate runs lint:rules\", run: () => h.ciJob('gate', 'lint:rules') },\n ]),\n ticket('UIG-7', [\n { what: 'a raw <input> is an error naming TextInput', run: probe(component('<input />'), 'error', 'TextInput') },\n { what: 'a raw <a> is an error naming Link', run: all(h, [probe(component('<a href=\"/topics\">Topics</a>'), 'error', 'Link'), probe(component('<a href=\"/issues\">Issues</a>'), 'error', 'Link')], 'a raw <a> is an error naming Link') },\n { what: 'a raw <form> is an error naming Form, and a file input one naming FilePicker', run: async () => {\n const form = await probe(component('<form />'), 'error', '`Form`')()\n if (form.result !== 'pass') return form\n return probe(component('<input type=\"file\" />'), 'error', 'FilePicker')()\n } },\n { what: '<textarea>, <select>, <dialog> and <label> name Textarea, Select, DialogShell and Field', run: async () => {\n for (const [element, part] of [['textarea', 'Textarea'], ['select', 'Select'], ['dialog', 'DialogShell'], ['label', 'Field']]) {\n const one = await probe(component(`<${element} />`), 'error', `\\`${part}\\``)()\n if (one.result !== 'pass') return one\n }\n return h.PASS('all four are errors, each naming its part')\n } },\n { what: 'an element the package has no part for is an error that says to ask for one', run: probe(component('<iframe title=\"Map\" />'), 'error', 'ask Katerina') },\n { what: 'an escaped element is not an error, while the same element is', run: async () => {\n const raw = await probe(component('<form />'), 'error')()\n if (raw.result !== 'pass') return h.FAIL(`a raw <form> is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe('export function Probe() {\\n return (\\n // @estiva-escape: a probe that keeps its element on purpose\\n <form />\\n )\\n}\\n', 'none')()\n } },\n { what: 'a real page, full of elements that are not controls, gets no error', run: realPage },\n ]),\n ticket('UIG-8', [\n { what: 'a Base UI import is an error naming the part built on it', run: probe(\"import { Popover } from '@base-ui/react/popover'\\nexport const Probe = Popover\\n\", 'error', '`Popover`') },\n { what: 'createPortal is an error naming the floating parts', run: probe(\"import { createPortal } from 'react-dom'\\nexport const Probe = (to: HTMLElement) => createPortal(<span />, to)\\n\", 'error', '`DialogShell`') },\n { what: 'a key listener on the whole page is an error, in a .ts file too', run: probe(\"export function listen() {\\n window.addEventListener('keydown', () => {})\\n}\\n\", 'error', '`DialogShell`', 'src/lib/__gates_probe__.ts') },\n { what: 'arrow keys by hand are an error; Enter in a field is not', run: async () => {\n const arrows = await probe(\"export const onKeyDown = (e: KeyboardEvent) => e.key === 'ArrowDown'\\n\", 'error', '`Menu`', 'src/lib/__gates_probe__.ts')()\n if (arrows.result !== 'pass') return arrows\n return probe(\"export const onKeyDown = (e: KeyboardEvent) => e.key === 'Enter'\\n\", 'none', undefined, 'src/lib/__gates_probe__.ts')()\n } },\n { what: 'a hand-written role=\"option\" is an error naming Select', run: probe(component('<div role=\"option\" />'), 'error', '`Select`') },\n { what: 'tabIndex={0} on a div is an error; tabIndex={-1} is not', run: async () => {\n const zero = await probe(component('<div tabIndex={0} />'), 'error', '`Button`')()\n if (zero.result !== 'pass') return h.FAIL(`tabIndex={0} is not caught yet, so this proves nothing: ${zero.detail}`)\n return probe(component('<div tabIndex={-1} />'), 'none')()\n } },\n { what: 'overflow-y-auto is an error naming ScrollArea', run: probe(component('<div className=\"h-64 overflow-y-auto\" />'), 'error', '`ScrollArea`') },\n { what: 'an escaped listener is not an error, while the same listener is', run: async () => {\n const raw = await probe(\"export function listen() {\\n document.addEventListener('mousedown', () => {})\\n}\\n\", 'error', undefined, 'src/lib/__gates_probe__.ts')()\n if (raw.result !== 'pass') return h.FAIL(`a page listener is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe(\"export function listen() {\\n // @estiva-escape: a probe that keeps its listener on purpose\\n document.addEventListener('mousedown', () => {})\\n}\\n\", 'none', undefined, 'src/lib/__gates_probe__.ts')()\n } },\n { what: 'a real page gets no error', run: realPage },\n ]),\n ticket('UIG-9', [\n { what: 'a border passed into Button is an error naming Button', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button className=\"border\">x</Button>')}`, 'error', 'on `Button` changes how it looks') },\n { what: 'placement passed into Button is not', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button className=\"mt-2 w-full flex-1 relative\">x</Button>')}`, 'none') },\n { what: 'an escaped look is not an error, while the same look is', run: async () => {\n const raw = await probe(`import { Link } from '@estiva-app/ui'\\n${component('<Link href=\"/x\" className=\"after:absolute after:inset-0\">x</Link>')}`, 'error', '`Link`')()\n if (raw.result !== 'pass') return h.FAIL(`a restyled part is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe(`import { Link } from '@estiva-app/ui'\\nexport function Probe() {\\n return (\\n // @estiva-escape: a probe that keeps its row link on purpose\\n <Link href=\"/x\" className=\"after:absolute after:inset-0\">x</Link>\\n )\\n}\\n`, 'none')()\n } },\n { what: 'a real page gets no error', run: realPage },\n ]),\n // UIG-10, reopened 18 September: a tab holds one relay client, the way Peek and Ship hold\n // theirs and a made app is written to. Measured on both mains that day: one each, and no\n // socket opened any other way. A second holder, or a socket of the app's own, is a second\n // connection the relay signs in and budgets separately.\n ticket('UIG-10', [\n { what: 'the app holds one relay client, at module level, and opens no socket of its own', run: () => {\n const sources = h.listFiles(at('src'), (n) => /\\.tsx?$/.test(n) && !/\\.(test|stories)\\.tsx?$/.test(n))\n const own = sources.filter((f) => /\\bcreateLiveClient\\(|\\bcreateLiveRelay\\(|\\bnew WebSocket\\(/.test(h.read(f)))\n if (own.length) return h.FAIL(`opens a socket of its own (${own.join(', ')}): hold one client from createLiveClientHolder()`)\n const holders = sources.filter((f) => /^\\s*(?:export\\s+)?const\\s+\\w+\\s*=\\s*createLiveClientHolder\\(\\)/m.test(h.read(f)))\n if (holders.length !== 1) return h.FAIL(holders.length ? `${holders.length} client holders (${holders.join(', ')}): a tab holds one` : 'no module-level createLiveClientHolder(): the app has no relay client')\n return h.PASS(`one client for the tab, held in ${holders[0]}`)\n } },\n ]),\n // Built by UIG-13, 18 September. An app's catalogue is built fresh from its code\n // and never committed: the package is public and the apps are private (Katerina).\n ticket('UIG-13', [\n { what: \"npm run ui:find is the package's search, over the package and this app\", run: () => h.contains(at('package.json'), /\"ui:find\":\\s*\"estiva-ui find\\b/, \"ui:find runs the package's estiva-ui find\") },\n { what: \"npm run registry:check is the package's check\", run: () => h.contains(at('package.json'), /\"registry:check\":\\s*\"estiva-ui check\\b/, 'registry:check runs estiva-ui check') },\n { what: \"CI's job gate refuses a part with no description\", run: () => h.ciJob('gate', 'registry:check') },\n // In the .gitignore of the app's own folder, where `estiva-ui build` would write it.\n { what: 'the catalogue is never committed: git ignores registry.json', run: () => h.contains(at('.gitignore'), /^\\/?registry\\.json\\r?$/m, `${at('.gitignore')} ignores registry.json`) },\n { what: 'every part is described and sorted, and every file is accounted for', run: () => h.catalogue(at('.')) },\n ]),\n ticket('UIG-19', [\n // The contract runs inside `estiva-ui check`, which the gate job already runs\n // (UIG-13), so an app changes no script: it takes the package that has it.\n { what: 'the installed package checks the usage-page contract and the story links', run: () => h.contains(at('node_modules/@estiva-app/ui/dist/registry/cli.js'), 'the usage-page contract', \"the installed estiva-ui check runs the contract\") },\n { what: \"CI's job gate runs it\", run: () => h.ciJob('gate', 'registry:check') },\n { what: 'the app keeps no copy of it in its own gates-checks.mjs', run: () => h.exists(at('scripts/gates-checks.mjs')) ? h.lacks(at('scripts/gates-checks.mjs'), '\"When not\"', 'the app checks no page sections itself') : h.PASS('the app has no gates-checks.mjs') },\n ]),\n ticket('UIG-20', [\n { what: 'a committed skill runs ui:find', run: () => (h.listFiles('.claude/skills', (n) => n === 'SKILL.md').some((f) => h.read(f).includes('ui:find')) ? h.PASS('a skill runs ui:find') : h.FAIL('no skill in .claude/skills runs ui:find')) },\n ]),\n ticket('UIG-21', [\n { what: 'path-scoped instructions exist', run: () => (h.listFiles('.claude/rules', (n) => n.endsWith('.md')).some((f) => /^paths:/m.test(h.read(f))) ? h.PASS('.claude/rules has paths: instructions') : h.FAIL('no .claude/rules file with paths:')) },\n ]),\n ticket('UIG-22', [\n { what: 'a hand-made header row is an error naming the header part', run: all(h, headers.map((name) => probe(component(`<section className=\"flex flex-col\"><div className=\"flex items-center px-3 py-2\"><span>${name === 'SectionHeader' ? 'Issues' : 'Folders'}</span></div><div /></section>`), 'error', name)), 'a hand-made header row is an error naming the header part') },\n ]),\n ticket('UIG-23', [\n { what: 'a hand-made empty line is an error naming EmptyState', run: probe(\"export function Probe({ items }: { items: string[] }) {\\n return <div>{items.length === 0 ? <p className=\\\"text-text-secondary\\\">Nothing here</p> : items.map((i) => <span key={i}>{i}</span>)}</div>\\n}\\n\", 'error', 'EmptyState') },\n ]),\n ticket('UIG-24', [\n { what: 'a title= is an error naming WithTooltip', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button title=\"Delete\">x</Button>')}`, 'error', 'WithTooltip') },\n ]),\n ticket('UIG-25', [\n { what: \"SectionLabel's class list, typed by hand, is a warning\", run: probe(component('<span className=\"text-[10px] uppercase tracking-wide text-text-muted\">Label</span>'), 'warning', 'SectionLabel') },\n ]),\n ticket('UIG-27', [\n { what: 'the installed package has Link', run: () => h.contains(installed, /\\bLink\\b/, 'the installed @estiva-app/ui exports Link') },\n { what: 'the installed package has ProgressBar', run: () => h.contains(installed, /\\bProgressBar\\b/, 'the installed @estiva-app/ui exports ProgressBar') },\n ]),\n ticket('UIG-28', [\n { what: 'text-[14px] is an error', run: tokenProbe(component('<div className=\"text-[14px]\">x</div>'), 'error') },\n { what: 'a colour in an inline style is an error', run: tokenProbe(component(\"<div style={{ backgroundColor: '#ff0000' }}>x</div>\"), 'error') },\n { what: 'h-[240px] is a warning', run: tokenProbe(component('<div className=\"h-[240px]\">x</div>'), 'warning') },\n { what: 'h-[240px] is not an error', run: tokenProbe(component('<div className=\"h-[240px]\">x</div>'), 'none') },\n { what: 'a hand-written line height behind an arbitrary variant is an error', run: tokenProbe(component('<div className=\"[&_p]:leading-[1.4]\">x</div>'), 'error') },\n { what: 'a raw colour in a class is an error (the pattern that matched nothing, R1)', run: tokenProbe(component('<div className=\"bg-[#5c69dc]\">x</div>'), 'error', 'raw colour') },\n { what: 'a width and height from a prop pass', run: tokenProbe(\"export function Probe({ size }: { size: number }) {\\n return <div style={{ width: size, height: size }}>x</div>\\n}\\n\", 'none') },\n { what: 'a test file is not checked for hand-written values, while source is', run: all(h, tokenConfigs.map((config) => async () => {\n const code = component(\"<div className=\\\"text-[14px]\\\" style={{ color: 'red' }}>x</div>\")\n const source = await probe(code, 'error', undefined, PROBE, config)()\n if (source.result !== 'pass') return h.FAIL(`source is not checked yet, so this proves nothing: ${source.detail}`)\n return probe(code, 'none', undefined, 'src/components/__gates_probe__.test.tsx', config)()\n }), 'a test file is not checked for hand-written values, while source is') },\n ]),\n ticket('UIG-30', [\n { what: 'the installed package has RichText', run: () => h.contains(installed, /\\bRichText\\b/, 'the installed @estiva-app/ui exports RichText') },\n ]),\n // Katerina's ruling of 17 September (docs/GATES.md \u00A723): one copy, in the package. An app made\n // by create-estiva-app passes this from its first commit; Peek and Ship do once UIG-32 lands.\n ticket('UIG-32', [\n { what: 'the gate pieces come from the package, and no repo keeps a copy', run: all(h, [\n () => h.contains(at('eslint.gates.config.js'), /from '@estiva-app\\/ui\\/gates'/, 'the gate config imports @estiva-app/ui/gates'),\n () => h.hook('.claude/settings.json', '@estiva-app/ui/dist/gates/cli.js'),\n // `estiva-gates status` where npm can find the command; by path where it cannot,\n // because the install is in the app's folder and the script is in the repo's top one (Ship).\n () => h.contains('package.json', /\"gates:status\":\\s*\"[^\"]*(?:estiva-gates|@estiva-app\\/ui\\/dist\\/gates\\/cli\\.js)[^\"]*status/, \"gates:status runs the package's engine\"),\n () => (h.exists('scripts/gates-status.mjs') ? h.FAIL('scripts/gates-status.mjs is a copy of the status engine: run estiva-gates status instead') : h.PASS('no copy of the status engine')),\n ], 'the gate config, the hook and gates:status are the package\\'s; no copy of the engine') },\n ]),\n ticket('UIG-37', [\n { what: 'a TypeScript eslint-disable comment breaks neither the gate nor the token lint', run: all(h, [\n probe(typescriptDirective, 'none'),\n tokenProbe(typescriptDirective, 'none'),\n ], `${[GATES, ...tokenConfigs].join(', ')}: a TypeScript directive is no error`) },\n { what: 'a token lint on its own is the package\\'s tokenConfig, not built by hand', run: () =>\n (h.exists(at('eslint.tokens.config.js'))\n ? h.contains(at('eslint.tokens.config.js'), /\\btokenConfig\\(/, 'eslint.tokens.config.js runs tokenConfig from @estiva-app/ui/gates')\n : h.PASS('no token lint on its own')) },\n ]),\n ]\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;AAmCA,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAE9B,OAAO,uBAAuB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,UAAU,kBAAkB,UAAU,wBAAwB;AAwBhE,IAAM,qBAAqB,CAAC,SAAS,qBAAqB,eAAe;AAUzE,IAAM,gBAA+C,EAAE,sBAAsB,iBAAkC;AAEtH,IAAM,qBAAqB;AAAA,EACzB,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO,CAAC,EAAE,MAAM,UAAU,GAAG,EAAE,MAAM,eAAe,CAAC;AACvD;AAEA,IAAM,QAAQ;AAAA,EACZ,KAAK;AAAA,IACH,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,SAAS;AAAA,IACT,KAAK;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF;AAMO,SAAS,UAAU,EAAE,iBAAiB,sBAAsB,WAAW,MAAM,IAAsB,CAAC,GAAkB;AAC3H,QAAM,QAAQ,MAAM,QAAQ;AAC5B,SAAO;AAAA,IACL,OAAO,CAAC,eAAe;AAAA,IACvB,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,eAAe,EAAE,cAAc,EAAE,KAAK,KAAK,EAAE;AAAA,IAC/C;AAAA,IACA,SAAS,EAAE,sBAAsB,kBAAkB;AAAA,IACnD,UAAU;AAAA,MACR,sBAAsB;AAAA,QACpB;AAAA,QACA,WAAW,CAAC,GAAG,oBAAoB,GAAG,kBAAkB;AAAA,MAC1D;AAAA,IACF;AAAA,IACA,OAAO;AAAA,MACL,yCAAyC;AAAA,MACzC,4CAA4C;AAAA,QAC1C;AAAA,QACA;AAAA,UACE,UAAU;AAAA,YACR,EAAE,SAAS,uDAAuD,SAAS,MAAM,KAAK;AAAA,YACtF;AAAA,cACE,SACE;AAAA,cACF,SAAS,MAAM;AAAA,YACjB;AAAA,YACA,EAAE,SAAS,qFAAqF,SAAS,MAAM,IAAI;AAAA,YACnH;AAAA,cACE,SAAS;AAAA,cACT,SAAS,MAAM;AAAA,YACjB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAKA,IAAM,IAAI;AACV,IAAM,aAAa;AACnB,IAAM,eAAe;AAAA,EACnB,MAAM,GAAG,CAAC,cAAc,UAAU;AAAA,EAClC,QAAQ,GAAG,CAAC;AAAA,EACZ,QAAQ,GAAG,CAAC;AAAA,EACZ,SAAS,GAAG,CAAC;AAAA,EACb,OAAO,GAAG,CAAC;AACb;AAEA,IAAM,gCACJ;AAWF,SAAS,aAAqB;AAC5B,QAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,QAAM,aAAaA,SAAQ,wBAAwB;AACnD,SAAO,WAAW,cAAc,IAAI,IAAI,4BAA4B,YAAY,GAAG,CAAC,CAAC;AACvF;AAUO,SAAS,YAAY,EAAE,WAAW,MAAM,IAAwC,CAAC,GAAkB;AACxG,QAAM,QAAQ,MAAM,QAAQ;AAC5B,QAAM,SAAS,WAAW;AAG1B,QAAM,eAAyC,CAAC;AAChD,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,MAAM,OAAO,QAAQ,GAAG;AACxE,UAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAC9C,KAAC,aAAa,IAAI,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,EAAE;AAAA,EAClD;AACA,QAAM,gBAA0C,CAAC;AACjD,aAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,OAAO,MAAM,OAAO,YAAY,EAAG,EAAC,cAAc,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,YAAY,YAAY,WAAW,IAAI,EAAE;AAEjK,SAAO;AAAA,IACL,OAAO,CAAC,eAAe;AAAA,IACvB,SAAS,CAAC,gBAAgB,eAAe;AAAA,IACzC,SAAS,EAAE,gBAAgB,mBAAmB,iBAAiB,kBAAkB;AAAA,IACjF,OAAO;AAAA,MACL,sCAAsC;AAAA,QACpC;AAAA,QACA;AAAA,UACE,UAAU;AAAA,YACR,GAAG,OAAO,QAAQ,YAAY,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO;AAAA,cACtD,SAAS,GAAG,CAAC,WAAW,KAAK,QAAQ,KAAK,KAAK,CAAC;AAAA,cAChD,SAAS,4CAA4C,MAAM,KAAK,MAAM,CAAC;AAAA,YACzE,EAAE;AAAA,YACF;AAAA,cACE,SAAS,aAAa;AAAA,cACtB,SAAS,MAAM;AAAA,YACjB;AAAA,YACA,GAAG,OAAO,QAAQ,aAAa,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO;AAAA,cACvD,SAAS,GAAG,CAAC,2DAA2D,KAAK,QAAQ,KAAK,KAAK,CAAC;AAAA,cAChG,SAAS,uCAAuC,MAAM,KAAK,MAAM,CAAC;AAAA,YACpE,EAAE;AAAA,YACF;AAAA,cACE,SAAS,aAAa;AAAA,cACtB,SAAS,MAAM;AAAA,YACjB;AAAA,YACA;AAAA,cACE,SAAS,aAAa;AAAA,cACtB,SAAS,MAAM;AAAA,YACjB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,uCAAuC;AAAA,QACrC;AAAA,QACA;AAAA,UACE,UAAU;AAAA,YACR;AAAA,cACE,SAAS,aAAa;AAAA,cACtB,SAAS;AAAA,YACX;AAAA,YACA;AAAA,cACE,SAAS,aAAa;AAAA,cACtB,SAAS;AAAA,YACX;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,wBAAwB;AAAA,QACtB;AAAA,QACA;AAAA,UACE,UAAU,mGAAmG,6BAA6B;AAAA,UAC1I,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAQO,SAAS,YAAY,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,GAAG,GAAG,QAAQ,IAAwB,CAAC,GAAoB;AAC9G,SAAO;AAAA,IACL,EAAE,SAAS,CAAC,GAAG,oBAAoB,GAAG,OAAO,EAAE;AAAA,IAC/C;AAAA,MACE,SAAS,EAAE,GAAG,eAAe,GAAG,MAAM;AAAA;AAAA;AAAA;AAAA,MAItC,eAAe,EAAE,+BAA+B,MAAM;AAAA,IACxD;AAAA,IACA,UAAU,OAAO;AAAA,IACjB,YAAY,EAAE,UAAU,QAAQ,SAAS,CAAC;AAAA,EAC5C;AACF;;;AChQA,OAAOC,wBAAuB;AAC9B,SAAS,UAAUC,yBAAwB;AAC3C,OAAO,YAAY;AAsBZ,SAAS,SAAS,EAAE,WAAW,MAAM,IAAyC,CAAC,GAAkB;AACtG,SAAO;AAAA,IACL,GAAI,aAAa,YAAY,OAAO,QAAQ,UAAU,OAAO,QAAQ;AAAA,IACrE,OAAO,aAAa,YAAY,CAAC,cAAc,IAAI,CAAC,mBAAmB;AAAA,IACvE,SAAS,CAAC,gBAAgB,eAAe;AAAA,IACzC,iBAAiB;AAAA,MACf,QAAQC;AAAA,MACR,eAAe,EAAE,cAAc,EAAE,KAAK,KAAK,EAAE;AAAA,IAC/C;AAAA,EACF;AACF;AAOO,SAAS,WAAW,EAAE,WAAW,OAAO,QAAQ,CAAC,GAAG,UAAU,CAAC,EAAE,IAAuB,CAAC,GAAoB;AAClH,SAAO;AAAA,IACL,EAAE,SAAS,CAAC,GAAG,oBAAoB,GAAG,OAAO,EAAE;AAAA,IAC/C;AAAA,MACE,SAAS;AAAA,QACP,GAAG;AAAA,QACH,GAAG;AAAA,QACH,sBAAsBC;AAAA,QACtB,gBAAgBA;AAAA,QAChB,iBAAiBA;AAAA,MACnB;AAAA;AAAA;AAAA;AAAA,MAIA,eAAe,EAAE,+BAA+B,MAAM;AAAA,IACxD;AAAA,IACA,SAAS,EAAE,SAAS,CAAC;AAAA,EACvB;AACF;;;ACzCO,IAAM,oBAA4C;AAAA,EACvD,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AACZ;AAGA,SAAS,IAAI,GAAgB,OAAqD,OAA2C;AAC3H,SAAO,YAAY;AACjB,eAAW,QAAQ,OAAO;AACxB,YAAM,IAAI,MAAM,KAAK;AACrB,UAAI,EAAE,WAAW,OAAQ,QAAO;AAAA,IAClC;AACA,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AACF;AAEO,SAAS,UAAU,GAAgB,EAAE,MAAM,KAAK,MAAM,QAAQ,EAAE,KAAK,SAAS,OAAO,MAAM,GAAG,eAAe,CAAC,2BAA2B,kBAAkB,GAAG,UAAU,CAAC,mBAAmB,eAAe,EAAE,GAAkC;AACpP,QAAM,MAAM,QAAQ,MAAM,KAAK,GAAG,IAAI,QAAQ,OAAO,EAAE,CAAC;AACxD,QAAM,MAAM,QAAQ,MAAM,SAAY;AACtC,QAAM,QAAQ;AACd,QAAM,QAAQ;AACd,QAAM,KAAK,CAAC,QAAgB,GAAG,GAAG,GAAG,GAAG;AACxC,QAAM,QAAQ,CAAC,MAAc,QAAsC,UAAmB,OAAO,OAAO,SAAS,UAAU,MACrH,EAAE,KAAK,EAAE,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC,GAAI,QAAQ,MAAM,MAAM,QAAQ,SAAS,CAAC;AAC1E,QAAM,YAAY,CAAC,SAAiB;AAAA,WAAuC,IAAI;AAAA;AAAA;AAC/E,QAAM,YAAY,GAAG,6CAA6C;AAClE,QAAM,WAAW,MAA4B,EAAE,KAAK,EAAE,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC,GAAI,QAAQ,OAAO,MAAM,MAAM,MAAM,EAAE,KAAK,GAAG,IAAI,CAAC,GAAG,QAAQ,OAAO,CAAC;AAClJ,QAAM,SAAS,UAAU,kCAAkC;AAE3D,QAAM,sBAAsB;AAC5B,QAAM,SAAS,CAAC,KAAa,QAAqB,QAAQ,WAAuB,EAAE,KAAK,OAAO,kBAAkB,GAAG,GAAG,OAAO,OAAO;AAErI,QAAM,aAAa,CAAC,MAAc,QAAsC,UAAmB,OAAO,UAChG,IAAI,GAAG,aAAa,IAAI,CAAC,WAAW,MAAM,MAAM,QAAQ,UAAU,MAAM,MAAM,CAAC,GAAG,GAAG,aAAa,KAAK,OAAO,CAAC,eAAe;AAEhI,SAAO;AAAA,IACL,OAAO,SAAS;AAAA,MACd,EAAE,MAAM,iCAAiC,KAAK,MAAM,EAAE,OAAO,gBAAgB,cAAc,EAAE;AAAA,IAC/F,CAAC;AAAA,IACD,OAAO,MAAM,KAAK;AAAA,MAChB,EAAE,MAAM,8BAA8B,KAAK,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,EAAE;AAAA,MACpE,EAAE,MAAM,GAAG,GAAG,cAAc,CAAC,mBAAmB,KAAK,MAAM,EAAE,OAAO,GAAG,cAAc,GAAG,YAAY,EAAE;AAAA,MACtG,EAAE,MAAM,sBAAsB,KAAK,MAAM,EAAE,GAAG,YAAY,EAAE;AAAA,MAC5D,EAAE,MAAM,yCAAyC,KAAK,MAAM,EAAE,KAAK,yBAAyB,OAAO,EAAE;AAAA,MACrG,EAAE,MAAM,6CAA6C,KAAK,MAAM,EAAE,KAAK,EAAE,OAAO,mBAAmB,IAAI,sBAAsB,GAAG,mBAAmB,CAAC,EAAE;AAAA,MACtJ,EAAE,MAAM,4CAA4C,KAAK,MAAM,QAAQ,SAAS,QAAQ,EAAE;AAAA,MAC1F,EAAE,MAAM,uBAAuB,KAAK,MAAM,QAAQ,SAAS,UAAU,yCAAyC,EAAE;AAAA,MAChH,EAAE,MAAM,+CAA+C,KAAK,YAAY;AACtE,cAAM,SAAS,MAAM,MAAM,QAAQ,SAAS,QAAQ,EAAE;AACtD,YAAI,OAAO,WAAW,OAAQ,QAAO,EAAE,KAAK,sDAAsD,OAAO,MAAM,EAAE;AACjH,eAAO,MAAM,QAAQ,QAAQ,QAAW,yCAAyC,EAAE;AAAA,MACrF,EAAE;AAAA,MACF,EAAE,MAAM,wBAAwB,KAAK,MAAM,EAAE,KAAK,oBAAoB,EAAE;AAAA,IAC1E,GAAG,MAAM,KAAK;AAAA,IACd,OAAO,SAAS;AAAA,MACd,EAAE,MAAM,qDAAqD,KAAK,MAAM,EAAE,gBAAgB,QAAQ,EAAE;AAAA,MACpG,EAAE,MAAM,iCAAiC,KAAK,MAAM,EAAE,MAAM,QAAQ,YAAY,EAAE;AAAA,IACpF,CAAC;AAAA,IACD,OAAO,SAAS;AAAA,MACd,EAAE,MAAM,8CAA8C,KAAK,MAAM,UAAU,WAAW,GAAG,SAAS,WAAW,EAAE;AAAA,MAC/G,EAAE,MAAM,qCAAqC,KAAK,IAAI,GAAG,CAAC,MAAM,UAAU,8BAA8B,GAAG,SAAS,MAAM,GAAG,MAAM,UAAU,8BAA8B,GAAG,SAAS,MAAM,CAAC,GAAG,mCAAmC,EAAE;AAAA,MACtO,EAAE,MAAM,gFAAgF,KAAK,YAAY;AACvG,cAAM,OAAO,MAAM,MAAM,UAAU,UAAU,GAAG,SAAS,QAAQ,EAAE;AACnE,YAAI,KAAK,WAAW,OAAQ,QAAO;AACnC,eAAO,MAAM,UAAU,uBAAuB,GAAG,SAAS,YAAY,EAAE;AAAA,MAC1E,EAAE;AAAA,MACF,EAAE,MAAM,2FAA2F,KAAK,YAAY;AAClH,mBAAW,CAAC,SAAS,IAAI,KAAK,CAAC,CAAC,YAAY,UAAU,GAAG,CAAC,UAAU,QAAQ,GAAG,CAAC,UAAU,aAAa,GAAG,CAAC,SAAS,OAAO,CAAC,GAAG;AAC7H,gBAAM,MAAM,MAAM,MAAM,UAAU,IAAI,OAAO,KAAK,GAAG,SAAS,KAAK,IAAI,IAAI,EAAE;AAC7E,cAAI,IAAI,WAAW,OAAQ,QAAO;AAAA,QACpC;AACA,eAAO,EAAE,KAAK,2CAA2C;AAAA,MAC3D,EAAE;AAAA,MACF,EAAE,MAAM,+EAA+E,KAAK,MAAM,UAAU,wBAAwB,GAAG,SAAS,cAAc,EAAE;AAAA,MAChK,EAAE,MAAM,iEAAiE,KAAK,YAAY;AACxF,cAAM,MAAM,MAAM,MAAM,UAAU,UAAU,GAAG,OAAO,EAAE;AACxD,YAAI,IAAI,WAAW,OAAQ,QAAO,EAAE,KAAK,2DAA2D,IAAI,MAAM,EAAE;AAChH,eAAO,MAAM,mIAAmI,MAAM,EAAE;AAAA,MAC1J,EAAE;AAAA,MACF,EAAE,MAAM,sEAAsE,KAAK,SAAS;AAAA,IAC9F,CAAC;AAAA,IACD,OAAO,SAAS;AAAA,MACd,EAAE,MAAM,4DAA4D,KAAK,MAAM,oFAAoF,SAAS,WAAW,EAAE;AAAA,MACzL,EAAE,MAAM,sDAAsD,KAAK,MAAM,oHAAoH,SAAS,eAAe,EAAE;AAAA,MACvN,EAAE,MAAM,mEAAmE,KAAK,MAAM,mFAAmF,SAAS,iBAAiB,4BAA4B,EAAE;AAAA,MACjO,EAAE,MAAM,4DAA4D,KAAK,YAAY;AACnF,cAAM,SAAS,MAAM,MAAM,0EAA0E,SAAS,UAAU,4BAA4B,EAAE;AACtJ,YAAI,OAAO,WAAW,OAAQ,QAAO;AACrC,eAAO,MAAM,sEAAsE,QAAQ,QAAW,4BAA4B,EAAE;AAAA,MACtI,EAAE;AAAA,MACF,EAAE,MAAM,0DAA0D,KAAK,MAAM,UAAU,uBAAuB,GAAG,SAAS,UAAU,EAAE;AAAA,MACtI,EAAE,MAAM,2DAA2D,KAAK,YAAY;AAClF,cAAM,OAAO,MAAM,MAAM,UAAU,sBAAsB,GAAG,SAAS,UAAU,EAAE;AACjF,YAAI,KAAK,WAAW,OAAQ,QAAO,EAAE,KAAK,2DAA2D,KAAK,MAAM,EAAE;AAClH,eAAO,MAAM,UAAU,uBAAuB,GAAG,MAAM,EAAE;AAAA,MAC3D,EAAE;AAAA,MACF,EAAE,MAAM,iDAAiD,KAAK,MAAM,UAAU,0CAA0C,GAAG,SAAS,cAAc,EAAE;AAAA,MACpJ,EAAE,MAAM,mEAAmE,KAAK,YAAY;AAC1F,cAAM,MAAM,MAAM,MAAM,uFAAuF,SAAS,QAAW,4BAA4B,EAAE;AACjK,YAAI,IAAI,WAAW,OAAQ,QAAO,EAAE,KAAK,8DAA8D,IAAI,MAAM,EAAE;AACnH,eAAO,MAAM,wJAAwJ,QAAQ,QAAW,4BAA4B,EAAE;AAAA,MACxN,EAAE;AAAA,MACF,EAAE,MAAM,6BAA6B,KAAK,SAAS;AAAA,IACrD,CAAC;AAAA,IACD,OAAO,SAAS;AAAA,MACd,EAAE,MAAM,yDAAyD,KAAK,MAAM;AAAA,EAA4C,UAAU,uCAAuC,CAAC,IAAI,SAAS,kCAAkC,EAAE;AAAA,MAC3N,EAAE,MAAM,uCAAuC,KAAK,MAAM;AAAA,EAA4C,UAAU,4DAA4D,CAAC,IAAI,MAAM,EAAE;AAAA,MACzL,EAAE,MAAM,2DAA2D,KAAK,YAAY;AAClF,cAAM,MAAM,MAAM,MAAM;AAAA,EAA0C,UAAU,mEAAmE,CAAC,IAAI,SAAS,QAAQ,EAAE;AACvK,YAAI,IAAI,WAAW,OAAQ,QAAO,EAAE,KAAK,8DAA8D,IAAI,MAAM,EAAE;AACnH,eAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAAoO,MAAM,EAAE;AAAA,MAC3P,EAAE;AAAA,MACF,EAAE,MAAM,6BAA6B,KAAK,SAAS;AAAA,IACrD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,IAKD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,mFAAmF,KAAK,MAAM;AACpG,cAAM,UAAU,EAAE,UAAU,GAAG,KAAK,GAAG,CAAC,MAAM,UAAU,KAAK,CAAC,KAAK,CAAC,0BAA0B,KAAK,CAAC,CAAC;AACrG,cAAM,MAAM,QAAQ,OAAO,CAAC,MAAM,6DAA6D,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAC9G,YAAI,IAAI,OAAQ,QAAO,EAAE,KAAK,8BAA8B,IAAI,KAAK,IAAI,CAAC,kDAAkD;AAC5H,cAAM,UAAU,QAAQ,OAAO,CAAC,MAAM,kEAAkE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AACvH,YAAI,QAAQ,WAAW,EAAG,QAAO,EAAE,KAAK,QAAQ,SAAS,GAAG,QAAQ,MAAM,oBAAoB,QAAQ,KAAK,IAAI,CAAC,uBAAuB,uEAAuE;AAC9M,eAAO,EAAE,KAAK,mCAAmC,QAAQ,CAAC,CAAC,EAAE;AAAA,MAC/D,EAAE;AAAA,IACJ,CAAC;AAAA;AAAA;AAAA,IAGD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,0EAA0E,KAAK,MAAM,EAAE,SAAS,GAAG,cAAc,GAAG,kCAAkC,2CAA2C,EAAE;AAAA,MAC3M,EAAE,MAAM,iDAAiD,KAAK,MAAM,EAAE,SAAS,GAAG,cAAc,GAAG,0CAA0C,qCAAqC,EAAE;AAAA,MACpL,EAAE,MAAM,oDAAoD,KAAK,MAAM,EAAE,MAAM,QAAQ,gBAAgB,EAAE;AAAA;AAAA,MAEzG,EAAE,MAAM,+DAA+D,KAAK,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,2BAA2B,GAAG,GAAG,YAAY,CAAC,wBAAwB,EAAE;AAAA,MACvL,EAAE,MAAM,uEAAuE,KAAK,MAAM,EAAE,UAAU,GAAG,GAAG,CAAC,EAAE;AAAA,IACjH,CAAC;AAAA,IACD,OAAO,UAAU;AAAA;AAAA;AAAA,MAGf,EAAE,MAAM,4EAA4E,KAAK,MAAM,EAAE,SAAS,GAAG,kDAAkD,GAAG,2BAA2B,iDAAiD,EAAE;AAAA,MAChP,EAAE,MAAM,yBAAyB,KAAK,MAAM,EAAE,MAAM,QAAQ,gBAAgB,EAAE;AAAA,MAC9E,EAAE,MAAM,2DAA2D,KAAK,MAAM,EAAE,OAAO,GAAG,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,0BAA0B,GAAG,cAAc,wCAAwC,IAAI,EAAE,KAAK,iCAAiC,EAAE;AAAA,IACvQ,CAAC;AAAA,IACD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,kCAAkC,KAAK,MAAO,EAAE,UAAU,kBAAkB,CAAC,MAAM,MAAM,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,SAAS,CAAC,IAAI,EAAE,KAAK,sBAAsB,IAAI,EAAE,KAAK,yCAAyC,EAAG;AAAA,IAChP,CAAC;AAAA,IACD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,kCAAkC,KAAK,MAAO,EAAE,UAAU,iBAAiB,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,WAAW,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,uCAAuC,IAAI,EAAE,KAAK,mCAAmC,EAAG;AAAA,IACxP,CAAC;AAAA,IACD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,6DAA6D,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC,SAAS,MAAM,UAAU,yFAAyF,SAAS,kBAAkB,WAAW,SAAS,gCAAgC,GAAG,SAAS,IAAI,CAAC,GAAG,2DAA2D,EAAE;AAAA,IAClX,CAAC;AAAA,IACD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,wDAAwD,KAAK,MAAM,6MAA+M,SAAS,YAAY,EAAE;AAAA,IACnT,CAAC;AAAA,IACD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,2CAA2C,KAAK,MAAM;AAAA,EAA4C,UAAU,mCAAmC,CAAC,IAAI,SAAS,aAAa,EAAE;AAAA,IACtL,CAAC;AAAA,IACD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,0DAA0D,KAAK,MAAM,UAAU,oFAAoF,GAAG,WAAW,cAAc,EAAE;AAAA,IAC3M,CAAC;AAAA,IACD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,kCAAkC,KAAK,MAAM,EAAE,SAAS,WAAW,YAAY,2CAA2C,EAAE;AAAA,MACpI,EAAE,MAAM,yCAAyC,KAAK,MAAM,EAAE,SAAS,WAAW,mBAAmB,kDAAkD,EAAE;AAAA,IAC3J,CAAC;AAAA,IACD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,2BAA2B,KAAK,WAAW,UAAU,sCAAsC,GAAG,OAAO,EAAE;AAAA,MAC/G,EAAE,MAAM,2CAA2C,KAAK,WAAW,UAAU,qDAAqD,GAAG,OAAO,EAAE;AAAA,MAC9I,EAAE,MAAM,0BAA0B,KAAK,WAAW,UAAU,oCAAoC,GAAG,SAAS,EAAE;AAAA,MAC9G,EAAE,MAAM,6BAA6B,KAAK,WAAW,UAAU,oCAAoC,GAAG,MAAM,EAAE;AAAA,MAC9G,EAAE,MAAM,sEAAsE,KAAK,WAAW,UAAU,8CAA8C,GAAG,OAAO,EAAE;AAAA,MAClK,EAAE,MAAM,8EAA8E,KAAK,WAAW,UAAU,uCAAuC,GAAG,SAAS,YAAY,EAAE;AAAA,MACjL,EAAE,MAAM,uCAAuC,KAAK,WAAW,yHAAyH,MAAM,EAAE;AAAA,MAChM,EAAE,MAAM,uEAAuE,KAAK,IAAI,GAAG,aAAa,IAAI,CAAC,WAAW,YAAY;AAClI,cAAM,OAAO,UAAU,+DAAiE;AACxF,cAAM,SAAS,MAAM,MAAM,MAAM,SAAS,QAAW,OAAO,MAAM,EAAE;AACpE,YAAI,OAAO,WAAW,OAAQ,QAAO,EAAE,KAAK,sDAAsD,OAAO,MAAM,EAAE;AACjH,eAAO,MAAM,MAAM,QAAQ,QAAW,2CAA2C,MAAM,EAAE;AAAA,MAC3F,CAAC,GAAG,qEAAqE,EAAE;AAAA,IAC7E,CAAC;AAAA,IACD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,sCAAsC,KAAK,MAAM,EAAE,SAAS,WAAW,gBAAgB,+CAA+C,EAAE;AAAA,IAClJ,CAAC;AAAA;AAAA;AAAA,IAGD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,mEAAmE,KAAK,IAAI,GAAG;AAAA,QACrF,MAAM,EAAE,SAAS,GAAG,wBAAwB,GAAG,iCAAiC,8CAA8C;AAAA,QAC9H,MAAM,EAAE,KAAK,yBAAyB,kCAAkC;AAAA;AAAA;AAAA,QAGxE,MAAM,EAAE,SAAS,gBAAgB,6FAA6F,wCAAwC;AAAA,QACtK,MAAO,EAAE,OAAO,0BAA0B,IAAI,EAAE,KAAK,0FAA0F,IAAI,EAAE,KAAK,8BAA8B;AAAA,MAC1L,GAAG,qFAAsF,EAAE;AAAA,IAC7F,CAAC;AAAA,IACD,OAAO,UAAU;AAAA,MACf,EAAE,MAAM,kFAAkF,KAAK,IAAI,GAAG;AAAA,QACpG,MAAM,qBAAqB,MAAM;AAAA,QACjC,WAAW,qBAAqB,MAAM;AAAA,MACxC,GAAG,GAAG,CAAC,OAAO,GAAG,YAAY,EAAE,KAAK,IAAI,CAAC,sCAAsC,EAAE;AAAA,MACjF,EAAE,MAAM,2EAA4E,KAAK,MACtF,EAAE,OAAO,GAAG,yBAAyB,CAAC,IACnC,EAAE,SAAS,GAAG,yBAAyB,GAAG,mBAAmB,oEAAoE,IACjI,EAAE,KAAK,0BAA0B,EAAG;AAAA,IAC5C,CAAC;AAAA,EACH;AACF;",
|
|
6
6
|
"names": ["require", "betterTailwindcss", "typescriptParser", "typescriptParser", "betterTailwindcss"]
|
|
7
7
|
}
|
package/dist/gates/status.d.ts
CHANGED
|
@@ -40,6 +40,8 @@ export interface GateHelpers {
|
|
|
40
40
|
share(files: string[], test: (file: string) => boolean, label: string): CheckResult;
|
|
41
41
|
/** Build the catalogue of the app in `appDir` (UIG-13): passes when every part is described and sorted. */
|
|
42
42
|
catalogue(appDir: string): Promise<CheckResult>;
|
|
43
|
+
/** Every reusable part keeps the usage-page contract (UIG-19): the package's one copy of it. */
|
|
44
|
+
contract(appDir: string): Promise<CheckResult>;
|
|
43
45
|
}
|
|
44
46
|
export interface GateCheck {
|
|
45
47
|
what: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/gates/status.ts"],"names":[],"mappings":"AAkCA,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAC1F,KAAK,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;AAE9B,MAAM,WAAW,SAAS;IACxB,wEAAwE;IACxE,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;IACpC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAA;IACjC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAA;IACjC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAA;IACjC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAA;IACpC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;IAC5B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,MAAM,EAAE,CAAA;IAC/E,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;IAC9B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;IAC3E,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;IACxE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;IACjD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IACxC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,CAAA;IACxC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAA;IAC/C,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAA;IACtD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;IACjF,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAC5C,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAAA;IAC7C,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAAA;IAC9C,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAAA;IACnF,2GAA2G;IAC3G,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/gates/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Linter } from 'eslint';
|
|
1
|
+
import type { ESLint, Linter } from 'eslint';
|
|
2
2
|
/** Who reads the messages. */
|
|
3
3
|
export type TokenAudience = 'app' | 'package';
|
|
4
4
|
export interface TokenLintOptions {
|
|
@@ -7,8 +7,27 @@ export interface TokenLintOptions {
|
|
|
7
7
|
/** `app` (the default) names the package; `package` names `tokens.css` and the preset. */
|
|
8
8
|
audience?: TokenAudience;
|
|
9
9
|
}
|
|
10
|
+
export interface TokenConfigOptions extends TokenLintOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Plugins whose `eslint-disable` directives appear in the source, registered
|
|
13
|
+
* with every rule off. ESLint refuses a directive for a rule it does not
|
|
14
|
+
* know. TypeScript's are registered already.
|
|
15
|
+
*/
|
|
16
|
+
quiet?: Record<string, ESLint.Plugin>;
|
|
17
|
+
/** Folders the token lint never reads, beside build output. */
|
|
18
|
+
ignores?: string[];
|
|
19
|
+
}
|
|
10
20
|
/** What the token lint never reads: build output. */
|
|
11
21
|
export declare const TOKEN_LINT_IGNORES: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Plugins a check that runs on its own registers, every rule off, so the
|
|
24
|
+
* directives another lint writes do not fail it (UIG-37). TypeScript's lint is
|
|
25
|
+
* in every app and writes `eslint-disable-next-line @typescript-eslint/...`;
|
|
26
|
+
* Peek PR #324 failed the token lint on one. Only the checks that run alone
|
|
27
|
+
* take this — a full lint that loads TypeScript's rules from its own copy would
|
|
28
|
+
* refuse a second plugin under the same name.
|
|
29
|
+
*/
|
|
30
|
+
export declare const KNOWN_PLUGINS: Record<string, ESLint.Plugin>;
|
|
12
31
|
/**
|
|
13
32
|
* The class rules. `no-unknown-classes` reads the app's own Tailwind config, so
|
|
14
33
|
* it knows exactly what the preset and the app generate.
|
|
@@ -23,4 +42,11 @@ export declare function tokenLint({ tailwindConfig, audience }?: TokenLintOption
|
|
|
23
42
|
* cannot also silence a raw colour on the same line.
|
|
24
43
|
*/
|
|
25
44
|
export declare function tokenValues({ audience }?: Pick<TokenLintOptions, 'audience'>): Linter.Config;
|
|
45
|
+
/**
|
|
46
|
+
* The token lint on its own — an app's `eslint.tokens.config.js`, `npm run
|
|
47
|
+
* lint:tokens`, the CI step. It fails on the token contract and on nothing
|
|
48
|
+
* else, so a backlog in the full lint never blocks it. Peek built this file by
|
|
49
|
+
* hand until UIG-37.
|
|
50
|
+
*/
|
|
51
|
+
export declare function tokenConfig({ quiet, ignores, ...options }?: TokenConfigOptions): Linter.Config[];
|
|
26
52
|
//# sourceMappingURL=token-lint.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-lint.d.ts","sourceRoot":"","sources":["../../src/gates/token-lint.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"token-lint.d.ts","sourceRoot":"","sources":["../../src/gates/token-lint.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAK5C,8BAA8B;AAC9B,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,SAAS,CAAA;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,uFAAuF;IACvF,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,aAAa,CAAA;CACzB;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;IACrC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,qDAAqD;AACrD,eAAO,MAAM,kBAAkB,UAAkD,CAAA;AAEjF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAA+D,CAAA;AA6BvH;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EAAE,cAAqC,EAAE,QAAgB,EAAE,GAAE,gBAAqB,GAAG,MAAM,CAAC,MAAM,CAqC3H;AAiCD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAgB,EAAE,GAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAM,GAAG,MAAM,CAAC,MAAM,CAqExG;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAU,EAAE,OAAY,EAAE,GAAG,OAAO,EAAE,GAAE,kBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,CAa9G"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildAppRegistry
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XCBN2EKW.js";
|
|
5
5
|
import "./chunk-NJN4MQAM.js";
|
|
6
6
|
import "./chunk-E4JNV7PC.js";
|
|
7
7
|
export {
|
|
8
8
|
buildAppRegistry
|
|
9
9
|
};
|
|
10
|
-
//# sourceMappingURL=app-
|
|
10
|
+
//# sourceMappingURL=app-FXN4EI34.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/registry/app.ts"],"names":[],"mappings":"AAuCA,OAAO,EAAwE,KAAK,QAAQ,EAAsB,MAAM,UAAU,CAAA;AAElI,MAAM,WAAW,eAAe;IAC9B,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oGAAoG;IACpG,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gIAAgI;IAChI,eAAe,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;IACjC,gIAAgI;IAChI,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC/B;AAwQD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,IAAoB,EAAE,IAAI,EAAE,eAAe,EAAE,mBAAmB,EAAE,GAAE,eAAoB,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/registry/app.ts"],"names":[],"mappings":"AAuCA,OAAO,EAAwE,KAAK,QAAQ,EAAsB,MAAM,UAAU,CAAA;AAElI,MAAM,WAAW,eAAe;IAC9B,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oGAAoG;IACpG,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gIAAgI;IAChI,eAAe,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;IACjC,gIAAgI;IAChI,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC/B;AAwQD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,IAAoB,EAAE,IAAI,EAAE,eAAe,EAAE,mBAAmB,EAAE,GAAE,eAAoB,GAAG,QAAQ,CAuhBrI"}
|
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/registry/contract.ts
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
var PAGE_SECTIONS = ["When", "When not", "How", "What it owns"];
|
|
7
|
+
var CONTRACT_KINDS = ["reusable", "promote-candidate"];
|
|
8
|
+
var SEEN_IN = /^\*\*Seen in\*\*/m;
|
|
9
|
+
function pageProblem(source) {
|
|
10
|
+
const text = source.replace(/\r\n/g, "\n");
|
|
11
|
+
const heads = [...text.matchAll(/^##+\s+(.+?)\s*$/gm)].map((m) => m[1]);
|
|
12
|
+
const at = PAGE_SECTIONS.map((s) => heads.indexOf(s));
|
|
13
|
+
const missing = PAGE_SECTIONS.filter((_, i) => at[i] < 0);
|
|
14
|
+
if (missing.length) return `no ${missing.map((s) => `"${s}"`).join(", ")} section`;
|
|
15
|
+
if (at.some((i, n) => n > 0 && i < at[n - 1])) return `sections out of order: they go ${PAGE_SECTIONS.join(", ")}`;
|
|
16
|
+
const opening = text.split(/^# .*$/m)[1]?.split(/^##\s/m)[0]?.split(/\n\s*\n/).map((s) => s.trim()).find((s) => s && !s.startsWith("<") && !s.startsWith("import "));
|
|
17
|
+
if (!opening) return "no opening line under the title saying what the part is";
|
|
18
|
+
if (!/^##\s+How\s*\n[\s\S]*?```tsx?\n/m.test(text)) return 'no code under "How"';
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function hasSeenIn(source) {
|
|
22
|
+
return SEEN_IN.test(source.replace(/\r\n/g, "\n"));
|
|
23
|
+
}
|
|
24
|
+
function linkProblems(registry, ids) {
|
|
25
|
+
const problems = [];
|
|
26
|
+
for (const entry of registry.entries) {
|
|
27
|
+
for (const key of ["storyId", "docsId"]) {
|
|
28
|
+
const id = entry[key];
|
|
29
|
+
if (id && !ids.has(id)) problems.push(`${entry.name} (${entry.sourceFile}) links to ${key === "storyId" ? "a story" : "a docs page"} its Storybook does not have: ${id}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return problems;
|
|
33
|
+
}
|
|
34
|
+
function contractProblems(registry, root) {
|
|
35
|
+
const problems = [];
|
|
36
|
+
for (const entry of registry.entries) {
|
|
37
|
+
const cls = entry.app?.class;
|
|
38
|
+
if (!cls || !CONTRACT_KINDS.includes(cls)) continue;
|
|
39
|
+
const page = join(entry.sourceFile.replace(/[^/\\]+$/, ""), `${entry.name}.mdx`).replace(/\\/g, "/");
|
|
40
|
+
const where = `${entry.name} (${cls}, ${entry.sourceFile})`;
|
|
41
|
+
if (!existsSync(join(root, page))) {
|
|
42
|
+
problems.push(`${where} has no usage page: write ${page}, with an opening line, then ${PAGE_SECTIONS.join(", ")}`);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const text = readFileSync(join(root, page), "utf8");
|
|
46
|
+
const problem = pageProblem(text);
|
|
47
|
+
if (problem) problems.push(`${where}: ${page} has ${problem}`);
|
|
48
|
+
if (entry.storyId === null && !hasSeenIn(text)) {
|
|
49
|
+
problems.push(`${where} is drawn nowhere: give it a story, or write a "**Seen in**" line on ${page} naming the stories that draw it, or why none can`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return problems;
|
|
53
|
+
}
|
|
54
|
+
|
|
3
55
|
// src/registry/find.ts
|
|
4
56
|
var STOP = /* @__PURE__ */ new Set([
|
|
5
57
|
"a",
|
|
@@ -180,9 +232,15 @@ function formatFindings(searched, findings, query) {
|
|
|
180
232
|
}
|
|
181
233
|
|
|
182
234
|
export {
|
|
235
|
+
PAGE_SECTIONS,
|
|
236
|
+
CONTRACT_KINDS,
|
|
237
|
+
pageProblem,
|
|
238
|
+
hasSeenIn,
|
|
239
|
+
linkProblems,
|
|
240
|
+
contractProblems,
|
|
183
241
|
findInRegistry,
|
|
184
242
|
findInRegistries,
|
|
185
243
|
docsLink,
|
|
186
244
|
formatFindings
|
|
187
245
|
};
|
|
188
|
-
//# sourceMappingURL=chunk-
|
|
246
|
+
//# sourceMappingURL=chunk-6OH3TWGF.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/registry/contract.ts", "../../src/registry/find.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The usage-page contract, checked against an app's catalogue (UIG-19).\n *\n * Every part an app classifies **reusable** (or **promote-candidate**) has a\n * page beside it \u2014 `<PartName>.mdx` in the component's own folder \u2014 that opens\n * with a line saying what the part is, then `When`, `When not`, `How` (with\n * code) and `What it owns`, in that order. And it is drawn somewhere: a story\n * of its own, or a **Seen in** line on its page naming the stories that draw\n * it, or saying why none can (Katerina, 21 and 23 September 2026). A one-off\n * needs only its class and a purpose line, which the catalogue already refuses\n * to build without.\n *\n * This is the one copy. Peek and Ship each used to carry their own, pasted into\n * their `scripts/gates-checks.mjs` and run only by `gates:status`, so a page\n * could lose its `When not` and still merge. `estiva-ui check` runs this in\n * every app's `gate` job (docs/GATES.md \u00A723: one copy, in the package).\n *\n * The package's own pages keep the fuller contract in `src/pages.test.ts`,\n * which also reads `What it owns` back against the lint rule.\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { Registry } from './schema'\n\n/** The four headings, in the order a page carries them. */\nexport const PAGE_SECTIONS = ['When', 'When not', 'How', 'What it owns'] as const\n\n/** The classes that owe a page and a picture. */\nexport const CONTRACT_KINDS = ['reusable', 'promote-candidate'] as const\n\n/** The line that stands in for a story of the part's own. */\nconst SEEN_IN = /^\\*\\*Seen in\\*\\*/m\n\n/**\n * Why a page breaks the contract, or null when it keeps it.\n *\n * Read on normalised text: the apps are checked out with CRLF on Windows.\n */\nexport function pageProblem(source: string): string | null {\n const text = source.replace(/\\r\\n/g, '\\n')\n const heads = [...text.matchAll(/^##+\\s+(.+?)\\s*$/gm)].map((m) => m[1])\n const at = PAGE_SECTIONS.map((s) => heads.indexOf(s))\n const missing = PAGE_SECTIONS.filter((_, i) => at[i] < 0)\n if (missing.length) return `no ${missing.map((s) => `\"${s}\"`).join(', ')} section`\n if (at.some((i, n) => n > 0 && i < at[n - 1])) return `sections out of order: they go ${PAGE_SECTIONS.join(', ')}`\n // The opening line: the first paragraph under the title that is not markup.\n const opening = text\n .split(/^# .*$/m)[1]\n ?.split(/^##\\s/m)[0]\n ?.split(/\\n\\s*\\n/)\n .map((s) => s.trim())\n .find((s) => s && !s.startsWith('<') && !s.startsWith('import '))\n if (!opening) return 'no opening line under the title saying what the part is'\n if (!/^##\\s+How\\s*\\n[\\s\\S]*?```tsx?\\n/m.test(text)) return 'no code under \"How\"'\n return null\n}\n\n/** Whether a page records where the part is drawn, in place of a story of its own. */\nexport function hasSeenIn(source: string): boolean {\n return SEEN_IN.test(source.replace(/\\r\\n/g, '\\n'))\n}\n\n/**\n * Every catalogue link that names a story or a docs page its Storybook does not\n * have (UIG-19: a dead link is a build failure, not a dead end found six weeks\n * later). `ids` is every entry id in the Storybook's own index \u2014 what\n * `storybook index` writes \u2014 so this is Storybook's answer, not a guess at how\n * it builds an id.\n */\nexport function linkProblems(registry: Registry, ids: ReadonlySet<string>): string[] {\n const problems: string[] = []\n for (const entry of registry.entries) {\n for (const key of ['storyId', 'docsId'] as const) {\n const id = entry[key]\n if (id && !ids.has(id)) problems.push(`${entry.name} (${entry.sourceFile}) links to ${key === 'storyId' ? 'a story' : 'a docs page'} its Storybook does not have: ${id}`)\n }\n }\n return problems\n}\n\n/**\n * Every way an app's catalogue breaks the contract, one line each, naming the\n * part, its file and what to do. Empty when it keeps it.\n *\n * `root` is the app's own folder: the one its catalogue's paths are relative to.\n */\nexport function contractProblems(registry: Registry, root: string): string[] {\n const problems: string[] = []\n for (const entry of registry.entries) {\n const cls = entry.app?.class\n if (!cls || !(CONTRACT_KINDS as readonly string[]).includes(cls)) continue\n // Where the page must be: beside the component, named after the part \u2014\n // the same place the catalogue looks (src/registry/app.ts).\n const page = join(entry.sourceFile.replace(/[^/\\\\]+$/, ''), `${entry.name}.mdx`).replace(/\\\\/g, '/')\n const where = `${entry.name} (${cls}, ${entry.sourceFile})`\n if (!existsSync(join(root, page))) {\n problems.push(`${where} has no usage page: write ${page}, with an opening line, then ${PAGE_SECTIONS.join(', ')}`)\n continue\n }\n const text = readFileSync(join(root, page), 'utf8')\n const problem = pageProblem(text)\n if (problem) problems.push(`${where}: ${page} has ${problem}`)\n if (entry.storyId === null && !hasSeenIn(text)) {\n problems.push(`${where} is drawn nowhere: give it a story, or write a \"**Seen in**\" line on ${page} naming the stories that draw it, or why none can`)\n }\n }\n return problems\n}\n", "/**\n * The catalogue's reader (UIG-12): `ui:find <words>`.\n *\n * It exists to prove the format. A schema nobody consumes is a schema nobody\n * has tested, and the questions it has to answer are regression tests, not the\n * scope: \"floating panel\" must find `Popover`, \"scrolling\" `ScrollArea`,\n * \"empty\" `EmptyState`.\n *\n * It is also the answer to \"what do we have for this?\", which is the question\n * the wall cannot answer: a lint rule refuses the wrong thing, and this is\n * where the right one is named (UIG-20 makes a session ask it first).\n */\nimport type { Registry, RegistryEntry } from './schema'\n\nexport interface Finding {\n entry: RegistryEntry\n /** How many of the query's words matched anywhere. Ranked on this first. */\n matched: number\n score: number\n /** Which fields matched, for the reader to say why. */\n where: string[]\n /**\n * The props the query named **by name**, so the answer shows those rather\n * than all of them. A prop whose note merely carries one of the words counts\n * towards the score and is not shown: on \"floating panel\", six of `Popover`'s\n * notes say \"panel\", and printing all six buries the answer.\n */\n props: string[]\n}\n\n/**\n * Words that are in nearly every sentence, and so tell a search nothing.\n *\n * Measured: `ui:find \"truncate a long link\"` printed `Link`'s `external` prop,\n * because its note says \"in **a** new tab\" and the one-letter word matched\n * itself exactly. The minimum length in `alike` only guards a *prefix* match;\n * an exact match short-circuits it, which is right for `tab` and useless for `a`.\n */\nconst STOP = new Set([\n 'a', 'an', 'and', 'are', 'as', 'at', 'be', 'but', 'by', 'can', 'do', 'for', 'from', 'how', 'i', 'if', 'in', 'is', 'it', 'its', 'me', 'my', 'no', 'not', 'of',\n 'on', 'or', 'our', 'so', 'that', 'the', 'their', 'them', 'then', 'there', 'they', 'this', 'to', 'up', 'was', 'we', 'what', 'when', 'which', 'with', 'you', 'your',\n])\n\n/**\n * The words of a piece of text, with names taken apart.\n *\n * `CommandPalette` is kept whole *and* split, so \"palette\" reaches it and\n * \"commandpalette\" still does. Prose is already words; this costs it nothing.\n */\nfunction words(text: string): string[] {\n const found: string[] = []\n for (const token of text.match(/[A-Za-z0-9]+/g) ?? []) {\n found.push(token.toLowerCase())\n const parts = token.split(/(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/)\n if (parts.length > 1) found.push(...parts.map((part) => part.toLowerCase()))\n }\n return found\n}\n\n/**\n * Whether a word of the query means a word of the text.\n *\n * Either being a prefix of the other covers \"scroll\" against \"scrolling\"; the\n * five-letter key covers \"scrolls\" against \"scrolling\", where neither is. Crude\n * on purpose \u2014 a stemmer is a dependency and a surprise, and the failure here\n * is an extra row in a list of five, not a wrong answer.\n *\n * **Three letters at least**, or a prefix match is no match at all: `person's`\n * puts the word \"s\" in the text, and every query on earth starts with some\n * letter. Measured \u2014 before this, \"scrolling\" found `Avatar`.\n */\nfunction alike(query: string, text: string): boolean {\n if (query === text) return true\n if (Math.min(query.length, text.length) < 3) return false\n if (text.startsWith(query) || query.startsWith(text)) return true\n return query.slice(0, 5) === text.slice(0, 5)\n}\n\nconst hit = (query: string, text: string) => words(text).some((word) => alike(query, word))\n\n/**\n * Search name, purpose, behaviours and variants.\n *\n * A row that answers more of the question comes first, whatever it scored:\n * \"floating panel\" has to reach `Popover`, whose purpose carries both words,\n * over `MenuPanel`, whose name carries one of them loudly.\n */\nexport function findInRegistry(registry: Registry, query: string, { limit = 5 }: { limit?: number } = {}): Finding[] {\n return findInRegistries([registry], query, { limit })\n}\n\n/**\n * Search several catalogues as one: the package's, the app's own, and any app\n * beside it (UIG-13).\n *\n * An app's pass-on is not a result of its own \u2014 it is the package's part, and\n * the package's entry answers for it, saying which apps hand it on. At the\n * same strength the package's part comes first: it is the one to reach for.\n */\nexport function findInRegistries(registries: Registry[], query: string, { limit = 5 }: { limit?: number } = {}): Finding[] {\n const all = words(query)\n // Drop the words that mean nothing. If the question was only those, keep them\n // rather than answer nothing at all.\n const carrying = all.filter((word) => word.length >= 3 && !STOP.has(word))\n const asked = carrying.length ? carrying : all\n if (asked.length === 0) return []\n\n const findings: Finding[] = []\n const entries = registries.flatMap((registry) => registry.entries).filter((entry) => entry.app?.class !== 're-export')\n for (const entry of entries) {\n let score = 0\n let matched = 0\n const where = new Set<string>()\n const byName = new Set<string>()\n const byNote = new Set<string>()\n for (const word of asked) {\n let best = 0\n if (entry.name.toLowerCase() === word) {\n best = 100\n where.add('name')\n } else if (hit(word, entry.name)) {\n best = 40\n where.add('name')\n }\n if (hit(word, entry.purpose)) {\n best = Math.max(best, 20)\n where.add('purpose')\n }\n for (const owned of entry.ownsBehaviours) {\n if (hit(word, owned.behaviour) || hit(word, owned.id)) {\n best = Math.max(best, 15)\n where.add('behaviour')\n }\n }\n for (const variant of entry.variants) {\n if (variant.values.some((value) => hit(word, value)) || hit(word, variant.prop)) {\n best = Math.max(best, 8)\n where.add('variant')\n byName.add(variant.prop)\n }\n }\n // What it can do. \"does Link already truncate?\" is the question this\n // answers, and answering it is what stops the thing being built twice.\n for (const prop of entry.props) {\n // A prop the question named by name is the answer; one whose note\n // happens to carry the word is a hint. Named ones go first, so a short\n // answer shows the right lines.\n if (hit(word, prop.name)) {\n best = Math.max(best, 14)\n where.add('prop')\n byName.add(prop.name)\n } else if (prop.note !== null && hit(word, prop.note)) {\n best = Math.max(best, 10)\n where.add('prop')\n byNote.add(prop.name)\n }\n }\n if (best > 0) matched += 1\n score += best\n }\n if (matched > 0) findings.push({ entry, matched, score, where: [...where], props: [...byName] })\n }\n\n const fromPackage = (finding: Finding) => (finding.entry.app === null ? 0 : 1)\n findings.sort((a, b) => b.matched - a.matched || b.score - a.score || fromPackage(a) - fromPackage(b) || a.entry.name.localeCompare(b.entry.name))\n return findings.slice(0, limit)\n}\n\n/** `http://localhost:6008/?path=/docs/overlays-popover--docs`, or `null` where the entry has no page. */\nexport function docsLink(registry: Registry, entry: RegistryEntry): string | null {\n if (!entry.docsId) return null\n return registry.storybook.devUrl + registry.storybook.docsPath.replace('{docsId}', entry.docsId)\n}\n\n/** What an app's part is, in the words the search prints. */\nconst CLASS_WORDS: Record<string, string> = {\n 'one-off': 'used in one place',\n reusable: 'used in several places',\n 'promote-candidate': 'used in several places; could move into the package',\n unused: 'used nowhere in the app',\n}\n\n/** What the command prints: the import line first, because that is what the reader came for. */\nexport function formatFindings(searched: Registry | Registry[], findings: Finding[], query: string): string {\n const registries = Array.isArray(searched) ? searched : [searched]\n if (findings.length === 0) {\n return [\n `Nothing in ${registries.map((registry) => registry.builtFrom.repo).join(', ')} matches \"${query}\".`,\n 'If nothing here does what you need, say so and ask \u2014 do not invent a component.',\n ].join('\\n')\n }\n const registryOf = (entry: RegistryEntry) => registries.find((registry) => registry.builtFrom.repo === entry.repo) ?? registries[0]\n // Which apps hand a package part on, and from where: in Peek, `Button` is\n // imported from `@/components/ui/Button`, and the answer should say so.\n const handedOn = new Map<string, string[]>()\n for (const registry of registries) {\n for (const entry of registry.entries) {\n if (entry.app?.class !== 're-export' || entry.app.handsOn === null) continue\n handedOn.set(entry.app.handsOn, [...(handedOn.get(entry.app.handsOn) ?? []), `${entry.repo} hands it on${entry.name === entry.app.handsOn ? '' : ` as ${entry.name}`} from ${entry.importPath}`])\n }\n }\n\n const blocks = findings.map((finding) => {\n const { entry } = finding\n const facts = entry.app\n const lines = [\n `${entry.name} \u00B7 ${facts ? CLASS_WORDS[facts.class] ?? facts.class : entry.kind} \u00B7 ${entry.repo} (matched ${finding.where.join(', ')})`,\n ` ${entry.purpose}`,\n facts?.defaultExport ? ` import ${entry.name} from '${entry.importPath}'` : ` import { ${entry.name} } from '${entry.importPath}'`,\n ]\n if (!facts) for (const said of handedOn.get(entry.name) ?? []) lines.push(` ${said}`)\n if (facts?.packageNamesake) lines.push(` the package has a ${facts.packageNamesake} too: check it first`)\n if (facts && facts.usedIn.length) lines.push(` used in ${facts.usedIn.slice(0, 2).join(', ')}${facts.usedIn.length > 2 ? ` and ${facts.usedIn.length - 2} more` : ''}`)\n if (entry.ownsBehaviours.length) lines.push(` owns: ${entry.ownsBehaviours.map((owned) => owned.behaviour).join(' \u00B7 ')}`)\n if (entry.variants.length) lines.push(` ${entry.variants.map((variant) => `${variant.prop}: ${variant.values.join(' | ')}`).join(' ')}`)\n // The props the question named, with their own line \u2014 not all of them. A\n // component with twenty props would bury its own answer.\n const shown = finding.props\n .filter((name) => !entry.variants.some((variant) => variant.prop === name))\n .slice(0, 3)\n .map((name) => entry.props.find((prop) => prop.name === name))\n for (const prop of shown) {\n if (prop) lines.push(` ${prop.name}: ${prop.takes}${prop.note ? ` \u2014 ${prop.note}` : ''}`)\n }\n if (entry.props.length) lines.push(` ${entry.props.length} props in all; see ${entry.docsId ? 'the page' : entry.sourceFile} for the rest`)\n const link = docsLink(registryOf(entry), entry)\n if (link) lines.push(` ${link}`)\n return lines.join('\\n')\n })\n\n return blocks.join('\\n\\n')\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -489,7 +489,8 @@ function buildAppRegistry({ root = process.cwd(), repo, packageRegistry, package
|
|
|
489
489
|
const storyOf = (p) => {
|
|
490
490
|
const stem = (file) => basename(file).replace(/\.stories\.[cm]?[jt]sx?$/, "");
|
|
491
491
|
const own = basename(p.file, extname(p.file));
|
|
492
|
-
const
|
|
492
|
+
const sameFile = (s2) => s2.imports.some((q) => q.file === p.file);
|
|
493
|
+
const candidates = stories.filter((s2) => s2.title && s2.names.length && (s2.component === p || s2.imports.includes(p) && (stem(s2.file) === p.name || stem(s2.file) === own) || stem(s2.file) === p.name && sameFile(s2)));
|
|
493
494
|
candidates.sort((a, b) => Number(b.component === p) - Number(a.component === p) || a.file.localeCompare(b.file));
|
|
494
495
|
const s = candidates[0];
|
|
495
496
|
if (!s || !s.title) return { docsId: null, storyId: null };
|
|
@@ -645,4 +646,4 @@ function buildAppRegistry({ root = process.cwd(), repo, packageRegistry, package
|
|
|
645
646
|
export {
|
|
646
647
|
buildAppRegistry
|
|
647
648
|
};
|
|
648
|
-
//# sourceMappingURL=chunk-
|
|
649
|
+
//# sourceMappingURL=chunk-XCBN2EKW.js.map
|