@toapi/vite-plugin 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -0
- package/dist/ApiHandler.d.ts +2 -0
- package/dist/ApiHandler.d.ts.map +1 -0
- package/dist/ApiHandler.js +0 -0
- package/dist/createApiMiddleware.d.ts +4 -0
- package/dist/createApiMiddleware.d.ts.map +1 -0
- package/dist/createApiMiddleware.js +24 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +163 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# @toapi/vite-plugin
|
|
2
|
+
|
|
3
|
+
Vite plugin for [Toapi](https://github.com/farbenmeer/tapi). Bundles your Toapi
|
|
4
|
+
API alongside your Vite frontend in a single project: serves the API as
|
|
5
|
+
middleware in dev and preview mode, and produces a deployable server bundle
|
|
6
|
+
for production.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm add -D @toapi/vite-plugin
|
|
12
|
+
pnpm add @toapi/server srvx
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Peer-deps: `vite ^8`, `@toapi/server`.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
`vite.config.ts`:
|
|
20
|
+
```ts
|
|
21
|
+
import { defineConfig } from "vite";
|
|
22
|
+
import toapi from "@toapi/vite-plugin";
|
|
23
|
+
|
|
24
|
+
export default defineConfig({
|
|
25
|
+
plugins: [toapi()],
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The plugin expects `src/api.ts` to export `api` (an `ApiDefinition`):
|
|
30
|
+
```ts
|
|
31
|
+
import { defineApi, defineHandler, TResponse } from "@toapi/server";
|
|
32
|
+
|
|
33
|
+
export const api = defineApi().route("/hello", {
|
|
34
|
+
GET: defineHandler({ authorize: () => true }, async () => {
|
|
35
|
+
return TResponse.json({ message: "hello" });
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
In dev (`vite`) and preview (`vite preview`) modes, the plugin attaches a
|
|
41
|
+
middleware to Vite's server that handles requests at the configured
|
|
42
|
+
`basePath` (default `/api`). The same Vite server serves both the frontend
|
|
43
|
+
and the API on a single port.
|
|
44
|
+
|
|
45
|
+
## Options
|
|
46
|
+
|
|
47
|
+
| Option | Type | Default | Description |
|
|
48
|
+
|---|---|---|---|
|
|
49
|
+
| `entry` | `string` | `"src/api.ts"` | Path to the file exporting `api`. Resolved against the Vite root. |
|
|
50
|
+
| `basePath` | `string` | `"/api"` | Prefix for API routes. Use `""` to mount at the root. |
|
|
51
|
+
| `port` | `number` | — | Default port for Vite's dev/preview server. Falls back to the `PORT` env var. |
|
|
52
|
+
| `external` | `(string \| RegExp)[]` | `[]` | Packages to keep external in the server bundle. By default everything is bundled. |
|
|
53
|
+
|
|
54
|
+
## Environment variables
|
|
55
|
+
|
|
56
|
+
### Dev (`pnpm dev`)
|
|
57
|
+
|
|
58
|
+
The plugin loads `.env`, `.env.local`, `.env.<mode>`, and `.env.<mode>.local`
|
|
59
|
+
via Vite's `loadEnv` and mirrors every key into `process.env` before the api
|
|
60
|
+
module is loaded. Existing `process.env` values keep precedence, so shell
|
|
61
|
+
vars override `.env` files.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
.env # all envs
|
|
65
|
+
.env.local # all envs, ignored by git
|
|
66
|
+
.env.development # dev only
|
|
67
|
+
.env.development.local # dev only, ignored by git
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This covers server-side libraries that read `process.env.X` directly
|
|
71
|
+
(BetterAuth, DB clients, OAuth secrets). Vite's `import.meta.env` injection
|
|
72
|
+
still applies to client code.
|
|
73
|
+
|
|
74
|
+
### Production (`srvx dist/server/server.js`)
|
|
75
|
+
|
|
76
|
+
The built server does **not** load `.env` files. In production, env vars come
|
|
77
|
+
from the runtime — Docker, systemd, or your PaaS.
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## Build output
|
|
81
|
+
|
|
82
|
+
`vite build` writes two clearly separated trees:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
dist/
|
|
86
|
+
├── client/ — static frontend assets (HTML, JS, CSS, images)
|
|
87
|
+
├── server.js — bundled server (sourcemap included)
|
|
88
|
+
└── server.js.map
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The split exists for safety: server-only code (database credentials,
|
|
92
|
+
third-party API keys, server-side libraries) lives in `dist/server.js` and
|
|
93
|
+
**must not** be deployed to a public static host. Treating `dist/client/` as
|
|
94
|
+
the static-deploy root makes it impossible to leak the server bundle by
|
|
95
|
+
accident.
|
|
96
|
+
|
|
97
|
+
## Service worker
|
|
98
|
+
|
|
99
|
+
To get tapi's offline / tag-based revalidation behavior, add a service
|
|
100
|
+
worker built by [`vite-plugin-pwa`](https://vite-pwa-org.netlify.app/) in
|
|
101
|
+
`injectManifest` mode. The two plugins compose cleanly: `tapi()` redirects
|
|
102
|
+
the client build to `dist/client/`, which is exactly where VitePWA emits
|
|
103
|
+
`sw.js`, and the production server bundle is built separately so
|
|
104
|
+
nothing leaks across.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pnpm add -D vite-plugin-pwa
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
// vite.config.ts
|
|
112
|
+
import { defineConfig } from "vite";
|
|
113
|
+
import tapi from "@toapi/vite-plugin";
|
|
114
|
+
import { VitePWA } from "vite-plugin-pwa";
|
|
115
|
+
|
|
116
|
+
export default defineConfig({
|
|
117
|
+
plugins: [
|
|
118
|
+
tapi(),
|
|
119
|
+
VitePWA({
|
|
120
|
+
strategies: "injectManifest",
|
|
121
|
+
srcDir: "src",
|
|
122
|
+
filename: "service-worker.ts",
|
|
123
|
+
injectRegister: "auto",
|
|
124
|
+
devOptions: { enabled: true, type: "module" },
|
|
125
|
+
// optional, pass-through to VitePWA:
|
|
126
|
+
// manifest: { name: "My App", short_name: "App", ... },
|
|
127
|
+
}),
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// src/service-worker.ts
|
|
134
|
+
import {
|
|
135
|
+
handleTapiRequest,
|
|
136
|
+
listenForInvalidations,
|
|
137
|
+
cleanup,
|
|
138
|
+
} from "@toapi/worker";
|
|
139
|
+
|
|
140
|
+
declare const self: ServiceWorkerGlobalScope;
|
|
141
|
+
|
|
142
|
+
self.addEventListener("activate", (event) => {
|
|
143
|
+
// Drop cache entries that have been expired longer than 7 days,
|
|
144
|
+
// remove orphans, and rebuild the tags index.
|
|
145
|
+
event.waitUntil(cleanup({ maximumStaleAge: 60 * 60 * 24 * 7 }));
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
self.addEventListener("fetch", (event) => {
|
|
149
|
+
const url = new URL(event.request.url);
|
|
150
|
+
if (
|
|
151
|
+
url.pathname.startsWith("/api") &&
|
|
152
|
+
!url.pathname.startsWith("/api/__tapi")
|
|
153
|
+
) {
|
|
154
|
+
event.respondWith(handleTapiRequest(event.request));
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
listenForInvalidations({ url: "/api/__tapi/invalidations" });
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Notes:
|
|
162
|
+
|
|
163
|
+
- Add `"WebWorker"` to your `tsconfig.json` `lib` array so TypeScript
|
|
164
|
+
recognizes `ServiceWorkerGlobalScope` and friends.
|
|
165
|
+
- `devOptions.enabled: true` makes the SW run during `vite dev` too;
|
|
166
|
+
otherwise it only runs in `vite preview` and production.
|
|
167
|
+
- Adjust the `/api` checks in the SW to match the `basePath` you pass to
|
|
168
|
+
`tapi()`.
|
|
169
|
+
- `cleanup`'s `maximumStaleAge` is the grace period (in seconds) past a
|
|
170
|
+
cache entry's `expiresAt` before it's actually deleted on next SW
|
|
171
|
+
activation.
|
|
172
|
+
|
|
173
|
+
## Deployment
|
|
174
|
+
|
|
175
|
+
The server bundle is a fetch-handler module. Serve it in production with the
|
|
176
|
+
[srvx](https://srvx.h3.dev) CLI:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
srvx --prod dist/server.js
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`srvx` picks up `PORT`, `HOST`, and other settings from environment variables.
|
|
183
|
+
|
|
184
|
+
### Static assets
|
|
185
|
+
|
|
186
|
+
Static frontend assets in `dist/client/` should ideally be served by a
|
|
187
|
+
dedicated static host — nginx, Caddy, or an S3-compatible bucket fronted by a
|
|
188
|
+
CDN. Dedicated static hosts give you better caching, compression, HTTP/2,
|
|
189
|
+
and offload the load from your Node.js server.
|
|
190
|
+
|
|
191
|
+
If you don't want a separate static host, srvx can serve them too with the
|
|
192
|
+
`-s` flag:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
srvx --prod -s client dist/server.js
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The path passed to `-s` is resolved **relative to the directory containing
|
|
199
|
+
the entry file** (`dist/`), so `client` points at `dist/client/`.
|
|
200
|
+
API routes are matched first; static files fall through when no API route
|
|
201
|
+
handles the request.
|
|
202
|
+
|
|
203
|
+
> **Note:** `srvx -s` serves files as-is. It has **no SPA history fallback**
|
|
204
|
+
> (deep-linking or reloading a client-side route returns `404`) and sets **no
|
|
205
|
+
> `Cache-Control`/`ETag`** — content-hashed assets are not marked `immutable`
|
|
206
|
+
> and there is no `304` revalidation. For a client-routed SPA in production,
|
|
207
|
+
> front it with a static server or CDN that handles the history fallback and
|
|
208
|
+
> caching. See the reference `Caddyfile` in
|
|
209
|
+
> [`examples/vite-plugin-tapi-demo`](https://github.com/farbenmeer/tapi/tree/main/examples/vite-plugin-tapi-demo).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiHandler.d.ts","sourceRoot":"","sources":["../src/ApiHandler.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC"}
|
|
File without changes
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Connect } from "vite";
|
|
2
|
+
import type { ApiHandler } from "./ApiHandler.js";
|
|
3
|
+
export declare function createApiMiddleware(getHandler: () => ApiHandler | undefined, basePath: string): Connect.NextHandleFunction;
|
|
4
|
+
//# sourceMappingURL=createApiMiddleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createApiMiddleware.d.ts","sourceRoot":"","sources":["../src/createApiMiddleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,UAAU,GAAG,SAAS,EACxC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAsB5B"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NodeRequest, sendNodeResponse } from "srvx/node";
|
|
2
|
+
export function createApiMiddleware(getHandler, basePath) {
|
|
3
|
+
return async (req, res, next) => {
|
|
4
|
+
try {
|
|
5
|
+
const handler = getHandler();
|
|
6
|
+
if (!handler)
|
|
7
|
+
return next();
|
|
8
|
+
const url = req.url ?? "/";
|
|
9
|
+
if (basePath && !url.startsWith(basePath))
|
|
10
|
+
return next();
|
|
11
|
+
const webReq = new NodeRequest({ req, res });
|
|
12
|
+
const webRes = await handler(webReq);
|
|
13
|
+
// When there's no basePath restriction, fall through to Vite on 404
|
|
14
|
+
// so static files, HMR, etc. are still served correctly.
|
|
15
|
+
if (webRes.status === 404 && !basePath)
|
|
16
|
+
return next();
|
|
17
|
+
await sendNodeResponse(res, webRes);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.error(`[vite-plugin-tapi]`, error);
|
|
21
|
+
next();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
export interface TapiPluginOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Path to the file that exports `api` (a TApi `ApiDefinition`).
|
|
5
|
+
* Resolved against the Vite root. Default: `"src/api.ts"`.
|
|
6
|
+
*/
|
|
7
|
+
entry?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Base path passed to `createRequestHandler`. Default: `"/api"`.
|
|
10
|
+
*/
|
|
11
|
+
basePath?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Default port for Vite's dev/preview server. Falls back to the `PORT`
|
|
14
|
+
* environment variable when set.
|
|
15
|
+
*/
|
|
16
|
+
port?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Packages to keep external in the server bundle.
|
|
19
|
+
* Passed directly to `rolldownOptions.external`.
|
|
20
|
+
* Default: `[]` — everything is bundled.
|
|
21
|
+
*/
|
|
22
|
+
external?: (string | RegExp)[];
|
|
23
|
+
}
|
|
24
|
+
export default function tapi(options?: TapiPluginOptions): Plugin;
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAKnC,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;CAChC;AAED,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CA4LpE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { createRequestHandler } from "@toapi/server";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { loadEnv } from "vite";
|
|
4
|
+
import { createApiMiddleware } from "./createApiMiddleware.js";
|
|
5
|
+
export default function tapi(options = {}) {
|
|
6
|
+
const entryOption = options.entry ?? "src/api.ts";
|
|
7
|
+
const basePath = options.basePath ?? "/api";
|
|
8
|
+
let userOutDirBase = "dist";
|
|
9
|
+
let resolvedEntry = "";
|
|
10
|
+
let resolvedRoot = "";
|
|
11
|
+
let serverOutDir = "";
|
|
12
|
+
let isBuildCommand = false;
|
|
13
|
+
let currentHandler;
|
|
14
|
+
return {
|
|
15
|
+
name: "vite-plugin-tapi",
|
|
16
|
+
config(userConfig, _env) {
|
|
17
|
+
// Capture the user's outDir BEFORE we override it, so we can compute
|
|
18
|
+
// the parallel server output directory in later hooks.
|
|
19
|
+
userOutDirBase = userConfig.build?.outDir ?? "dist";
|
|
20
|
+
const port = options.port ??
|
|
21
|
+
(process.env.PORT ? Number(process.env.PORT) : undefined);
|
|
22
|
+
// Always redirect the client build to <outDir>/client so server code
|
|
23
|
+
// (written by closeBundle into <outDir>/server) cannot leak into the
|
|
24
|
+
// static hosting deploy.
|
|
25
|
+
return {
|
|
26
|
+
build: { outDir: path.join(userOutDirBase, "client") },
|
|
27
|
+
...(port ? { server: { port }, preview: { port } } : {}),
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
configResolved(config) {
|
|
31
|
+
resolvedEntry = path.isAbsolute(entryOption)
|
|
32
|
+
? entryOption
|
|
33
|
+
: path.resolve(config.root, entryOption);
|
|
34
|
+
resolvedRoot = config.root;
|
|
35
|
+
serverOutDir = path.resolve(config.root, userOutDirBase);
|
|
36
|
+
isBuildCommand = config.command === "build";
|
|
37
|
+
},
|
|
38
|
+
async configureServer(vite) {
|
|
39
|
+
// Vite injects .env values into `import.meta.env` for the client only;
|
|
40
|
+
// the api runs in this same Node process and reads `process.env`. Mirror
|
|
41
|
+
// every var (empty prefix, not just VITE_*) into process.env so server
|
|
42
|
+
// code sees secrets like BETTER_AUTH_SECRET. Existing process.env wins.
|
|
43
|
+
const env = loadEnv(vite.config.mode, vite.config.envDir, "");
|
|
44
|
+
for (const [k, v] of Object.entries(env)) {
|
|
45
|
+
if (process.env[k] === undefined)
|
|
46
|
+
process.env[k] = v;
|
|
47
|
+
}
|
|
48
|
+
const loadHandler = async () => {
|
|
49
|
+
const mod = (await vite.ssrLoadModule(resolvedEntry));
|
|
50
|
+
if (!mod.api) {
|
|
51
|
+
throw new Error(`[vite-plugin-tapi] ${resolvedEntry} must export \`api\` (an ApiDefinition).`);
|
|
52
|
+
}
|
|
53
|
+
if (!mod.api.logger) {
|
|
54
|
+
mod.api.logger = {
|
|
55
|
+
error: (error) => {
|
|
56
|
+
if (error instanceof Error)
|
|
57
|
+
vite.ssrFixStacktrace(error);
|
|
58
|
+
console.error(error);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
currentHandler = createRequestHandler(mod.api, {
|
|
63
|
+
basePath,
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
try {
|
|
67
|
+
await loadHandler();
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
if (err instanceof Error)
|
|
71
|
+
vite.ssrFixStacktrace(err);
|
|
72
|
+
console.error("[vite-plugin-tapi] initial load failed:", err);
|
|
73
|
+
}
|
|
74
|
+
vite.middlewares.use(createApiMiddleware(() => currentHandler, basePath));
|
|
75
|
+
const onChange = async () => {
|
|
76
|
+
vite.moduleGraph.invalidateAll();
|
|
77
|
+
try {
|
|
78
|
+
await loadHandler();
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (err instanceof Error)
|
|
82
|
+
vite.ssrFixStacktrace(err);
|
|
83
|
+
console.error("[vite-plugin-tapi] reload failed:", err);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
vite.watcher.on("change", onChange);
|
|
87
|
+
vite.watcher.on("add", onChange);
|
|
88
|
+
vite.watcher.on("unlink", onChange);
|
|
89
|
+
},
|
|
90
|
+
async configurePreviewServer(previewServer) {
|
|
91
|
+
// Same rationale as configureServer: mirror .env values into process.env
|
|
92
|
+
// so the bundled server (running in this same Node process) can read
|
|
93
|
+
// secrets via process.env. Existing process.env wins.
|
|
94
|
+
const env = loadEnv(previewServer.config.mode, previewServer.config.envDir, "");
|
|
95
|
+
for (const [k, v] of Object.entries(env)) {
|
|
96
|
+
if (process.env[k] === undefined)
|
|
97
|
+
process.env[k] = v;
|
|
98
|
+
}
|
|
99
|
+
const serverJsPath = path.join(path.resolve(previewServer.config.root, userOutDirBase), "server.js");
|
|
100
|
+
let fetchHandler;
|
|
101
|
+
try {
|
|
102
|
+
const mod = (await import(serverJsPath));
|
|
103
|
+
fetchHandler = mod.default?.fetch ?? mod.fetch;
|
|
104
|
+
if (!fetchHandler) {
|
|
105
|
+
console.warn("[vite-plugin-tapi] dist/server/server.js has no fetch export — run `vite build` first.");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
console.error("[vite-plugin-tapi] failed to import dist/server/server.js for preview:", err);
|
|
110
|
+
}
|
|
111
|
+
if (!fetchHandler)
|
|
112
|
+
return;
|
|
113
|
+
previewServer.middlewares.use(createApiMiddleware(() => fetchHandler, basePath));
|
|
114
|
+
},
|
|
115
|
+
async closeBundle() {
|
|
116
|
+
if (!isBuildCommand)
|
|
117
|
+
return;
|
|
118
|
+
const VIRTUAL_SERVER_ID = "virtual:tapi-server";
|
|
119
|
+
const RESOLVED_VIRTUAL_SERVER_ID = "\0" + VIRTUAL_SERVER_ID;
|
|
120
|
+
const inlinePlugin = () => ({
|
|
121
|
+
name: "tapi-server-virtual",
|
|
122
|
+
resolveId(id) {
|
|
123
|
+
return id === VIRTUAL_SERVER_ID ? RESOLVED_VIRTUAL_SERVER_ID : null;
|
|
124
|
+
},
|
|
125
|
+
load(id) {
|
|
126
|
+
if (id !== RESOLVED_VIRTUAL_SERVER_ID)
|
|
127
|
+
return null;
|
|
128
|
+
return [
|
|
129
|
+
`import { createRequestHandler } from "@toapi/server";`,
|
|
130
|
+
`import { api } from ${JSON.stringify(resolvedEntry)};`,
|
|
131
|
+
``,
|
|
132
|
+
`export const fetch = createRequestHandler(api, { basePath: ${JSON.stringify(basePath)} });`,
|
|
133
|
+
`export default { fetch };`,
|
|
134
|
+
].join("\n");
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
const { build } = await import("vite");
|
|
138
|
+
await build({
|
|
139
|
+
root: resolvedRoot,
|
|
140
|
+
configFile: false,
|
|
141
|
+
logLevel: "silent",
|
|
142
|
+
plugins: [inlinePlugin()],
|
|
143
|
+
// noExternal: true so node_modules are bundled by default;
|
|
144
|
+
// only packages in options.external are kept external.
|
|
145
|
+
ssr: { noExternal: true },
|
|
146
|
+
build: {
|
|
147
|
+
ssr: true,
|
|
148
|
+
outDir: serverOutDir,
|
|
149
|
+
emptyOutDir: false,
|
|
150
|
+
sourcemap: true,
|
|
151
|
+
rolldownOptions: {
|
|
152
|
+
input: { server: VIRTUAL_SERVER_ID },
|
|
153
|
+
output: {
|
|
154
|
+
format: "esm",
|
|
155
|
+
entryFileNames: "[name].js",
|
|
156
|
+
},
|
|
157
|
+
external: options.external ?? [],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toapi/vite-plugin",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "Michel Smola",
|
|
6
|
+
"email": "michel.smola@farbenmeer.de"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"module": "dist/index.js",
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"private": false,
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"srvx": "^0.11.15"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"typescript": "^5 || ^6.0.0",
|
|
27
|
+
"vite": "^8.0.0",
|
|
28
|
+
"@toapi/server": "^0.12.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^25.0.3",
|
|
32
|
+
"vite": "^8.0.9",
|
|
33
|
+
"@toapi/server": "^0.12.1"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/farbenmeer/tapi.git"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsc --noEmit false",
|
|
41
|
+
"release": "pnpm build && pnpm publish --no-git-checks"
|
|
42
|
+
}
|
|
43
|
+
}
|