diff --git a/client/scripts/lib/layers.mjs b/client/scripts/lib/layers.mjs index 6cdcd4154..eb867e123 100644 --- a/client/scripts/lib/layers.mjs +++ b/client/scripts/lib/layers.mjs @@ -13,8 +13,11 @@ const VIEW = ['components', 'mobile', 'pages', 'hooks'] * may use a shared hook, and the phone screens reuse both. Nothing below the * views (stores, repos, sync, api, the Dexie schema and the helpers) knows a * view exists, a repo sits under the stores, and the Dexie schema under all - * of them. Directories without an entry (mobile, pages, the app root) are - * free. + * of them. help/ (the help center's screen registry, plain data) and vacay/ + * (date helpers) are helpers like utils/. Directories without an entry are + * free on purpose: mobile and pages sit at the top, the app root wires + * everything, and managed/ is the slot an operator fills with whole screens + * at build time. */ export const RULES = { components: ['pages', 'mobile'], @@ -29,6 +32,8 @@ export const RULES = { services: VIEW, push: VIEW, types: VIEW, + help: VIEW, + vacay: VIEW, repo: [...VIEW, 'store'], db: [...VIEW, 'store', 'repo', 'sync', 'api'], } diff --git a/client/tests/unit/scripts/layer-lint.test.ts b/client/tests/unit/scripts/layer-lint.test.ts index 5053513b1..ec90696e8 100644 --- a/client/tests/unit/scripts/layer-lint.test.ts +++ b/client/tests/unit/scripts/layer-lint.test.ts @@ -110,6 +110,21 @@ describe('lint:layers', () => { expect(JSON.parse(readFileSync(tree.path(BASELINE), 'utf8'))).toEqual({ 'src/components/A.tsx': 1 }); }); + it('LAYERS-007: help/ and vacay/ are helpers and import no view', () => { + for (const helper of ['help', 'vacay']) { + expect(RULES[helper as keyof typeof RULES]).toEqual( + expect.arrayContaining(['components', 'mobile', 'pages', 'hooks']) + ); + } + const files = { + 'src/help/contexts/trip.ts': "import { Panel } from '../../components/Help/Panel'\n", + 'src/vacay/yearWindow.ts': "import { useIsPhone } from '../hooks/useIsPhone'\n", + }; + expect(run({}, files)).toBe(1); + expect(tree.error.join('\n')).toMatch(/src\/help\/contexts\/trip\.ts: 1 import\(s\) against the layering/); + expect(tree.error.join('\n')).toMatch(/src\/vacay\/yearWindow\.ts: 1 import\(s\) against the layering/); + }); + it('LAYERS-006: a missing baseline stops the check', () => { tree = ratchetTree({ 'src/a.ts': '' }); expect(() => check({ root: tree.root, baselinePath: tree.path(BASELINE), ...tree.out })).toThrow(RatchetError);