@sigil-dev/grimoire 0.7.5 → 0.7.6
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/.grimoire/_routes.dom.js +8 -0
- package/.grimoire/_routes.hydrate.js +8 -0
- package/.grimoire/tsconfig.generated.json +11 -0
- package/.grimoire/types/ambient.d.ts +59 -0
- package/.grimoire/types/api/hello/$types.d.ts +50 -0
- package/.grimoire/types/api/items/$types.d.ts +50 -0
- package/.grimoire/types/echo/$types.d.ts +50 -0
- package/.grimoire/types/env-private.d.ts +5 -0
- package/.grimoire/types/env-public.d.ts +5 -0
- package/.grimoire/types/mixed/$types.d.ts +50 -0
- package/.grimoire/types/params/[docId]/$types.d.ts +52 -0
- package/.grimoire/types/reject/$types.d.ts +50 -0
- package/index.ts +34 -34
- package/package.json +8 -4
- package/preload.js +2 -0
- package/public/__grimoire__/hydrate.js +585 -0
- package/public/__grimoire__/index.js +490 -0
- package/src/client/head.ts +29 -0
- package/src/client/router.ts +224 -76
- package/src/env/index.ts +25 -0
- package/src/env/plugin.ts +13 -0
- package/src/env/private.ts +5 -0
- package/src/env/public.ts +7 -0
- package/src/env/typegen.ts +51 -0
- package/src/integrations/vite.ts +72 -72
- package/src/rendering/head.ts +22 -2
- package/src/rendering/hydrate.ts +81 -27
- package/src/rendering/index.ts +199 -186
- package/src/rendering/ssrPlugin.ts +53 -47
- package/src/routing/manifest-gen.ts +39 -26
- package/src/routing/router.ts +106 -98
- package/src/routing/scanner.ts +135 -129
- package/src/routing/transform-routes.ts +101 -101
- package/src/server/build.ts +147 -90
- package/src/server/coordinator.ts +306 -297
- package/src/server/hooks.ts +24 -3
- package/src/server/index.ts +144 -70
- package/src/server/worker.ts +59 -59
- package/src/typegen/index.ts +353 -340
- package/src/types.ts +269 -260
- package/test/context.test.ts +52 -52
- package/test/hydration.test.ts +119 -119
- package/test/middleware.test.ts +223 -221
- package/test/rendering.test.ts +425 -425
- package/test/routing.test.ts +83 -45
- package/test/scanning.test.ts +181 -169
- package/test/server.test.ts +229 -229
- package/test/streaming.test.ts +106 -106
- package/test/transform-routes.test.ts +84 -84
- package/test/typegen.test.ts +19 -1
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
import { transformSync } from "@babel/core";
|
|
2
|
-
import sigilPlugin from "@sigil-dev/compiler/babel";
|
|
3
|
-
import { basename, dirname, join, relative, resolve } from "path";
|
|
4
|
-
import type { GrimoirePlugin } from "../types";
|
|
5
|
-
import type { RouteFile } from "./scanner";
|
|
6
|
-
|
|
7
|
-
const STYLE_RE = /<style[^>]*>([\s\S]*?)<\/style>/gi;
|
|
8
|
-
|
|
9
|
-
// Inline the same hash helper used in bun-plugin.ts to avoid importing
|
|
10
|
-
// unexported internals from @sigil-dev/compiler.
|
|
11
|
-
function computeHash(filePath: string): string {
|
|
12
|
-
let h = 0x811c9dc5;
|
|
13
|
-
for (let i = 0; i < filePath.length; i++) {
|
|
14
|
-
h ^= filePath.charCodeAt(i);
|
|
15
|
-
h = (h * 0x01000193) >>> 0;
|
|
16
|
-
}
|
|
17
|
-
return h.toString(36);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function rewriteRelativeImports(filePath: string) {
|
|
21
|
-
const basedir = dirname(filePath);
|
|
22
|
-
const rewrite = (source?: { value: string }) => {
|
|
23
|
-
if (!source?.value.startsWith(".")) return;
|
|
24
|
-
source.value = resolve(basedir, source.value);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
name: "sigil-rewrite-relative-route-imports",
|
|
29
|
-
visitor: {
|
|
30
|
-
ImportDeclaration(path: any) {
|
|
31
|
-
rewrite(path.node.source);
|
|
32
|
-
},
|
|
33
|
-
ExportNamedDeclaration(path: any) {
|
|
34
|
-
rewrite(path.node.source);
|
|
35
|
-
},
|
|
36
|
-
ExportAllDeclaration(path: any) {
|
|
37
|
-
rewrite(path.node.source);
|
|
38
|
-
},
|
|
39
|
-
CallExpression(path: any) {
|
|
40
|
-
if (path.node.callee.type !== "Import") return;
|
|
41
|
-
rewrite(path.node.arguments[0]);
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Pre-transforms route .tsx/.jsx files to plain JS using the Babel sigil
|
|
49
|
-
* pipeline, writing the output to `outDir`. Returns a Map from the original
|
|
50
|
-
* filePath to the compiled .js path.
|
|
51
|
-
*
|
|
52
|
-
* Workaround for Bun 1.3.13: Bun.build's native parser runs before onLoad
|
|
53
|
-
* hooks fire, crashing on TypeScript syntax. Pre-transforming to JS means
|
|
54
|
-
* Bun.build only ever sees plain JavaScript.
|
|
55
|
-
*/
|
|
56
|
-
export async function transformRoutes(
|
|
57
|
-
routes: RouteFile[],
|
|
58
|
-
outDir: string,
|
|
59
|
-
mode: "hydrate" | "dom",
|
|
60
|
-
plugins: GrimoirePlugin[] = [],
|
|
61
|
-
): Promise<Map<string, string>> {
|
|
62
|
-
const map = new Map<string, string>();
|
|
63
|
-
// loader: "ts" — Babel already consumed JSX, Bun only strips remaining types
|
|
64
|
-
const transpiler = new Bun.Transpiler({ loader: "ts", target: "browser" });
|
|
65
|
-
|
|
66
|
-
await Promise.all(
|
|
67
|
-
routes.map(async (route, index) => {
|
|
68
|
-
let code = await Bun.file(route.filePath).text();
|
|
69
|
-
const hash = computeHash(route.filePath);
|
|
70
|
-
code = code.replace(STYLE_RE, "");
|
|
71
|
-
|
|
72
|
-
const res = transformSync(code, {
|
|
73
|
-
configFile: false,
|
|
74
|
-
babelrc: false,
|
|
75
|
-
parserOpts: {
|
|
76
|
-
plugins: ["typescript", "jsx"], // no isTSX
|
|
77
|
-
},
|
|
78
|
-
plugins: [
|
|
79
|
-
[sigilPlugin, { hash, mode }],
|
|
80
|
-
rewriteRelativeImports(route.filePath),
|
|
81
|
-
],
|
|
82
|
-
filename: route.filePath,
|
|
83
|
-
});
|
|
84
|
-
let out = transpiler.transformSync(res?.code ?? "");
|
|
85
|
-
for (const plugin of plugins) {
|
|
86
|
-
if (plugin.transform)
|
|
87
|
-
out = (await plugin.transform(out, route.filePath)) ?? out;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const rel = relative(process.cwd(), route.filePath)
|
|
91
|
-
.replace(/\.tsx?$/, "")
|
|
92
|
-
.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
93
|
-
const nameBase = rel || basename(route.filePath).replace(/\.[jt]sx$/, "");
|
|
94
|
-
const outPath = join(outDir, `${index}-${nameBase}.${mode}.js`);
|
|
95
|
-
|
|
96
|
-
await Bun.write(outPath, out);
|
|
97
|
-
map.set(route.filePath, outPath);
|
|
98
|
-
}),
|
|
99
|
-
);
|
|
100
|
-
return map;
|
|
101
|
-
}
|
|
1
|
+
import { transformSync } from "@babel/core";
|
|
2
|
+
import sigilPlugin from "@sigil-dev/compiler/babel";
|
|
3
|
+
import { basename, dirname, join, relative, resolve } from "path";
|
|
4
|
+
import type { GrimoirePlugin } from "../types";
|
|
5
|
+
import type { RouteFile } from "./scanner";
|
|
6
|
+
|
|
7
|
+
const STYLE_RE = /<style[^>]*>([\s\S]*?)<\/style>/gi;
|
|
8
|
+
|
|
9
|
+
// Inline the same hash helper used in bun-plugin.ts to avoid importing
|
|
10
|
+
// unexported internals from @sigil-dev/compiler.
|
|
11
|
+
function computeHash(filePath: string): string {
|
|
12
|
+
let h = 0x811c9dc5;
|
|
13
|
+
for (let i = 0; i < filePath.length; i++) {
|
|
14
|
+
h ^= filePath.charCodeAt(i);
|
|
15
|
+
h = (h * 0x01000193) >>> 0;
|
|
16
|
+
}
|
|
17
|
+
return h.toString(36);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function rewriteRelativeImports(filePath: string) {
|
|
21
|
+
const basedir = dirname(filePath);
|
|
22
|
+
const rewrite = (source?: { value: string }) => {
|
|
23
|
+
if (!source?.value.startsWith(".")) return;
|
|
24
|
+
source.value = resolve(basedir, source.value);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
name: "sigil-rewrite-relative-route-imports",
|
|
29
|
+
visitor: {
|
|
30
|
+
ImportDeclaration(path: any) {
|
|
31
|
+
rewrite(path.node.source);
|
|
32
|
+
},
|
|
33
|
+
ExportNamedDeclaration(path: any) {
|
|
34
|
+
rewrite(path.node.source);
|
|
35
|
+
},
|
|
36
|
+
ExportAllDeclaration(path: any) {
|
|
37
|
+
rewrite(path.node.source);
|
|
38
|
+
},
|
|
39
|
+
CallExpression(path: any) {
|
|
40
|
+
if (path.node.callee.type !== "Import") return;
|
|
41
|
+
rewrite(path.node.arguments[0]);
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Pre-transforms route .tsx/.jsx files to plain JS using the Babel sigil
|
|
49
|
+
* pipeline, writing the output to `outDir`. Returns a Map from the original
|
|
50
|
+
* filePath to the compiled .js path.
|
|
51
|
+
*
|
|
52
|
+
* Workaround for Bun 1.3.13: Bun.build's native parser runs before onLoad
|
|
53
|
+
* hooks fire, crashing on TypeScript syntax. Pre-transforming to JS means
|
|
54
|
+
* Bun.build only ever sees plain JavaScript.
|
|
55
|
+
*/
|
|
56
|
+
export async function transformRoutes(
|
|
57
|
+
routes: RouteFile[],
|
|
58
|
+
outDir: string,
|
|
59
|
+
mode: "hydrate" | "dom",
|
|
60
|
+
plugins: GrimoirePlugin[] = [],
|
|
61
|
+
): Promise<Map<string, string>> {
|
|
62
|
+
const map = new Map<string, string>();
|
|
63
|
+
// loader: "ts" — Babel already consumed JSX, Bun only strips remaining types
|
|
64
|
+
const transpiler = new Bun.Transpiler({ loader: "ts", target: "browser" });
|
|
65
|
+
|
|
66
|
+
await Promise.all(
|
|
67
|
+
routes.map(async (route, index) => {
|
|
68
|
+
let code = await Bun.file(route.filePath).text();
|
|
69
|
+
const hash = computeHash(route.filePath);
|
|
70
|
+
code = code.replace(STYLE_RE, "");
|
|
71
|
+
|
|
72
|
+
const res = transformSync(code, {
|
|
73
|
+
configFile: false,
|
|
74
|
+
babelrc: false,
|
|
75
|
+
parserOpts: {
|
|
76
|
+
plugins: ["typescript", "jsx"], // no isTSX
|
|
77
|
+
},
|
|
78
|
+
plugins: [
|
|
79
|
+
[sigilPlugin, { hash, mode }],
|
|
80
|
+
rewriteRelativeImports(route.filePath),
|
|
81
|
+
],
|
|
82
|
+
filename: route.filePath,
|
|
83
|
+
});
|
|
84
|
+
let out = transpiler.transformSync(res?.code ?? "");
|
|
85
|
+
for (const plugin of plugins) {
|
|
86
|
+
if (plugin.transform)
|
|
87
|
+
out = (await plugin.transform(out, route.filePath)) ?? out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const rel = relative(process.cwd(), route.filePath)
|
|
91
|
+
.replace(/\.tsx?$/, "")
|
|
92
|
+
.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
93
|
+
const nameBase = rel || basename(route.filePath).replace(/\.[jt]sx$/, "");
|
|
94
|
+
const outPath = join(outDir, `${index}-${nameBase}.${mode}.js`);
|
|
95
|
+
|
|
96
|
+
await Bun.write(outPath, out);
|
|
97
|
+
map.set(route.filePath, outPath);
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
return map;
|
|
101
|
+
}
|
package/src/server/build.ts
CHANGED
|
@@ -1,90 +1,147 @@
|
|
|
1
|
-
import { sigil } from "@sigil-dev/compiler/bun";
|
|
2
|
-
import { mkdir } from "fs/promises";
|
|
3
|
-
import { isAbsolute, join } from "path";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
1
|
+
import { sigil } from "@sigil-dev/compiler/bun";
|
|
2
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
3
|
+
import { isAbsolute, join } from "path";
|
|
4
|
+
import { cwd } from "process";
|
|
5
|
+
import { generateManifest } from "../routing/manifest-gen";
|
|
6
|
+
import type { RouteTree } from "../routing/scanner";
|
|
7
|
+
import { scanRoutes } from "../routing/scanner";
|
|
8
|
+
import { transformRoutes } from "../routing/transform-routes";
|
|
9
|
+
import { generateTypes } from "../typegen";
|
|
10
|
+
import type { BuildResult, GrimoireConfig, GrimoirePlugin } from "../types";
|
|
11
|
+
import { runHook } from "./plugins";
|
|
12
|
+
|
|
13
|
+
export async function buildProject(
|
|
14
|
+
config: GrimoireConfig,
|
|
15
|
+
plugins: GrimoirePlugin[] = [],
|
|
16
|
+
): Promise<{ result: BuildResult; tree: RouteTree }> {
|
|
17
|
+
await runHook(plugins, "onBuildStart");
|
|
18
|
+
|
|
19
|
+
const { routes = "src/routes" } = config;
|
|
20
|
+
|
|
21
|
+
const routesDir = isAbsolute(routes)
|
|
22
|
+
? routes.replace(/\0/g, "")
|
|
23
|
+
: join(process.cwd(), routes).replace(/\0/g, "");
|
|
24
|
+
const tree = await scanRoutes(routesDir, process.cwd());
|
|
25
|
+
|
|
26
|
+
const outDir = join(process.cwd(), ".grimoire");
|
|
27
|
+
await mkdir(outDir, { recursive: true });
|
|
28
|
+
|
|
29
|
+
await generateTypes(tree, {
|
|
30
|
+
projectRoot: process.cwd(),
|
|
31
|
+
routesDir,
|
|
32
|
+
outDir: join(outDir, "types"),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Generate $env type declarations from .env
|
|
36
|
+
const { generateEnvTypes } = await import("../env/typegen");
|
|
37
|
+
const envTypeDir = join(outDir, "types");
|
|
38
|
+
await mkdir(envTypeDir, { recursive: true });
|
|
39
|
+
await generateEnvTypes(process.cwd(), envTypeDir);
|
|
40
|
+
|
|
41
|
+
const compiledDir = join(outDir, "compiled");
|
|
42
|
+
await mkdir(compiledDir, { recursive: true });
|
|
43
|
+
|
|
44
|
+
const clientRoutes = [
|
|
45
|
+
...tree.routes.filter((r) => r.type === "page" || r.type === "simple"),
|
|
46
|
+
...tree.layouts.filter((r) => r.type === "layout"),
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const [hydrateFiles, domFiles] = await Promise.all([
|
|
50
|
+
transformRoutes(clientRoutes, compiledDir, "hydrate", plugins),
|
|
51
|
+
transformRoutes(clientRoutes, compiledDir, "dom", plugins),
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
const hydrateManifest = join(process.cwd(), ".grimoire/_routes.hydrate.js");
|
|
55
|
+
const domManifest = join(process.cwd(), ".grimoire/_routes.dom.js");
|
|
56
|
+
await Promise.all([
|
|
57
|
+
Bun.write(hydrateManifest, generateManifest(clientRoutes, hydrateFiles)),
|
|
58
|
+
Bun.write(domManifest, generateManifest(clientRoutes, domFiles)),
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
const makeRoutesPlugin = (manifestPath: string) => ({
|
|
62
|
+
name: "grimoire-routes",
|
|
63
|
+
setup(build: any) {
|
|
64
|
+
build.onResolve({ filter: /^#grimoire-routes$/ }, () => ({
|
|
65
|
+
path: manifestPath,
|
|
66
|
+
}));
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const resolvedAlias: Record<string, string> = {
|
|
71
|
+
...Object.fromEntries(
|
|
72
|
+
Object.entries(config.alias ?? {}).map(([k, v]) => [k, join(cwd(), v)]),
|
|
73
|
+
),
|
|
74
|
+
"$env/static/public": join(import.meta.dir, "../env/public.ts"),
|
|
75
|
+
"$env/static/private": join(import.meta.dir, "../env/private.ts"),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const bundleStrategy = config.bundleStrategy ?? "split";
|
|
79
|
+
const publicDir = join(process.cwd(), "public/__grimoire__");
|
|
80
|
+
|
|
81
|
+
if (bundleStrategy === "single") {
|
|
82
|
+
// Single bundle: combine hydrate + dom in one output
|
|
83
|
+
const singleResult = await Bun.build({
|
|
84
|
+
entrypoints: [
|
|
85
|
+
join(import.meta.dir, "../rendering/hydrate.ts"),
|
|
86
|
+
join(import.meta.dir, "../client/index.ts"),
|
|
87
|
+
],
|
|
88
|
+
outdir: publicDir,
|
|
89
|
+
plugins: [
|
|
90
|
+
sigil({ mode: "hydrate" }),
|
|
91
|
+
sigil({ mode: "dom" }),
|
|
92
|
+
makeRoutesPlugin(hydrateManifest),
|
|
93
|
+
makeRoutesPlugin(domManifest),
|
|
94
|
+
],
|
|
95
|
+
// @ts-expect-error alias is a valid Bun.build option not yet in types
|
|
96
|
+
alias: resolvedAlias,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
if (!singleResult.success) {
|
|
100
|
+
for (const log of singleResult.logs) console.error(log);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const result: BuildResult = {
|
|
104
|
+
success: singleResult.success,
|
|
105
|
+
outputs: singleResult.outputs.map((o) => o.path),
|
|
106
|
+
errors: singleResult.logs.map(String),
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
await runHook(plugins, "onBuildEnd", result);
|
|
110
|
+
return { result, tree };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const [hydrateResult, domResult] = await Promise.all([
|
|
114
|
+
Bun.build({
|
|
115
|
+
entrypoints: [join(import.meta.dir, "../rendering/hydrate.ts")],
|
|
116
|
+
outdir: publicDir,
|
|
117
|
+
plugins: [sigil({ mode: "hydrate" }), makeRoutesPlugin(hydrateManifest)],
|
|
118
|
+
// @ts-expect-error alias is a valid Bun.build option not yet in types
|
|
119
|
+
alias: resolvedAlias,
|
|
120
|
+
}),
|
|
121
|
+
Bun.build({
|
|
122
|
+
entrypoints: [join(import.meta.dir, "../client/index.ts")],
|
|
123
|
+
outdir: publicDir,
|
|
124
|
+
plugins: [sigil({ mode: "dom" }), makeRoutesPlugin(domManifest)],
|
|
125
|
+
// @ts-expect-error alias is a valid Bun.build option not yet in types
|
|
126
|
+
alias: resolvedAlias,
|
|
127
|
+
}),
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
if (!hydrateResult.success) {
|
|
131
|
+
for (const log of hydrateResult.logs) console.error(log);
|
|
132
|
+
}
|
|
133
|
+
if (!domResult.success) {
|
|
134
|
+
for (const log of domResult.logs) console.error(log);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const result: BuildResult = {
|
|
138
|
+
success: hydrateResult.success && domResult.success,
|
|
139
|
+
outputs: [...hydrateResult.outputs, ...domResult.outputs].map(
|
|
140
|
+
(o) => o.path,
|
|
141
|
+
),
|
|
142
|
+
errors: [...hydrateResult.logs, ...domResult.logs].map(String),
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
await runHook(plugins, "onBuildEnd", result);
|
|
146
|
+
return { result, tree };
|
|
147
|
+
}
|