diff --git a/README.md b/README.md index 03e003e..8cb0343 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ wrangler login Let's install the dependencies. ```sh -npm i +npm install ``` Now we can set up the project. @@ -59,9 +59,9 @@ git push ### Recap šŸŒ€ ```sh -npm i @cloudflare/wrangler -g +npm install @cloudflare/wrangler -g wrangler login -npm i +npm install npm run setup # configure CF_API_TOKEN action secret git add -A -m "" @@ -76,70 +76,19 @@ This starter template comes with a simple DO implementation to keep track of the If you're starting with DO and not sure what it is, go through the official docs on [Durable Objects](https://developers.cloudflare.com/workers/runtime-apis/durable-objects/) will be a good start! And checkout [using durable objects](https://developers.cloudflare.com/workers/learning/using-durable-objects/) for more applications of DO. -### Defining a DO Class - -To create a new DO class, create a new directory inside `packages`. Create four files, - -1. `package.json` - - ```json - { - "name": "", - "version": "0.0.0", - "license": "MIT", - "main": "index.ts", - "types": "index.ts", - "scripts": { - "lint": "eslint .", - "typecheck": "tsc -b" - }, - "devDependencies": { - "@cloudflare/workers-types": "^3.10.0", - "eslint": "^8.15.0", - "eslint-config-custom": "*", - "tsconfig": "*", - "typescript": "^4.6.4" - } - } - ``` - -2. `tsconfig.json` - - ```json - { - "$schema": "https://json.schemastore.org/tsconfig", - "extends": "tsconfig/base.json", - "compilerOptions": { - "target": "ES2019", - "types": ["@cloudflare/workers-types"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "incremental": true, - "esModuleInterop": true, - "module": "esnext", - "resolveJsonModule": true, - "isolatedModules": true, - "moduleResolution": "node" - }, - "include": ["**/*.ts"], - "exclude": ["node_modules"] - } - ``` - -3. `eslintrc.js` - - ```js - module.exports = { - root: true, - extends: ['custom'], - } - ``` - -4. `index.ts` - Define your Durable Object class here. +### Defining a Durable Object + +This template comes with a script to create the boilerplate for a new Durable Object class. + +```sh +npm run new:do +``` + +The script will have instructions to initialise the DO with the worker. Don't forget to follow them! + +#### More information on DO + +> You can skip this section if you have used the script to generate the DO class. Continue for more information on DO :) To define a DO class, check out the [docs](https://developers.cloudflare.com/workers/runtime-apis/durable-objects/#durable-object-class-definition). @@ -151,6 +100,8 @@ bindings = [ {name = "", class_name = ""}, ] +For development add the following to [`wrangler.dev.toml`](packages/worker/wrangler.dev.toml) + [env.dev.durable_objects] bindings = [ {name = "", class_name = ""}, diff --git a/package-lock.json b/package-lock.json index f9397f7..02546ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12692,6 +12692,10 @@ "rimraf": "bin.js" } }, + "node_modules/test-do": { + "resolved": "packages/test-do", + "link": true + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -14294,6 +14298,17 @@ "@types/react": "*" } }, + "packages/test-do": { + "version": "0.0.0", + "license": "MIT", + "devDependencies": { + "@cloudflare/workers-types": "^3.10.0", + "eslint": "^8.15.0", + "eslint-config-custom": "*", + "tsconfig": "*", + "typescript": "^4.6.4" + } + }, "packages/tsconfig": { "version": "0.0.0", "extraneous": true @@ -23634,6 +23649,16 @@ } } }, + "test-do": { + "version": "file:packages/test-do", + "requires": { + "@cloudflare/workers-types": "^3.10.0", + "eslint": "^8.15.0", + "eslint-config-custom": "*", + "tsconfig": "*", + "typescript": "^4.6.4" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/scripts/new-do.ts b/scripts/new-do.ts new file mode 100644 index 0000000..fbb1024 --- /dev/null +++ b/scripts/new-do.ts @@ -0,0 +1,100 @@ +import fs from 'fs-extra' +import path from 'path' +import inquirer from 'inquirer' + +function format(str: string) { + let lowercase = str.toLowerCase() + let uppercase = str.toUpperCase() + + return { + capitalize: lowercase.charAt(0).toUpperCase() + lowercase.slice(1), + lowercase, + uppercase, + } +} + +async function run() { + let answer = await inquirer.prompt<{ name: string }>([ + { + name: 'name', + type: 'input', + message: 'What is the name of the Durable Object?', + validate: (input: string) => { + if (input.length === 0) { + return 'Please enter a name for the Durable Object.' + } + return true + }, + }, + ]) + let { capitalize, lowercase, uppercase } = format(answer.name) + let projectName = `${lowercase}-do` + let outputDir = path.join(process.cwd(), `packages/${projectName}`) + if (fs.existsSync(outputDir)) { + console.error(`\nšŸšØ Directory '${outputDir}' already exists.\n`) + throw new Error(`Directory ${outputDir} already exists.`) + } + await fs.mkdirp(outputDir) + + let baseDir = path.join(process.cwd(), 'scripts/new-do') + let [eslintrc, packageJson, tsconfig] = await Promise.all([ + fs.readFile(path.join(baseDir, 'tmp-eslintrc.js'), 'utf8'), + fs.readFile(path.join(baseDir, 'tmp-package.json'), 'utf8'), + fs.readFile(path.join(baseDir, 'tmp-tsconfig.json'), 'utf8'), + ]) + let parsedPackageJson = JSON.parse(packageJson) + parsedPackageJson.name = projectName + let index = `export default class ${capitalize}DurableObject { + constructor(private state: DurableObjectState) {} +} + ` + await Promise.all([ + fs.writeFile(path.join(outputDir, '.eslintrc.js'), eslintrc), + fs.writeFile( + path.join(outputDir, 'package.json'), + JSON.stringify(parsedPackageJson, null, 2), + ), + fs.writeFile(path.join(outputDir, 'tsconfig.json'), tsconfig), + fs.writeFile(path.join(outputDir, 'index.ts'), index), + ]) + + console.error(`\nšŸ”Ø Created ${projectName} package.\n +Now we need to add the Durable Objects bindings to your worker configuration file. +1. Open 'package.json' of 'packages/worker', and add the following to the 'dependencies' section:\n + "${projectName}": "*"\n +2. Open 'packages/worker/wrangler.toml' and add the following to the 'durable_objects' section:\n + {name = "${uppercase}", class_name = "${capitalize}DurableObject"}\n +3. Open 'packages/worker/wrangler.dev.toml' and add the following to the 'durable_objects' section:\n + {name = "${uppercase}", class_name = "${capitalize}DurableObject"}\n +4. We also need to add a migration for the Durable Object in the 'migrations' section of 'packages/worker/wrangler.toml' and as well as 'packages/worker/wrangler.dev.toml'.\n + [[migrations]] + tag = "vx" + new_classes = ["${capitalize}DurableObject"]\n +5. Now, open 'packages/worker/src/index.ts' and add the following export at the top of the file.\n + export { default as ${capitalize}DurableObject } from '${projectName}'\n +6. Great, just one last thing. Open 'config/cloudflare-env/index.d.ts' and add the following for types\n + ${uppercase}: DurableObjectNamespace\n +6. Now from the project root run 'npm install' +`) + + console.error( + `\nšŸšØ Be sure to checkout the instructions specified above\nšŸŽ‰ Successfully created "${projectName}"`, + ) + + console.error( + `\ncd ${path.relative( + process.cwd(), + path.join(outputDir, 'index.ts'), + )} and create awsome things!\n`, + ) + console.error( + 'Visit https://developers.cloudflare.com/workers/learning/using-durable-objects/ for more information.', + ) +} + +run().catch(e => { + console.error(e) + process.exit(1) +}) + +export {} diff --git a/scripts/new-do/tmp-eslintrc.js b/scripts/new-do/tmp-eslintrc.js new file mode 100644 index 0000000..b56159e --- /dev/null +++ b/scripts/new-do/tmp-eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ['custom'], +} diff --git a/scripts/new-do/tmp-package.json b/scripts/new-do/tmp-package.json new file mode 100644 index 0000000..45335fb --- /dev/null +++ b/scripts/new-do/tmp-package.json @@ -0,0 +1,18 @@ +{ + "name": "new-do", + "version": "0.0.0", + "license": "MIT", + "main": "index.ts", + "types": "index.ts", + "scripts": { + "lint": "eslint .", + "typecheck": "tsc -b" + }, + "devDependencies": { + "@cloudflare/workers-types": "^3.10.0", + "eslint": "^8.15.0", + "eslint-config-custom": "*", + "tsconfig": "*", + "typescript": "^4.6.4" + } +} diff --git a/scripts/new-do/tmp-tsconfig.json b/scripts/new-do/tmp-tsconfig.json new file mode 100644 index 0000000..8e12bc2 --- /dev/null +++ b/scripts/new-do/tmp-tsconfig.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "tsconfig/base.json", + "compilerOptions": { + "target": "ES2019", + "types": ["@cloudflare/workers-types"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "resolveJsonModule": true, + "isolatedModules": true, + "moduleResolution": "node" + }, + "include": ["**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/scripts/setup.ts b/scripts/setup.ts index ec74c00..b23321e 100644 --- a/scripts/setup.ts +++ b/scripts/setup.ts @@ -70,6 +70,14 @@ async function run() { ) console.error('\nšŸŽ‰ Done!') + + console.error('\nAvailable commands:') + console.error('npm run dev - Starts the development server') + console.error('npm run build - Builds the monorepo') + console.error('npm run lint - Lints the monorepo') + console.error('npm run format - Formats the monorepo') + console.error('npm run typecheck - Typechecks the monorepo') + console.error('npm run new:do - Creates a new Durable Object class file') } run().catch(e => {