Skip to content

Commit

Permalink
create-graphql-yoga CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Apr 20, 2023
1 parent bc743aa commit 9ddf129
Show file tree
Hide file tree
Showing 8 changed files with 2,195 additions and 1,975 deletions.
6 changes: 6 additions & 0 deletions .changeset/small-dogs-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@graphql-yoga/template-node-ts': patch
'create-graphql-yoga': patch
---

New CLI to create Yoga projects
30 changes: 0 additions & 30 deletions examples/node-ts/build.js

This file was deleted.

14 changes: 6 additions & 8 deletions examples/node-ts/package.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
{
"name": "example-node-ts",
"private": true,
"version": "1.0.0",
"name": "@graphql-yoga/template-node-ts",
"version": "0.0.0",
"scripts": {
"start": "ts-node src/index.ts",
"build": "node ./build.js",
"check": "tsc --pretty --noEmit"
},
"dependencies": {
"graphql-yoga": "3.9.1",
"graphql": "16.6.0"
"graphql-yoga": "^3.9.1",
"graphql": "^16.6.0"
},
"devDependencies": {
"ts-node": "10.9.1",
"typescript": "5.0.4"
"ts-node": "^10.9.1",
"typescript": "^5.0.4"
}
}
71 changes: 71 additions & 0 deletions packages/create-graphql-yoga/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "create-graphql-yoga",
"version": "0.0.0",
"description": "",
"repository": {
"type": "git",
"url": "https://github.com/dotansimha/graphql-yoga.git",
"directory": "packages/create-graphql-yoga"
},
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"bin": {
"create-graphql-yoga": "dist/esm/bin.js"
},
"buildOptions": {
"bin": {
"create-graphql-yoga": {
"input": "src/bin.ts"
}
},
"./package.json": "./package.json"
},
"scripts": {
"check": "tsc --pretty --noEmit"
},
"keywords": [
"graphql",
"server",
"api",
"graphql-server"
],
"author": "Arda TANRIKULU <ardatanrikulu@gmail.com>",
"license": "MIT",
"exports": {
".": {
"require": {
"types": "./dist/typings/index.d.cts",
"default": "./dist/cjs/index.js"
},
"import": {
"types": "./dist/typings/index.d.ts",
"default": "./dist/esm/index.js"
},
"default": {
"types": "./dist/typings/index.d.ts",
"default": "./dist/esm/index.js"
}
},
"./package.json": "./package.json"
},
"typings": "dist/typings/index.d.ts",
"typescript": {
"definition": "dist/typings/index.d.ts"
},
"publishConfig": {
"directory": "dist",
"access": "public"
},
"dependencies": {
"@whatwg-node/fetch": "^0.8.5",
"ora": "^6.3.0",
"tslib": "^2.3.1",
"tar": "^6.1.13"
},
"devDependencies": {
"@types/tar": "^6.1.4",
"ts-node": "^10.9.1"
},
"type": "module",
"sideEffects": false
}
7 changes: 7 additions & 0 deletions packages/create-graphql-yoga/src/bin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createGraphQLYoga } from './index.js'

createGraphQLYoga().catch((e) => {
// eslint-disable-next-line no-console
console.error(e)
process.exit(1)
})
104 changes: 104 additions & 0 deletions packages/create-graphql-yoga/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import ora from 'ora'
import { parseArgs } from 'node:util'
import { fetch } from '@whatwg-node/fetch'
import tar from 'tar'
import { join } from 'node:path'
import { Readable } from 'node:stream'
import { existsSync, mkdirSync } from 'node:fs'

const spinner = ora()

const options = {
template: {
type: 'string',
short: 't',
},
} as const

function getRegistryAPIUrl(packageName: string, version: string) {
return `https://registry.npmjs.org/${packageName}/${version}`
}

async function getVersionByTag(packageName: string, tag: string) {
const url = getRegistryAPIUrl(packageName, tag)
const response = await fetch(url)
if (response.status === 404) {
spinner.fail(`Package not found: ${packageName}`)
process.exit(1)
}
if (!response.ok) {
spinner.fail(
`Failed to fetch package ${packageName} with ${
response.status
}: ${await response.text()}`,
)
process.exit(1)
}
const { version } = await response.json()
return version
}

function getTarballUrl(packageName: string, version: string) {
return `https://registry.npmjs.org/${packageName}/-/${packageName}-${version}.tgz`
}

function getPackageNameAndTagForTemplate(template: string) {
const [suffix, tag] = template.split('@')
return {
packageName: `@graphql-yoga/template-${suffix}`,
tag: tag ?? 'latest',
}
}

export async function createGraphQLYoga(fullArgs: string[] = process.argv) {
const args = [...fullArgs]
while (args[0].startsWith('/') || args[0] === '--') {
args.shift()
}
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)
const url = getTarballUrl(packageName, version)
const response = await fetch(url)
if (response.status === 404) {
spinner.fail(`Template not found: ${template}`)
process.exit(1)
}
if (!response.ok) {
spinner.fail(
`Failed to fetch template ${template} with ${
response.status
}: ${await response.text()}`,
)
process.exit(1)
}
if (!response.body) {
spinner.fail(`Failed to fetch template ${template} with empty body`)
process.exit(1)
}
const nodeStream = Readable.from(
response.body as unknown as AsyncIterable<Uint8Array>,
)
const targetDir = join(process.cwd(), template)
const extractedTarStream = tar.extract({
strip: 1,
C: targetDir,
})
if (existsSync(targetDir)) {
spinner.fail(`Target directory ${targetDir} already exists.`)
process.exit(1)
}
mkdirSync(targetDir, { recursive: true })
nodeStream
.pipe(extractedTarStream)
.once('error', (err) => {
spinner.fail(`Failed to extract template ${template} with ${err}`)
process.exit(1)
})
.once('close', () => {
spinner.succeed(`Template ${template} created on ${targetDir}.`)
})
}
6 changes: 6 additions & 0 deletions packages/create-graphql-yoga/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "nodenext"
}
}
Loading

0 comments on commit 9ddf129

Please sign in to comment.