diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1601369b37..cc5ea7878c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,8 @@ Initial setup to install dependencies for Vite+: just init ``` +`just init` bootstraps the pinned `rolldown` and `vite` source trees needed for local development, then installs workspace dependencies. + ### Windows You'll need the following tools installed on your system. You can use [winget](https://learn.microsoft.com/en-us/windows/package-manager/). @@ -43,6 +45,8 @@ Initial setup to install dependencies for Vite+: just init ``` +`just init` bootstraps the pinned `rolldown` and `vite` source trees needed for local development, then installs workspace dependencies. + **Note:** Run commands in PowerShell or Windows Terminal. Some commands may require elevated permissions. ## Build Vite+ and upstream dependencies diff --git a/justfile b/justfile index 67dd2e2203..48350043b6 100644 --- a/justfile +++ b/justfile @@ -18,7 +18,7 @@ _clean_dist: init: _clean_dist cargo binstall watchexec-cli cargo-insta typos-cli cargo-shear dprint taplo-cli -y - node packages/tools/src/index.ts sync-remote + node packages/tools/src/index.ts sync-remote --clone-only pnpm install pnpm -C docs install @@ -31,7 +31,7 @@ build: pnpm --filter=@voidzero-dev/vite-plus-core build pnpm --filter=@voidzero-dev/vite-plus-test build pnpm --filter=@voidzero-dev/vite-plus-prompts build - pnpm --filter=vite-plus build + node packages/tools/src/with-curl-http1.ts pnpm --filter=vite-plus build ready: git diff --exit-code --quiet diff --git a/package.json b/package.json index 98018d0aad..bf644252b5 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "build": "pnpm -F @voidzero-dev/* -F vite-plus build", - "bootstrap-cli": "pnpm build && cargo build -p vite_global_cli -p vite_trampoline --release && pnpm install-global-cli", + "bootstrap-cli": "just build && node packages/tools/src/with-curl-http1.ts cargo build -p vite_global_cli -p vite_trampoline --release && pnpm install-global-cli", "bootstrap-cli:ci": "pnpm install-global-cli", "install-global-cli": "tool install-global-cli", "tsgo": "tsgo -b tsconfig.json", diff --git a/packages/tools/src/sync-remote-deps.ts b/packages/tools/src/sync-remote-deps.ts index edf8e6bb29..21b8c7bbe6 100755 --- a/packages/tools/src/sync-remote-deps.ts +++ b/packages/tools/src/sync-remote-deps.ts @@ -63,6 +63,46 @@ function execCommand(command: string, cwd?: string): string { } } +function normalizeGithubRepoUrl(repoUrl: string): string { + const httpsMatch = repoUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/); + if (httpsMatch) { + return `github:${httpsMatch[1]}/${httpsMatch[2]}`; + } + + const sshMatch = repoUrl.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/); + if (sshMatch) { + return `github:${sshMatch[1]}/${sshMatch[2]}`; + } + + return repoUrl; +} + +function toGithubSshUrl(repoUrl: string): string { + const match = repoUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/); + if (!match) { + return repoUrl; + } + + return `git@github.com:${match[1]}/${match[2]}.git`; +} + +function resolveRepoUrl(repoUrl: string, cwd?: string): string { + if (!repoUrl.startsWith('https://github.com/')) { + return repoUrl; + } + + try { + const originUrl = execCommand('git remote get-url origin', cwd); + if (originUrl.startsWith('git@github.com:')) { + return toGithubSshUrl(repoUrl); + } + } catch { + // Fall back to the configured upstream URL when git metadata is unavailable. + } + + return repoUrl; +} + function cloneOrResetRepo(repoUrl: string, dir: string, branch: string = 'main', hash?: string) { log(`Processing ${dir}...`); @@ -84,10 +124,12 @@ function cloneOrResetRepo(repoUrl: string, dir: string, branch: string = 'main', // Check remote URL const remoteUrl = execCommand('git remote get-url origin', dir); - if (remoteUrl !== repoUrl) { - log(`${dir} has wrong remote (${remoteUrl} vs ${repoUrl}), removing and re-cloning...`); + if (normalizeGithubRepoUrl(remoteUrl) !== normalizeGithubRepoUrl(resolveRepoUrl(repoUrl, dir))) { + log( + `${dir} has wrong remote (${remoteUrl} vs ${resolveRepoUrl(repoUrl, dir)}), removing and re-cloning...`, + ); rmSync(dir, { recursive: true, force: true }); - cloneRepo(repoUrl, dir, branch, hash); + cloneRepo(resolveRepoUrl(repoUrl, dir), dir, branch, hash); return; } @@ -125,10 +167,10 @@ function cloneOrResetRepo(repoUrl: string, dir: string, branch: string = 'main', `Failed to reset ${dir} (${error instanceof Error ? error.message : String(error)}), removing and re-cloning...`, ); rmSync(dir, { recursive: true, force: true }); - cloneRepo(repoUrl, dir, branch, hash); + cloneRepo(resolveRepoUrl(repoUrl, dir), dir, branch, hash); } } else { - cloneRepo(repoUrl, dir, branch, hash); + cloneRepo(resolveRepoUrl(repoUrl, dir), dir, branch, hash); } } @@ -624,6 +666,9 @@ export async function syncRemote() { clean: { type: 'boolean', }, + 'clone-only': { + type: 'boolean', + }, 'update-hashes': { type: 'boolean', }, @@ -671,6 +716,11 @@ export async function syncRemote() { upstreamVersions['vite'].hash, ); + if (values['clone-only']) { + log('Clone-only mode enabled, skipping workspace merge and dependency sync.'); + return; + } + // Dynamically import dependencies after git clone let parseYaml: typeof import('yaml').parse; let stringifyYaml: typeof import('yaml').stringify; diff --git a/packages/tools/src/with-curl-http1.ts b/packages/tools/src/with-curl-http1.ts new file mode 100644 index 0000000000..e4c92a219c --- /dev/null +++ b/packages/tools/src/with-curl-http1.ts @@ -0,0 +1,37 @@ +import { spawnSync } from 'node:child_process'; +import { chmodSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { delimiter, join } from 'node:path'; + +const [command, ...args] = process.argv.slice(2); + +if (!command) { + console.error('Usage: node packages/tools/src/with-curl-http1.ts [args...]'); + process.exit(1); +} + +let tempDir: string | undefined; +const env = { ...process.env }; + +if (process.platform === 'darwin') { + tempDir = mkdtempSync(join(tmpdir(), 'vite-plus-curl-')); + const curlPath = join(tempDir, 'curl'); + writeFileSync(curlPath, '#!/bin/sh\nexec /usr/bin/curl --http1.1 "$@"\n'); + chmodSync(curlPath, 0o755); + env.PATH = env.PATH ? `${tempDir}${delimiter}${env.PATH}` : tempDir; +} + +const result = spawnSync(command, args, { + env, + stdio: 'inherit', +}); + +if (tempDir) { + rmSync(tempDir, { force: true, recursive: true }); +} + +if (result.error) { + throw result.error; +} + +process.exit(result.status ?? 1); \ No newline at end of file