Skip to content

Commit 0e205fe

Browse files
test(ui/scripts): the inputs guards and the lychee pins read the configs that run; ui/lychee.toml is deleted, ui/turbo.objectui.json stays (#27281)
Part of #26080 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:实测确认 `ui/turbo.objectui.json` 和 `ui/lychee.toml` 没有任何真实运行会读(turbo 只解析根 `turbo.json`,`check-links.yml` 只读根 `lychee.toml`)。三组守卫原本对着这两份死配置断言,现在改读真正生效的根配置:`type-check` 输入守卫、包级 `lint` 输入守卫、lychee「不许有 remap」守卫。断言一条没删,也没放宽。另把 lychee 的站内路由守卫改为读 check-links.yml 的 lychee 参数(只读不改 workflow),`ui/lychee.toml` 已无读者,删除。 - **为什么改**:守卫对着死文件断言,根配置漂了也照样是绿的。改完之后,从根 `turbo.json` 删掉一条 ui 输入,立刻红 38 条。 - **风险与回滚**:只改 `ui/scripts/__tests__` 下的 4 个测试文件与 1 份死配置,不动运行时、构建配置、workflow,也不动任何发布包;回滚 = revert。`ui/lychee.toml` 已删;`ui/turbo.objectui.json` 这次**不删**:用根配置去跑旧断言会红 147 条,其中不少属性在根配置里根本没有对应项。按裁决不许放宽,这些守卫只能继续读旧文件,具体列在 Acceptance notes,要你来定。 - **先看**:1) `ui/scripts/__tests__/helpers/turbo-inputs.ts`(新增 `liveTurboConfig` / `parkedTurboConfig`,默认值仍是旧文件);2) `ui/scripts/__tests__/check-links-workflow.test.ts` 的 `liveLycheeConfigPath`;3) Acceptance notes 第一条:根 `build` 任务没有哈希 ui 的根脚本。 - **两分钟验证**:`pnpm --dir ui exec vitest run scripts/__tests__/turbo-type-check-inputs.test.ts scripts/__tests__/turbo-lint-inputs.test.ts scripts/__tests__/check-links-workflow.test.ts`,预期 3 个文件全绿;在根 `turbo.json` 的 `type-check.inputs` 里删掉 `"$TURBO_ROOT$/ui/tsconfig.json",` 再跑,预期 type-check 守卫红。 - **需要你做的**:无,席位落地(根 turbo.json 的 ui 输入缺口由席位另立卡 #27283)。 ## What changes Slice 2 of ruling A, item (3): `ui/turbo.objectui.json` and `ui/lychee.toml`. **Measured: no real run on `main` reads either file** (readings below). Every `turbo` invocation, whether in workflows, the root `package.json` scripts, or turbo's own config resolution, resolves the root `turbo.json` plus the package-level `ui/apps/console/turbo.json`. Turbo has no flag here that names another root config, and none is passed. `check-links.yml` passes `--config lychee.toml` from the repository root, and no workflow reads `ui/lychee.toml`. So both files are dead config, read only by tests. **The retarget.** The live counterpart of the parked turbo config is `uiScopedTurboConfig()` (`ui/scripts/workspace-root.mjs`): the root `turbo.json`, narrowed to the `ui/` subset. It is the same reader the production `select-type-check-scope.mjs` already uses. Swapping the parked file's content for that live config (probe below) sorts each guard into one of two groups. | Reader | Live counterpart holds? | This PR | |---|---|---| | `turbo-type-check-inputs.test.ts` (via `helpers/turbo-inputs.ts`) | yes, all cases | reads `liveTurboConfig()` | | `turbo-lint-inputs.test.ts`, the package `lint` describe | yes | reads `liveTurboConfig()` | | `turbo-lint-inputs.test.ts`, the `//#lint:root` describe | no (5 red) | unchanged, parked | | `turbo-build-inputs.test.ts` | no (38 red) | unchanged, parked | | `turbo-test-inputs.test.ts` | no (41 red) | unchanged, parked | | `turbo-task-guard-coverage.test.ts` | no (55 red) | unchanged, parked | | `check-dist-completeness.test.ts` (build-input case) | no (1 red) | unchanged, parked | | `e2e-type-check.test.ts` (wiring cases) | no (4 red) | unchanged, parked | | `select-type-check-scope.test.ts` | no (2 red) | unchanged, parked | | `turbo-build-outputs-cover-published-entries.test.ts` | no (1 red) | unchanged, parked | | `workspace-root.test.ts` (`uiTurboConfigPath` itself) | n/a, pins the resolver | unchanged | | `check-links-workflow.test.ts`, "carries no remap" | yes (root file has no `remap`) | reads the root `lychee.toml` | | `check-links-workflow.test.ts`, site-route pin | yes: restated against `check-links.yml` `LYCHEE_ARGS` (`--config lychee.toml`, absolute `--root-dir` over the docs tree, `--fallback-extensions mdx,md`) | reads the workflow argv | `ui/lychee.toml` has no reader left and is deleted. `helpers/turbo-inputs.ts` now exports `liveTurboConfig()` and `parkedTurboConfig()`. `taskInputs` / `rootAnchoredInputs` take the config as an optional second argument, defaulting to the parked copy, so every guard not listed as retargeted reads exactly what it read before. **`ui/turbo.objectui.json` is not deleted.** Readers whose property has no live counterpart still need them, and the ruling forbids loosening those guards. See Acceptance notes. **Item (2), checked:** `ui/docs/adr`'s index prefix is settled on `main`. PR #26503 merged as `79cd9ef8` (`git merge-base --is-ancestor 79cd9ef HEAD` → exit 0, a self-proving leg). `docs/adr/README.md:11` and `:215` list the ObjectUI registry under the `ui-` prefix, with files untouched, and `scripts/adr-index.mjs` generates it. No edit is needed, and none is made. A pointer would land in `ui/docs/adr/**` (governed) and buys nothing. ## Tests - `pnpm --dir ui exec vitest run --maxWorkers=2 scripts/` on head `88bd2bb7` (lock VERDICT command-exit 0): **Test Files 174 passed | 2 skipped (176), Tests 5003 passed | 2 skipped (5005)**. - The 11 reader files, before and after the change: **11 passed, 338 tests**, both times. That is the same count, so no case was dropped. - `pnpm --dir ui run type-check:scripts`: exit 0. - **Teeth.** Each mutation was made on disk and verified, then restored. The restore was proven by `git hash-object` = the HEAD blob, plus an empty `git status --porcelain`. - `node scripts/ablation-replace.mjs --file turbo.json --anchor '"$TURBO_ROOT$/ui/tsconfig.json",' --delete` (anchor 1 → 0, blob `6d3dece1ae49` → `5b5820c9f5f8`). Result: `turbo-type-check-inputs.test.ts` **38 failed** (one per `@object-ui/*` package). Before this PR, the same root edit could not reach that guard: it read only the parked copy. - Appending `remap = [...]` to the root `lychee.toml` (`grep -c '^remap'` 0 → 1). Result: "carries no remap pretending to resolve fumadocs routes" **failed**. - Deleting `--root-dir`, then `--fallback-extensions`, from the on-disk `check-links.yml` (`ablation-replace`, blob `fd75bd9ccaee` restored by hash, never committed) turns the site-route pin red with its own message each time. ## Acceptance notes 1. **Open question: the live root `build` and `test` tasks do not hash the ui tree's root files.** - The parked copy declares `$TURBO_ROOT$/tsconfig.json`, `scripts/vite-*.ts`, `scripts/build-plugin-stylesheet.mjs`, `scripts/check-dist-completeness.mjs`, `scripts/invoked-as.mjs` and `scripts/vitest-invocation-guard.mjs` for `build`, plus the vitest config and setup set for `test`. - The root `turbo.json` carries none of them under `ui/`. Measured: `pnpm exec turbo run build --filter=@object-ui/components --dry=json` → the task's root-anchored inputs are exactly `scripts/check-dts-emitted.mjs`, `scripts/invoked-as.mjs` and `scripts/tsup-drop-sources-content.mjs`. Meanwhile `ui/packages/components/vite.config.ts:14-15` imports `ui/scripts/vite-dts-*.ts`, and its `tsconfig.json` extends `ui/tsconfig.json`. - So an edit to either file replays a cached `@object-ui/*` build in any `turbo run build` that includes ui packages (root `pnpm build`). - The guards that would catch this (`turbo-build-inputs`, `turbo-test-inputs`, `check-dist-completeness`'s build-input case) stay green because they read the dead copy. - Options: - **A**: port the ui entries into the root `build` / `test` inputs. Every `@objectstack/*` package then also hashes them, so this edits the real cache keys. Then retarget those guards and delete the parked file. - **B**: give the ui packages package-level `turbo.json` files that extend the root. - **C**: keep the status quo. - Recommendation: **A**, because it closes the stale-replay path and makes the parked file deletable. It is a root `turbo.json` behaviour change, so it is its own card, not this slice. Seat: #27283 carries the root turbo.json ui-input gap, with these guards riding it. 2. **Open question: the properties that have no live counterpart by construction.** - `//#lint:root` and `//#type-check:e2e` are root tasks of objectui's own manifest. In the root config, `//` is the objectstack root package, so they cannot exist there. CI runs them as plain steps (`pnpm --dir ui run type-check:e2e`, ci.yml's ported-jobs header). - The guards pinning them (`turbo-lint-inputs` `//#lint:root` describe, `e2e-type-check` wiring, `select-type-check-scope`'s root-task case, part of `turbo-task-guard-coverage`) can only ever read the parked copy. - Recommendation: a follow-up rules each guard either retired with a named duplicate (per the deletion rule) or rewritten against the CI step that runs the task. Until then the parked file stays. Seat: #27283 carries this too. 3. **Resolved in this PR (seat answer A):** the site-route pin is restated against the live argv, and `ui/lychee.toml` is deleted. 4. `select-type-check-scope.test.ts` drives the selector with the parked config, while the selector in production (`select-type-check-scope.mjs:324`) reads `uiScopedTurboConfig`. Against the live config, two cases go red: e2e files no longer widen, because `//#type-check:e2e` does not exist there. The test stays as is (point 2). 5. `turbo-build-outputs-cover-published-entries`: `ui/apps/console/turbo.json` restates the parked `build.outputs`, not the root's (it lacks `json-schema/**` and `!.next/cache/**`). The "no package configuration narrows the root outputs" case is the one red against live. It is noted only: console emits neither path. ## Verification <details><summary>Reading 1: no turbo invocation names the parked file</summary> ``` $ git grep -n -i 'root-turbo-json\|ROOT_TURBO_JSON\|turbo.objectui' -- . ':!ui/CHANGELOG.md' | grep -v '^ui/scripts/__tests__\|^ui/scripts/workspace-root' (no hits; the only hits outside those two paths are vscode-extension `*.objectui.json` schema-file globs, a different thing) $ find . -name turbo.json -not -path '*/node_modules/*' ./turbo.json ./ui/apps/console/turbo.json # "extends": ["//"] $ grep -n 'turbo' package.json # root scripts: build, spec:rebuild, test, test:smoke, typecheck, clean, console:build -- all plain `turbo run` $ grep -n '"turbo \|turbo run' ui/package.json (no hits) $ grep -n 'turbo run' .github/workflows/*.yml # every one runs from the repo root, e.g. ci.yml:3314, :3486, :3576, :3584 $ ls ui/.github ls: cannot access 'ui/.github': No such file or directory ``` </details> <details><summary>Reading 2: no workflow reads ui/lychee.toml</summary> ``` $ git grep -n 'lychee' -- ':!ui/CHANGELOG.md' | grep -v '^content/docs/releases' | grep -v '^.github/workflows/check-links.yml' ui/lychee.toml ... (its own text) ui/scripts/__tests__/check-links-workflow.test.ts ... scripts/check-adr-links.mjs / scripts/check-doc-anchors.mjs (prose naming the ROOT lychee.toml) $ sed -n 120,126p .github/workflows/check-links.yml --root-dir ${{ github.workspace }}/content --fallback-extensions mdx,md --config lychee.toml ``` </details> <details><summary>Reading 3: probe, the live config substituted for the parked one (base 4d3a077, restored by hash)</summary> ``` parked tasks: build test test:dist test:watch lint //#lint:root type-check //#type-check:e2e clean dev DIFF build.inputs: onlyParked=["$TURBO_ROOT$/tsconfig.json","$TURBO_ROOT$/scripts/vite-*.ts","$TURBO_ROOT$/scripts/build-plugin-stylesheet.mjs","$TURBO_ROOT$/scripts/check-dist-completeness.mjs","$TURBO_ROOT$/scripts/invoked-as.mjs","$TURBO_ROOT$/scripts/vitest-invocation-guard.mjs"] DIFF test.inputs: onlyParked=["src/**", ..., "$TURBO_ROOT$/vitest.config.mts", "$TURBO_ROOT$/vitest.setup.*", ...] MISSING in live: //#lint:root MISSING in live: //#type-check:e2e $ vitest run (the 11 reader files) with ui/turbo.objectui.json := JSON of uiScopedTurboConfig() Test Files 8 failed | 3 passed (11) Tests 147 failed | 294 passed (441) per file: turbo-task-guard-coverage 55 · turbo-test-inputs 41 · turbo-build-inputs 38 · turbo-lint-inputs 5 (all in the //#lint:root describe) · e2e-type-check 4 · select-type-check-scope 2 · check-dist-completeness 1 · turbo-build-outputs-cover-published-entries 1 green: turbo-type-check-inputs, workspace-root, check-links-workflow ``` </details> <details><summary>Reading 4: derived gates on head 88bd2bb</summary> ``` $ node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack # 18 families, change set vs merge base 1287b2f node scripts/check-closing-keyword-parity.mjs :: exit 0 node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 node scripts/check-comment-mask-corpus.mjs :: exit 0 pnpm --dir ui exec vitest run scripts/ :: exit 0 pnpm check:cross-package-test-inputs :: exit 0 pnpm check:driver-memory-census :: exit 0 pnpm check:engine-double-contract :: exit 0 pnpm check:gitlink-declared :: exit 0 pnpm check:nul-bytes :: exit 0 pnpm check:published-files :: exit 0 pnpm check:query-options-erasure :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:tier-file-adoption :: exit 0 pnpm check:type-check-coverage :: exit 0 pnpm check:type-check-debt :: exit 0 pnpm check:undeclared-dep-imports :: exit 0 pnpm check:watch-hint-literal :: exit 0 pnpm check:where-matcher :: exit 0 $ node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.txt ✓ dispatch-gates --ran: 18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED (a DERIVED zero — all 18 recorded an exit code and none of them is 3). ``` </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01QSeKYmTYfaPbLQD4BJfomW --- _Generated by [Claude Code](https://claude.ai/code/session_01QSeKYmTYfaPbLQD4BJfomW)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 79ed6f7 commit 0e205fe

5 files changed

Lines changed: 99 additions & 148 deletions

File tree

‎ui/lychee.toml‎

Lines changed: 0 additions & 111 deletions
This file was deleted.

‎ui/scripts/__tests__/check-links-workflow.test.ts‎

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
22
import fs from 'node:fs';
33
import path from 'node:path';
44
import { fileURLToPath } from 'node:url';
5+
import { workspaceRoot } from '../workspace-root.mjs';
56

67
/**
78
* objectui#3449 — `check-links.yml` was pointed at the wrong tree.
@@ -25,13 +26,17 @@ import { fileURLToPath } from 'node:url';
2526
* tree again and this test goes red on the same day, instead of the
2627
* workflow going quietly blind for another year.
2728
*
28-
* 2. **The `root_dir` / `exclude` pair in `lychee.toml`.** Widening the scope
29-
* without it does not produce a working sweep, it produces 316 hard errors:
30-
* `content/docs` carries ~297 site-absolute links (`/docs/...`), and Lychee
31-
* fails those while CONSTRUCTING the URI — before `exclude` is consulted,
32-
* so an `^/docs` exclude pattern cannot suppress them. The two settings only
33-
* work together; removing either alone re-breaks the sweep, so neither may
34-
* leave without the other.
29+
* 2. **Site-absolute routes are given a root.** Widening the scope without
30+
* one does not produce a working sweep, it produces hundreds of hard errors:
31+
* `content/docs` carries site-absolute links (`/docs/...`), and Lychee fails
32+
* those while CONSTRUCTING the URI — before `exclude` is consulted, so an
33+
* `^/docs` exclude pattern cannot suppress them. objectui's `lychee.toml`
34+
* answered with a `root_dir` sentinel plus its exclude. The lane that runs
35+
* now, the root `check-links.yml`, answers with `--root-dir` at the content
36+
* root plus `--fallback-extensions`, so the routes RESOLVE; this pin reads
37+
* that argv (objectstack#26080). The two flags only work together: a route
38+
* omits its extension, so a root alone turns every route into "file not
39+
* found".
3540
*
3641
* 3. **No document count, here or in the workflow's header.** objectui#7825.
3742
* Both prose sentences above once carried a hand-copied size — one per tree
@@ -51,8 +56,36 @@ import { fileURLToPath } from 'node:url';
5156
* and this header's own count pin stand.
5257
*/
5358

54-
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
55-
const lycheeConfigPath = path.join(repoRoot, 'lychee.toml');
59+
/**
60+
* The lychee run that exists: the root `check-links.yml`, which passes
61+
* `--config lychee.toml` from the repository root. objectui's own
62+
* `lychee.toml` was read by no workflow and is gone (objectstack#26080).
63+
*/
64+
const liveRoot = workspaceRoot();
65+
const liveLycheeConfigPath = path.join(liveRoot, 'lychee.toml');
66+
const checkLinksWorkflowPath = path.join(liveRoot, '.github/workflows/check-links.yml');
67+
68+
/**
69+
* The lychee argv `check-links.yml` declares once in `env.LYCHEE_ARGS`, one
70+
* token per entry. A `${{ … }}` expression is collapsed to one token first
71+
* (`${{github.workspace}}/content`), so its inner spaces do not split it.
72+
*/
73+
function lycheeArgs(): string[] {
74+
const lines = fs.readFileSync(checkLinksWorkflowPath, 'utf8').split('\n');
75+
const start = lines.findIndex((line) => /^\s*LYCHEE_ARGS:\s*>-\s*$/.test(line));
76+
expect(start, 'check-links.yml no longer declares LYCHEE_ARGS as a folded block').toBeGreaterThan(-1);
77+
const indent = /^\s*/.exec(lines[start])![0].length;
78+
const block: string[] = [];
79+
for (const line of lines.slice(start + 1)) {
80+
if (line.trim() !== '' && /^\s*/.exec(line)![0].length <= indent) break;
81+
block.push(line.trim());
82+
}
83+
return block
84+
.join(' ')
85+
.replace(/\$\{\{\s*([^}]*?)\s*\}\}/g, (_, expr: string) => `\${{${expr}}}`)
86+
.split(/\s+/)
87+
.filter(Boolean);
88+
}
5689

5790
/**
5891
* This file's own leading block comment — the second surface the count pin reads.
@@ -209,25 +242,38 @@ describe('check-links — the header count pin', () => {
209242
});
210243
});
211244

212-
describe('lychee.toml — site-absolute routes are skipped, not resolved', () => {
213-
it('resolves site-absolute routes into a sentinel root and excludes it', () => {
214-
// Both halves or neither: `root_dir` alone turns 297 unresolvable links into
215-
// 297 "file not found" errors, and the exclude alone cannot fire at all,
216-
// because the failure happens before filtering.
217-
const config = fs.readFileSync(lycheeConfigPath, 'utf8');
218-
const rootDir = /^\s*root_dir\s*=\s*"([^"]+)"/m.exec(config);
219-
expect(rootDir, 'lychee.toml must set root_dir, or site-absolute links are hard errors').not.toBeNull();
220-
expect(rootDir![1].startsWith('/'), 'root_dir must be an absolute path').toBe(true);
221-
expect(fs.existsSync(rootDir![1]), 'root_dir is a sentinel namespace and must not exist on disk').toBe(false);
222-
expect(config, `root_dir ${rootDir![1]} is set but never excluded`).toContain(`"^file://${rootDir![1]}/`);
245+
describe('check-links — site-absolute routes are resolved against the content root', () => {
246+
it('hands lychee a root for site-absolute routes, and the extensions a route omits', () => {
247+
// Both halves or neither: without `--root-dir` every `/docs/...` link is a
248+
// hard error before any filter runs, and a root without
249+
// `--fallback-extensions` turns every extension-less route into a
250+
// "file not found".
251+
const args = lycheeArgs();
252+
const value = (flag: string): string | undefined => {
253+
const at = args.indexOf(flag);
254+
return at === -1 ? undefined : args[at + 1];
255+
};
256+
expect(value('--config'), 'the lane must read the root lychee.toml').toBe('lychee.toml');
257+
258+
const rootDir = value('--root-dir');
259+
expect(rootDir, 'check-links.yml must pass --root-dir, or site-absolute links are hard errors').toBeDefined();
260+
const workspace = '${{github.workspace}}/';
261+
expect(rootDir!.startsWith(workspace), '--root-dir must be absolute, i.e. anchored on the workspace').toBe(true);
262+
const rel = rootDir!.slice(workspace.length);
263+
expect(fs.existsSync(path.join(liveRoot, rel, 'docs')), `--root-dir ${rel} must be the tree \`/docs/...\` routes live under`).toBe(true);
264+
265+
const fallback = (value('--fallback-extensions') ?? '').split(',');
266+
expect(fallback, '--root-dir alone leaves every extension-less route unresolved').toEqual(
267+
expect.arrayContaining(['mdx', 'md']),
268+
);
223269
});
224270

225271
it('carries no remap pretending to resolve fumadocs routes', () => {
226272
// The removed `^/docs/(.*)$ file://./docs/$1` never fired (remap operates on
227273
// a parsed URL; `/docs/x` fails to parse first) and pointed at the internal
228274
// `docs/` tree besides. Route-to-file resolution has exactly one owner:
229275
// scripts/check-doc-links.mjs.
230-
const config = fs.readFileSync(lycheeConfigPath, 'utf8');
276+
const config = fs.readFileSync(liveLycheeConfigPath, 'utf8');
231277
const active = config
232278
.split('\n')
233279
.filter((line) => !/^\s*#/.test(line))

‎ui/scripts/__tests__/helpers/turbo-inputs.ts‎

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,37 @@ import fs from 'node:fs';
2121
import path from 'node:path';
2222

2323
import { uiRoot } from '../../ui-root.mjs';
24-
import { uiTurboConfigPath, workspaceGlobs } from '../../workspace-root.mjs';
24+
import { uiScopedTurboConfig, uiTurboConfigPath, workspaceGlobs } from '../../workspace-root.mjs';
2525

2626
/** Repo root: the objectui tree, standalone or as the monorepo's `ui/`. */
2727
export const repoRoot = uiRoot();
2828

29+
/** The `tasks` shape these guards read out of a turbo config. */
30+
export interface TurboTasks {
31+
readonly tasks?: Record<string, { inputs?: string[] }>;
32+
}
33+
34+
/**
35+
* The config turbo RUNS: the nearest `turbo.json` upward, narrowed to this tree
36+
* (`$TURBO_ROOT$/ui/x` read as `$TURBO_ROOT$/x`, the monorepo's own entries
37+
* dropped) by `uiScopedTurboConfig`. Under the monorepo that is the root
38+
* `turbo.json`; no turbo invocation reads anything else (objectstack#26080).
39+
* A guard whose property the root config carries reads this one.
40+
*/
41+
export function liveTurboConfig(): TurboTasks {
42+
return uiScopedTurboConfig();
43+
}
44+
2945
/**
3046
* objectui's OWN task declaration: `turbo.json` at the repo root, or the copy
31-
* the cutover parks as `turbo.objectui.json` (a nested turbo.json must `extends`
32-
* the root, so under the monorepo the live one is the root's, with these
33-
* inputs re-rooted to `$TURBO_ROOT$/ui/...`). These guards pin the shapes
34-
* objectui declares, relative to its own tree, so they read this one
35-
* (objectstack#25723).
47+
* the cutover parks as `turbo.objectui.json`. ⚠️ No turbo run reads the parked
48+
* copy (objectstack#26080 measured every invocation); it is still the default
49+
* here only for the guards whose property the root config does not carry yet
50+
* (`build` and `test` inputs), so none of them is loosened by a retarget.
3651
*/
37-
const turboConfigPath = uiTurboConfigPath();
52+
export function parkedTurboConfig(): TurboTasks {
53+
return JSON.parse(fs.readFileSync(uiTurboConfigPath(), 'utf8')) as TurboTasks;
54+
}
3855

3956
/** POSIX-separated, repo-relative. The one path spelling these guards compare in. */
4057
export function rel(absolute: string): string {
@@ -106,10 +123,7 @@ export function packagesWithScript(script: string): WorkspacePackage[] {
106123
* turbo's default, which is the defect all four guards exist to close, so the
107124
* absence throws rather than reading as an empty list.
108125
*/
109-
export function taskInputs(task: string): string[] {
110-
const turbo = JSON.parse(fs.readFileSync(turboConfigPath, 'utf8')) as {
111-
tasks?: Record<string, { inputs?: string[] }>;
112-
};
126+
export function taskInputs(task: string, turbo: TurboTasks = parkedTurboConfig()): string[] {
113127
const definition = turbo.tasks?.[task];
114128
if (!definition) throw new Error(`turbo.json must define a \`${task}\` task`);
115129
const inputs = definition.inputs ?? [];
@@ -124,8 +138,8 @@ export function taskInputs(task: string): string[] {
124138
* outside the package directory — turbo has no `..` escape, which is why
125139
* `$TURBO_ROOT$` exists at all.
126140
*/
127-
export function rootAnchoredInputs(task: string): string[] {
128-
return taskInputs(task)
141+
export function rootAnchoredInputs(task: string, turbo: TurboTasks = parkedTurboConfig()): string[] {
142+
return taskInputs(task, turbo)
129143
.filter((entry) => entry.startsWith('$TURBO_ROOT$/'))
130144
.map((entry) => entry.slice('$TURBO_ROOT$/'.length));
131145
}

‎ui/scripts/__tests__/turbo-lint-inputs.test.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
rel,
1111
repoFilesOnDisk,
1212
repoRoot,
13+
liveTurboConfig,
1314
rootAnchoredInputs,
1415
taskInputs,
1516
} from './helpers/turbo-inputs';
@@ -88,7 +89,7 @@ const INPUTS_NOT_DERIVABLE: ReadonlyMap<string, string> = new Map();
8889

8990
const PACKAGES = packagesWithScript(TASK);
9091
const DERIVED = PACKAGES.map((pkg) => ({ ...pkg, outside: outOfPackageFiles(pkg) }));
91-
const ROOT_INPUTS = rootAnchoredInputs(TASK);
92+
const ROOT_INPUTS = rootAnchoredInputs(TASK, liveTurboConfig());
9293
const MATCHERS = ROOT_INPUTS.map((glob) => ({ glob, re: globToRegExp(glob) }));
9394

9495
describe('turbo `lint` inputs cover every out-of-package file (objectui#4184)', () => {
@@ -167,7 +168,7 @@ describe('turbo `lint` inputs cover every out-of-package file (objectui#4184)',
167168
*/
168169
it('the explicit inputs list still carries $TURBO_DEFAULT$', () => {
169170
expect(
170-
taskInputs(TASK),
171+
taskInputs(TASK, liveTurboConfig()),
171172
`turbo.json \`${TASK}\` declares an explicit inputs list, so it no longer falls back to ` +
172173
`turbo's default. Without "$TURBO_DEFAULT$" in the list the package's OWN files stop ` +
173174
`being hashed.`,

‎ui/scripts/__tests__/turbo-type-check-inputs.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
packagesWithScript,
55
rel,
66
repoFilesOnDisk,
7+
liveTurboConfig,
78
rootAnchoredInputs,
89
} from './helpers/turbo-inputs';
910
import { outOfPackageFiles } from './helpers/tsc-program';
@@ -131,7 +132,7 @@ const INPUTS_NOT_DERIVABLE: ReadonlyMap<string, string> = new Map([
131132

132133
const PACKAGES = packagesWithScript(TASK);
133134
const DERIVED = PACKAGES.map((pkg) => ({ ...pkg, outside: outOfPackageFiles(pkg.dir, pkg.script) }));
134-
const ROOT_INPUTS = rootAnchoredInputs(TASK);
135+
const ROOT_INPUTS = rootAnchoredInputs(TASK, liveTurboConfig());
135136
const MATCHERS = ROOT_INPUTS.map((glob) => ({ glob, re: globToRegExp(glob) }));
136137

137138
describe('turbo `type-check` inputs cover every out-of-package file (objectui#3514)', () => {

0 commit comments

Comments
 (0)