UNPKG

create-defuss

Version:

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

1 lines 11.8 kB
{"version":3,"file":"cli.cjs","sources":["../src/git.ts","../src/cli.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} 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(\"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 * 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","#!/usr/bin/env node\n\nimport { performSparseCheckout } from \"./git.js\";\n\nexport * from \"./git.js\";\n\n// define an asynchronous main function to handle the CLI logic\nconst main = async () => {\n // retrieve command-line arguments, excluding the first two (node and script path)\n const args = process.argv.slice(2);\n\n // check if the number of arguments is valid (either 1 or 2)\n if (args.length < 1 || args.length > 2) {\n // if not, print usage instructions and exit with an error code\n console.error(\n \"Usage: create-defuss <repo-url> [destination-folder]\\n\" +\n \"Example: create-defuss https://github.com/kyr0/defuss/tree/main/examples/with-astro-ts ./my-new-project\"\n );\n process.exit(1);\n }\n\n // assign the first argument to repoUrl and the second to destFolder, defaulting to \".\" (current directory) if not provided\n const repoUrl = args[0];\n const destFolder = args[1];\n\n // call the performSparseCheckout function with the provided arguments\n performSparseCheckout(repoUrl, destFolder);\n};\n\n// execute the main function and handle any unexpected errors\nmain().catch((err) => {\n // log the error and exit with an error code\n console.error(\"Unexpected error:\", err);\n process.exit(1);\n});"],"names":["normalize","isAbsolute","sep","basename","resolve","existsSync","mkdirSync","mkdtempSync","join","tmpdir","spawnSync","rmSync","readdirSync","lstatSync","readlinkSync","symlinkSync","copyFileSync"],"mappings":"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"}