UNPKG

create-defuss

Version:

Checks out git projects from sub-directories. Originally for jump-starting defuss projects from templates.

1 lines 12 kB
{"version":3,"file":"git-B92u1z1f.mjs","sources":["../src/git.ts"],"sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport {\n resolve,\n join,\n normalize,\n isAbsolute,\n sep,\n basename,\n} from \"node:path\";\nimport {\n existsSync,\n rmSync,\n readdirSync,\n mkdtempSync,\n mkdirSync,\n copyFileSync,\n lstatSync,\n readlinkSync,\n symlinkSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { tmpdir } from \"node:os\";\n\nexport const defaultScmHostPattern =\n /^(https:\\/\\/(?:github|gitlab|bitbucket)\\.com)\\/([^\\/]+)\\/([^\\/]+)\\/(?:tree|src)\\/([^\\/]+)\\/(.+)$/;\n\nexport const performSparseCheckout = (\n repoUrl: string,\n destFolder?: string,\n scmHostPattern = defaultScmHostPattern\n) => {\n try {\n const match = repoUrl.match(scmHostPattern);\n\n if (!match) {\n throw new Error(\n \"Invalid URL format. Use a subdirectory URL (https) from GitHub, GitLab, or Bitbucket.\"\n );\n }\n\n const [, platformUrl, owner, repo, branch, subdir] = match;\n\n // Validate inputs to prevent command injection and path traversal\n [owner, repo, branch].forEach((input) => {\n if (!/^[\\w\\-]+$/.test(input)) {\n throw new Error(`Invalid characters in input: ${input}`);\n }\n });\n\n // Validate subdir\n const subdirNormalized = normalize(subdir);\n\n // Check if subdir is absolute or contains path traversal\n if (\n isAbsolute(subdirNormalized) ||\n subdirNormalized.startsWith(\"..\") ||\n subdirNormalized.includes(`${sep}..${sep}`)\n ) {\n throw new Error(\"Invalid subdirectory path.\");\n }\n\n const sanitizedSubdir = subdirNormalized;\n\n const fallbackDestFolder = destFolder || basename(sanitizedSubdir);\n const targetPath = resolve(process.cwd(), fallbackDestFolder);\n\n if (existsSync(targetPath)) {\n throw new Error(`Destination folder \"${fallbackDestFolder}\" already exists.`);\n }\n\n // Create the destination directory\n mkdirSync(targetPath, { recursive: true });\n\n // Create a temporary directory for the clone\n const tempDir = mkdtempSync(join(tmpdir(), \"sparse-checkout-\"));\n\n console.log(\"Cloning repository with sparse checkout into temporary directory...\");\n const cloneResult = spawnSync(\n \"git\",\n [\"clone\", \"--no-checkout\", `${platformUrl}/${owner}/${repo}.git`, tempDir],\n { stdio: \"inherit\" }\n );\n if (cloneResult.status !== 0) {\n throw new Error(\"Git clone failed.\");\n }\n\n const subdirPath = resolve(tempDir, sanitizedSubdir);\n\n // Ensure subdirPath is within tempDir\n if (!subdirPath.startsWith(tempDir + sep) && subdirPath !== tempDir) {\n throw new Error(\"Subdirectory path traversal detected.\");\n }\n\n console.log(\"Initializing sparse-checkout...\");\n const initResult = spawnSync(\"git\", [\"-C\", tempDir, \"sparse-checkout\", \"init\"], {\n stdio: \"inherit\",\n });\n if (initResult.status !== 0) {\n throw new Error(\"Git sparse-checkout init failed.\");\n }\n\n console.log(`Setting sparse-checkout to subdirectory: ${sanitizedSubdir}`);\n const setResult = spawnSync(\n \"git\",\n [\"-C\", tempDir, \"sparse-checkout\", \"set\", sanitizedSubdir],\n { stdio: \"inherit\" }\n );\n if (setResult.status !== 0) {\n throw new Error(\"Git sparse-checkout set failed.\");\n }\n\n console.log(`Checking out branch: ${branch}...`);\n const checkoutResult = spawnSync(\"git\", [\"-C\", tempDir, \"checkout\", branch], {\n stdio: \"inherit\",\n });\n if (checkoutResult.status !== 0) {\n throw new Error(\"Git checkout failed.\");\n }\n\n if (!existsSync(subdirPath)) {\n throw new Error(`Subdirectory \"${sanitizedSubdir}\" does not exist in the repository.`);\n }\n\n console.log(\"Copying files to the destination directory...\");\n copyDirectoryContents(subdirPath, targetPath);\n\n console.log(\"Replacing workspace:* versions with latest npm versions...\");\n replaceWorkspaceVersions(targetPath);\n\n console.log(\"Cleaning up temporary directory...\");\n rmSync(tempDir, { recursive: true, force: true });\n\n console.log(\"Initializing a new git repository...\");\n const initNewRepoResult = spawnSync(\"git\", [\"init\"], {\n cwd: targetPath,\n stdio: \"inherit\",\n });\n if (initNewRepoResult.status !== 0) {\n throw new Error(\"Initializing new git repository failed.\");\n }\n\n console.log(\"🎉 All done! Your new project has been set up!\");\n\n console.log(`\\nTo get started, run the following commands:\\n\\n cd ${fallbackDestFolder}\\n`);\n } catch (err) {\n console.error(\"Error during sparse checkout:\", (err as Error).message);\n process.exit(1);\n }\n};\n\n/**\n * Gets the latest version of a package from npm.\n */\nfunction getNpmLatestVersion(packageName: string): string | null {\n const result = spawnSync(\"npm\", [\"view\", packageName, \"version\"], {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n if (result.status === 0 && result.stdout) {\n return result.stdout.trim();\n }\n return null;\n}\n\n/**\n * Replaces all \"workspace:*\" versions in the root package.json with the latest npm versions.\n */\nfunction replaceWorkspaceVersions(targetPath: string): void {\n const packageJsonPath = join(targetPath, \"package.json\");\n\n if (!existsSync(packageJsonPath)) {\n console.log(\"No package.json found in the root, skipping workspace version replacement.\");\n return;\n }\n\n const packageJsonContent = readFileSync(packageJsonPath, \"utf-8\");\n const packageJson = JSON.parse(packageJsonContent);\n\n let hasChanges = false;\n\n const depTypes = [\"dependencies\", \"devDependencies\", \"peerDependencies\", \"optionalDependencies\"];\n\n for (const depType of depTypes) {\n const deps = packageJson[depType];\n if (!deps || typeof deps !== \"object\") continue;\n\n for (const [pkgName, version] of Object.entries(deps)) {\n if (typeof version === \"string\" && version.startsWith(\"workspace:\")) {\n console.log(`Resolving latest npm version for ${pkgName}...`);\n const latestVersion = getNpmLatestVersion(pkgName);\n\n if (latestVersion) {\n console.log(` ${pkgName}: workspace:* -> ^${latestVersion}`);\n deps[pkgName] = `^${latestVersion}`;\n hasChanges = true;\n } else {\n console.warn(` Warning: Could not fetch latest version for ${pkgName}, keeping workspace:* reference.`);\n }\n }\n }\n }\n\n if (hasChanges) {\n writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + \"\\n\", \"utf-8\");\n console.log(\"Updated package.json with resolved npm versions.\");\n } else {\n console.log(\"No workspace:* versions found in package.json.\");\n }\n}\n\n/**\n * Recursively copies all files and directories from `source` to `destination`.\n */\nfunction copyDirectoryContents(source: string, destination: string) {\n if (!existsSync(source)) {\n throw new Error(`Source directory \"${source}\" does not exist.`);\n }\n\n const items = readdirSync(source);\n\n for (const item of items) {\n const srcPath = join(source, item);\n const destPath = join(destination, item);\n const stats = lstatSync(srcPath);\n\n if (stats.isDirectory()) {\n mkdirSync(destPath, { recursive: true });\n copyDirectoryContents(srcPath, destPath);\n } else if (stats.isSymbolicLink()) {\n const symlink = readlinkSync(srcPath);\n symlinkSync(symlink, destPath);\n } else {\n copyFileSync(srcPath, destPath);\n }\n }\n}\n"],"names":["defaultScmHostPattern","performSparseCheckout","__name","repoUrl","destFolder","scmHostPattern","match","platformUrl","owner","repo","branch","subdir","input","subdirNormalized","normalize","isAbsolute","sep","sanitizedSubdir","fallbackDestFolder","basename","targetPath","resolve","existsSync","mkdirSync","tempDir","mkdtempSync","join","tmpdir","spawnSync","subdirPath","copyDirectoryContents","replaceWorkspaceVersions","rmSync","err","getNpmLatestVersion","packageName","result","packageJsonPath","packageJsonContent","readFileSync","packageJson","hasChanges","depTypes","depType","deps","pkgName","version","latestVersion","writeFileSync","source","destination","items","readdirSync","item","srcPath","destPath","stats","lstatSync","symlink","readlinkSync","symlinkSync","copyFileSync"],"mappings":"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"}