@lukoweb/apitogo 0.1.15 → 0.1.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
package/src/vite/build.ts CHANGED
@@ -1,237 +1,237 @@
1
- import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { build as esbuild } from "esbuild";
4
- import type { Rollup } from "vite";
5
- import { build as viteBuild, mergeConfig } from "vite";
6
- import { ZuploEnv } from "../app/env.js";
7
- import { getZudokuRootDir } from "../cli/common/package-json.js";
8
- import {
9
- findOutputPathOfServerConfig,
10
- loadZudokuConfig,
11
- } from "../config/loader.js";
12
- import { getIssuer } from "../lib/auth/issuer.js";
13
- import invariant from "../lib/util/invariant.js";
14
- import { joinUrl } from "../lib/util/joinUrl.js";
15
- import { getViteConfig } from "./config.js";
16
- import { getBuildHtml } from "./html.js";
17
- import { writeOutput } from "./output.js";
18
- import { prerender } from "./prerender/prerender.js";
19
-
20
- const DIST_DIR = "dist";
21
-
22
- const extractAssets = (result: Rollup.RollupOutput) => {
23
- const jsEntry = result.output.find(
24
- (o) => "isEntry" in o && o.isEntry,
25
- )?.fileName;
26
- const cssEntries = result.output
27
- .filter((o) => o.fileName.endsWith(".css"))
28
- .map((o) => o.fileName);
29
-
30
- if (!jsEntry || cssEntries.length === 0) {
31
- throw new Error("Build failed. No js or css assets found");
32
- }
33
-
34
- return { jsEntry, cssEntries };
35
- };
36
-
37
- export type BuildOptions = {
38
- dir: string;
39
- ssr?: boolean;
40
- adapter?: "node" | "cloudflare" | "vercel";
41
- /** Suppress Vite build table and most build logs (used with `apitogo deploy --json-only`). */
42
- jsonOnly?: boolean;
43
- };
44
-
45
- export async function runBuild(options: BuildOptions) {
46
- const { dir, ssr, adapter = "node", jsonOnly = false } = options;
47
-
48
- // Build client and server bundles
49
- const viteClientConfig = await getViteConfig(dir, {
50
- mode: "production",
51
- command: "build",
52
- });
53
- const viteServerConfig = await getViteConfig(dir, {
54
- mode: "production",
55
- command: "build",
56
- isSsrBuild: true,
57
- });
58
-
59
- const clientResult = await viteBuild(
60
- jsonOnly
61
- ? mergeConfig(viteClientConfig, { logLevel: "silent" as const })
62
- : viteClientConfig,
63
- );
64
- const serverResult = await viteBuild({
65
- ...viteServerConfig,
66
- logLevel: "silent",
67
- });
68
-
69
- if (Array.isArray(clientResult) || !("output" in clientResult)) {
70
- throw new Error("Client build failed");
71
- }
72
- if (Array.isArray(serverResult) || !("output" in serverResult)) {
73
- throw new Error("Server build failed");
74
- }
75
-
76
- const { config } = await loadZudokuConfig(
77
- { mode: "production", command: "build" },
78
- dir,
79
- );
80
-
81
- const { jsEntry, cssEntries } = extractAssets(clientResult);
82
-
83
- const html = getBuildHtml({
84
- jsEntry: joinUrl(viteClientConfig.base, jsEntry),
85
- cssEntries: cssEntries.map((css) => joinUrl(viteClientConfig.base, css)),
86
- dir: config.site?.dir,
87
- });
88
-
89
- invariant(viteClientConfig.build?.outDir, "Client build outDir is missing");
90
- invariant(viteServerConfig.build?.outDir, "Server build outDir is missing");
91
-
92
- const clientOutDir = viteClientConfig.build.outDir;
93
- const serverOutDir = viteServerConfig.build.outDir;
94
- const serverConfigFilename = findOutputPathOfServerConfig(serverResult);
95
-
96
- if (ssr) {
97
- // SSR: bundle entry.js and remove index.html
98
- await bundleSSREntry({
99
- dir,
100
- adapter,
101
- serverOutDir,
102
- serverConfigFilename,
103
- html,
104
- basePath: config.basePath,
105
- });
106
- await rm(path.join(clientOutDir, "index.html"), { force: true });
107
- } else {
108
- // SSG: prerender and clean up server
109
- await runPrerender({
110
- dir,
111
- config,
112
- html,
113
- clientOutDir,
114
- serverOutDir,
115
- serverResult,
116
- });
117
- }
118
- }
119
-
120
- type PrerenderOptions = {
121
- dir: string;
122
- config: Awaited<ReturnType<typeof loadZudokuConfig>>["config"];
123
- html: string;
124
- clientOutDir: string;
125
- serverOutDir: string;
126
- serverResult: Rollup.RollupOutput;
127
- };
128
-
129
- const runPrerender = async (options: PrerenderOptions) => {
130
- const { dir, config, html, clientOutDir, serverOutDir, serverResult } =
131
- options;
132
- const issuer = await getIssuer(config);
133
- const serverConfigFilename = findOutputPathOfServerConfig(serverResult);
134
-
135
- try {
136
- const { workerResults, rewrites } = await prerender({
137
- html,
138
- dir,
139
- basePath: config.basePath,
140
- serverConfigFilename,
141
- writeRedirects: process.env.VERCEL === undefined,
142
- });
143
-
144
- const indexHtml = path.join(clientOutDir, "index.html");
145
- if (!workerResults.find((r) => r.outputPath === indexHtml)) {
146
- await writeFile(indexHtml, html, "utf-8");
147
- }
148
-
149
- // Move status pages (400, 404, 500) to root path
150
- const statusPages = workerResults.flatMap((r) =>
151
- /400|404|500\.html$/.test(r.outputPath) ? r.outputPath : [],
152
- );
153
- for (const statusPage of statusPages) {
154
- await rename(
155
- statusPage,
156
- path.join(dir, DIST_DIR, path.basename(statusPage)),
157
- );
158
- }
159
-
160
- // Delete server build - not needed after prerender
161
- await rm(serverOutDir, { recursive: true, force: true });
162
-
163
- if (process.env.VERCEL) {
164
- await mkdir(path.join(dir, ".vercel/output/static"), { recursive: true });
165
- await rename(
166
- path.join(dir, DIST_DIR),
167
- path.join(dir, ".vercel/output/static"),
168
- );
169
- }
170
-
171
- await writeOutput(dir, {
172
- config,
173
- redirects: workerResults.flatMap((r) => r.redirect ?? []),
174
- rewrites,
175
- });
176
-
177
- if (ZuploEnv.isZuplo && issuer) {
178
- await writeFile(
179
- path.join(dir, DIST_DIR, ".output/zuplo.json"),
180
- JSON.stringify({ issuer }, null, 2),
181
- "utf-8",
182
- );
183
- }
184
- } catch (e) {
185
- // biome-ignore lint/suspicious/noConsole: Logging allowed here
186
- console.error(e);
187
- throw e;
188
- }
189
- };
190
-
191
- type SSREntryOptions = {
192
- dir: string;
193
- adapter: "node" | "cloudflare" | "vercel";
194
- serverOutDir: string;
195
- serverConfigFilename: string;
196
- html: string;
197
- basePath?: string;
198
- };
199
-
200
- const bundleSSREntry = async (options: SSREntryOptions) => {
201
- const { dir, adapter, serverOutDir, serverConfigFilename, html, basePath } =
202
- options;
203
- const tempEntryPath = path.join(dir, "__ssr-entry.ts");
204
-
205
- const packageRoot = getZudokuRootDir();
206
-
207
- const templateContent = await readFile(
208
- path.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
209
- "utf-8",
210
- );
211
-
212
- const entryContent = templateContent
213
- .replace('"__TEMPLATE__"', JSON.stringify(html))
214
- .replace('"__CONFIG_FILE__"', JSON.stringify(`./${serverConfigFilename}`))
215
- .replace(
216
- '"__BASE_PATH__"',
217
- basePath ? JSON.stringify(basePath) : "undefined",
218
- );
219
-
220
- await writeFile(tempEntryPath, entryContent, "utf-8");
221
-
222
- try {
223
- await esbuild({
224
- entryPoints: [tempEntryPath],
225
- bundle: true,
226
- platform: adapter === "node" ? "node" : "neutral",
227
- target: "es2022",
228
- format: "esm",
229
- outfile: path.join(serverOutDir, "entry.js"),
230
- external: ["./entry.server.js", `./${serverConfigFilename}`],
231
- nodePaths: [path.join(packageRoot, "node_modules")],
232
- banner: { js: "// Bundled SSR entry" },
233
- });
234
- } finally {
235
- await rm(tempEntryPath, { force: true });
236
- }
237
- };
1
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { build as esbuild } from "esbuild";
4
+ import type { Rollup } from "vite";
5
+ import { build as viteBuild, mergeConfig } from "vite";
6
+ import { ZuploEnv } from "../app/env.js";
7
+ import { getZudokuRootDir } from "../cli/common/package-json.js";
8
+ import {
9
+ findOutputPathOfServerConfig,
10
+ loadZudokuConfig,
11
+ } from "../config/loader.js";
12
+ import { getIssuer } from "../lib/auth/issuer.js";
13
+ import invariant from "../lib/util/invariant.js";
14
+ import { joinUrl } from "../lib/util/joinUrl.js";
15
+ import { getViteConfig } from "./config.js";
16
+ import { getBuildHtml } from "./html.js";
17
+ import { writeOutput } from "./output.js";
18
+ import { prerender } from "./prerender/prerender.js";
19
+
20
+ const DIST_DIR = "dist";
21
+
22
+ const extractAssets = (result: Rollup.RollupOutput) => {
23
+ const jsEntry = result.output.find(
24
+ (o) => "isEntry" in o && o.isEntry,
25
+ )?.fileName;
26
+ const cssEntries = result.output
27
+ .filter((o) => o.fileName.endsWith(".css"))
28
+ .map((o) => o.fileName);
29
+
30
+ if (!jsEntry || cssEntries.length === 0) {
31
+ throw new Error("Build failed. No js or css assets found");
32
+ }
33
+
34
+ return { jsEntry, cssEntries };
35
+ };
36
+
37
+ export type BuildOptions = {
38
+ dir: string;
39
+ ssr?: boolean;
40
+ adapter?: "node" | "cloudflare" | "vercel";
41
+ /** Suppress Vite build table and most build logs (used with `apitogo deploy --json-only`). */
42
+ jsonOnly?: boolean;
43
+ };
44
+
45
+ export async function runBuild(options: BuildOptions) {
46
+ const { dir, ssr, adapter = "node", jsonOnly = false } = options;
47
+
48
+ // Build client and server bundles
49
+ const viteClientConfig = await getViteConfig(dir, {
50
+ mode: "production",
51
+ command: "build",
52
+ });
53
+ const viteServerConfig = await getViteConfig(dir, {
54
+ mode: "production",
55
+ command: "build",
56
+ isSsrBuild: true,
57
+ });
58
+
59
+ const clientResult = await viteBuild(
60
+ jsonOnly
61
+ ? mergeConfig(viteClientConfig, { logLevel: "silent" as const })
62
+ : viteClientConfig,
63
+ );
64
+ const serverResult = await viteBuild({
65
+ ...viteServerConfig,
66
+ logLevel: "silent",
67
+ });
68
+
69
+ if (Array.isArray(clientResult) || !("output" in clientResult)) {
70
+ throw new Error("Client build failed");
71
+ }
72
+ if (Array.isArray(serverResult) || !("output" in serverResult)) {
73
+ throw new Error("Server build failed");
74
+ }
75
+
76
+ const { config } = await loadZudokuConfig(
77
+ { mode: "production", command: "build" },
78
+ dir,
79
+ );
80
+
81
+ const { jsEntry, cssEntries } = extractAssets(clientResult);
82
+
83
+ const html = getBuildHtml({
84
+ jsEntry: joinUrl(viteClientConfig.base, jsEntry),
85
+ cssEntries: cssEntries.map((css) => joinUrl(viteClientConfig.base, css)),
86
+ dir: config.site?.dir,
87
+ });
88
+
89
+ invariant(viteClientConfig.build?.outDir, "Client build outDir is missing");
90
+ invariant(viteServerConfig.build?.outDir, "Server build outDir is missing");
91
+
92
+ const clientOutDir = viteClientConfig.build.outDir;
93
+ const serverOutDir = viteServerConfig.build.outDir;
94
+ const serverConfigFilename = findOutputPathOfServerConfig(serverResult);
95
+
96
+ if (ssr) {
97
+ // SSR: bundle entry.js and remove index.html
98
+ await bundleSSREntry({
99
+ dir,
100
+ adapter,
101
+ serverOutDir,
102
+ serverConfigFilename,
103
+ html,
104
+ basePath: config.basePath,
105
+ });
106
+ await rm(path.join(clientOutDir, "index.html"), { force: true });
107
+ } else {
108
+ // SSG: prerender and clean up server
109
+ await runPrerender({
110
+ dir,
111
+ config,
112
+ html,
113
+ clientOutDir,
114
+ serverOutDir,
115
+ serverResult,
116
+ });
117
+ }
118
+ }
119
+
120
+ type PrerenderOptions = {
121
+ dir: string;
122
+ config: Awaited<ReturnType<typeof loadZudokuConfig>>["config"];
123
+ html: string;
124
+ clientOutDir: string;
125
+ serverOutDir: string;
126
+ serverResult: Rollup.RollupOutput;
127
+ };
128
+
129
+ const runPrerender = async (options: PrerenderOptions) => {
130
+ const { dir, config, html, clientOutDir, serverOutDir, serverResult } =
131
+ options;
132
+ const issuer = await getIssuer(config);
133
+ const serverConfigFilename = findOutputPathOfServerConfig(serverResult);
134
+
135
+ try {
136
+ const { workerResults, rewrites } = await prerender({
137
+ html,
138
+ dir,
139
+ basePath: config.basePath,
140
+ serverConfigFilename,
141
+ writeRedirects: process.env.VERCEL === undefined,
142
+ });
143
+
144
+ const indexHtml = path.join(clientOutDir, "index.html");
145
+ if (!workerResults.find((r) => r.outputPath === indexHtml)) {
146
+ await writeFile(indexHtml, html, "utf-8");
147
+ }
148
+
149
+ // Move status pages (400, 404, 500) to root path
150
+ const statusPages = workerResults.flatMap((r) =>
151
+ /400|404|500\.html$/.test(r.outputPath) ? r.outputPath : [],
152
+ );
153
+ for (const statusPage of statusPages) {
154
+ await rename(
155
+ statusPage,
156
+ path.join(dir, DIST_DIR, path.basename(statusPage)),
157
+ );
158
+ }
159
+
160
+ // Delete server build - not needed after prerender
161
+ await rm(serverOutDir, { recursive: true, force: true });
162
+
163
+ if (process.env.VERCEL) {
164
+ await mkdir(path.join(dir, ".vercel/output/static"), { recursive: true });
165
+ await rename(
166
+ path.join(dir, DIST_DIR),
167
+ path.join(dir, ".vercel/output/static"),
168
+ );
169
+ }
170
+
171
+ await writeOutput(dir, {
172
+ config,
173
+ redirects: workerResults.flatMap((r) => r.redirect ?? []),
174
+ rewrites,
175
+ });
176
+
177
+ if (ZuploEnv.isZuplo && issuer) {
178
+ await writeFile(
179
+ path.join(dir, DIST_DIR, ".output/zuplo.json"),
180
+ JSON.stringify({ issuer }, null, 2),
181
+ "utf-8",
182
+ );
183
+ }
184
+ } catch (e) {
185
+ // biome-ignore lint/suspicious/noConsole: Logging allowed here
186
+ console.error(e);
187
+ throw e;
188
+ }
189
+ };
190
+
191
+ type SSREntryOptions = {
192
+ dir: string;
193
+ adapter: "node" | "cloudflare" | "vercel";
194
+ serverOutDir: string;
195
+ serverConfigFilename: string;
196
+ html: string;
197
+ basePath?: string;
198
+ };
199
+
200
+ const bundleSSREntry = async (options: SSREntryOptions) => {
201
+ const { dir, adapter, serverOutDir, serverConfigFilename, html, basePath } =
202
+ options;
203
+ const tempEntryPath = path.join(dir, "__ssr-entry.ts");
204
+
205
+ const packageRoot = getZudokuRootDir();
206
+
207
+ const templateContent = await readFile(
208
+ path.join(packageRoot, "src/vite/ssr-templates", `${adapter}.ts`),
209
+ "utf-8",
210
+ );
211
+
212
+ const entryContent = templateContent
213
+ .replace('"__TEMPLATE__"', JSON.stringify(html))
214
+ .replace('"__CONFIG_FILE__"', JSON.stringify(`./${serverConfigFilename}`))
215
+ .replace(
216
+ '"__BASE_PATH__"',
217
+ basePath ? JSON.stringify(basePath) : "undefined",
218
+ );
219
+
220
+ await writeFile(tempEntryPath, entryContent, "utf-8");
221
+
222
+ try {
223
+ await esbuild({
224
+ entryPoints: [tempEntryPath],
225
+ bundle: true,
226
+ platform: adapter === "node" ? "node" : "neutral",
227
+ target: "es2022",
228
+ format: "esm",
229
+ outfile: path.join(serverOutDir, "entry.js"),
230
+ external: ["./entry.server.js", `./${serverConfigFilename}`],
231
+ nodePaths: [path.join(packageRoot, "node_modules")],
232
+ banner: { js: "// Bundled SSR entry" },
233
+ });
234
+ } finally {
235
+ await rm(tempEntryPath, { force: true });
236
+ }
237
+ };