UNPKG

create-graphql-yoga

Version:
97 lines (96 loc) 3.65 kB
import ora from 'ora'; import { parseArgs } from 'node:util'; import tar from 'tar'; import { join } from 'node:path'; import { Readable } from 'node:stream'; import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import { createInterface } from 'node:readline'; import getNpmTarballUrl from 'get-npm-tarball-url'; export const spinner = ora(); const options = { template: { type: 'string', short: 't', }, }; function getRegistryAPIUrl(packageName, version) { return `https://registry.npmjs.org/${packageName}/${version}`; } async function getVersionByTag(packageName, tag, fetchFn) { const url = getRegistryAPIUrl(packageName, tag); const response = await fetchFn(url); if (response.status === 404) { throw new Error(`Package not found: ${packageName}`); } if (!response.ok) { throw new Error(`Failed to fetch package ${packageName} with ${response.status}: ${await response.text()}`); } const { version } = await response.json(); return version; } function getPackageNameAndTagForTemplate(template) { const [suffix, tag] = template.split('@'); return { packageName: `@graphql-yoga/template-${suffix}`, tag: tag ?? 'latest', }; } export async function createGraphQLYoga({ argv, input, output, fetchFn, }) { const args = [...argv]; while (args[0].startsWith('/') || args[0] === '--') { args.shift(); } const rl = createInterface({ input, output, }); const projectName = await new Promise((resolve) => { rl.question('What is the name of your project? ', (answer) => { resolve(answer); }); }); const { values: { template = 'node-ts' }, } = parseArgs({ args, options, allowPositionals: true }); spinner.start(`Fetching template ${template}...`); const { packageName, tag } = getPackageNameAndTagForTemplate(template); const version = await getVersionByTag(packageName, tag, fetchFn); const url = getNpmTarballUrl(packageName, version); const response = await fetchFn(url); if (response.status === 404) { throw new Error(`Template not found: ${template}`); } if (!response.ok) { throw new Error(`Failed to fetch template ${template} with ${response.status}: ${await response.text()}`); } if (!response.body) { throw new Error(`Failed to fetch template ${template} with empty body`); } const nodeStream = Readable.from(response.body); const targetDir = join(process.cwd(), projectName); if (existsSync(targetDir)) { throw new Error(`Target directory ${targetDir} already exists.`); } mkdirSync(targetDir, { recursive: true }); await new Promise((resolve, reject) => { const extractedTarStream = tar.extract({ strip: 1, cwd: targetDir, }); nodeStream .pipe(extractedTarStream) .once('error', (err) => { reject(new Error(`Failed to extract template ${template} with ${err}`)); }) .once('close', () => { resolve(); }); }); const packageJsonPath = join(targetDir, 'package.json'); const packageJsonContent = readFileSync(packageJsonPath, 'utf-8'); const packageJson = JSON.parse(packageJsonContent); packageJson.name = projectName; delete packageJson.version; packageJson.private = true; const newPackageJsonContent = JSON.stringify(packageJson, null, 2); writeFileSync(packageJsonPath, newPackageJsonContent); spinner.succeed(`Project ${projectName} created on ${targetDir}.`); }