diff --git a/.changeset/runner-typecheck-2917.md b/.changeset/runner-typecheck-2917.md new file mode 100644 index 0000000000..e52c02c641 --- /dev/null +++ b/.changeset/runner-typecheck-2917.md @@ -0,0 +1,55 @@ +--- +"@object-ui/runner": patch +--- + +fix(runner): type-check the package at all, and fix the `DataSource` contract violation that hid behind a broken import (#2917) + +`@object-ui/runner` was the worst-covered package in the repo: `build` is +`vite build` (transpile only), it had no `type-check` script, and — uniquely — +**no `tsconfig.json` at all**. Nothing had ever type-checked it, despite it being +a published package. + +**It was not broken at runtime.** The two bad imports were `import type`, so they +were erased before they could fail, and the one value import +(`emulateBatchTransaction`) does exist. `MockDataSource` is also unreferenced +anywhere in the repo. So this is a correctness and reference-quality fix, not an +outage. + +**What the missing check actually hid.** `DataSource` and +`BatchTransactionOperation` were imported from `@object-ui/core`, which does not +export them — they live in `@object-ui/types`. Because that import never +resolved, `class MockDataSource implements DataSource` was silently a no-op, and +three separate commits maintained the class *as if* it were being verified +(`62b9ab510` added `batchTransaction`, `09d9669c7` made `getObjectSchema` +required, `5527388b0` added input validation). With the `implements` clause +inert, a real contract violation survived all three: + +```ts +async find(resource: string, params?: any): Promise { return []; } +``` + +`DataSource.find` returns a `QueryResult` envelope, not a bare array. Anyone +copying this mock as the starting point for their own adapter — which is exactly +what its doc comment invites — would hand every consumer an array where `.data` +and `.total` are `undefined`. Now typed as `Promise` and returning +`{ data: [], total: 0 }`. + +Also in this change: + +- `packages/runner/tsconfig.json` added, mirroring `apps/console` rather than the + library packages: `runner` is a Vite app, so it wants `bundler` resolution, + `allowImportingTsExtensions` (for `./App.tsx`) and `types: ["vite/client"]` + (for `import.meta.glob` in `MetadataLoader` and the `./index.css` side-effect + import). Keeping it standalone instead of extending the root config also means + it never inherits the root `paths`, so workspace deps resolve through built + `.d.ts` and the TS6059 `rootDir` class of error cannot appear. +- unused parameters prefixed with `_` (6x in `mockDataSource`), and an unused + `Circle` icon import dropped from `LayoutRenderer`. +- `"type-check": "tsc --noEmit"` added, and the package's `DEBT` entry deleted + from `scripts/check-type-check-coverage.mjs`. Coverage goes 35 -> 36 of 45 and + outstanding errors 46 -> 32. + +Verified the gate genuinely covers the package now, rather than trusting the +green: injecting a type error into `runner/src/App.tsx` makes `pnpm type-check` +fail with `Failed: @object-ui/runner#type-check`, which was impossible before +this change. diff --git a/packages/runner/package.json b/packages/runner/package.json index 175240bd87..9c8d803125 100644 --- a/packages/runner/package.json +++ b/packages/runner/package.json @@ -16,6 +16,7 @@ "scripts": { "dev": "vite", "build": "vite build", + "type-check": "tsc --noEmit", "preview": "vite preview", "test": "vitest run" }, diff --git a/packages/runner/src/LayoutRenderer.tsx b/packages/runner/src/LayoutRenderer.tsx index ea848d649e..6e41eb4729 100644 --- a/packages/runner/src/LayoutRenderer.tsx +++ b/packages/runner/src/LayoutRenderer.tsx @@ -12,7 +12,6 @@ import { Bell, Box, ChevronDown, - Circle, Menu, Moon, Search, diff --git a/packages/runner/src/lib/mockDataSource.ts b/packages/runner/src/lib/mockDataSource.ts index 717d61f4ff..14d4f524c3 100644 --- a/packages/runner/src/lib/mockDataSource.ts +++ b/packages/runner/src/lib/mockDataSource.ts @@ -6,7 +6,12 @@ * LICENSE file in the root directory of this source tree. */ -import type { DataSource, BatchTransactionOperation } from '@object-ui/core'; +import type { + DataSource, + BatchTransactionOperation, + QueryParams, + QueryResult, +} from '@object-ui/types'; import { emulateBatchTransaction } from '@object-ui/core'; /** @@ -14,12 +19,15 @@ import { emulateBatchTransaction } from '@object-ui/core'; * 在真实项目中,你会在这里使用 fetch/axios 调用你的 API。 */ export class MockDataSource implements DataSource { - async find(resource: string, params?: any): Promise { + async find(resource: string, params?: QueryParams): Promise { console.log(`[DataSource] Querying ${resource}`, params); - return []; + // `find` returns an envelope, not a bare array — consumers read `.data` + // and `.total` (see QueryResult). Returning `[]` here would leave every + // caller with `undefined` data. + return { data: [], total: 0 }; } - async findOne(resource: string, id: string): Promise { + async findOne(_resource: string, _id: string): Promise { return null; } @@ -33,11 +41,11 @@ export class MockDataSource implements DataSource { return { id: Math.random().toString(), ...data }; } - async update(resource: string, id: string, data: any): Promise { + async update(_resource: string, _id: string, data: any): Promise { return data; } - async delete(resource: string, id: string): Promise { + async delete(_resource: string, _id: string): Promise { return true; } diff --git a/packages/runner/tsconfig.json b/packages/runner/tsconfig.json new file mode 100644 index 0000000000..bc8c136558 --- /dev/null +++ b/packages/runner/tsconfig.json @@ -0,0 +1,32 @@ +{ + // Standalone rather than extending the root config: `runner` is a Vite app, + // so it wants the same shape as apps/console — and not inheriting the root + // `paths` keeps `@object-ui/*` resolving through each dependency's built + // `.d.ts` instead of pulling sibling sources in as program inputs. + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + + /* `import.meta.glob` in MetadataLoader, and CSS side-effect imports. */ + "types": ["vite/client"] + }, + "include": ["src"], + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"] +} diff --git a/scripts/check-type-check-coverage.mjs b/scripts/check-type-check-coverage.mjs index 0652d43ce8..142c5db232 100644 --- a/scripts/check-type-check-coverage.mjs +++ b/scripts/check-type-check-coverage.mjs @@ -29,7 +29,6 @@ const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); // #2911 sweep (bare `tsc --noEmit` with the `paths` override its type-checked // peers already carry, so the TS6059 rootDir noise is excluded). const DEBT = { - "@object-ui/runner": { errors: 14, issue: 2917, note: "no tsconfig.json at all; also imports two exports @object-ui/core does not have" }, "@object-ui/plugin-form": { errors: 10, issue: 2919, note: "6x t() fallback-signature mismatch, 2x undefined index, 2x string|number" }, "@object-ui/site": { errors: 7, issue: 2919, note: "TS2304 on Next's generated LayoutProps/PageProps; needs .next/types from a prior next build" }, "@object-ui/plugin-grid": { errors: 4, issue: 2919, note: "2x t() call signature + 2x TS2367 that are closure-mutation narrowing artifacts, NOT a logic bug" },