From 8d3b74d673ce82ff7814b90dc1986b9631274ede Mon Sep 17 00:00:00 2001 From: helloimalastair <37487746+helloimalastair@users.noreply.github.com> Date: Tue, 14 Nov 2023 00:25:17 +0100 Subject: [PATCH] init commit Co-authored-by: Isaac McFadyen --- .gitattributes | 1 + .gitignore | 2 ++ .prettierrc | 4 +++ .vscode/settings.json | 6 ++++ README.md | 13 +++++++ build.ts | 15 ++++++++ bun.lockb | Bin 0 -> 73197 bytes package.json | 28 +++++++++++++++ src/constants.ts | 1 + src/index.ts | 25 ++++++++++++++ src/init/clone.ts | 26 ++++++++++++++ src/init/express.ts | 42 +++++++++++++++++++++++ src/init/interactive.ts | 51 ++++++++++++++++++++++++++++ src/init/utils.ts | 42 +++++++++++++++++++++++ templates/hello-world/package.json | 15 ++++++++ templates/hello-world/src/index.ts | 33 ++++++++++++++++++ templates/hello-world/tsconfig.json | 23 +++++++++++++ templates/hello-world/wrangler.toml | 3 ++ tsconfig.json | 18 ++++++++++ 19 files changed, 348 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 build.ts create mode 100755 bun.lockb create mode 100644 package.json create mode 100644 src/constants.ts create mode 100644 src/index.ts create mode 100644 src/init/clone.ts create mode 100644 src/init/express.ts create mode 100644 src/init/interactive.ts create mode 100644 src/init/utils.ts create mode 100644 templates/hello-world/package.json create mode 100644 templates/hello-world/src/index.ts create mode 100644 templates/hello-world/tsconfig.json create mode 100644 templates/hello-world/wrangler.toml create mode 100644 tsconfig.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..cc672fe --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.lockb binary diff=lockb \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76add87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..40a498b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "useTabs": true, + "trailingComma": "all" +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..22e43fe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "editor.insertSpaces": false, + "editor.detectIndentation": false, + "editor.tabSize": 2, + "editor.defaultFormatter": "esbenp.prettier-vscode" +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..09d372d --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# CloudSpark + +> [!WARNING] +> This package is under construction and is **not** ready for production use. + +CloudSpark is a Node CLI tool that allows easy bootstrapping of a Cloudflare Developer Platform project, which may include a Worker, Pages site, or any other dependent bindings. + +## Installation + +While under development, CloudSpark is not on NPM. To install from the latest GitHub commit: +```bash +npm install -g https://github.com/Cloudflare-Community/cloudspark +``` \ No newline at end of file diff --git a/build.ts b/build.ts new file mode 100644 index 0000000..0707662 --- /dev/null +++ b/build.ts @@ -0,0 +1,15 @@ +import { build } from "esbuild"; + +await build({ + entryPoints: ["./src/index.ts"], + bundle: true, + minify: true, + sourcemap: true, + outdir: "./dist", + format: "esm", + platform: "node", + target: "esnext", + banner: { + js: `import path from "path";\nimport { fileURLToPath } from "url";\nimport { createRequire as topLevelCreateRequire } from "module";\nconst require = topLevelCreateRequire(import.meta.url);\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);`, + }, +}) \ No newline at end of file diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..57c3857d2de711f6be732e7a1c3bb0b31266fef7 GIT binary patch literal 73197 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 { + if (repo == null) { + await interactive(args.force ?? false); + } else { + await express(repo, folder, args.force ?? false) + } + }) + +await cli.parseAsync(); diff --git a/src/init/clone.ts b/src/init/clone.ts new file mode 100644 index 0000000..5cdd39f --- /dev/null +++ b/src/init/clone.ts @@ -0,0 +1,26 @@ +import degit from "degit"; +import { log, outro, spinner } from "@clack/prompts"; + +export default async function clone(repo: string, target: string) { + // Start a loading spinner. + const spin = spinner(); + spin.start("Cloning template..."); + + // Create a degit emitter. + const emitter = degit(repo, { + cache: false, + force: true + }); + + // Clone the repo. + emitter.on("warn", (warning) => log.warn(warning.message)); + try { + await emitter.clone(target); + } catch (e: any) { + spin.stop(); + log.error(e.message); + outro("Cancelled. Artifacts may be present."); + process.exit(1); + } + spin.stop("Successfully cloned template."); +} \ No newline at end of file diff --git a/src/init/express.ts b/src/init/express.ts new file mode 100644 index 0000000..3eeb49a --- /dev/null +++ b/src/init/express.ts @@ -0,0 +1,42 @@ +import clone from "./clone"; +import { normalize } from "path"; +import { statSync, readdirSync, existsSync } from "fs"; +import { intro, outro, select, text, log, isCancel, confirm } from "@clack/prompts"; +import { validateFolder } from "./utils"; + +export default async (template: string, target: string | null, force: boolean) => { + // User didn't provide a repo, enter interactive mode. + intro("Cloudspark CLI ⚡️"); + + let finalTarget: string; + if (target != null) { + log.message(`Express mode active. Initializing "${template}" in folder "${target}".`); + finalTarget = target; + } else { + log.message(`Express mode active. Initializing "${template}".`); + + // Ask the user for a target directory. + const target = await text({ + message: "Enter the target directory you want to initialize into", + placeholder: `./worker`, + defaultValue: `./worker`, + }); + if (isCancel(target)) { + outro("Cancelled. No changes were made."); + process.exit(0); + } + finalTarget = target; + } + + // Normalize the path. + const normalizedTarget = normalize(finalTarget); + + // Perform validation on the path. + await validateFolder(normalizedTarget, force); + + // Clone the repo. + await clone(template, normalizedTarget); + + outro(`Done! Your Worker is ready to go at ${finalTarget}`) + process.exit(0); +} \ No newline at end of file diff --git a/src/init/interactive.ts b/src/init/interactive.ts new file mode 100644 index 0000000..09151c3 --- /dev/null +++ b/src/init/interactive.ts @@ -0,0 +1,51 @@ +import clone from "./clone"; +import { normalize } from "path"; +import { statSync, readdirSync, existsSync, rmSync } from "fs"; +import { intro, outro, select, text, log, isCancel, confirm } from "@clack/prompts"; +import { validateFolder } from "./utils"; + +export default async (force: boolean) => { + // User didn't provide a repo, enter interactive mode. + intro("Cloudspark CLI ⚡️"); + log.message("Let's get started by selecting a template."); + log.message("You can also run `cloudspark init ` to initialize a specific repository."); + + // Ask the user for a template selection. + const result: string | symbol = await select({ + message: "Select a template", + options: [ + { + label: "Hello World", + value: "hello-world", + hint: "A simple hello world Worker template" + }, + ] + }) + if (isCancel(result)) { + outro("Cancelled. No changes were made."); + process.exit(0); + } + + // Ask the user for a target directory. + const target = await text({ + message: "Enter the target directory you want to initialize in", + placeholder: `./${result}`, + defaultValue: `./${result}`, + }); + if (isCancel(target)) { + outro("Cancelled. No changes were made."); + process.exit(0); + } + + // Normalize the path. + const normalizedTarget = normalize(target); + + // Perform validation on the path. + await validateFolder(normalizedTarget, force); + + // Clone the repo. + await clone(`${THIS_REPO}/${result}`, normalizedTarget); + + outro(`Done! Your Worker is ready to go at ${target}`) + process.exit(0); +} \ No newline at end of file diff --git a/src/init/utils.ts b/src/init/utils.ts new file mode 100644 index 0000000..5131490 --- /dev/null +++ b/src/init/utils.ts @@ -0,0 +1,42 @@ +import { statSync, readdirSync, existsSync, rmSync } from "fs"; +import { intro, outro, select, text, log, isCancel, confirm } from "@clack/prompts"; + +export const validateFolder = async (target: string, force: boolean) => { + if (existsSync(target)) { + // Check whether target exists and is a file or symlink. + const statResult = statSync(target); + if (statResult.isFile() || statResult.isSymbolicLink()) { + if (force) { + log.warn("Target is a file or symlink, but force flag is set, removing file and continuing."); + } else { + log.error("Target is a file or symlink."); + const overwrite = await confirm({ + message: "Remove and continue?", + initialValue: false, + }); + if (isCancel(overwrite) || !overwrite) { + outro("Cancelled. No changes were made."); + process.exit(0); + } + } + rmSync(target); + } + + // Check whether target already has files + if (statResult.isDirectory() && readdirSync(target).length > 0) { + if (force) { + log.warn("Target directory is not empty but force flag is set, continuing.") + } else { + log.warn("Target directory is not empty."); + const overwrite = await confirm({ + message: "Continue anyway?", + initialValue: false, + }); + if (isCancel(overwrite) || !overwrite) { + outro("Cancelled. No changes were made."); + process.exit(0); + } + } + } + } +} \ No newline at end of file diff --git a/templates/hello-world/package.json b/templates/hello-world/package.json new file mode 100644 index 0000000..e1fe09c --- /dev/null +++ b/templates/hello-world/package.json @@ -0,0 +1,15 @@ +{ + "name": "hello-world", + "type": "module", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "wrangler dev", + "deploy": "wrangler publish" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20231025.0", + "typescript": "^5.2.2", + "wrangler": "^3.15.0" + } +} \ No newline at end of file diff --git a/templates/hello-world/src/index.ts b/templates/hello-world/src/index.ts new file mode 100644 index 0000000..c4b579a --- /dev/null +++ b/templates/hello-world/src/index.ts @@ -0,0 +1,33 @@ +/** + * Welcome to Cloudflare Workers! This is your first worker. + * + * - Run `wrangler dev src/index.ts` in your terminal to start a development server + * - Open a browser tab at http://localhost:8787/ to see your worker in action + * - Run `wrangler publish src/index.ts --name my-worker` to publish your worker + * + * Learn more at https://developers.cloudflare.com/workers/ + */ + +export interface Env { + // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/ + // MY_KV_NAMESPACE: KVNamespace; + // + // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/ + // MY_DURABLE_OBJECT: DurableObjectNamespace; + // + // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/ + // MY_BUCKET: R2Bucket; + // + // Example binding to a Service. Learn more at https://developers.cloudflare.com/workers/runtime-apis/service-bindings/ + // MY_SERVICE: Fetcher; +} + +export default { + async fetch( + request: Request, + env: Env, + ctx: ExecutionContext + ): Promise { + return new Response("Hello World!"); + }, +}; diff --git a/templates/hello-world/tsconfig.json b/templates/hello-world/tsconfig.json new file mode 100644 index 0000000..39a4387 --- /dev/null +++ b/templates/hello-world/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es2021", + "lib": [ + "es2021" + ], + "jsx": "react", + "module": "es2022", + "moduleResolution": "node", + "types": [ + "@cloudflare/workers-types" + ], + "resolveJsonModule": true, + "allowJs": true, + "checkJs": false , + "noEmit": true, + "isolatedModules": true , + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +} \ No newline at end of file diff --git a/templates/hello-world/wrangler.toml b/templates/hello-world/wrangler.toml new file mode 100644 index 0000000..b103181 --- /dev/null +++ b/templates/hello-world/wrangler.toml @@ -0,0 +1,3 @@ +name = "hello-world" +main = "src/index.ts" +compatibility_date = "2023-11-12" \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2e01fe6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "lib": ["esnext"], + "isolatedModules": true, + "types": ["node"], + "noEmit": true, + "strict": true, + "esModuleInterop": true, + }, + "include": [ + "src", + "types", + "build.ts" + ] +}