From 34b18dca09286b53e3ea38403d0bcd20e6ac6a6c Mon Sep 17 00:00:00 2001 From: Yusuke Wada Date: Sat, 14 Sep 2024 16:08:28 +0900 Subject: [PATCH] add adapters --- packages/build/package.json | 11 ++- packages/build/src/adapter/bun/index.ts | 26 +++--- .../src/adapter/cloudflare-pages/index.ts | 61 +++++++++++++ .../src/adapter/cloudflare-workers/index.ts | 16 ++++ packages/build/src/adapter/node/index.ts | 40 +++++++++ packages/build/src/base.ts | 55 ++++++------ .../build/src/{entry.ts => entry/index.ts} | 29 +++++-- packages/build/src/entry/serve-static.ts | 12 +++ packages/build/src/index.ts | 3 + packages/build/test/basic.test.ts | 31 +++++++ packages/build/test/bun.test.ts | 40 +++++++++ packages/build/test/cloudflare-pages.test.ts | 86 +++++++++++++++++++ .../app-static-files/customDir/_routes.json | 1 + .../app-static-files/customDir/_worker.js | 1 + .../mocks/app-static-files/customDir/foo.txt | 1 + .../app-static-files/customDir/js/client.js | 1 + .../public-routes-json/_routes.json | 1 + .../mocks/app-static-files/public/foo.txt | 1 + .../app-static-files/public/js/client.js | 1 + .../test/mocks/app-static-files/src/server.ts | 7 ++ packages/build/test/mocks/app/src/server.ts | 7 ++ yarn.lock | 32 +++---- 22 files changed, 404 insertions(+), 59 deletions(-) create mode 100644 packages/build/src/adapter/cloudflare-pages/index.ts create mode 100644 packages/build/src/adapter/cloudflare-workers/index.ts create mode 100644 packages/build/src/adapter/node/index.ts rename packages/build/src/{entry.ts => entry/index.ts} (66%) create mode 100644 packages/build/src/entry/serve-static.ts create mode 100644 packages/build/test/basic.test.ts create mode 100644 packages/build/test/bun.test.ts create mode 100644 packages/build/test/cloudflare-pages.test.ts create mode 100644 packages/build/test/mocks/app-static-files/customDir/_routes.json create mode 100644 packages/build/test/mocks/app-static-files/customDir/_worker.js create mode 100644 packages/build/test/mocks/app-static-files/customDir/foo.txt create mode 100644 packages/build/test/mocks/app-static-files/customDir/js/client.js create mode 100644 packages/build/test/mocks/app-static-files/public-routes-json/_routes.json create mode 100644 packages/build/test/mocks/app-static-files/public/foo.txt create mode 100644 packages/build/test/mocks/app-static-files/public/js/client.js create mode 100644 packages/build/test/mocks/app-static-files/src/server.ts create mode 100644 packages/build/test/mocks/app/src/server.ts diff --git a/packages/build/package.json b/packages/build/package.json index 1333884..67e2d1c 100644 --- a/packages/build/package.json +++ b/packages/build/package.json @@ -1,5 +1,5 @@ { - "name": "@hono/build", + "name": "@hono/vite-build", "description": "Vite plugin to build your Hono app", "version": "0.0.0", "types": "dist/index.d.ts", @@ -23,6 +23,10 @@ "./bun": { "types": "./dist/adapter/bun/index.d.ts", "import": "./dist/adapter/bun/index.js" + }, + "./node": { + "types": "./dist/adapter/node/index.d.ts", + "import": "./dist/adapter/node/index.js" } }, "typesVersions": { @@ -32,6 +36,9 @@ ], "bun": [ "./dist/adapter/bun/index.d.ts" + ], + "node": [ + "./dist/adapter/node/index.d.ts" ] } }, @@ -61,4 +68,4 @@ "engines": { "node": ">=18.14.1" } -} +} \ No newline at end of file diff --git a/packages/build/src/adapter/bun/index.ts b/packages/build/src/adapter/bun/index.ts index 580b86b..0562558 100644 --- a/packages/build/src/adapter/bun/index.ts +++ b/packages/build/src/adapter/bun/index.ts @@ -1,23 +1,27 @@ import type { Plugin } from 'vite' -import type { BuildOptions } from '../../base' -import { buildPlugin } from '../../base' +import type { BuildOptions } from '../../base.js' +import buildPlugin from '../../base.js' +import { serveStaticHook } from '../../entry/serve-static.js' export type BunBuildOptions = { staticRoot?: string | undefined } & BuildOptions -export const bunBuildPlugin = (pluginOptions: BunBuildOptions): Plugin => { +const bunBuildPlugin = (pluginOptions?: BunBuildOptions): Plugin => { return { ...buildPlugin({ ...{ - entryContentBeforeHook: async (appName, options) => { - // eslint-disable-next-line quotes - let code = "import { serveStatic } from 'hono/bun'\n" - for (const path of options?.staticPaths ?? []) { - code += `${appName}.use('${path}', serveStatic({ root: '${pluginOptions.staticRoot ?? './'}' }))\n` - } - return code - }, + entryContentBeforeHooks: [ + async (appName, options) => { + // eslint-disable-next-line quotes + let code = "import { serveStatic } from 'hono/bun'\n" + code += serveStaticHook(appName, { + filePaths: options?.staticPaths, + root: pluginOptions?.staticRoot, + }) + return code + }, + ], }, ...pluginOptions, }), diff --git a/packages/build/src/adapter/cloudflare-pages/index.ts b/packages/build/src/adapter/cloudflare-pages/index.ts new file mode 100644 index 0000000..467aa78 --- /dev/null +++ b/packages/build/src/adapter/cloudflare-pages/index.ts @@ -0,0 +1,61 @@ +import { readdir, writeFile } from 'node:fs/promises' +import { resolve } from 'node:path' +import type { Plugin, ResolvedConfig } from 'vite' +import type { BuildOptions } from '../../base.js' +import buildPlugin, { defaultOptions } from '../../base.js' + +export type CloudflarePagesBuildOptions = BuildOptions + +const WORKER_JS_NAME = '_worker.js' +const ROUTES_JSON_NAME = '_routes.json' + +type StaticRoutes = { version: number; include: string[]; exclude: string[] } + +const bunBuildPlugin = (pluginOptions?: CloudflarePagesBuildOptions): Plugin => { + let config: ResolvedConfig + const staticPaths: string[] = [] + + return { + ...buildPlugin({ + ...pluginOptions, + output: WORKER_JS_NAME, + }), + configResolved: async (resolvedConfig) => { + config = resolvedConfig + }, + writeBundle: async () => { + const paths = await readdir(resolve(config.root, config.build.outDir), { + withFileTypes: true, + }) + // If _routes.json already exists, don't create it + if (paths.some((p) => p.name === ROUTES_JSON_NAME)) { + return + } else { + paths.forEach((p) => { + if (p.isDirectory()) { + staticPaths.push(`/${p.name}/*`) + } else { + if (p.name === WORKER_JS_NAME) { + return + } + staticPaths.push(`/${p.name}`) + } + }) + const staticRoutes: StaticRoutes = { + version: 1, + include: ['/*'], + exclude: staticPaths, + } + const path = resolve( + config.root, + pluginOptions?.outputDir ?? defaultOptions.outputDir, + '_routes.json' + ) + await writeFile(path, JSON.stringify(staticRoutes)) + } + }, + name: '@hono/vite-build/cloudflare-pages', + } +} + +export default bunBuildPlugin diff --git a/packages/build/src/adapter/cloudflare-workers/index.ts b/packages/build/src/adapter/cloudflare-workers/index.ts new file mode 100644 index 0000000..42d0161 --- /dev/null +++ b/packages/build/src/adapter/cloudflare-workers/index.ts @@ -0,0 +1,16 @@ +import type { Plugin } from 'vite' +import type { BuildOptions } from '../../base.js' +import buildPlugin from '../../base.js' + +export type CloudflareWorkersBuildOptions = BuildOptions + +const nodeBuildPlugin = (pluginOptions?: CloudflareWorkersBuildOptions): Plugin => { + return { + ...buildPlugin({ + ...pluginOptions, + }), + name: '@hono/vite-build/cloudflare-workers', + } +} + +export default nodeBuildPlugin diff --git a/packages/build/src/adapter/node/index.ts b/packages/build/src/adapter/node/index.ts new file mode 100644 index 0000000..4c6b338 --- /dev/null +++ b/packages/build/src/adapter/node/index.ts @@ -0,0 +1,40 @@ +import type { Plugin } from 'vite' +import type { BuildOptions } from '../../base.js' +import buildPlugin from '../../base.js' +import { serveStaticHook } from '../../entry/serve-static.js' + +export type NodeBuildOptions = { + staticRoot?: string | undefined +} & BuildOptions + +const nodeBuildPlugin = (pluginOptions?: NodeBuildOptions): Plugin => { + return { + ...buildPlugin({ + ...{ + entryContentBeforeHooks: [ + async (appName, options) => { + // eslint-disable-next-line quotes + let code = "import { serveStatic } from '@hono/node-server/serve-static'\n" + code += serveStaticHook(appName, { + filePaths: options?.staticPaths, + root: pluginOptions?.staticRoot, + }) + return code + }, + ], + entryContentAfterHooks: [ + async (appName) => { + // eslint-disable-next-line quotes + let code = "import { serve } from '@hono/node-server'\n" + code += `serve(${appName})` + return code + }, + ], + }, + ...pluginOptions, + }), + name: '@hono/vite-build/node', + } +} + +export default nodeBuildPlugin diff --git a/packages/build/src/base.ts b/packages/build/src/base.ts index 61ba135..9190444 100644 --- a/packages/build/src/base.ts +++ b/packages/build/src/base.ts @@ -1,18 +1,19 @@ import { builtinModules } from 'module' -import { readdir } from 'node:fs/promises' +import { readdirSync, statSync } from 'node:fs' import { resolve } from 'url' import type { ConfigEnv, Plugin, ResolvedConfig, UserConfig } from 'vite' -import { getEntryContent } from './entry.js' -import type { GetEntryContentOptions } from './entry.js' +import { getEntryContent } from './entry/index.js' +import type { GetEntryContentOptions } from './entry/index.js' export type BuildOptions = { /** - * @default ['./src/index.tsx', './app/server.ts'] + * @default ['src/index.ts', './src/index.tsx', './app/server.ts'] */ - entry: string | string[] + entry?: string | string[] /** * @default './dist' */ + output?: string outputDir?: string external?: string[] /** @@ -24,8 +25,10 @@ export type BuildOptions = { } & Omit export const defaultOptions: Required< - Omit + Omit > = { + entry: ['src/index.ts', './src/index.tsx', './app/server.ts'], + output: 'index.js', outputDir: './dist', external: [], minify: true, @@ -36,12 +39,10 @@ export const defaultOptions: Required< } return false }, - staticPaths: [''], + staticPaths: [], } -const EntryFileName = 'app.js' - -export const buildPlugin = (options: BuildOptions): Plugin => { +const buildPlugin = (options: BuildOptions): Plugin => { const virtualEntryId = 'virtual:build-entry-module' const resolvedVirtualEntryId = '\0' + virtualEntryId let config: ResolvedConfig @@ -59,20 +60,24 @@ export const buildPlugin = (options: BuildOptions): Plugin => { async load(id) { if (id === resolvedVirtualEntryId) { const staticPaths: string[] = [] - const paths = await readdir(resolve(config.root, config.publicDir), { - withFileTypes: true, - }) - paths.forEach((p) => { - if (p.isDirectory()) { - staticPaths.push(`/${p.name}/*`) - } else { - staticPaths.push(`/${p.name}`) - } - }) + try { + statSync(config.publicDir) + const paths = readdirSync(resolve(config.root, config.publicDir), { + withFileTypes: true, + }) + paths.forEach((p) => { + if (p.isDirectory()) { + staticPaths.push(`/${p.name}/*`) + } else { + staticPaths.push(`/${p.name}`) + } + }) + } catch {} + const entry = options.entry ?? defaultOptions.entry return await getEntryContent({ - entry: Array.isArray(options.entry) ? options.entry : [options.entry], - entryContentBeforeHook: options.entryContentBeforeHook, - entryContentAfterHook: options.entryContentAfterHook, + entry: Array.isArray(entry) ? entry : [entry], + entryContentBeforeHooks: options.entryContentBeforeHooks, + entryContentAfterHooks: options.entryContentAfterHooks, staticPaths, }) } @@ -94,7 +99,7 @@ export const buildPlugin = (options: BuildOptions): Plugin => { external: [...builtinModules, /^node:/], input: virtualEntryId, output: { - entryFileNames: EntryFileName, + entryFileNames: options.output ?? defaultOptions.output, }, }, }, @@ -102,3 +107,5 @@ export const buildPlugin = (options: BuildOptions): Plugin => { }, } } + +export default buildPlugin diff --git a/packages/build/src/entry.ts b/packages/build/src/entry/index.ts similarity index 66% rename from packages/build/src/entry.ts rename to packages/build/src/entry/index.ts index 69a53a4..9103dae 100644 --- a/packages/build/src/entry.ts +++ b/packages/build/src/entry/index.ts @@ -11,8 +11,8 @@ export type EntryContentHook = ( export type GetEntryContentOptions = { entry: string[] - entryContentBeforeHook?: EntryContentHook - entryContentAfterHook?: EntryContentHook + entryContentBeforeHooks?: EntryContentHook[] + entryContentAfterHooks?: EntryContentHook[] staticPaths?: string[] } @@ -31,6 +31,23 @@ export const getEntryContent = async (options: GetEntryContentOptions) => { const globStr = normalizePaths(options.entry) .map((e) => `'${e}'`) .join(',') + + const hooksToString = async (appName: string, hooks?: EntryContentHook[]) => { + if (hooks) { + const str = ( + await Promise.all( + hooks.map((hook) => { + return hook(appName, { + staticPaths, + }) + }) + ) + ).join('\n') + return str + } + return '' + } + const appStr = `const modules = import.meta.glob([${globStr}], { import: 'default', eager: true }) let added = false for (const [, app] of Object.entries(modules)) { @@ -43,16 +60,16 @@ export const getEntryContent = async (options: GetEntryContentOptions) => { if (!added) { throw new Error("Can't import modules from [${globStr}]") }` - const foo = `import { Hono } from 'hono' + const mainAppStr = `import { Hono } from 'hono' const mainApp = new Hono() -${options.entryContentBeforeHook ? await options.entryContentBeforeHook('mainApp', { staticPaths }) : ''} +${await hooksToString('mainApp', options.entryContentBeforeHooks)} ${appStr} -${options.entryContentAfterHook ? await options.entryContentAfterHook('mainApp', { staticPaths }) : ''} +${await hooksToString('mainApp', options.entryContentAfterHooks)} export default mainApp` - return foo + return mainAppStr } diff --git a/packages/build/src/entry/serve-static.ts b/packages/build/src/entry/serve-static.ts new file mode 100644 index 0000000..2de7fec --- /dev/null +++ b/packages/build/src/entry/serve-static.ts @@ -0,0 +1,12 @@ +type ServeStaticHookOptions = { + filePaths?: string[] + root?: string +} + +export const serveStaticHook = (appName: string, options: ServeStaticHookOptions) => { + let code = '' + for (const path of options.filePaths ?? []) { + code += `${appName}.use('${path}', serveStatic({ root: '${options.root ?? './'}' }))\n` + } + return code +} diff --git a/packages/build/src/index.ts b/packages/build/src/index.ts index 1999a88..679d9d5 100644 --- a/packages/build/src/index.ts +++ b/packages/build/src/index.ts @@ -1,2 +1,5 @@ import { defaultOptions } from './base.js' export { defaultOptions } + +import basePlugin from './base.js' +export default basePlugin diff --git a/packages/build/test/basic.test.ts b/packages/build/test/basic.test.ts new file mode 100644 index 0000000..301c0e8 --- /dev/null +++ b/packages/build/test/basic.test.ts @@ -0,0 +1,31 @@ +import { existsSync, readFileSync, rmSync } from 'node:fs' +import { build } from 'vite' +import { describe, it, expect, afterAll } from 'vitest' +import buildPlugin from '../src/base' + +describe('Base Plugin', () => { + const testDir = './test/mocks/app' + const entry = './src/server.ts' + + afterAll(() => { + rmSync(`${testDir}/dist`, { recursive: true, force: true }) + }) + + it('Should build the project correctly with the plugin', async () => { + const outputFile = `${testDir}/dist/index.js` + + await build({ + root: testDir, + plugins: [ + buildPlugin({ + entry, + }), + ], + }) + + expect(existsSync(outputFile)).toBe(true) + + const output = readFileSync(outputFile, 'utf-8') + expect(output).toContain('Hello World') + }) +}) diff --git a/packages/build/test/bun.test.ts b/packages/build/test/bun.test.ts new file mode 100644 index 0000000..4d4b169 --- /dev/null +++ b/packages/build/test/bun.test.ts @@ -0,0 +1,40 @@ +import { existsSync, readFileSync, rmSync } from 'node:fs' +import { build } from 'vite' +import { describe, it, expect, afterAll } from 'vitest' +import bunBuildPlugin from '../src/adapter/bun' + +describe('Build Plugin with Bun Adapter', () => { + const testDir = './test/mocks/app-static-files' + const entry = './src/server.ts' + + afterAll(() => { + rmSync(`${testDir}/dist`, { recursive: true, force: true }) + }) + + it('Should build the project correctly with the plugin', async () => { + const outputFile = `${testDir}/dist/index.js` + + await build({ + root: testDir, + plugins: [ + bunBuildPlugin({ + entry, + }), + ], + }) + + expect(existsSync(outputFile)).toBe(true) + + const output = readFileSync(outputFile, 'utf-8') + expect(output).toContain('Hello World') + expect(output).toContain('use("/foo.txt"') + expect(output).toContain('use("/js/*"') + + const outputFooTxt = readFileSync(`${testDir}/dist/foo.txt`, 'utf-8') + expect(outputFooTxt).toContain('foo') + + const outputJsClientJs = readFileSync(`${testDir}/dist/js/client.js`, 'utf-8') + // eslint-disable-next-line quotes + expect(outputJsClientJs).toContain("console.log('foo')") + }) +}) diff --git a/packages/build/test/cloudflare-pages.test.ts b/packages/build/test/cloudflare-pages.test.ts new file mode 100644 index 0000000..60c3f66 --- /dev/null +++ b/packages/build/test/cloudflare-pages.test.ts @@ -0,0 +1,86 @@ +import { existsSync, readFileSync, rmSync } from 'node:fs' +import { build } from 'vite' +import { describe, it, expect, afterAll } from 'vitest' +import cloudflarePagesPlugin from '../src/adapter/cloudflare-pages' + +describe('cloudflarePagesPlugin', () => { + const testDir = './test/mocks/app-static-files' + + afterAll(() => { + rmSync(`${testDir}/dist`, { recursive: true, force: true }) + }) + + it('Should build the project correctly with the plugin', async () => { + const outputFile = `${testDir}/dist/_worker.js` + const routesFile = `${testDir}/dist/_routes.json` + + await build({ + root: testDir, + plugins: [ + cloudflarePagesPlugin({ + entry: 'src/server.ts', + }), + ], + }) + + expect(existsSync(outputFile)).toBe(true) + expect(existsSync(routesFile)).toBe(true) + + const output = readFileSync(outputFile, 'utf-8') + expect(output).toContain('Hello World') + + const routes = readFileSync(routesFile, 'utf-8') + expect(routes).toContain('{"version":1,"include":["/*"],"exclude":["/foo.txt","/js/*"]}') + }) + + it('Should build the project correctly with custom output directory', async () => { + const outputFile = `${testDir}/customDir/_worker.js` + const routesFile = `${testDir}/customDir/_routes.json` + + await build({ + root: testDir, + plugins: [ + cloudflarePagesPlugin({ + outputDir: 'customDir', + entry: 'src/server.ts', + }), + ], + build: { + emptyOutDir: true, + }, + }) + + expect(existsSync(outputFile)).toBe(true) + expect(existsSync(routesFile)).toBe(true) + + const output = readFileSync(outputFile, 'utf-8') + expect(output).toContain('Hello World') + + const routes = readFileSync(routesFile, 'utf-8') + expect(routes).toContain('{"version":1,"include":["/*"],"exclude":["/foo.txt","/js/*"]}') + }) + + it('Should not create a new _routes.json when _routes.json on output directory.', async () => { + const outputFile = `${testDir}/dist/_worker.js` + const routesFile = `${testDir}/dist/_routes.json` + + await build({ + publicDir: 'public-routes-json', + root: testDir, + plugins: [ + cloudflarePagesPlugin({ + entry: 'src/server.ts', + }), + ], + }) + + expect(existsSync(outputFile)).toBe(true) + expect(existsSync(routesFile)).toBe(true) + + const output = readFileSync(outputFile, 'utf-8') + expect(output).toContain('Hello World') + + const routes = readFileSync(routesFile, 'utf-8') + expect(routes).toContain('{"version":1,"include":["/"],"exclude":["/customRoute"]}') + }) +}) diff --git a/packages/build/test/mocks/app-static-files/customDir/_routes.json b/packages/build/test/mocks/app-static-files/customDir/_routes.json new file mode 100644 index 0000000..cee1033 --- /dev/null +++ b/packages/build/test/mocks/app-static-files/customDir/_routes.json @@ -0,0 +1 @@ +{"version":1,"include":["/*"],"exclude":["/foo.txt","/js/*"]} \ No newline at end of file diff --git a/packages/build/test/mocks/app-static-files/customDir/_worker.js b/packages/build/test/mocks/app-static-files/customDir/_worker.js new file mode 100644 index 0000000..08675ca --- /dev/null +++ b/packages/build/test/mocks/app-static-files/customDir/_worker.js @@ -0,0 +1 @@ +var xe=Object.defineProperty;var Z=e=>{throw TypeError(e)};var Oe=(e,t,r)=>t in e?xe(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r;var u=(e,t,r)=>Oe(e,typeof t!="symbol"?t+"":t,r),ee=(e,t,r)=>t.has(e)||Z("Cannot "+r);var d=(e,t,r)=>(ee(e,t,"read from private field"),r?r.call(e):t.get(e)),P=(e,t,r)=>t.has(e)?Z("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,r),p=(e,t,r,s)=>(ee(e,t,"write to private field"),s?s.call(e,r):t.set(e,r),r);var je={Stringify:1,BeforeStream:2,Stream:3},ce=async(e,t,r,s,n)=>{const i=e.callbacks;return i!=null&&i.length?(n?n[0]+=e:n=[e],Promise.all(i.map(c=>c({phase:t,buffer:n,context:s}))).then(c=>Promise.all(c.filter(Boolean).map(o=>ce(o,t,!1,s,n))).then(()=>n[0]))):Promise.resolve(e)},Pe="text/plain; charset=UTF-8",G=(e,t={})=>(Object.entries(t).forEach(([r,s])=>e.set(r,s)),e),F,A,g,m,E,$,ie,K=(ie=class{constructor(e,t){u(this,"req");u(this,"env",{});u(this,"_var");u(this,"finalized",!1);u(this,"error");P(this,F,200);P(this,A);P(this,g);P(this,m);P(this,E);P(this,$,!0);u(this,"layout");u(this,"renderer",e=>this.html(e));u(this,"notFoundHandler",()=>new Response);u(this,"render",(...e)=>this.renderer(...e));u(this,"setLayout",e=>this.layout=e);u(this,"getLayout",()=>this.layout);u(this,"setRenderer",e=>{this.renderer=e});u(this,"header",(e,t,r)=>{if(t===void 0){d(this,g)?d(this,g).delete(e):d(this,m)&&delete d(this,m)[e.toLocaleLowerCase()],this.finalized&&this.res.headers.delete(e);return}r!=null&&r.append?(d(this,g)||(p(this,$,!1),p(this,g,new Headers(d(this,m))),p(this,m,{})),d(this,g).append(e,t)):d(this,g)?d(this,g).set(e,t):(d(this,m)??p(this,m,{}),d(this,m)[e.toLowerCase()]=t),this.finalized&&(r!=null&&r.append?this.res.headers.append(e,t):this.res.headers.set(e,t))});u(this,"status",e=>{p(this,$,!1),p(this,F,e)});u(this,"set",(e,t)=>{this._var??(this._var={}),this._var[e]=t});u(this,"get",e=>this._var?this._var[e]:void 0);u(this,"newResponse",(e,t,r)=>{if(d(this,$)&&!r&&!t&&d(this,F)===200)return new Response(e,{headers:d(this,m)});if(t&&typeof t!="number"){const n=new Headers(t.headers);d(this,g)&&d(this,g).forEach((a,c)=>{c==="set-cookie"?n.append(c,a):n.set(c,a)});const i=G(n,d(this,m));return new Response(e,{headers:i,status:t.status??d(this,F)})}const s=typeof t=="number"?t:d(this,F);d(this,m)??p(this,m,{}),d(this,g)??p(this,g,new Headers),G(d(this,g),d(this,m)),d(this,E)&&(d(this,E).headers.forEach((n,i)=>{var a,c;i==="set-cookie"?(a=d(this,g))==null||a.append(i,n):(c=d(this,g))==null||c.set(i,n)}),G(d(this,g),d(this,m))),r??(r={});for(const[n,i]of Object.entries(r))if(typeof i=="string")d(this,g).set(n,i);else{d(this,g).delete(n);for(const a of i)d(this,g).append(n,a)}return new Response(e,{status:s,headers:d(this,g)})});u(this,"body",(e,t,r)=>typeof t=="number"?this.newResponse(e,t,r):this.newResponse(e,t));u(this,"text",(e,t,r)=>{if(!d(this,m)){if(d(this,$)&&!r&&!t)return new Response(e);p(this,m,{})}return d(this,m)["content-type"]=Pe,typeof t=="number"?this.newResponse(e,t,r):this.newResponse(e,t)});u(this,"json",(e,t,r)=>{const s=JSON.stringify(e);return d(this,m)??p(this,m,{}),d(this,m)["content-type"]="application/json; charset=UTF-8",typeof t=="number"?this.newResponse(s,t,r):this.newResponse(s,t)});u(this,"html",(e,t,r)=>(d(this,m)??p(this,m,{}),d(this,m)["content-type"]="text/html; charset=UTF-8",typeof e=="object"&&(e instanceof Promise||(e=e.toString()),e instanceof Promise)?e.then(s=>ce(s,je.Stringify,!1,{})).then(s=>typeof t=="number"?this.newResponse(s,t,r):this.newResponse(s,t)):typeof t=="number"?this.newResponse(e,t,r):this.newResponse(e,t)));u(this,"redirect",(e,t)=>(d(this,g)??p(this,g,new Headers),d(this,g).set("Location",e),this.newResponse(null,t??302)));u(this,"notFound",()=>this.notFoundHandler(this));this.req=e,t&&(p(this,A,t.executionCtx),this.env=t.env,t.notFoundHandler&&(this.notFoundHandler=t.notFoundHandler))}get event(){if(d(this,A)&&"respondWith"in d(this,A))return d(this,A);throw Error("This context has no FetchEvent")}get executionCtx(){if(d(this,A))return d(this,A);throw Error("This context has no ExecutionContext")}get res(){return p(this,$,!1),d(this,E)||p(this,E,new Response("404 Not Found",{status:404}))}set res(e){if(p(this,$,!1),d(this,E)&&e){d(this,E).headers.delete("content-type");for(const[t,r]of d(this,E).headers.entries())if(t==="set-cookie"){const s=d(this,E).headers.getSetCookie();e.headers.delete("set-cookie");for(const n of s)e.headers.append("set-cookie",n)}else e.headers.set(t,r)}p(this,E,e),this.finalized=!0}get var(){return{...this._var}}},F=new WeakMap,A=new WeakMap,g=new WeakMap,m=new WeakMap,E=new WeakMap,$=new WeakMap,ie),te=(e,t,r)=>(s,n)=>{let i=-1;return a(0);async function a(c){if(c<=i)throw new Error("next() called multiple times");i=c;let o,h=!1,l;if(e[c]?(l=e[c][0][0],s instanceof K&&(s.req.routeIndex=c)):l=c===e.length&&n||void 0,!l)s instanceof K&&s.finalized===!1&&r&&(o=await r(s));else try{o=await l(s,()=>a(c+1))}catch(f){if(f instanceof Error&&s instanceof K&&t)s.error=f,o=await t(f,s),h=!0;else throw f}return o&&(s.finalized===!1||h)&&(s.res=o),s}},He=async(e,t=Object.create(null))=>{const{all:r=!1,dot:s=!1}=t,i=(e instanceof fe?e.raw.headers:e.headers).get("Content-Type");return i!==null&&i.startsWith("multipart/form-data")||i!==null&&i.startsWith("application/x-www-form-urlencoded")?Se(e,{all:r,dot:s}):{}};async function Se(e,t){const r=await e.formData();return r?Ae(r,t):{}}function Ae(e,t){const r=Object.create(null);return e.forEach((s,n)=>{t.all||n.endsWith("[]")?$e(r,n,s):r[n]=s}),t.dot&&Object.entries(r).forEach(([s,n])=>{s.includes(".")&&(_e(r,s,n),delete r[s])}),r}var $e=(e,t,r)=>{e[t]!==void 0?Array.isArray(e[t])?e[t].push(r):e[t]=[e[t],r]:e[t]=r},_e=(e,t,r)=>{let s=e;const n=t.split(".");n.forEach((i,a)=>{a===n.length-1?s[i]=r:((!s[i]||typeof s[i]!="object"||Array.isArray(s[i])||s[i]instanceof File)&&(s[i]=Object.create(null)),s=s[i])})},he=e=>{const t=e.split("/");return t[0]===""&&t.shift(),t},Fe=e=>{const{groups:t,path:r}=Ie(e),s=he(r);return De(s,t)},Ie=e=>{const t=[];return e=e.replace(/\{[^}]+\}/g,(r,s)=>{const n=`@${s}`;return t.push([n,r]),n}),{groups:t,path:e}},De=(e,t)=>{for(let r=t.length-1;r>=0;r--){const[s]=t[r];for(let n=e.length-1;n>=0;n--)if(e[n].includes(s)){e[n]=e[n].replace(s,t[r][1]);break}}return e},k={},re=e=>{if(e==="*")return"*";const t=e.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);return t?(k[e]||(t[2]?k[e]=[e,t[1],new RegExp("^"+t[2]+"$")]:k[e]=[e,t[1],!0]),k[e]):null},Ce=e=>{try{return decodeURI(e)}catch{return e.replace(/(?:%[0-9A-Fa-f]{2})+/g,t=>{try{return decodeURI(t)}catch{return t}})}},le=e=>{const t=e.url,r=t.indexOf("/",8);let s=r;for(;s{const t=le(e);return t.length>1&&t[t.length-1]==="/"?t.slice(0,-1):t},N=(...e)=>{let t="",r=!1;for(let s of e)t[t.length-1]==="/"&&(t=t.slice(0,-1),r=!0),s[0]!=="/"&&(s=`/${s}`),s==="/"&&r?t=`${t}/`:s!=="/"&&(t=`${t}${s}`),s==="/"&&t===""&&(t="/");return t},ue=e=>{if(!e.match(/\:.+\?$/))return null;const t=e.split("/"),r=[];let s="";return t.forEach(n=>{if(n!==""&&!/\:/.test(n))s+="/"+n;else if(/\:/.test(n))if(/\?/.test(n)){r.length===0&&s===""?r.push("/"):r.push(s);const i=n.replace("?","");s+="/"+i,r.push(s)}else s+="/"+n}),r.filter((n,i,a)=>a.indexOf(n)===i)},X=e=>/[%+]/.test(e)?(e.indexOf("+")!==-1&&(e=e.replace(/\+/g," ")),/%/.test(e)?Q(e):e):e,de=(e,t,r)=>{let s;if(!r&&t&&!/[%+]/.test(t)){let a=e.indexOf(`?${t}`,8);for(a===-1&&(a=e.indexOf(`&${t}`,8));a!==-1;){const c=e.charCodeAt(a+t.length+1);if(c===61){const o=a+t.length+2,h=e.indexOf("&",o);return X(e.slice(o,h===-1?void 0:h))}else if(c==38||isNaN(c))return"";a=e.indexOf(`&${t}`,a+1)}if(s=/[%+]/.test(e),!s)return}const n={};s??(s=/[%+]/.test(e));let i=e.indexOf("?",8);for(;i!==-1;){const a=e.indexOf("&",i+1);let c=e.indexOf("=",i);c>a&&a!==-1&&(c=-1);let o=e.slice(i+1,c===-1?a===-1?void 0:a:c);if(s&&(o=X(o)),i=a,o==="")continue;let h;c===-1?h="":(h=e.slice(c+1,a===-1?void 0:a),s&&(h=X(h))),r?(n[o]&&Array.isArray(n[o])||(n[o]=[]),n[o].push(h)):n[o]??(n[o]=h)}return t?n[t]:n},Ne=de,Te=(e,t)=>de(e,t,!0),Q=decodeURIComponent,C,x,oe,fe=(oe=class{constructor(e,t="/",r=[[]]){u(this,"raw");P(this,C);P(this,x);u(this,"routeIndex",0);u(this,"path");u(this,"bodyCache",{});u(this,"cachedBody",e=>{const{bodyCache:t,raw:r}=this,s=t[e];if(s)return s;const n=Object.keys(t)[0];return n?t[n].then(i=>(n==="json"&&(i=JSON.stringify(i)),new Response(i)[e]())):t[e]=r[e]()});this.raw=e,this.path=t,p(this,x,r),p(this,C,{})}param(e){return e?this.getDecodedParam(e):this.getAllDecodedParams()}getDecodedParam(e){const t=d(this,x)[0][this.routeIndex][1][e],r=this.getParamValue(t);return r?/\%/.test(r)?Q(r):r:void 0}getAllDecodedParams(){const e={},t=Object.keys(d(this,x)[0][this.routeIndex][1]);for(const r of t){const s=this.getParamValue(d(this,x)[0][this.routeIndex][1][r]);s&&typeof s=="string"&&(e[r]=/\%/.test(s)?Q(s):s)}return e}getParamValue(e){return d(this,x)[1]?d(this,x)[1][e]:e}query(e){return Ne(this.url,e)}queries(e){return Te(this.url,e)}header(e){if(e)return this.raw.headers.get(e.toLowerCase())??void 0;const t={};return this.raw.headers.forEach((r,s)=>{t[s]=r}),t}async parseBody(e){var t;return(t=this.bodyCache).parsedBody??(t.parsedBody=await He(this,e))}json(){return this.cachedBody("json")}text(){return this.cachedBody("text")}arrayBuffer(){return this.cachedBody("arrayBuffer")}blob(){return this.cachedBody("blob")}formData(){return this.cachedBody("formData")}addValidatedData(e,t){d(this,C)[e]=t}valid(e){return d(this,C)[e]}get url(){return this.raw.url}get method(){return this.raw.method}get matchedRoutes(){return d(this,x)[0].map(([[,e]])=>e)}get routePath(){return d(this,x)[0].map(([[,e]])=>e)[this.routeIndex].path}},C=new WeakMap,x=new WeakMap,oe),y="ALL",Me="all",Be=["get","post","put","delete","options","patch"],pe="Can not add a route since the matcher is already built.",ge=class extends Error{},Ue=Symbol("composedHandler"),We=e=>e.text("404 Not Found",404),se=(e,t)=>"getResponse"in e?e.getResponse():(console.error(e),t.text("Internal Server Error",500)),O,ae,we=(ae=class{constructor(t={}){u(this,"get");u(this,"post");u(this,"put");u(this,"delete");u(this,"options");u(this,"patch");u(this,"all");u(this,"on");u(this,"use");u(this,"router");u(this,"getPath");u(this,"_basePath","/");P(this,O,"/");u(this,"routes",[]);u(this,"notFoundHandler",We);u(this,"errorHandler",se);u(this,"onError",t=>(this.errorHandler=t,this));u(this,"notFound",t=>(this.notFoundHandler=t,this));u(this,"fetch",(t,...r)=>this.dispatch(t,r[1],r[0],t.method));u(this,"request",(t,r,s,n)=>{if(t instanceof Request)return r!==void 0&&(t=new Request(t,r)),this.fetch(t,s,n);t=t.toString();const i=/^https?:\/\//.test(t)?t:`http://localhost${N("/",t)}`,a=new Request(i,r);return this.fetch(a,s,n)});u(this,"fire",()=>{addEventListener("fetch",t=>{t.respondWith(this.dispatch(t.request,t,void 0,t.request.method))})});[...Be,Me].forEach(n=>{this[n]=(i,...a)=>(typeof i=="string"?p(this,O,i):this.addRoute(n,d(this,O),i),a.forEach(c=>{typeof c!="string"&&this.addRoute(n,d(this,O),c)}),this)}),this.on=(n,i,...a)=>{for(const c of[i].flat()){p(this,O,c);for(const o of[n].flat())a.map(h=>{this.addRoute(o.toUpperCase(),d(this,O),h)})}return this},this.use=(n,...i)=>(typeof n=="string"?p(this,O,n):(p(this,O,"*"),i.unshift(n)),i.forEach(a=>{this.addRoute(y,d(this,O),a)}),this);const s=t.strict??!0;delete t.strict,Object.assign(this,t),this.getPath=s?t.getPath??le:Le}clone(){const t=new we({router:this.router,getPath:this.getPath});return t.routes=this.routes,t}route(t,r){const s=this.basePath(t);return r.routes.map(n=>{let i;r.errorHandler===se?i=n.handler:(i=async(a,c)=>(await te([],r.errorHandler)(a,()=>n.handler(a,c))).res,i[Ue]=n.handler),s.addRoute(n.method,n.path,i)}),this}basePath(t){const r=this.clone();return r._basePath=N(this._basePath,t),r}mount(t,r,s){let n,i;s&&(typeof s=="function"?i=s:(i=s.optionHandler,n=s.replaceRequest));const a=i?o=>{const h=i(o);return Array.isArray(h)?h:[h]}:o=>{let h;try{h=o.executionCtx}catch{}return[o.env,h]};n||(n=(()=>{const o=N(this._basePath,t),h=o==="/"?0:o.length;return l=>{const f=new URL(l.url);return f.pathname=f.pathname.slice(h)||"/",new Request(f,l)}})());const c=async(o,h)=>{const l=await r(n(o.req.raw),...a(o));if(l)return l;await h()};return this.addRoute(y,N(t,"*"),c),this}addRoute(t,r,s){t=t.toUpperCase(),r=N(this._basePath,r);const n={path:r,method:t,handler:s};this.router.add(t,r,[s,n]),this.routes.push(n)}matchRoute(t,r){return this.router.match(t,r)}handleError(t,r){if(t instanceof Error)return this.errorHandler(t,r);throw t}dispatch(t,r,s,n){if(n==="HEAD")return(async()=>new Response(null,await this.dispatch(t,r,s,"GET")))();const i=this.getPath(t,{env:s}),a=this.matchRoute(n,i),c=new K(new fe(t,i,a),{env:s,executionCtx:r,notFoundHandler:this.notFoundHandler});if(a[0].length===1){let h;try{h=a[0][0][0][0](c,async()=>{c.res=await this.notFoundHandler(c)})}catch(l){return this.handleError(l,c)}return h instanceof Promise?h.then(l=>l||(c.finalized?c.res:this.notFoundHandler(c))).catch(l=>this.handleError(l,c)):h??this.notFoundHandler(c)}const o=te(a[0],this.errorHandler,this.notFoundHandler);return(async()=>{try{const h=await o(c);if(!h.finalized)throw new Error("Context is not finalized. Did you forget to return a Response object or `await next()`?");return h.res}catch(h){return this.handleError(h,c)}})()}},O=new WeakMap,ae),q="[^/]+",M=".*",B="(?:|/.*)",T=Symbol(),ke=new Set(".\\+*[^]$()");function Ke(e,t){return e.length===1?t.length===1?eR!==M&&R!==B))throw T;if(i)return;h=this.children[f]=new J,l!==""&&(h.varIndex=n.varIndex++)}!i&&l!==""&&s.push([l,h.varIndex])}else if(h=this.children[a],!h){if(Object.keys(this.children).some(l=>l.length>1&&l!==M&&l!==B))throw T;if(i)return;h=this.children[a]=new J}h.insert(c,r,s,n,i)}buildRegExpStr(){const r=Object.keys(this.children).sort(Ke).map(s=>{const n=this.children[s];return(typeof n.varIndex=="number"?`(${s})@${n.varIndex}`:ke.has(s)?`\\${s}`:s)+n.buildRegExpStr()});return typeof this.index=="number"&&r.unshift(`#${this.index}`),r.length===0?"":r.length===1?r[0]:"(?:"+r.join("|")+")"}},Ve=class{constructor(){u(this,"context",{varIndex:0});u(this,"root",new J)}insert(e,t,r){const s=[],n=[];for(let a=0;;){let c=!1;if(e=e.replace(/\{[^}]+\}/g,o=>{const h=`@\\${a}`;return n[a]=[h,o],a++,c=!0,h}),!c)break}const i=e.match(/(?::[^\/]+)|(?:\/\*$)|./g)||[];for(let a=n.length-1;a>=0;a--){const[c]=n[a];for(let o=i.length-1;o>=0;o--)if(i[o].indexOf(c)!==-1){i[o]=i[o].replace(c,n[a][1]);break}}return this.root.insert(i,t,s,this.context,r),s}buildRegExp(){let e=this.root.buildRegExpStr();if(e==="")return[/^$/,[],[]];let t=0;const r=[],s=[];return e=e.replace(/#(\d+)|@(\d+)|\.\*\$/g,(n,i,a)=>typeof i<"u"?(r[++t]=Number(i),"$()"):(typeof a<"u"&&(s[Number(a)]=++t),"")),[new RegExp(`^${e}`),r,s]}},me=[],qe=[/^$/,[],Object.create(null)],V=Object.create(null);function Re(e){return V[e]??(V[e]=new RegExp(e==="*"?"":`^${e.replace(/\/\*$|([.\\+*[^\]$()])/g,(t,r)=>r?`\\${r}`:"(?:|/.*)")}$`))}function ze(){V=Object.create(null)}function Ge(e){var h;const t=new Ve,r=[];if(e.length===0)return qe;const s=e.map(l=>[!/\*|\/:/.test(l[0]),...l]).sort(([l,f],[R,H])=>l?1:R?-1:f.length-H.length),n=Object.create(null);for(let l=0,f=-1,R=s.length;l[j,Object.create(null)]),me]:f++;let b;try{b=t.insert(w,f,H)}catch(j){throw j===T?new ge(w):j}H||(r[f]=v.map(([j,_])=>{const S=Object.create(null);for(_-=1;_>=0;_--){const[U,W]=b[_];S[U]=W}return[j,S]}))}const[i,a,c]=t.buildRegExp();for(let l=0,f=r.length;ln.length-s.length))if(Re(r).test(t))return[...e[r]]}}var Xe=class{constructor(){u(this,"name","RegExpRouter");u(this,"middleware");u(this,"routes");this.middleware={[y]:Object.create(null)},this.routes={[y]:Object.create(null)}}add(e,t,r){var c;const{middleware:s,routes:n}=this;if(!s||!n)throw new Error(pe);s[e]||[s,n].forEach(o=>{o[e]=Object.create(null),Object.keys(o[y]).forEach(h=>{o[e][h]=[...o[y][h]]})}),t==="/*"&&(t="*");const i=(t.match(/\/:/g)||[]).length;if(/\*$/.test(t)){const o=Re(t);e===y?Object.keys(s).forEach(h=>{var l;(l=s[h])[t]||(l[t]=D(s[h],t)||D(s[y],t)||[])}):(c=s[e])[t]||(c[t]=D(s[e],t)||D(s[y],t)||[]),Object.keys(s).forEach(h=>{(e===y||e===h)&&Object.keys(s[h]).forEach(l=>{o.test(l)&&s[h][l].push([r,i])})}),Object.keys(n).forEach(h=>{(e===y||e===h)&&Object.keys(n[h]).forEach(l=>o.test(l)&&n[h][l].push([r,i]))});return}const a=ue(t)||[t];for(let o=0,h=a.length;o{var R;(e===y||e===f)&&((R=n[f])[l]||(R[l]=[...D(s[f],l)||D(s[y],l)||[]]),n[f][l].push([r,i-h+o+1]))})}}match(e,t){ze();const r=this.buildAllMatchers();return this.match=(s,n)=>{const i=r[s]||r[y],a=i[2][n];if(a)return a;const c=n.match(i[0]);if(!c)return[[],me];const o=c.indexOf("",1);return[i[1][o],c]},this.match(e,t)}buildAllMatchers(){const e=Object.create(null);return[...Object.keys(this.routes),...Object.keys(this.middleware)].forEach(t=>{e[t]||(e[t]=this.buildMatcher(t))}),this.middleware=this.routes=void 0,e}buildMatcher(e){const t=[];let r=e===y;return[this.middleware,this.routes].forEach(s=>{const n=s[e]?Object.keys(s[e]).map(i=>[i,s[e][i]]):[];n.length!==0?(r||(r=!0),t.push(...n)):e!==y&&t.push(...Object.keys(s[y]).map(i=>[i,s[y][i]]))}),r?Ge(t):null}},Qe=class{constructor(e){u(this,"name","SmartRouter");u(this,"routers",[]);u(this,"routes",[]);Object.assign(this,e)}add(e,t,r){if(!this.routes)throw new Error(pe);this.routes.push([e,t,r])}match(e,t){if(!this.routes)throw new Error("Fatal error");const{routers:r,routes:s}=this,n=r.length;let i=0,a;for(;i{c.add(...o)}),a=c.match(e,t)}catch(o){if(o instanceof ge)continue;throw o}this.match=c.match.bind(c),this.routers=[c],this.routes=void 0;break}if(i===n)throw new Error("Fatal error");return this.name=`SmartRouter + ${this.activeRouter.name}`,a}get activeRouter(){if(this.routes||this.routers.length!==1)throw new Error("No active router has been determined yet.");return this.routers[0]}},ye=class{constructor(e,t,r){u(this,"methods");u(this,"children");u(this,"patterns");u(this,"order",0);u(this,"name");u(this,"params",Object.create(null));if(this.children=r||Object.create(null),this.methods=[],this.name="",e&&t){const s=Object.create(null);s[e]={handler:t,possibleKeys:[],score:0,name:this.name},this.methods=[s]}this.patterns=[]}insert(e,t,r){this.name=`${e} ${t}`,this.order=++this.order;let s=this;const n=Fe(t),i=[];for(let o=0,h=n.length;ol.indexOf(o)===h),name:this.name,score:this.order};return a[e]=c,s.methods.push(a),s}gHSets(e,t,r,s){const n=[];for(let i=0,a=e.methods.length;i{const f=h[o.name];o.params[l]=s[l]&&!f?s[l]:r[l]??s[l],h[o.name]=!0}),n.push(o))}return n}search(e,t){const r=[];this.params=Object.create(null);let n=[this];const i=he(t);for(let c=0,o=i.length;cc.score-o.score).map(({handler:c,params:o})=>[c,o])]}},Je=class{constructor(){u(this,"name","TrieRouter");u(this,"node");this.node=new ye}add(e,t,r){const s=ue(t);if(s){for(const n of s)this.node.insert(e,n,r);return}this.node.insert(e,t,r)}match(e,t){return this.node.search(e,t)}},ve=class extends we{constructor(e={}){super(e),this.router=e.router??new Qe({routers:[new Xe,new Je]})}};const be=new ve;be.get("/",e=>e.text("Hello World"));const ne=new ve,Ye=Object.assign({"/src/server.ts":be});let Ee=!1;for(const[,e]of Object.entries(Ye))e&&(ne.route("/",e),ne.notFound(e.notFoundHandler),Ee=!0);if(!Ee)throw new Error("Can't import modules from ['/src/server.ts']");export{ne as default}; diff --git a/packages/build/test/mocks/app-static-files/customDir/foo.txt b/packages/build/test/mocks/app-static-files/customDir/foo.txt new file mode 100644 index 0000000..1910281 --- /dev/null +++ b/packages/build/test/mocks/app-static-files/customDir/foo.txt @@ -0,0 +1 @@ +foo \ No newline at end of file diff --git a/packages/build/test/mocks/app-static-files/customDir/js/client.js b/packages/build/test/mocks/app-static-files/customDir/js/client.js new file mode 100644 index 0000000..a5b9709 --- /dev/null +++ b/packages/build/test/mocks/app-static-files/customDir/js/client.js @@ -0,0 +1 @@ +console.log('foo') diff --git a/packages/build/test/mocks/app-static-files/public-routes-json/_routes.json b/packages/build/test/mocks/app-static-files/public-routes-json/_routes.json new file mode 100644 index 0000000..2a43272 --- /dev/null +++ b/packages/build/test/mocks/app-static-files/public-routes-json/_routes.json @@ -0,0 +1 @@ +{"version":1,"include":["/"],"exclude":["/customRoute"]} \ No newline at end of file diff --git a/packages/build/test/mocks/app-static-files/public/foo.txt b/packages/build/test/mocks/app-static-files/public/foo.txt new file mode 100644 index 0000000..1910281 --- /dev/null +++ b/packages/build/test/mocks/app-static-files/public/foo.txt @@ -0,0 +1 @@ +foo \ No newline at end of file diff --git a/packages/build/test/mocks/app-static-files/public/js/client.js b/packages/build/test/mocks/app-static-files/public/js/client.js new file mode 100644 index 0000000..a5b9709 --- /dev/null +++ b/packages/build/test/mocks/app-static-files/public/js/client.js @@ -0,0 +1 @@ +console.log('foo') diff --git a/packages/build/test/mocks/app-static-files/src/server.ts b/packages/build/test/mocks/app-static-files/src/server.ts new file mode 100644 index 0000000..c2381bb --- /dev/null +++ b/packages/build/test/mocks/app-static-files/src/server.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +const app = new Hono() + +app.get('/', (c) => c.text('Hello World')) + +export default app diff --git a/packages/build/test/mocks/app/src/server.ts b/packages/build/test/mocks/app/src/server.ts new file mode 100644 index 0000000..c2381bb --- /dev/null +++ b/packages/build/test/mocks/app/src/server.ts @@ -0,0 +1,7 @@ +import { Hono } from 'hono' + +const app = new Hono() + +app.get('/', (c) => c.text('Hello World')) + +export default app diff --git a/yarn.lock b/yarn.lock index 0f71850..c382d8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -882,22 +882,6 @@ __metadata: languageName: node linkType: hard -"@hono/build@workspace:packages/build": - version: 0.0.0-use.local - resolution: "@hono/build@workspace:packages/build" - dependencies: - glob: ^10.3.10 - hono: ^4.2.7 - publint: ^0.1.12 - rimraf: ^5.0.1 - tsup: ^7.2.0 - vite: ^5.4.4 - vitest: ^1.2.1 - peerDependencies: - hono: "*" - languageName: unknown - linkType: soft - "@hono/eslint-config@npm:^0.0.4": version: 0.0.4 resolution: "@hono/eslint-config@npm:0.0.4" @@ -923,6 +907,22 @@ __metadata: languageName: node linkType: hard +"@hono/vite-build@workspace:packages/build": + version: 0.0.0-use.local + resolution: "@hono/vite-build@workspace:packages/build" + dependencies: + glob: ^10.3.10 + hono: ^4.2.7 + publint: ^0.1.12 + rimraf: ^5.0.1 + tsup: ^7.2.0 + vite: ^5.4.4 + vitest: ^1.2.1 + peerDependencies: + hono: "*" + languageName: unknown + linkType: soft + "@hono/vite-cloudflare-pages@workspace:packages/cloudflare-pages": version: 0.0.0-use.local resolution: "@hono/vite-cloudflare-pages@workspace:packages/cloudflare-pages"