Skip to content

Commit 9151b13

Browse files
committed
fix(platform): hold the embedded organization views to the design check
check:platform-design skipped platform/embedded, which ships to every organization. Walk it, excepting only what is built on the host (imports and the host's tokens) and the app/o routes that mount its private components. Count the rest per file and rule in a shrink-only baseline that holds no other tree. The exemption keyed on an import's target let anything import embedded, so ban edges between the zone and embedded in both directions, here and in check:boundaries. Trace the organization routes through a dynamic section table so variant use is derived, and drop the hand-kept list.
1 parent e23c9cb commit 9151b13

6 files changed

Lines changed: 641 additions & 57 deletions

‎scripts/check-monorepo-boundaries.test.ts‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,44 @@ describe('package boundary command', () => {
223223
})
224224
})
225225

226+
it.each([
227+
[
228+
'apps/sim/platform/app/page.tsx',
229+
"import { Row } from '@/platform/embedded/app/(shell)/row'",
230+
],
231+
['apps/sim/platform/app/page.tsx', "import type { Row } from '../embedded/app/(shell)/row'"],
232+
['apps/sim/platform/lib/data/home.ts', "import '@/platform/embedded/lib/data/home'"],
233+
['apps/sim/platform/checks/home.test.ts', "import '@/platform/embedded/lib/data/home'"],
234+
['apps/sim/platform/design-system/row.tsx', "import('@/platform/embedded/lib/time')"],
235+
[LIB_FILE, "import { home } from '@/platform/lib/data/home'"],
236+
[VIEW_FILE, "import type { Home } from '@/platform/app/(shell)/home/model'"],
237+
[VIEW_FILE, "import '../../../../lib/data/home'"],
238+
])('keeps the zone and the organization views apart: %s, %s', (file, source) => {
239+
withFixture((fixture, audit) => {
240+
mkdirSync(path.join(fixture, path.dirname(file)), { recursive: true })
241+
writeFileSync(path.join(fixture, file), `${source}\n`)
242+
const result = audit()
243+
expect(result.status, result.stderr).toBe(1)
244+
expect(result.stderr).toContain(`${file}:1`)
245+
expect(audit('--update', '--init').status).toBe(0)
246+
expect(audit().status).toBe(1)
247+
})
248+
})
249+
250+
it.each([
251+
[VIEW_FILE, "import { Button } from '@/platform/design-system/components/button'"],
252+
[LIB_FILE, "import { time } from '@/platform/embedded/lib/time'"],
253+
['apps/sim/platform/app/page.tsx', "import '@/platform/design-system/components/button'"],
254+
['apps/sim/platform/app/page.tsx', "import '@/platform/lib/data/home'"],
255+
])('allows what the zone and the views each own: %s, %s', (file, source) => {
256+
withFixture((fixture, audit) => {
257+
mkdirSync(path.join(fixture, path.dirname(file)), { recursive: true })
258+
writeFileSync(path.join(fixture, file), `${source}\n`)
259+
const result = audit()
260+
expect(result.status, result.stderr).toBe(0)
261+
})
262+
})
263+
226264
it('keeps platform/embedded/lib from importing the workspace surface, even for a type', () => {
227265
withFixture((fixture, audit) => {
228266
const cases = [

‎scripts/check-monorepo-boundaries.ts‎

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,13 @@
3838
* would inherit that surface's components and hooks. Fix by moving the shared part under `lib/`
3939
* or `hooks/`.
4040
*
41-
* Rules 1 to 3 have no allowlist. Rules 4 to 6 predate their enforcement, so their existing edges
41+
* 7. The Platform zone and the organization views are two apps sharing `platform/design-system`. Nothing
42+
* under `apps/sim/platform` outside `embedded` imports `@/platform/embedded`, and `embedded` imports
43+
* nothing under `@/platform` but itself and the design system, tests included: the views are built on
44+
* the host app, so an edge from the zone would pull the host into a separate build, and an edge
45+
* the other way would make the zone part of what every organization ships.
46+
*
47+
* Rules 1 to 3 and 7 have no allowlist. Rules 4 to 6 predate their enforcement, so their existing edges
4248
* are counted per file in `scripts/check-monorepo-boundaries.baseline.json`, one section per rule. A
4349
* file above its count fails, and so does one below it until the baseline is rewritten, so the
4450
* baseline only shrinks. Regenerate after removing edges with `--update`, which refuses to raise a
@@ -57,6 +63,7 @@ const SIM_DIR = path.join(ROOT, 'apps/sim')
5763
const PLATFORM_DIR = path.join(SIM_DIR, 'platform')
5864
const PLATFORM_LIB_DIR = path.join(PLATFORM_DIR, 'embedded/lib')
5965
const PLATFORM_VIEWS_DIR = path.join(SIM_DIR, 'platform/embedded/app')
66+
const EMBEDDED_DIR = path.join(PLATFORM_DIR, 'embedded')
6067
const EE_DATA_HOOK = /^@\/ee\/(?:.+\/)?hooks(?:\/|$)/
6168
const SEAM_BASELINE = path.join(ROOT, 'scripts/check-monorepo-boundaries.baseline.json')
6269

@@ -119,6 +126,23 @@ function viewSeamViolation(specifier: string, bindings: readonly string[] | 'all
119126
return null
120127
}
121128

129+
/** Why a file under `apps/sim/platform` may not take this edge across the zone/embedded line. */
130+
function zoneEmbeddedViolation(file: string, specifier: string): string | null {
131+
if (!file.startsWith(`${EMBEDDED_DIR}${path.sep}`)) {
132+
return matchesModule(specifier, '@/platform/embedded')
133+
? `the Platform zone imports the organization views: ${specifier}`
134+
: null
135+
}
136+
if (
137+
!specifier.startsWith('@/platform/') ||
138+
matchesModule(specifier, '@/platform/embedded') ||
139+
matchesModule(specifier, '@/platform/design-system')
140+
) {
141+
return null
142+
}
143+
return `the organization views import the Platform zone: ${specifier}`
144+
}
145+
122146
/**
123147
* Runtime (non-type) names an import/export declaration binds: `'all'` for a
124148
* namespace, default, star, empty-clause or side-effect edge, `[]` for one the compiler erases.
@@ -239,13 +263,17 @@ async function walk(dir: string, results: string[] = []): Promise<string[]> {
239263
return results
240264
}
241265

242-
/** Every file under `dir` but tests; a directory that does not exist has none. */
243-
async function sourceFiles(dir: string): Promise<string[]> {
244-
const files = await walk(dir).catch((error: NodeJS.ErrnoException) => {
266+
/** Every file under `dir`; a directory that does not exist has none. */
267+
async function allFiles(dir: string): Promise<string[]> {
268+
return walk(dir).catch((error: NodeJS.ErrnoException) => {
245269
if (error.code === 'ENOENT') return []
246270
throw error
247271
})
248-
return files.filter((file) => !TEST_FILE.test(file))
272+
}
273+
274+
/** Every file under `dir` but tests. */
275+
async function sourceFiles(dir: string): Promise<string[]> {
276+
return (await allFiles(dir)).filter((file) => !TEST_FILE.test(file))
249277
}
250278

251279
const SEAM_RATCHETS = ['viewDataHooks', 'platformToRoutes', 'platformLibToWorkspace'] as const
@@ -379,6 +407,14 @@ async function main() {
379407
)
380408
}
381409

410+
for (const file of await allFiles(PLATFORM_DIR)) {
411+
offenders.push(
412+
...(await scanImports(file, ({ module }) =>
413+
zoneEmbeddedViolation(file, aliasedSpecifier(file, module.text))
414+
))
415+
)
416+
}
417+
382418
for (const file of await sourceFiles(PLATFORM_DIR)) {
383419
if (platformLibFileSet.has(file)) continue
384420
seamHits.platformToRoutes.push(
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
{
2+
"platform/embedded/app/(shell)/_components/conversation/conversation.module.css": {
3+
"css-owner": 1,
4+
"unknown-token": 1
5+
},
6+
"platform/embedded/app/(shell)/_components/conversation/dialogs.tsx": {
7+
"consumer-style": 2,
8+
"style-api": 1
9+
},
10+
"platform/embedded/app/(shell)/_components/conversation/page-composer.module.css": {
11+
"css-owner": 15,
12+
"unknown-token": 10
13+
},
14+
"platform/embedded/app/(shell)/_components/conversation/page-composer.tsx": {
15+
"consumer-style": 2,
16+
"prop-spread": 2
17+
},
18+
"platform/embedded/app/(shell)/_components/conversation/page-conversation.tsx": {
19+
"consumer-style": 2,
20+
"prop-spread": 4
21+
},
22+
"platform/embedded/app/(shell)/_components/conversation/search-input-bar.tsx": {
23+
"consumer-style": 2
24+
},
25+
"platform/embedded/app/(shell)/_components/conversation/skeletons.tsx": {
26+
"consumer-style": 4
27+
},
28+
"platform/embedded/app/(shell)/_components/provider-mark/provider-mark.tsx": {
29+
"private-composition": 1
30+
},
31+
"platform/embedded/app/(shell)/_components/route-status/route-status.module.css": {
32+
"raw-geometry": 1
33+
},
34+
"platform/embedded/app/(shell)/_components/search/search.module.css": {
35+
"focus-style": 1,
36+
"raw-geometry": 2
37+
},
38+
"platform/embedded/app/(shell)/_components/search/source-row.tsx": {
39+
"consumer-style": 3,
40+
"style-api": 1
41+
},
42+
"platform/embedded/app/(shell)/_components/sidebar/rail-flyout.tsx": {
43+
"primitive-owner": 1,
44+
"private-recipe": 1,
45+
"prop-spread": 1
46+
},
47+
"platform/embedded/app/(shell)/_components/sidebar/sidebar.module.css": {
48+
"raw-color": 1,
49+
"raw-geometry": 5
50+
},
51+
"platform/embedded/app/(shell)/_components/site-mark/site-mark.tsx": {
52+
"consumer-style": 1
53+
},
54+
"platform/embedded/app/(shell)/home/_components/activity-chart/activity-chart.module.css": {
55+
"css-owner": 1,
56+
"unknown-token": 4
57+
},
58+
"platform/embedded/app/(shell)/home/_components/activity-list/activity-list.module.css": {
59+
"css-owner": 1
60+
},
61+
"platform/embedded/app/(shell)/home/_components/activity-list/activity-list.tsx": {
62+
"prop-spread": 1
63+
},
64+
"platform/embedded/app/(shell)/home/_components/calendar/calendar-day.module.css": {
65+
"css-owner": 11,
66+
"focus-style": 2,
67+
"unknown-token": 11,
68+
"variant-selector": 3
69+
},
70+
"platform/embedded/app/(shell)/home/_components/calendar/calendar-day.tsx": {
71+
"consumer-style": 4
72+
},
73+
"platform/embedded/app/(shell)/home/_components/connected-widget/connected-widget.module.css": {
74+
"raw-geometry": 2
75+
},
76+
"platform/embedded/app/(shell)/home/_components/home-conversation/home-conversation.tsx": {
77+
"private-composition": 1,
78+
"prop-spread": 1
79+
},
80+
"platform/embedded/app/(shell)/home/_components/home-dashboard/home-dashboard-skeleton.tsx": {
81+
"private-recipe": 1
82+
},
83+
"platform/embedded/app/(shell)/integrations/_components/connected-credential/connected-credential.module.css": {
84+
"raw-geometry": 1
85+
},
86+
"platform/embedded/app/(shell)/integrations/_components/integration-mark/integration-mark.tsx": {
87+
"consumer-style": 1
88+
},
89+
"platform/embedded/app/(shell)/integrations/_components/integrations-page/integrations-route.tsx": {
90+
"prop-spread": 2
91+
},
92+
"platform/embedded/app/(shell)/integrations/_components/secrets-editor/secrets-editor.tsx": {
93+
"private-composition": 1
94+
},
95+
"platform/embedded/app/(shell)/integrations/_components/slack-search-actions/slack-search-actions.tsx": {
96+
"private-recipe": 1
97+
},
98+
"platform/embedded/app/(shell)/people/_components/directory-sync/directory-sync.module.css": {
99+
"raw-geometry": 1
100+
},
101+
"platform/embedded/app/(shell)/people/_components/directory/directory.tsx": {
102+
"consumer-style": 1
103+
},
104+
"platform/embedded/app/(shell)/people/_components/org-chart/chart-canvas.tsx": {
105+
"prop-spread": 2
106+
},
107+
"platform/embedded/app/(shell)/people/_components/org-chart/org-chart.module.css": {
108+
"focus-style": 1
109+
},
110+
"platform/embedded/app/(shell)/search/_components/search-surface/search-surface.module.css": {
111+
"css-owner": 2,
112+
"unknown-token": 2
113+
},
114+
"platform/embedded/app/(shell)/search/_components/search-surface/search-surface.tsx": {
115+
"private-composition": 1,
116+
"private-recipe": 1,
117+
"prop-spread": 1
118+
},
119+
"platform/embedded/app/(shell)/settings/_components/sections/access-control/group-catalog-tabs.tsx": {
120+
"prop-spread": 3
121+
},
122+
"platform/embedded/app/(shell)/settings/_components/sections/connected-accounts/group-access.tsx": {
123+
"prop-spread": 1
124+
},
125+
"platform/embedded/app/(shell)/settings/_components/sections/connected-accounts/group-people.tsx": {
126+
"private-composition": 1
127+
},
128+
"platform/embedded/app/(shell)/settings/_components/sections/connected-accounts/group-providers.tsx": {
129+
"private-composition": 1
130+
},
131+
"platform/embedded/app/(shell)/settings/_components/sections/connected-accounts/provider-catalog-modal.tsx": {
132+
"private-composition": 1
133+
},
134+
"platform/embedded/app/(shell)/settings/_components/sections/integrations/add-source-modal.tsx": {
135+
"private-composition": 1
136+
},
137+
"platform/embedded/app/(shell)/settings/_components/sections/integrations/api-key-field.module.css": {
138+
"css-owner": 1
139+
},
140+
"platform/embedded/app/(shell)/settings/_components/sections/integrations/api-key-field.tsx": {
141+
"private-recipe": 1
142+
},
143+
"platform/embedded/app/(shell)/settings/_components/sections/integrations/assistant-apps.tsx": {
144+
"private-composition": 1
145+
},
146+
"platform/embedded/app/(shell)/settings/_components/sections/integrations/connector-config-fields.tsx": {
147+
"private-recipe": 1
148+
},
149+
"platform/embedded/app/(shell)/settings/_components/sections/integrations/connector-settings-fields.tsx": {
150+
"prop-spread": 3
151+
},
152+
"platform/embedded/app/(shell)/settings/_components/sections/integrations/integrations-section.tsx": {
153+
"private-composition": 1
154+
},
155+
"platform/embedded/app/(shell)/settings/_components/sections/integrations/source-detail.tsx": {
156+
"prop-spread": 2
157+
},
158+
"platform/embedded/app/(shell)/settings/_components/sections/integrations/source-setup-host.tsx": {
159+
"private-composition": 1
160+
},
161+
"platform/embedded/app/(shell)/settings/_components/sections/integrations/source-setup.tsx": {
162+
"prop-spread": 4
163+
},
164+
"platform/embedded/app/(shell)/settings/_components/sections/search-slack/search-slack-section.tsx": {
165+
"prop-spread": 1
166+
},
167+
"platform/embedded/app/(shell)/settings/_components/sections/sso/client-secret-field.tsx": {
168+
"consumer-style": 1
169+
},
170+
"platform/embedded/app/(shell)/settings/_components/sections/usage/usage-credits.tsx": {
171+
"consumer-style": 1
172+
},
173+
"platform/embedded/app/(shell)/settings/_components/sections/usage/usage-heatmap.tsx": {
174+
"consumer-style": 4
175+
}
176+
}

0 commit comments

Comments
 (0)