feat(create-sanity): bundle init logic into single file#759
feat(create-sanity): bundle init logic into single file#759rexxars wants to merge 35 commits intobundled-createfrom
init logic into single file#759Conversation
|
Claude finished @mttdnt's task in 7m 12s —— View job Review
Issues1. The barrel exports in
const instance = new RealClass()
instance instanceof DeprecatedWrapper // FALSE — prototype chains don't overlapThe real classes are thrown internally by
Fix: Use a plain re-export with a JSDoc deprecation tag instead of wrapping with /** @deprecated Import from `@sanity/cli-core/errors` */
export {NonInteractiveError} from '../errors/NonInteractiveError.js'This preserves the prototype chain for 2. Bundle import failure in CI The bundle stats comment shows: A CJS module in the bundle is trying to mutate its exports at runtime after the rollup 3. Swallowed error context in dataset import ( } catch {
output.warn(
'Sample dataset import failed. Your studio will work fine without it.\n' + ...
)
}Using a bare } catch (err) {
output.warn(...)
// log raw error in debug mode or append to message
}ChangesetThe changeset is present ( Minor observations (no action required)
|
f51850a to
020c660
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
895b6fa to
e6848b3
Compare
Coverage Delta
Comparing 32 changed files against main @ Overall Coverage
|
3d2e1e9 to
a27419e
Compare
c2b174f to
9efab2a
Compare
|
❌ Bundle Stats — An error occurred while calculating bundle sizes. Error details |
9efab2a to
0ff423a
Compare
0ff423a to
b7a94b2
Compare
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
320cf80 to
009e2d9
Compare
📦 Bundle Stats —
|
| Metric | Value | vs bundled-create (d82d20e) |
|---|---|---|
| Internal (raw) | 2.1 KB | - |
| Internal (gzip) | 799 B | - |
| Bundled (raw) | 1.54 MB | -9.40 MB, -86.0% |
| Bundled (gzip) | 394.7 KB | -1.67 MB, -81.2% |
| Import time | 856ms | +44ms, +5.4% |
bin:sanity
| Metric | Value | vs bundled-create (d82d20e) |
|---|---|---|
| Internal (raw) | 975 B | - |
| Internal (gzip) | 460 B | - |
| Bundled (raw) | 9.84 MB | - |
| Bundled (gzip) | 1.77 MB | - |
| Import time | 1.97s | +32ms, +1.6% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — @sanity/cli-core
Compared against bundled-create (d82d20e5)
| Metric | Value | vs bundled-create (d82d20e) |
|---|---|---|
| Internal (raw) | 98.0 KB | +4.9 KB, +5.3% |
| Internal (gzip) | 23.3 KB | +1.4 KB, +6.4% |
| Bundled (raw) | 21.65 MB | +35.6 KB, +0.2% |
| Bundled (gzip) | 3.43 MB | +8.5 KB, +0.2% |
| Import time | 808ms | +27ms, +3.4% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — create-sanity
Compared against bundled-create (d82d20e5)
| Metric | Value | vs bundled-create (d82d20e) |
|---|---|---|
| Internal (raw) | 2.37 MB | +2.37 MB, +254497.4% |
| Internal (gzip) | 555.3 KB | +554.8 KB, +112049.7% |
| Bundled (raw) | 2.37 MB | +2.37 MB, +248141.6% |
| Bundled (gzip) | 555.3 KB | +554.8 KB, +110520.0% |
| Import time | ❌ TypeError: Cannot add property upath, object is not extensible | - |
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
Coverage Delta
Comparing 32 changed files against main @ Overall Coverage
|
009e2d9 to
16a082a
Compare
Coverage Delta
Comparing 31 changed files against main @ Overall Coverage
|
16a082a to
0492ffa
Compare
0492ffa to
00daf50
Compare
…rectly Replaces the spawn-wrapper approach with a TypeScript entry point that parses flags using @oclif/core/parser (reusing InitCommand.flags for exact parity) and calls initAction() directly. Uses no-op telemetry stub. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch from esbuild to Rollup for the create-sanity standalone bundle to enable better tree-shaking of barrel exports. Rollup resolves @sanity/cli-core source directly via alias plugin so it can tree-shake unused exports from the barrel. Plugins: alias, node-resolve, commonjs, json, esbuild (transpile only). Bundle size is ~13MB (same as esbuild - tree-shaking improvements will come in subsequent tasks with stub removal). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests run against the built dist/index.js bundle and cover: - --help output with flag listing - Package manager detection (npm/pnpm/yarn) in help text - Hidden flags not shown in help - -h shorthand - --help precedence over other flags - Invalid flag produces clean error (no stack trace) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove unused devDeps: @oclif/core, tsx - Add debug as devDep (used in rollup.config.ts alias) - Remove unused InitFlagName type export - Add knip ignore for @sanity/cli (imported via relative source paths) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace dynamic import of ImportDatasetCommand with spawning the project's own sanity binary via execa. The dynamic import pulled the entire oclif command tree into the create-sanity bundle because Rollup inlines all dynamic imports. The scaffolded project always has sanity as a dependency, so spawning its binary for dataset import works in both standalone and oclif contexts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The function is 4 lines - no need for a separate package. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace custom detectPackageManager with the shared getRunningPackageManager from @sanity/cli-core/package-manager. Adds bun support for help text (`bun create sanity@latest`). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
create-sanity now handles --help itself via parseArgs, so it never delegates to `sanity init --help`. The --from-create flag, the SanityHelp.replaceInitWithCreateCommand rewriting, and the fromCreate option in InitOptions are all unused. Remove them. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npm requires `--` to forward flags to the create script (e.g. `npm create sanity@latest -- --template moviedb`). Other package managers don't need it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move flagsToInitOptions, InitCommandFlags, InitCommandArgs, and the narrowing helpers out of types.ts into flagsToInitOptions.ts. types.ts now only contains type definitions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add fs.access check before spawning the sanity binary for dataset import. Gives a clear error message instead of a raw ENOENT if dependencies weren't installed correctly. Also fix misleading vitest config comment. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use execa preferLocal instead of manual .bin path (Windows compat) - Fix exit:0 being silently discarded in CLIError (0 || undefined bug) - Remove duplicate --env validation from InitCommand (flagsToInitOptions is the canonical location, shared with create-sanity) - Remove unused overrides parameter from toOclifFlags Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… constraints parseArgs doesn't support oclif features like aliases, option validation, or exclusive constraints. Add post-parse handling: - Register flag aliases (e.g. --project-id) as separate parseArgs options, then resolve them back to canonical names - Validate string flags with `options` against allowed values (--package-manager, --visibility) - Check exclusive constraints and error if conflicting flags are passed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move all parseArgs logic (option building, alias resolution, negation merging, option/exclusive validation, help printing) out of index.ts into a dedicated parseArgs.ts module. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace ImportDatasetCommand mock with execa mock - Update assertion to match new call signature (binary name, args, options) - Fix env validation exit code expectation (now InitError exit:1, not oclif exit:2) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Establish consistent exit code semantics: - 1: init action runtime failure (auth, missing deps, etc) - 2: flag parsing/validation error (unknown flag, invalid option value, exclusive constraint violation) - 3: domain-specific flag validation (e.g. --env must start with .env) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add pretest script so `pnpm test` builds the bundle before running - Wrap execa call in initStudio with try/catch so dataset import failures produce a clean InitError instead of dumping ExecaError - Add explicit default:false to bare, dataset-default, no-git flags so parseArgs always populates them, matching InitCommandFlags types Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use helpLabel when defined (e.g. --[no-]git) - Show --[no-] prefix for allowNo boolean flags - Wrap long descriptions to terminal width - Show --help in the options list - Error when both a flag and its alias are passed (--project + --project-id) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On Windows, environment variables are case-insensitive but process.env preserves the original casing. When overriding npm_config_user_agent in tests, the original key (potentially different casing) wasn't being removed, so the override was ineffective. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrite printHelp to match oclif's help formatting:
- Terminal width detection: OCLIF_COLUMNS env > TTY width > 80 fallback,
clamped to minimum 40
- Use wrap-ansi with {hard: true} for description wrapping
- Dynamic left column width based on widest flag label
- Automatic multiline format (flag on own line, description below) when
any description wraps to >4 lines at current terminal width
- Wrap first, then indent (matching oclif's ordering)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…sanity Move mcpMode computation into flagsToInitOptions so the identical logic in init.ts and create-sanity/index.ts cannot diverge. The function now takes an `interactive` boolean and derives both `isUnattended` and `mcpMode` internally. Add deprecation warnings in parseArgs.ts so that create-sanity users see feedback when using --reconfigure, --create-project, or --quickstart (oclif handles this automatically for `sanity init`, but the standalone path was silent). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove oclif type dependency from Output interface (cli-core/types.ts) - Replace process.exit() in parseArgs validation with FlagValidationError - Add explicit return type on warn adapter in create-sanity entry point - Extract shared INIT_DESCRIPTION constant to deduplicate help text - Suppress @iarna/toml eval warning in Rollup config Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
00daf50 to
bc02ced
Compare

Description
Note
Part 3/4 of the ongoing work to make
create-sanityfaster by bundling theinitlogicThis mostly completes the work - the remaining part is the telemetry integration and an optional but wanted step at reducing the amount of dependencies we rely on for the init step.
The bundling script has a few hacks that would be nice to get rid of over time, but not the end of the world.
What to review
Testing