Skip to content

fix(cli): give the objectui init scaffold the vite-env.d.ts its own build script needs (#4111) - #4120

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4111-init-scaffold-tsc
Aug 10, 2026
Merged

yinlianghui merged 1 commit into
mainfrom
claude/issue-4111-init-scaffold-tsc

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #4111

commands/init.ts is the CLI's third app generator, with its own tsconfig object, and it was outside the surface of #4113 (#3853). Unlike the two temp-app generators that PR fixed, this one's tsconfig is not decorative: the package.json the scaffold writes declares

"build": "tsc && vite build"

so tsc runs first, on the way to a production build. A user who runs objectui init and then the npm run build the generated README names hits a type error in a file the tool wrote for them, before Vite is ever reached.

Ordering

PR #4113 had merged by the time this work started (origin/main @ 9b9fa4961), so this branches off main normally — no stacking, and the gate this extends is already there.

Premise check — reproduced on the tip this branches from

Generated buildInitFiles('sample-app', 'simple') to disk and ran the workspace's tsc -p with the scaffold's own tsconfig.json:

src/App.tsx(1,32): error TS2307: Cannot find module '@object-ui/react' or its corresponding type declarations.
src/App.tsx(4,8):  error TS2882: Cannot find module or type declarations for side-effect import of '@object-ui/components'.
src/main.tsx(4,8): error TS2882: Cannot find module or type declarations for side-effect import of './index.css'.

The issue's line and column hold verbatim. The two @object-ui/* diagnostics are the probe not installing them, which is the exemption #4113 already defines and bounds.

Measured, not assumed: this scaffold has ONE of the five error classes, not seventeen

The card asked for a full run rather than the targeted probe the issue was filed from. Measured twice, and both say the same thing.

1. As the gate sees it. No unused import (TS6133/TS6196), no implicit any (TS7031/TS7006), no required-prop call site (TS2741). That is not luck: sixteen of #4113's seventeen live in src/Layout.tsx and the routed src/App.tsx, and this scaffold has neither — its whole src/** is a 9-line main.tsx, a 4-line App.tsx and a stylesheet.

2. With the exempt modules resolved, so the exemption hides nothing. The one thing a bare-specifier exemption can conceal here is the call site SchemaRenderer schema={schema}: with @object-ui/react unresolved that component is any, so the app.json each template inlines is checked against nothing. Mapped @object-ui/react and @object-ui/components onto their in-repo source via tsconfig paths — real prop types, real resolveJsonModule inference — and ran all three templates:

template diagnostics in the scaffold's own files
simple src/main.tsx(4,8): TS2882 './index.css'
form src/main.tsx(4,8): TS2882 './index.css'
dashboard src/main.tsx(4,8): TS2882 './index.css'

So the one error is the whole defect, the three templates agree, and the schema prop is genuinely clean against the library's real type rather than merely unchecked.

The fix

buildInitFiles now writes src/vite-env.d.ts holding the vite/client triple-slash reference — the file create-vite's own React+TS template writes for exactly this reason, and where the declare module ambient declarations for stylesheets live. vite is already in SCAFFOLD_DEV_DEPENDENCIES, so nothing new is declared.

Its bytes are identical to APP_VITE_ENV_D_TS in utils/app-generator.ts. I deliberately did not export that constant and import it across: commands/init.ts would then depend on the temp-app generator module (pulling js-yaml into the init command's load path) to share a 40-character ecosystem-standard string. Instead the three copies are pinned to one literal by the reachability test below, which is the same backstop DEPENDENCY_ANCHORS gives the ranges — drift fails a test instead of shipping. Happy to switch to a shared constant if a reviewer prefers the coupling.

The gate

Four additions to the #4113 tsc block, all judging the artifact rather than this repo's source text:

  • type-checks the init scaffold, whose own build script really runs tsc — a real tsc -p over the scaffold written to disk, through the same expectCleanTypeCheck / bare-vs-relative exemption the other two use. Generated under the repo's .objectui-tmp/ for the module-resolution reason fix(cli): make the generated temp app pass its own strict tsconfig, and gate it with real tsc (#3853) #4113 records.
  • pins that the init scaffold is the generator whose build really runs tsc — the premise that makes this one live rather than latent. If that script ever stops running tsc, a reader of the gate above needs to know.
  • writes one src tree for all three templates, so one tsc run judges them all — why simple alone is gated rather than paying ~1s three times: the templates differ in app.json and in nothing else, asserted file by file, plus a non-vacuity check that the three app.json really are three.
  • goes red on the pre-fix init scaffold, with the defect the issue measured — the in-suite self-test.

The reachability test exempts the ambient declaration, and nothing else, from reachability now loops over the init map too, which is what pins the three generators to one string.

init() writes with fs directly and derives its target from process.cwd(), so the gate writes the file map — sound because cli-bin.test.ts already pins init() against buildInitFiles byte for byte through the real bin. That equivalence is cited in the new helper's comment.

Reverse verification — direction predicted first, then run

Predicted: reverting init.ts turns the new tsc gate red with TS2882 naming './index.css' and nothing else; the ambient-exemption test red on the file being absent; and the new self-test's own "nothing to revert" tripwire fires, since deleting a file it has already deleted is a no-op. Reverted with git checkout origin/main -- packages/cli/src/commands/init.ts (never git stash — shared stack), and measured:

× exempts the ambient declaration, and nothing else, from reachability
× type-checks the init scaffold, whose own build script really runs tsc
× goes red on the pre-fix init scaffold, with the defect the issue measured
  Tests  3 failed | 38 passed (41)

AssertionError: init: generated sources fail the tsconfig the generator wrote beside them
+ ".objectui-tmp/tsc-gate-3853-v7pAxy/src/main.tsx: TS2882: Cannot find module or type
   declarations for side-effect import of './index.css'."
AssertionError: nothing to revert — the generator moved: expected false to be true

Three for three, including the tripwire. Restored, green again.

The self-test pins exactly { TS2882: 1 } and then asserts the specifier and the file, because @object-ui/components is a TS2882 as well — a gate that counted codes alone could not tell the exempt one from the defect.

Verification

Changeset: patch on @object-ui/cli — the scaffold's template source ships inside the released package.

Out of scope

Nothing new filed. Everything measured here is either fixed above or already recorded on #4111 / #4113.


Generated by Claude Code

… build needs (#4111)

The scaffold `objectui init` writes declares `build: "tsc && vite build"`, so
unlike the two temp-app generators objectui#3853 fixed, its tsconfig is not
decorative: tsc runs first, on the way to a production build. Its
`src/main.tsx` did `import './index.css'` with no ambient declaration behind
it, so a user who followed the generated README — `objectui init`, then
`npm run build` — met TS2882 in a file the tool had just written, before Vite
was ever reached.

Fixed at the template, the same way and with the same bytes as the sibling
generators: the scaffold now writes `src/vite-env.d.ts` carrying the
`vite/client` triple-slash reference, where the `declare module '*.css'`
declarations live. `vite` was already in SCAFFOLD_DEV_DEPENDENCIES.

Measured, not assumed: generating the scaffold to disk and running the
workspace tsc with its own tsconfig found that one error and none of
objectui#3853's other four classes — no unused import, no implicit any, no
required-prop call site. Those live in a `src/Layout.tsx` this scaffold does
not have. Measured a second time with `@object-ui/react`/`@object-ui/components`
mapped onto their in-repo source via tsconfig `paths`, so the diagnostics the
unresolved-module exemption would hide became visible: all three templates
clean apart from the `./index.css` TS2882.

The objectui#3853 tsc gate now covers the third generator, with the reverse
verification (delete the file, TS2882 for './index.css' and nothing else) as an
in-suite self-test, and pins the two premises that test rests on: that the
scaffold's build script really runs tsc, and that its three templates share one
`src/**` tree so one tsc run judges all three.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 10, 2026 11:10am

Request Review

@github-actions github-actions Bot added the tests label Aug 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.3 KB 350 KB
Entry file index-CcETve-r.js —
Status PASS —

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.66KB 3.13KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 485.06KB 107.21KB
core (index.js) 3.04KB 1.15KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 140.66KB 36.25KB
fields (index.js) 229.40KB 56.93KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.65KB 1.06KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 9.48KB 3.27KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 4.52KB 1.96KB
layout (index.js) 38.87KB 10.80KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 45.23KB 12.45KB
plugin-charts (index.js) 61.49KB 17.48KB
plugin-chatbot (index.js) 180.33KB 42.79KB
plugin-dashboard (index.js) 118.50KB 30.66KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 237.80KB 59.48KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 114.58KB 27.68KB
plugin-gantt (index.js) 162.81KB 39.67KB
plugin-grid (index.js) 188.04KB 49.91KB
plugin-kanban (index.js) 48.60KB 13.41KB
plugin-list (index.js) 110.04KB 26.67KB
plugin-map (index.js) 17.00KB 5.32KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.58KB 10.58KB
plugin-timeline (index.js) 26.21KB 7.52KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 23.71KB 7.95KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.23KB 0.66KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.71KB 1.34KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@yinlianghui
yinlianghui marked this pull request as ready for review August 10, 2026 11:19
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit 64cda47 Aug 10, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4111-init-scaffold-tsc branch August 10, 2026 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

objectui init scaffold fails its own npm run build: src/main.tsx imports ./index.css with no ambient declaration (TS2882)

1 participant