diff --git a/packages/cloudflare-pages/README.md b/packages/cloudflare-pages/README.md new file mode 100644 index 0000000..7bc5433 --- /dev/null +++ b/packages/cloudflare-pages/README.md @@ -0,0 +1,125 @@ +# @hono/vite-cloudflare-pages + +`@hono/vite-cloudflare-pages` is Vite plugin to build your Hono application for Cloudflare Pages. + +## Usage + +### Installation + +You can install `vite` and `@hono/vite-cloudflare-pages` via npm. + +```plain +npm i -D vite @hono/cloudflare-pages +``` + +Or you can install them with Bun. + +```plain +bun add vite @hono/cloudflare-pages +``` + +### Settings + +Add `"type": "module"` to your `package.json`. Then, create `vite.config.ts` and edit it. + +```ts +import { defineConfig } from 'vite' +import pages from '@hono/vite-cloudflare-pages' + +export default defineConfig({ + plugins: [pages()], +}) +``` + +### Build + +Just run `vite build`. + +```text +npm exec vite build +``` + +Or + +```text +bunx --bun vite build +``` + +### Deploy to Cloudflare Pages + +Run the `wrangler` command. + +```text +wrangler pages deploy ./dist +``` + +## Options + +The options are below. + +```ts +type CloudflarePagesOptions = { + entry?: string + outputDir?: string + external?: string[] + minify?: boolean + emptyOutDir?: boolean +} +``` + +Default values: + +```ts +export const defaultOptions = { + entry: defaultEntry, // node_modules/@hono/vite-cloudflare-pages/dist/entry/_worker.js + outputDir: './dist', + external: ['react', 'react-dom'], + minify: true, + emptyOutDir: true, +} +``` + +## Build a client + +If you also want to build a client-side script, you can configure it as follows. + +```ts +export default defineConfig(({ mode }) => { + if (mode === 'client') { + return { + build: { + lib: { + entry: './src/client.ts', + formats: ['es'], + fileName: 'client', + name: 'client', + }, + rollupOptions: { + output: { + dir: './dist/static', + }, + }, + copyPublicDir: false, + }, + } + } else { + return { + plugins: [pages()], + } + } +}) +``` + +The build command: + +```text +vite build && vite build --mode client +``` + +## Authors + +- Yusuke Wada + +## License + +MIT diff --git a/packages/cloudflare-pages/package.json b/packages/cloudflare-pages/package.json new file mode 100644 index 0000000..18c629f --- /dev/null +++ b/packages/cloudflare-pages/package.json @@ -0,0 +1,57 @@ +{ + "name": "@hono/vite-cloudflare-pages", + "description": "Vite plugin to build your Hono app for Cloudflare Pages", + "version": "0.0.0", + "types": "dist/index.d.ts", + "module": "dist/index.js", + "type": "module", + "scripts": { + "build:entry": "tsup --no-config --format esm --external /src/index -d dist/entry ./src/entry/_worker.js", + "build": "rimraf dist && tsup && yarn build:entry && publint", + "watch": "tsup --watch", + "prepublishOnly": "yarn build", + "release": "bumpp --tag \"@hono/cloudflare-pages@v%s\" --commit \"chore(cloudflare-pages): release v%s\" && yarn publish" + }, + "files": [ + "dist" + ], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "typesVersions": { + "*": { + "types": [ + "./dist/types" + ] + } + }, + "author": "Yusuke Wada (https://github.com/yusukebe)", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/honojs/vite-plugins.git" + }, + "publishConfig": { + "registry": "https://registry.npmjs.org", + "access": "public" + }, + "homepage": "https://github.com/honojs/vite-plugins", + "devDependencies": { + "bumpp": "^9.2.0", + "glob": "^10.3.10", + "hono": "^3.8.3", + "publint": "^0.1.12", + "rimraf": "^5.0.1", + "tsup": "^7.2.0", + "vite": "^4.4.9" + }, + "peerDependencies": { + "hono": "*" + }, + "engines": { + "node": ">=18.14.1" + } +} \ No newline at end of file diff --git a/packages/cloudflare-pages/src/cloudflare-pages.ts b/packages/cloudflare-pages/src/cloudflare-pages.ts new file mode 100644 index 0000000..949fd6d --- /dev/null +++ b/packages/cloudflare-pages/src/cloudflare-pages.ts @@ -0,0 +1,52 @@ +import { builtinModules } from 'module' +import path from 'node:path' +import { fileURLToPath } from 'url' +import type { Plugin, UserConfig } from 'vite' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +const defaultEntry = path.join(__dirname, 'entry', '_worker.js') + +type CloudflarePagesOptions = { + entry?: string + outputDir?: string + external?: string[] + minify?: boolean + emptyOutDir?: boolean +} + +export const defaultOptions = { + entry: defaultEntry, // node_modules/@hono/vite-cloudflare-pages/dist/entry/_worker.js + outputDir: './dist', + external: ['react', 'react-dom'], + minify: true, + emptyOutDir: true, +} + +export const cloudflarePagesPlugin = (options?: CloudflarePagesOptions): Plugin => { + const entry = options?.entry ?? defaultOptions.entry + return { + name: '@hono/vite-cloudflare-pages', + config: async (): Promise => { + return { + ssr: { + external: options?.external ?? defaultOptions.external, + noExternal: true, + }, + build: { + emptyOutDir: options?.emptyOutDir ?? defaultOptions.emptyOutDir, + ssr: entry, + minify: options?.minify ?? defaultOptions.minify, + rollupOptions: { + external: [...builtinModules, /^node:/], + input: entry, + output: { + dir: options?.outputDir ?? defaultOptions.outputDir, + }, + }, + }, + } + }, + } +} diff --git a/packages/cloudflare-pages/src/entry/_worker.js b/packages/cloudflare-pages/src/entry/_worker.js new file mode 100644 index 0000000..40b609b --- /dev/null +++ b/packages/cloudflare-pages/src/entry/_worker.js @@ -0,0 +1,11 @@ +import { Hono } from 'hono' +import { serveStatic } from 'hono/cloudflare-pages' +import app from '/src/index' + +const worker = new Hono() +worker.get('/favicon.ico', serveStatic()) +worker.get('/static/*', serveStatic()) + +worker.route('/', app) + +export default worker diff --git a/packages/cloudflare-pages/src/index.ts b/packages/cloudflare-pages/src/index.ts new file mode 100644 index 0000000..95b2989 --- /dev/null +++ b/packages/cloudflare-pages/src/index.ts @@ -0,0 +1,2 @@ +import { cloudflarePagesPlugin } from './cloudflare-pages' +export default cloudflarePagesPlugin diff --git a/packages/cloudflare-pages/tsconfig.build.json b/packages/cloudflare-pages/tsconfig.build.json new file mode 100644 index 0000000..563f9b1 --- /dev/null +++ b/packages/cloudflare-pages/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "./src/" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.test.ts" + ] +} \ No newline at end of file diff --git a/packages/cloudflare-pages/tsconfig.json b/packages/cloudflare-pages/tsconfig.json new file mode 100644 index 0000000..14d4f5b --- /dev/null +++ b/packages/cloudflare-pages/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.base.json", + "include": [ + "src", + "test" + ], + "compilerOptions": { + "module": "ES2022", + "target": "ES2022", + "types": [ + "vite/client" + ] + }, +} \ No newline at end of file diff --git a/packages/cloudflare-pages/tsup.config.ts b/packages/cloudflare-pages/tsup.config.ts new file mode 100644 index 0000000..db5ddae --- /dev/null +++ b/packages/cloudflare-pages/tsup.config.ts @@ -0,0 +1,17 @@ +import { glob } from 'glob' +import { defineConfig } from 'tsup' + +const entryPoints = glob.sync('./src/**/*.+(ts|tsx|json)', { + ignore: ['./src/**/*.test.+(ts|tsx)'], +}) + +export default defineConfig({ + entry: entryPoints, + dts: true, + tsconfig: './tsconfig.build.json', + splitting: false, + minify: false, + format: ['esm'], + bundle: false, + platform: 'node', +}) diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json index c4dc24c..03d87cb 100644 --- a/packages/dev-server/package.json +++ b/packages/dev-server/package.json @@ -10,7 +10,7 @@ "build": "rimraf dist && tsup && publint", "watch": "tsup --watch", "prepublishOnly": "yarn build && yarn test", - "release": "bumpp --tag \"@hono/vite-dev-server@v%s\" --commit \"chore(dev-server): release v%s\" && pnpm publish" + "release": "bumpp --tag \"@hono/vite-dev-server@v%s\" --commit \"chore(dev-server): release v%s\" && yarn publish" }, "files": [ "dist" @@ -57,4 +57,4 @@ "@hono/node-server": "^1.2.0", "miniflare": "^3.20231016.0" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 4caa89f..379d647 100644 --- a/yarn.lock +++ b/yarn.lock @@ -257,6 +257,22 @@ __metadata: languageName: node linkType: hard +"@hono/vite-cloudflare-pages@workspace:packages/cloudflare-pages": + version: 0.0.0-use.local + resolution: "@hono/vite-cloudflare-pages@workspace:packages/cloudflare-pages" + dependencies: + bumpp: ^9.2.0 + glob: ^10.3.10 + hono: ^3.8.3 + publint: ^0.1.12 + rimraf: ^5.0.1 + tsup: ^7.2.0 + vite: ^4.4.9 + peerDependencies: + hono: "*" + languageName: unknown + linkType: soft + "@hono/vite-dev-server@workspace:packages/dev-server": version: 0.0.0-use.local resolution: "@hono/vite-dev-server@workspace:packages/dev-server" @@ -2158,7 +2174,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.4, glob@npm:^10.3.7": +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.4, glob@npm:^10.3.7": version: 10.3.10 resolution: "glob@npm:10.3.10" dependencies: @@ -2333,7 +2349,7 @@ __metadata: languageName: unknown linkType: soft -"hono@npm:^3.5.8": +"hono@npm:^3.5.8, hono@npm:^3.8.3": version: 3.8.3 resolution: "hono@npm:3.8.3" checksum: cd1c70527f016b8e40216337386aa5871483c1cc8e55297e41d7cea974659d688dea0752779f77f1af2b4ae1184955cecb2851504e3d84cd869896f34423ee0e