Skip to content

Commit a65de7d

Browse files
committed
refactor(create): collapse bundled-resolution lets + cover gitignore edge cases
Post-simplify cleanup: - `bin.ts` — replace four parallel `let`s (`bundledLocalPath`, `bundledEntryName`, `bundledOrgScope`, `isBundledMonorepo`) with one `bundled: Extract<OrgResolution, { kind: 'bundled' }> | undefined`. Downstream uses `bundled?.<field>` everywhere; the `&& bundledOrgScope` guard at the inject-call drops since the type guarantees `scope` is set on the bundled variant. `isBundledMonorepo` is now a derived `bundled?.monorepo === true` constant. Reduces stateful surface area and tightens the link to the resolution shape. - `utils.spec.ts` — three new `ensureGitignoreNodeModules` cases: CRLF line endings (no-op), `node_modules/sub` subpath (must not count as a match), and `!node_modules` un-ignore override (must not silently re-exclude). - `create-org-bundled-monorepo/tarballs/README.md` — document the exact regeneration command. The mock manifest's `integrity` field locks the bytes, and macOS's default `tar` injects AppleDouble metadata that breaks the snap; spell out `COPYFILE_DISABLE=1` + `--no-mac-metadata` for future maintainers.
1 parent 27c91c2 commit a65de7d

3 files changed

Lines changed: 64 additions & 15 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Tarball regeneration
2+
3+
`create-1.0.0.tgz` is committed binary. The mock manifest's
4+
`integrity: "sha512-…"` field locks the bytes — regenerating without
5+
matching the original bit-for-bit will break the snap-test.
6+
7+
To regenerate (e.g. after editing one of the bundled template files):
8+
9+
```sh
10+
mkdir -p /tmp/vp-bundled-monorepo-build/package/templates/workspace
11+
# … recreate the template files inside that workspace dir …
12+
13+
cd /tmp/vp-bundled-monorepo-build
14+
COPYFILE_DISABLE=1 tar --no-mac-metadata -czf \
15+
<repo>/packages/cli/snap-tests/create-org-bundled-monorepo/tarballs/create-1.0.0.tgz \
16+
package/
17+
18+
# Then update mock-manifest.json's `integrity` field:
19+
node -e "
20+
const fs = require('node:fs');
21+
const crypto = require('node:crypto');
22+
const buf = fs.readFileSync(
23+
'<repo>/packages/cli/snap-tests/create-org-bundled-monorepo/tarballs/create-1.0.0.tgz',
24+
);
25+
console.log('sha512-' + crypto.createHash('sha512').update(buf).digest('base64'));
26+
"
27+
```
28+
29+
`COPYFILE_DISABLE=1` and `--no-mac-metadata` keep macOS from injecting
30+
`._*` AppleDouble files that pollute the extracted output and break the
31+
snap-test.

packages/cli/src/create/__tests__/utils.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,23 @@ describe('ensureGitignoreNodeModules', () => {
138138
ensureGitignoreNodeModules(projectDir);
139139
expect(gitignore()).toBe(existing);
140140
});
141+
142+
it('handles CRLF line endings without re-appending', () => {
143+
const existing = 'node_modules\r\ndist\r\n';
144+
fs.writeFileSync(path.join(projectDir, '.gitignore'), existing);
145+
ensureGitignoreNodeModules(projectDir);
146+
expect(gitignore()).toBe(existing);
147+
});
148+
149+
it('does not consider a `node_modules/sub` subpath as already excluded', () => {
150+
fs.writeFileSync(path.join(projectDir, '.gitignore'), 'node_modules/sub\n');
151+
ensureGitignoreNodeModules(projectDir);
152+
expect(gitignore()).toBe('node_modules/sub\nnode_modules\n');
153+
});
154+
155+
it('does not match `!node_modules` (an explicit un-ignore override)', () => {
156+
fs.writeFileSync(path.join(projectDir, '.gitignore'), '!node_modules\n');
157+
ensureGitignoreNodeModules(projectDir);
158+
expect(gitignore()).toBe('!node_modules\nnode_modules\n');
159+
});
141160
});

packages/cli/src/create/bin.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ import {
4949
import type { ExecutionResult } from './command.ts';
5050
import { discoverTemplate, inferGitHubRepoName, inferParentDir, isGitHubUrl } from './discovery.ts';
5151
import { getInitialTemplateOptions } from './initial-template-options.ts';
52-
import { getConfiguredDefaultTemplate, resolveOrgManifestForCreate } from './org-resolve.ts';
52+
import {
53+
getConfiguredDefaultTemplate,
54+
type OrgResolution,
55+
resolveOrgManifestForCreate,
56+
} from './org-resolve.ts';
5357
import {
5458
cancelAndExit,
5559
checkProjectDirExists,
@@ -442,10 +446,7 @@ async function main() {
442446
let selectedParentDir: string | undefined;
443447
let remoteTargetDir: string | undefined;
444448
let shouldSetupHooks = false;
445-
let bundledLocalPath: string | undefined;
446-
let bundledEntryName: string | undefined;
447-
let bundledOrgScope: string | undefined;
448-
let isBundledMonorepo = false;
449+
let bundled: Extract<OrgResolution, { kind: 'bundled' }> | undefined;
449450
let skipShorthandExpansion = false;
450451
const installArgs = process.env.CI ? ['--no-frozen-lockfile'] : undefined;
451452

@@ -469,10 +470,7 @@ async function main() {
469470
// into `@your-org/create-template-web`.
470471
skipShorthandExpansion = true;
471472
} else if (resolved.kind === 'bundled') {
472-
bundledLocalPath = resolved.bundledLocalPath;
473-
bundledEntryName = resolved.entryName;
474-
bundledOrgScope = resolved.scope;
475-
isBundledMonorepo = resolved.monorepo === true;
473+
bundled = resolved;
476474
} else if (resolved.kind === 'escape-hatch') {
477475
selectedTemplateName = '';
478476
}
@@ -509,7 +507,8 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
509507
}
510508

511509
const isBuiltinTemplate = selectedTemplateName.startsWith('vite:');
512-
const isBundledTemplate = bundledLocalPath !== undefined;
510+
const isBundledTemplate = bundled !== undefined;
511+
const isBundledMonorepo = bundled?.monorepo === true;
513512
const isDirectScaffoldTemplate = isBuiltinTemplate || isBundledTemplate;
514513

515514
// Remote templates (e.g., @tanstack/cli, custom templates) run their own
@@ -636,8 +635,8 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
636635
}
637636
}
638637

639-
const directScaffoldFallbackName = isBundledTemplate
640-
? `vite-plus-${bundledEntryName}`
638+
const directScaffoldFallbackName = bundled
639+
? `vite-plus-${bundled.entryName}`
641640
: selectedTemplateName === BuiltinTemplate.monorepo
642641
? 'vite-plus-monorepo'
643642
: `vite-plus-${selectedTemplateName.split(':')[1]}`;
@@ -800,7 +799,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
800799
selectedTemplateArgs,
801800
workspaceInfo,
802801
options.interactive,
803-
bundledLocalPath,
802+
bundled?.bundledLocalPath,
804803
skipShorthandExpansion,
805804
);
806805

@@ -903,14 +902,14 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
903902
workspaceInfo.rootDir = fullPath;
904903
updateCreateProgress('Integrating monorepo');
905904
rewriteMonorepo(workspaceInfo, undefined, compactOutput);
906-
if (isBundledMonorepo && bundledOrgScope) {
905+
if (bundled?.monorepo) {
907906
// Wire `create.defaultTemplate: '<scope>'` into the new workspace's
908907
// vite.config.ts so a bare `vp create` from inside it opens the
909908
// same org's picker. Only triggers when the user just scaffolded
910909
// from `vp create @scope:<entry>` — for builtin `vite:monorepo`,
911910
// even a scoped package name doesn't imply the user wants that
912911
// scope as their template default.
913-
injectCreateDefaultTemplate(fullPath, bundledOrgScope, compactOutput);
912+
injectCreateDefaultTemplate(fullPath, bundled.scope, compactOutput);
914913
}
915914
if (shouldSetupHooks) {
916915
installGitHooks(fullPath, compactOutput);

0 commit comments

Comments
 (0)