brustjs 0.1.46-alpha → 0.1.48-alpha
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 +7 -7
- package/runtime/cli/build.ts +134 -9
- package/runtime/cli/dev.ts +14 -2
- package/runtime/cli/help.ts +8 -0
- package/runtime/cli/native-routes-emit.ts +9 -1
- package/runtime/cli/ssg.ts +321 -10
- package/runtime/generator.ts +76 -0
- package/runtime/index.d.ts +6 -0
- package/runtime/index.js +52 -52
- package/runtime/index.ts +27 -0
- package/runtime/islands/bootstrap.ts +170 -9
- package/runtime/islands/build.ts +1 -1
- package/runtime/islands/fallback.ts +198 -0
- package/runtime/md/emit.ts +11 -1
- package/runtime/render/inject-generator.ts +71 -0
- package/runtime/render/stream.ts +11 -3
- package/runtime/routes.ts +97 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brustjs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.48-alpha",
|
|
4
4
|
"description": "Bun + Rust SSR framework — React on the server, Rust everywhere else (napi cdylib + per-worker SharedArrayBuffer).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
"typescript": "^6.0.3"
|
|
42
42
|
},
|
|
43
43
|
"optionalDependencies": {
|
|
44
|
-
"brustjs-darwin-x64": "0.1.
|
|
45
|
-
"brustjs-darwin-arm64": "0.1.
|
|
46
|
-
"brustjs-linux-x64-gnu": "0.1.
|
|
47
|
-
"brustjs-linux-arm64-gnu": "0.1.
|
|
48
|
-
"brustjs-linux-x64-musl": "0.1.
|
|
49
|
-
"brustjs-linux-arm64-musl": "0.1.
|
|
44
|
+
"brustjs-darwin-x64": "0.1.48-alpha",
|
|
45
|
+
"brustjs-darwin-arm64": "0.1.48-alpha",
|
|
46
|
+
"brustjs-linux-x64-gnu": "0.1.48-alpha",
|
|
47
|
+
"brustjs-linux-arm64-gnu": "0.1.48-alpha",
|
|
48
|
+
"brustjs-linux-x64-musl": "0.1.48-alpha",
|
|
49
|
+
"brustjs-linux-arm64-musl": "0.1.48-alpha"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"react": "^19.2.6",
|
package/runtime/cli/build.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
|
-
import { copyFile, cp, mkdir, readdir, rm } from 'node:fs/promises'
|
|
2
|
+
import { copyFile, cp, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from 'node:fs/promises'
|
|
3
3
|
import { createRequire } from 'node:module'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
4
5
|
import path, { isAbsolute, resolve } from 'node:path'
|
|
5
6
|
import type { BunPlugin } from 'bun'
|
|
6
7
|
import { emitNativeTemplates } from './native-routes-emit.ts'
|
|
@@ -144,6 +145,7 @@ export interface ParsedArgs {
|
|
|
144
145
|
target: string // --target value (default 'auto')
|
|
145
146
|
ssg: boolean // --ssg — prerender static routes after the build
|
|
146
147
|
ssgOut: string | null // --ssg-out value (absolute); null → <outDir>/static computed later
|
|
148
|
+
generatorVersion: boolean // false ⇔ --no-generator-version (name-only generator tag)
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
/** Parse `brust build` argv. Pure (no fs access, no process.exit) so it's
|
|
@@ -154,6 +156,7 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
154
156
|
let target = 'auto'
|
|
155
157
|
let ssg = false
|
|
156
158
|
let ssgOut: string | undefined
|
|
159
|
+
let generatorVersion = true
|
|
157
160
|
|
|
158
161
|
for (let i = 0; i < args.length; i++) {
|
|
159
162
|
const a = args[i]
|
|
@@ -176,6 +179,8 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
176
179
|
}
|
|
177
180
|
} else if (a === '--ssg') {
|
|
178
181
|
ssg = true
|
|
182
|
+
} else if (a === '--no-generator-version') {
|
|
183
|
+
generatorVersion = false
|
|
179
184
|
} else if (a === '--ssg-out') {
|
|
180
185
|
ssgOut = args[++i]
|
|
181
186
|
if (!ssgOut) {
|
|
@@ -213,7 +218,7 @@ export function parseArgs(args: string[]): ParsedArgs {
|
|
|
213
218
|
}
|
|
214
219
|
const ssgOutPath = ssgOut ? (isAbsolute(ssgOut) ? ssgOut : resolve(cwd, ssgOut)) : null
|
|
215
220
|
|
|
216
|
-
return { entry: entryPath, outDir: outPath, target, ssg, ssgOut: ssgOutPath }
|
|
221
|
+
return { entry: entryPath, outDir: outPath, target, ssg, ssgOut: ssgOutPath, generatorVersion }
|
|
217
222
|
}
|
|
218
223
|
|
|
219
224
|
export async function runBuild(args: string[]): Promise<void> {
|
|
@@ -321,6 +326,22 @@ export async function runBuild(args: string[]): Promise<void> {
|
|
|
321
326
|
// the same dual-emit the jinja mirror below does). Strict no-op without md
|
|
322
327
|
// routes: no files, no dirs, byte-identical dist.
|
|
323
328
|
const jinjaDir = path.join(outDir, 'jinja')
|
|
329
|
+
|
|
330
|
+
// Generator tag decision — baked at build time into BOTH jinja dirs so every
|
|
331
|
+
// runtime path (prebuilt dist reads <distDir>/jinja; dev/source reads
|
|
332
|
+
// .brust/jinja) picks up the same artifact. Runs unconditionally: even a
|
|
333
|
+
// React-only app with zero native/md routes needs the artifact so the React
|
|
334
|
+
// stream injector and X-Powered-By thread share the same decision.
|
|
335
|
+
// The .brust/jinja write is defense-in-depth: the 4.1 mirror cp below
|
|
336
|
+
// overwrites it with identical content, but this write must NOT be removed —
|
|
337
|
+
// it keeps the artifact correct even if that cp ever becomes conditional.
|
|
338
|
+
{
|
|
339
|
+
const { generatorStrings, writeGeneratorArtifact } = await import('../generator.ts')
|
|
340
|
+
const gen = generatorStrings(parsed.generatorVersion)
|
|
341
|
+
writeGeneratorArtifact(jinjaDir, gen)
|
|
342
|
+
writeGeneratorArtifact(path.join(process.cwd(), '.brust', 'jinja'), gen)
|
|
343
|
+
}
|
|
344
|
+
|
|
324
345
|
let mdIslands = new Map<string, string>()
|
|
325
346
|
if (existsSync(routesFile) && loadedRoutes !== undefined) {
|
|
326
347
|
const { emitMdArtifacts } = await import('../md/emit.ts')
|
|
@@ -345,7 +366,17 @@ export async function runBuild(args: string[]): Promise<void> {
|
|
|
345
366
|
const islandMap = existsSync(routesFile)
|
|
346
367
|
? scanIslandChunks(routesFile, mdIslands)
|
|
347
368
|
: new Map<string, string>()
|
|
348
|
-
|
|
369
|
+
// `fallback: 'client'` SSG routes need the islands RUNTIME (_bootstrap.js
|
|
370
|
+
// drives the client takeover; _react*.js back the fallback chunk's
|
|
371
|
+
// externals) even when the app ships ZERO islands — buildIslands with an
|
|
372
|
+
// empty map emits exactly those runtime files. Without this, the fallback
|
|
373
|
+
// shell references a /_brust/islands/_bootstrap.js that was never built.
|
|
374
|
+
const needsFallbackRuntime =
|
|
375
|
+
parsed.ssg &&
|
|
376
|
+
((loadedRoutes ?? []) as { chain?: Array<{ ssg?: { fallback?: string } }> }[]).some(
|
|
377
|
+
(r) => r.chain?.at(-1)?.ssg?.fallback === 'client',
|
|
378
|
+
)
|
|
379
|
+
if (islandMap.size > 0 || needsFallbackRuntime) {
|
|
349
380
|
const islandsOutDir = path.join(outDir, 'islands')
|
|
350
381
|
const result = await buildIslands(islandMap, {
|
|
351
382
|
outDir: islandsOutDir,
|
|
@@ -570,22 +601,108 @@ export async function runBuild(args: string[]): Promise<void> {
|
|
|
570
601
|
console.log(`[brust build] native: ${name}`)
|
|
571
602
|
}
|
|
572
603
|
|
|
604
|
+
// 7.5. SSG fallback chunks (--ssg only): for every route whose LEAF declares
|
|
605
|
+
// `ssg.fallback: 'client'`, bundle a browser chunk that re-exports the leaf
|
|
606
|
+
// component + its `clientLoader` (generated entry → islands buildOne, react
|
|
607
|
+
// externals, content-addressed `Fallback_<Name>_<hash>.js`). Constraint
|
|
608
|
+
// (documented in the spec): the leaf Component must be a DEFAULT import in
|
|
609
|
+
// routes.tsx (scanImports resolution), and its file must export clientLoader.
|
|
610
|
+
// Step 8 hands these to exportStatic (shell/payload crawl + manifest + 404).
|
|
611
|
+
const ssgFallbacks: Array<{ pattern: string; chunk: string }> = []
|
|
612
|
+
if (parsed.ssg) {
|
|
613
|
+
const fallbackRoutes = (
|
|
614
|
+
(loadedRoutes ?? []) as {
|
|
615
|
+
fullPath: string
|
|
616
|
+
chain?: Array<{ ssg?: { fallback?: string }; Component?: { name?: string } }>
|
|
617
|
+
}[]
|
|
618
|
+
).filter((r) => r.chain?.at(-1)?.ssg?.fallback === 'client')
|
|
619
|
+
if (fallbackRoutes.length > 0) {
|
|
620
|
+
const { fallbackEntrySource, hasClientLoaderExport } = await import('./ssg.ts')
|
|
621
|
+
const { scanImports } = await import('./native-routes-emit.ts')
|
|
622
|
+
const { buildOne } = await import('../islands/build.ts')
|
|
623
|
+
const { islandChunkBasename } = await import('../islands/chunk-id.ts')
|
|
624
|
+
const importMap = scanImports(routesFile)
|
|
625
|
+
const islandsOutDir = path.join(outDir, 'islands')
|
|
626
|
+
await mkdir(islandsOutDir, { recursive: true })
|
|
627
|
+
// Temp dir for the generated entry modules; removed after the bundles land.
|
|
628
|
+
// Error paths THROW (not process.exit) so the finally's cleanup runs —
|
|
629
|
+
// exit(1) inside the try would skip it and leak the temp dir; the catch
|
|
630
|
+
// below owns stderr + exit.
|
|
631
|
+
const entryTmp = await mkdtemp(path.join(tmpdir(), 'brust-fallback-'))
|
|
632
|
+
try {
|
|
633
|
+
for (const [i, route] of fallbackRoutes.entries()) {
|
|
634
|
+
const name = route.chain?.at(-1)?.Component?.name
|
|
635
|
+
const source = name ? importMap.get(name) : undefined
|
|
636
|
+
if (!name || !source) {
|
|
637
|
+
throw new Error(
|
|
638
|
+
`[brust build] ssg: fallback 'client' on "${route.fullPath}": leaf Component must be a DEFAULT import in the routes file`,
|
|
639
|
+
)
|
|
640
|
+
}
|
|
641
|
+
const text = await readFile(source, 'utf8')
|
|
642
|
+
if (!hasClientLoaderExport(text)) {
|
|
643
|
+
throw new Error(
|
|
644
|
+
`[brust build] ssg: fallback 'client' on "${route.fullPath}": ` +
|
|
645
|
+
`${path.relative(process.cwd(), source)} must \`export const clientLoader\``,
|
|
646
|
+
)
|
|
647
|
+
}
|
|
648
|
+
const file = `Fallback_${islandChunkBasename(name, source)}.js`
|
|
649
|
+
// Index-suffixed entry name: two fallback routes may share a component
|
|
650
|
+
// NAME (different files) — the chunk name is already content-addressed.
|
|
651
|
+
const entryPath = path.join(entryTmp, `${name}_${i}.entry.ts`)
|
|
652
|
+
await writeFile(entryPath, fallbackEntrySource(source))
|
|
653
|
+
try {
|
|
654
|
+
await buildOne(
|
|
655
|
+
[entryPath],
|
|
656
|
+
islandsOutDir,
|
|
657
|
+
file,
|
|
658
|
+
['react', 'react/jsx-runtime', 'react-dom/client'],
|
|
659
|
+
cssBuildPlugins,
|
|
660
|
+
)
|
|
661
|
+
} catch (err) {
|
|
662
|
+
// Browser-safety convention: server-only deps at the component file's
|
|
663
|
+
// top level surface here as bundle errors.
|
|
664
|
+
throw new Error(
|
|
665
|
+
`[brust build] ssg: fallback chunk for "${route.fullPath}": ${err instanceof Error ? err.message : String(err)}`,
|
|
666
|
+
)
|
|
667
|
+
}
|
|
668
|
+
ssgFallbacks.push({ pattern: route.fullPath, chunk: `/_brust/islands/${file}` })
|
|
669
|
+
}
|
|
670
|
+
} catch (err) {
|
|
671
|
+
await rm(entryTmp, { recursive: true, force: true })
|
|
672
|
+
console.error(err instanceof Error ? err.message : String(err))
|
|
673
|
+
process.exit(1)
|
|
674
|
+
}
|
|
675
|
+
await rm(entryTmp, { recursive: true, force: true })
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
573
679
|
// 8. SSG export (--ssg): boot the just-built dist once and crawl every
|
|
574
680
|
// statically-renderable route into <--ssg-out | outDir/static>. Reuses the
|
|
575
681
|
// routes module ALREADY loaded for the MCP/css steps (loadedRoutes) — no
|
|
576
682
|
// second import. Without the flag this is a strict no-op.
|
|
577
683
|
if (parsed.ssg) {
|
|
578
|
-
const { collectStaticPaths, exportStatic } = await import('./ssg.ts')
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
684
|
+
const { collectStaticPaths, expandDynamicRoutes, exportStatic } = await import('./ssg.ts')
|
|
685
|
+
// Initialized to [] so the post-catch read type-checks even where
|
|
686
|
+
// process.exit isn't narrowed to `never` (exit(1) still prevents use).
|
|
687
|
+
let expanded: Awaited<ReturnType<typeof expandDynamicRoutes>> = []
|
|
688
|
+
try {
|
|
689
|
+
expanded = await expandDynamicRoutes(
|
|
690
|
+
(loadedRoutes ?? []) as Parameters<typeof expandDynamicRoutes>[0],
|
|
691
|
+
)
|
|
692
|
+
} catch (err) {
|
|
693
|
+
console.error(`[brust build] ssg: ${err instanceof Error ? err.message : String(err)}`)
|
|
694
|
+
process.exit(1)
|
|
695
|
+
}
|
|
696
|
+
const expandedCount = expanded.length - (loadedRoutes ?? []).length
|
|
697
|
+
const decisions = collectStaticPaths(expanded)
|
|
582
698
|
const staticOut = parsed.ssgOut ?? path.join(outDir, 'static')
|
|
583
699
|
try {
|
|
584
|
-
const { written, navWritten, skipped } = await exportStatic({
|
|
700
|
+
const { written, navWritten, fallbackWritten, skipped } = await exportStatic({
|
|
585
701
|
distDir: outDir,
|
|
586
702
|
entryDir,
|
|
587
703
|
staticOut,
|
|
588
704
|
routes: decisions,
|
|
705
|
+
fallbacks: ssgFallbacks,
|
|
589
706
|
})
|
|
590
707
|
const counts = new Map<string, number>()
|
|
591
708
|
for (const s of skipped) {
|
|
@@ -597,9 +714,17 @@ export async function runBuild(args: string[]): Promise<void> {
|
|
|
597
714
|
.map((r) => `${r}=${counts.get(r)}`)
|
|
598
715
|
.join(', ')
|
|
599
716
|
const skippedDesc = skipped.length > 0 ? ` (skipped ${skipped.length}: ${reasons})` : ''
|
|
717
|
+
const expandedDesc = expandedCount > 0 ? `, expanded ${expandedCount} dynamic page(s)` : ''
|
|
718
|
+
const fallbackDesc =
|
|
719
|
+
fallbackWritten.length > 0 ? `, ${fallbackWritten.length} fallback file(s)` : ''
|
|
600
720
|
console.log(
|
|
601
|
-
`[brust build] ssg: ${written.length} pages + ${navWritten.length} spa payloads → ${staticOut}${skippedDesc}`,
|
|
721
|
+
`[brust build] ssg: ${written.length} pages + ${navWritten.length} spa payloads${expandedDesc}${fallbackDesc} → ${staticOut}${skippedDesc}`,
|
|
602
722
|
)
|
|
723
|
+
for (const f of ssgFallbacks) {
|
|
724
|
+
console.log(
|
|
725
|
+
`[brust build] ssg: fallback chunk ${path.basename(f.chunk)} (${f.pattern})`,
|
|
726
|
+
)
|
|
727
|
+
}
|
|
603
728
|
} catch (err) {
|
|
604
729
|
console.error(`[brust build] ssg: ${err instanceof Error ? err.message : String(err)}`)
|
|
605
730
|
process.exit(1)
|
package/runtime/cli/dev.ts
CHANGED
|
@@ -10,11 +10,13 @@ const REPO_ROOT = path.resolve(import.meta.dir, '..', '..')
|
|
|
10
10
|
interface ParsedArgs {
|
|
11
11
|
entry: string
|
|
12
12
|
port: number | undefined
|
|
13
|
+
generatorVersion: boolean
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
function parseArgs(args: string[]): ParsedArgs {
|
|
16
17
|
let entry: string | undefined
|
|
17
18
|
let port: number | undefined
|
|
19
|
+
let generatorVersion = true
|
|
18
20
|
for (let i = 0; i < args.length; i++) {
|
|
19
21
|
const a = args[i]
|
|
20
22
|
if (a === '--port') {
|
|
@@ -28,6 +30,8 @@ function parseArgs(args: string[]): ParsedArgs {
|
|
|
28
30
|
console.error(`brust dev: invalid port ${v}`)
|
|
29
31
|
process.exit(1)
|
|
30
32
|
}
|
|
33
|
+
} else if (a === '--no-generator-version') {
|
|
34
|
+
generatorVersion = false
|
|
31
35
|
} else if (a.startsWith('--port=')) {
|
|
32
36
|
port = parseInt(a.slice('--port='.length), 10)
|
|
33
37
|
} else if (a.startsWith('-')) {
|
|
@@ -50,14 +54,22 @@ function parseArgs(args: string[]): ParsedArgs {
|
|
|
50
54
|
console.error(`brust dev: no entry file at ${entryPath}; pass a path or create ./index.ts`)
|
|
51
55
|
process.exit(1)
|
|
52
56
|
}
|
|
53
|
-
return { entry: entryPath, port }
|
|
57
|
+
return { entry: entryPath, port, generatorVersion }
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
export async function runDev(args: string[]): Promise<void> {
|
|
57
|
-
const { entry, port } = parseArgs(args)
|
|
61
|
+
const { entry, port, generatorVersion } = parseArgs(args)
|
|
58
62
|
process.env.BRUST_DEV = '1'
|
|
59
63
|
if (port !== undefined) process.env.BRUST_PORT = String(port)
|
|
60
64
|
|
|
65
|
+
// Bake the generator decision BEFORE the first emit — emitters and the boot
|
|
66
|
+
// re-emit paths all resolve <cwd>/.brust/jinja/generator.json internally.
|
|
67
|
+
const { generatorStrings, writeGeneratorArtifact } = await import('../generator.ts')
|
|
68
|
+
writeGeneratorArtifact(
|
|
69
|
+
path.join(process.cwd(), '.brust', 'jinja'),
|
|
70
|
+
generatorStrings(generatorVersion),
|
|
71
|
+
)
|
|
72
|
+
|
|
61
73
|
// Sub-project J — emit .brust/jinja/<Name>.jinja templates BEFORE handing
|
|
62
74
|
// off to the user's entry. The runtime loads these on boot. Dev-mode HMR
|
|
63
75
|
// on .tsx edit is deferred per spec S12 (restart-to-reload for v2).
|
package/runtime/cli/help.ts
CHANGED
|
@@ -61,6 +61,10 @@ export const COMMANDS: CommandDef[] = [
|
|
|
61
61
|
flag: '--ssg-out <dir>',
|
|
62
62
|
desc: 'Output directory for prerendered HTML (default <out-dir>/static)',
|
|
63
63
|
},
|
|
64
|
+
{
|
|
65
|
+
flag: '--no-generator-version',
|
|
66
|
+
desc: 'Drop the version from the generator meta tag + X-Powered-By header (the name stays)',
|
|
67
|
+
},
|
|
64
68
|
],
|
|
65
69
|
notes: [
|
|
66
70
|
'Markdown pages: routes mounted with mdRoutes(<contentDir>) compile to native',
|
|
@@ -75,6 +79,10 @@ export const COMMANDS: CommandDef[] = [
|
|
|
75
79
|
flags: [
|
|
76
80
|
{ flag: '[entry]', desc: 'Entry file (default ./index.ts)' },
|
|
77
81
|
{ flag: '--port <n>', desc: 'Port to listen on' },
|
|
82
|
+
{
|
|
83
|
+
flag: '--no-generator-version',
|
|
84
|
+
desc: 'Drop the version from the generator meta tag + X-Powered-By header (the name stays)',
|
|
85
|
+
},
|
|
78
86
|
],
|
|
79
87
|
},
|
|
80
88
|
{
|
|
@@ -2,6 +2,7 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node
|
|
|
2
2
|
import { createRequire } from 'node:module'
|
|
3
3
|
import { dirname, relative, resolve } from 'node:path'
|
|
4
4
|
import { buildDevClientTag } from '../dev/client.ts'
|
|
5
|
+
import { insertGeneratorMeta, resolveGenerator } from '../generator.ts'
|
|
5
6
|
import { islandChunkBasename } from '../islands/chunk-id.ts'
|
|
6
7
|
import { DIRECTIVES_BOOTSTRAP, ISLANDS_IMPORTMAP_AND_BOOTSTRAP } from '../islands/importmap.ts'
|
|
7
8
|
|
|
@@ -527,6 +528,12 @@ export async function emitNativeTemplates(opts: NativeRouteEmitOpts): Promise<vo
|
|
|
527
528
|
nativeRoutes.length > 0 &&
|
|
528
529
|
(await import('../native/build.ts')).scanDirectiveComponents(opts.entryFile).size > 0
|
|
529
530
|
|
|
531
|
+
// Generator meta: resolved INTERNALLY from the out dir's artifact (NOT a
|
|
532
|
+
// caller param) — emit re-runs from five call sites (build, dev, boot
|
|
533
|
+
// staleness, md boot re-emit, dev HMR) and a param would silently drop the
|
|
534
|
+
// tag on re-emit. Fallback (no artifact) = version-on defaults.
|
|
535
|
+
const generatorMeta = resolveGenerator(opts.outDir).meta
|
|
536
|
+
|
|
530
537
|
const built: string[] = []
|
|
531
538
|
for (const r of nativeRoutes) {
|
|
532
539
|
const name = r.nativeTemplate!
|
|
@@ -632,8 +639,9 @@ export async function emitNativeTemplates(opts: NativeRouteEmitOpts): Promise<vo
|
|
|
632
639
|
// injection, so splice the /_brust/dev WS script in here. reEmitJinja() runs
|
|
633
640
|
// this on every hot reload, so the script is always present in dev.
|
|
634
641
|
const withDirectives = bakeDirectivesIfUsed(compiled.template, hasDirectives)
|
|
642
|
+
const withGenerator = insertGeneratorMeta(withDirectives, generatorMeta)
|
|
635
643
|
const template =
|
|
636
|
-
process.env.BRUST_DEV === '1' ? injectDevClientIntoTemplate(
|
|
644
|
+
process.env.BRUST_DEV === '1' ? injectDevClientIntoTemplate(withGenerator) : withGenerator
|
|
637
645
|
writeFileSync(outPath, template)
|
|
638
646
|
built.push(name)
|
|
639
647
|
|