@kudzujs/core 0.8.53 → 0.8.56
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/MIGRATION_ROADMAP.md +2 -1
- package/PERFORMANCE.md +13 -1
- package/README.md +1 -1
- package/RELEASES.md +101 -0
- package/docs/next-architecture/README.md +1 -1
- package/docs/next-architecture/compiler-current-architecture.md +14 -11
- package/docs/next-architecture/large-application-ai-native-roadmap.md +4 -4
- package/docs/next-architecture/versioning.md +4 -1
- package/framework/README.md +4 -0
- package/framework/build.mjs +257 -85
- package/framework/compiler/effect-codegen.mjs +17 -17
- package/framework/compiler/param-codegen.mjs +2 -2
- package/framework/compiler/route-artifact-report.mjs +133 -0
- package/framework/compiler/runtime-family-planner.mjs +48 -0
- package/framework/compiler/source-compiler.mjs +2 -1
- package/framework/compiler/worker-compiler.mjs +23 -4
- package/framework/core.d.ts +2 -0
- package/framework/core.mjs +2 -2
- package/framework/dev-server.mjs +7 -2
- package/package.json +1 -1
package/framework/build.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto"
|
|
2
2
|
import { cp, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises"
|
|
3
|
-
import { dirname, join, relative, resolve, sep } from "node:path"
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"
|
|
4
4
|
import { pathToFileURL } from "node:url"
|
|
5
5
|
import { build as bundle, transform } from "esbuild"
|
|
6
6
|
import { createEffectCodegen } from "./compiler/effect-codegen.mjs"
|
|
@@ -8,9 +8,11 @@ import { generateListRuntime } from "./compiler/list-runtime-codegen.mjs"
|
|
|
8
8
|
import { assetPath, browserPath, relativeModulePath, withBase } from "./compiler/path-helpers.mjs"
|
|
9
9
|
import { createProjectSession } from "./compiler/project-session.mjs"
|
|
10
10
|
import { createRouteBuildRecord, planRouteArtifacts } from "./compiler/route-build-record.mjs"
|
|
11
|
+
import { createRouteArtifactReport } from "./compiler/route-artifact-report.mjs"
|
|
12
|
+
import { planRuntimeFamilies } from "./compiler/runtime-family-planner.mjs"
|
|
11
13
|
import { createSourceCompiler } from "./compiler/source-compiler.mjs"
|
|
12
14
|
import { createParamCodegen } from "./compiler/param-codegen.mjs"
|
|
13
|
-
import {
|
|
15
|
+
import { usesRouteDependencyRuntime } from "./compiler/route-capability-planner.mjs"
|
|
14
16
|
import { generateBindingRuntime, generateCoreRuntime, generateEffectRuntime, generateNativeRuntime, generateNavigationRuntime, specializeRuntime } from "./compiler/runtime-codegen.mjs"
|
|
15
17
|
import { renderPage } from "./core.mjs"
|
|
16
18
|
import { parseDevHost, parseDevPort, startDevServer } from "./dev-server.mjs"
|
|
@@ -31,6 +33,10 @@ async function loadConfig(root) {
|
|
|
31
33
|
|
|
32
34
|
export async function build({ quiet = false, minify = true, root: projectRoot = process.cwd() } = {}) {
|
|
33
35
|
const project = createProjectSession(projectRoot)
|
|
36
|
+
return buildWithSession(project, { quiet, minify })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function buildWithSession(project, { changedFiles, quiet = false, minify = true } = {}) {
|
|
34
40
|
const { root, outputDirectory } = project
|
|
35
41
|
const stagedOutput = join(root, ".kudzu-dist-staging")
|
|
36
42
|
const backupOutput = join(root, ".kudzu-dist-backup")
|
|
@@ -39,8 +45,9 @@ export async function build({ quiet = false, minify = true, root: projectRoot =
|
|
|
39
45
|
try {
|
|
40
46
|
await recoverOutput(outputDirectory, backupOutput)
|
|
41
47
|
await rm(stagedOutput, { recursive: true, force: true })
|
|
42
|
-
const { result, pageCount, behaviorCount } = await buildInto(project, stagedOutput, { minify })
|
|
48
|
+
const { result, pageCount, behaviorCount, cache } = await buildInto(project, stagedOutput, { changedFiles, minify })
|
|
43
49
|
await promoteOutput(stagedOutput, outputDirectory, backupOutput)
|
|
50
|
+
project.buildCache = cache
|
|
44
51
|
if (!quiet) console.log(`Built ${pageCount} page(s), ${behaviorCount} interactive page(s) into dist/`)
|
|
45
52
|
return result
|
|
46
53
|
} finally {
|
|
@@ -56,8 +63,11 @@ export async function build({ quiet = false, minify = true, root: projectRoot =
|
|
|
56
63
|
}
|
|
57
64
|
}
|
|
58
65
|
|
|
59
|
-
async function buildInto(project, outputDirectory, { minify }) {
|
|
66
|
+
async function buildInto(project, outputDirectory, { changedFiles, minify }) {
|
|
60
67
|
const { root, sourceDirectory, pagesDirectory, workDirectory } = project
|
|
68
|
+
const previous = project.buildCache
|
|
69
|
+
project.buildGeneration = (project.buildGeneration ?? 0) + 1
|
|
70
|
+
project.buildDirectory = project.buildGeneration > 1 ? join(workDirectory, "build", String(project.buildGeneration)) : workDirectory
|
|
61
71
|
const { collectClientModules, compileClientModule, compiledPath, compileSource, layoutExportError, orderSourceStyles, reachableSourceFiles, safeStaticFiles } = createSourceCompiler(project)
|
|
62
72
|
const config = await loadConfig(root)
|
|
63
73
|
const base = normalizeBase(config.base)
|
|
@@ -66,7 +76,6 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
66
76
|
const globalStyleUrlSet = new Set(globalStyleUrls)
|
|
67
77
|
const publicDirectory = normalizePublicDirectory(config.publicDir, project)
|
|
68
78
|
const navigationGroups = normalizeNavigation(config.navigation)
|
|
69
|
-
const navigationRoutes = navigationGroups.flatMap(group => group.routes)
|
|
70
79
|
const navigationByRoute = new Map(navigationGroups.flatMap(group => group.routes.map(route => [route, group])))
|
|
71
80
|
for (const group of navigationGroups) {
|
|
72
81
|
group.assetPath = assetPath(base, `assets/${group.assetName}`)
|
|
@@ -74,6 +83,7 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
74
83
|
group.layoutId = `l-${group.id}`
|
|
75
84
|
group.records = []
|
|
76
85
|
group.routeRecords = []
|
|
86
|
+
group.buildRecords = []
|
|
77
87
|
group.hasEffects = false
|
|
78
88
|
group.hasParams = false
|
|
79
89
|
}
|
|
@@ -87,11 +97,14 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
87
97
|
if (!allSourceFiles.length) throw new Error("No TypeScript files found in src/")
|
|
88
98
|
const allSourceFileSet = new Set(allSourceFiles)
|
|
89
99
|
const sourceIndex = project.sourceIndex
|
|
100
|
+
for (const file of sourceIndex.keys()) if (file.startsWith(`${sourceDirectory}${sep}`) && !allSourceFileSet.has(file)) sourceIndex.delete(file)
|
|
90
101
|
for (const [file, source] of await Promise.all(allSourceFiles.map(async file => [file, await readFile(file, "utf8")]))) sourceIndex.set(file, source)
|
|
91
102
|
const pageFiles = allSourceFiles.filter(file => file.startsWith(`${pagesDirectory}${sep}`) && file.endsWith(".tsx"))
|
|
92
103
|
if (!pageFiles.length) throw new Error("No pages found in src/pages/")
|
|
93
|
-
const
|
|
104
|
+
const pageSources = new Map(pageFiles.map(file => [file, new Set(reachableSourceFiles([file], allSourceFileSet, sourceIndex))]))
|
|
105
|
+
const sourceFiles = [...new Set([...pageSources.values()].flatMap(files => [...files]))].sort()
|
|
94
106
|
const sourceFileSet = project.sourceFiles
|
|
107
|
+
sourceFileSet.clear()
|
|
95
108
|
for (const file of sourceFiles) sourceFileSet.add(file)
|
|
96
109
|
const staticFiles = await safeStaticFiles(projectFiles)
|
|
97
110
|
const stylesByPage = new Map(pageFiles.map(file => [file, orderSourceStyles([file], sourceFiles, sourceIndex, staticFiles).filter(style => !configuredStyleSources.has(style))]))
|
|
@@ -99,15 +112,26 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
99
112
|
const importedAssets = new Set()
|
|
100
113
|
const { cssModules, cssOutputs } = await prepareSourceStyles(cssFiles, staticFiles, importedAssets, base, project)
|
|
101
114
|
|
|
115
|
+
const affectedPages = affectedPageFiles({ changedFiles, pageFiles, pageSources, previous, sourceDirectory })
|
|
116
|
+
expandAffectedNavigationGroups(affectedPages, previous?.pageRenders, navigationGroups)
|
|
117
|
+
const affectedSources = new Set([...affectedPages].flatMap(file => [...pageSources.get(file)]))
|
|
102
118
|
const sourceResults = []
|
|
119
|
+
const sourceResultsByFile = new Map()
|
|
120
|
+
let compiledModules = 0
|
|
103
121
|
for (const file of sourceFiles) {
|
|
104
122
|
if (file.endsWith(".worker.ts")) continue
|
|
105
|
-
|
|
123
|
+
let result = !affectedSources.has(file) ? previous?.sourceResults.get(file) : undefined
|
|
124
|
+
if (!result) {
|
|
125
|
+
result = compileSource(file, sourceFileSet, sourceIndex, staticFiles, cssModules, base)
|
|
126
|
+
compiledModules++
|
|
127
|
+
}
|
|
128
|
+
result = { ...result, buildModule: { ...result.buildModule, path: relative(root, compiledPath(file)).replaceAll(sep, "/") } }
|
|
106
129
|
for (const asset of result.importedAssets) importedAssets.add(resolve(root, asset))
|
|
107
130
|
const output = resolve(root, result.buildModule.path)
|
|
108
131
|
await mkdir(dirname(output), { recursive: true })
|
|
109
132
|
await writeFile(output, result.buildModule.code)
|
|
110
133
|
sourceResults.push(result)
|
|
134
|
+
sourceResultsByFile.set(file, result)
|
|
111
135
|
}
|
|
112
136
|
const handlerModules = sourceResults.flatMap(result => result.handlerModule ? [result.handlerModule] : [])
|
|
113
137
|
const workerReferences = sourceResults.flatMap(result => result.moduleIR.effects.flatMap(effect => {
|
|
@@ -116,7 +140,8 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
116
140
|
return effect.workers.map(worker => ({ ...worker, module: assetPath(base, `assets/${result.handlerModule.path}`), handler: handler.exportName }))
|
|
117
141
|
}))
|
|
118
142
|
|
|
119
|
-
|
|
143
|
+
let routeRecords = []
|
|
144
|
+
const routeDrafts = []
|
|
120
145
|
const routeEntryTransforms = new Map()
|
|
121
146
|
const routeEntrySources = new Map()
|
|
122
147
|
const routeEntryPaths = new Map()
|
|
@@ -124,9 +149,38 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
124
149
|
const emittedRoutes = new Set()
|
|
125
150
|
const emittedApplicationRoutes = new Set()
|
|
126
151
|
const emittedNavigationRecords = []
|
|
127
|
-
const
|
|
152
|
+
const navigationAssets = new Map()
|
|
153
|
+
const placeholders = previous?.placeholders ?? {
|
|
154
|
+
runtime: `/__kudzu_runtime_${randomUUID()}.js`,
|
|
155
|
+
binding: `/__kudzu_binding_${randomUUID()}.js`,
|
|
156
|
+
list: `/__kudzu_list_${randomUUID()}.js`
|
|
157
|
+
}
|
|
158
|
+
const runtimePlaceholder = placeholders.runtime
|
|
159
|
+
const bindingPlaceholder = placeholders.binding
|
|
160
|
+
const listPlaceholder = placeholders.list
|
|
161
|
+
const pageRenders = new Map()
|
|
162
|
+
let renderedPages = 0
|
|
128
163
|
|
|
129
164
|
for (const pageFile of pageFiles) {
|
|
165
|
+
const cached = !affectedPages.has(pageFile) ? previous?.pageRenders.get(pageFile) : undefined
|
|
166
|
+
if (cached) {
|
|
167
|
+
replayPageRender(cached, {
|
|
168
|
+
emittedApplicationRoutes,
|
|
169
|
+
emittedNavigationRecords,
|
|
170
|
+
emittedRoutes,
|
|
171
|
+
navigationAssets,
|
|
172
|
+
navigationByRoute,
|
|
173
|
+
routeDrafts,
|
|
174
|
+
routeRecords,
|
|
175
|
+
rewrites
|
|
176
|
+
})
|
|
177
|
+
pageRenders.set(pageFile, cached)
|
|
178
|
+
continue
|
|
179
|
+
}
|
|
180
|
+
renderedPages++
|
|
181
|
+
const draftOffset = routeDrafts.length
|
|
182
|
+
const navigationOffset = emittedNavigationRecords.length
|
|
183
|
+
const rewriteOffset = rewrites.length
|
|
130
184
|
const sourceStyleUrls = stylesByPage.get(pageFile).map(file => assetPath(base, `assets/${relative(sourceDirectory, file).replaceAll(sep, "/")}`)).filter(url => !globalStyleUrlSet.has(url))
|
|
131
185
|
const styleUrls = [...sourceStyleUrls, ...globalStyleUrls]
|
|
132
186
|
const compiledFile = compiledPath(pageFile)
|
|
@@ -156,6 +210,7 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
156
210
|
const pageMetadata = await resolveDocumentMetadata(module.metadata, metadataContext, `${relative(root, pageFile)} metadata`)
|
|
157
211
|
const navigationGroup = navigationByRoute.get(applicationRoute)
|
|
158
212
|
const navigable = Boolean(navigationGroup)
|
|
213
|
+
if (navigationGroup) navigationAssets.set(routePath, navigationGroup.assetPath)
|
|
159
214
|
const effectPath = `effects/${route ? `${route}/index` : "index"}.js`
|
|
160
215
|
const nativePath = `native/${route ? `${route}/index` : "index"}.js`
|
|
161
216
|
const paramPath = `params/${route ? `${route}/index` : "index"}.js`
|
|
@@ -182,6 +237,8 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
182
237
|
managedStyles: navigable ? sourceStyleUrls : [],
|
|
183
238
|
base,
|
|
184
239
|
runtimeAsset: runtimePlaceholder,
|
|
240
|
+
bindingAsset: bindingPlaceholder,
|
|
241
|
+
listAsset: listPlaceholder,
|
|
185
242
|
effectAsset: assetPath(base, `assets/${effectPath}`),
|
|
186
243
|
nativeAsset: assetPath(base, `assets/${nativePath}`),
|
|
187
244
|
paramAsset: assetPath(base, `assets/${paramPath}`),
|
|
@@ -193,30 +250,16 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
193
250
|
navigationGroup.hasParams ||= result.hasParams
|
|
194
251
|
}
|
|
195
252
|
const usesDependencyRuntime = usesRouteDependencyRuntime({ plan: result.plan, navigable, hasBindings: result.hasBindings, hasLists: result.hasLists })
|
|
196
|
-
const routeRuntimeName = usesDependencyRuntime ? "kudzu-deps.js" : "kudzu.js"
|
|
197
253
|
const plan = { route: routePath, ...result.plan }
|
|
198
|
-
const entries = {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
entries.param = entry.path
|
|
203
|
-
html = html.replaceAll(assetPath(base, `assets/${paramPath}`), assetPath(base, `assets/${entry.path}`))
|
|
204
|
-
}
|
|
205
|
-
if (result.hasEffects) {
|
|
206
|
-
const entry = retainRouteEntry(effectPath, output => printEffectEntry(runtimeEffects(plan.effects, navigable), output, handlerModules, join(outputDirectory, "assets"), base, entries.param, routeRuntimeName, navigable), routeEntrySources, routeEntryPaths, outputDirectory)
|
|
207
|
-
entries.effect = entry.path
|
|
208
|
-
html = html.replaceAll(assetPath(base, `assets/${effectPath}`), assetPath(base, `assets/${entry.path}`))
|
|
209
|
-
}
|
|
210
|
-
if (plan.events.some(event => event.native)) {
|
|
211
|
-
const modules = [...new Set(plan.events.filter(event => event.native).map(event => event.native.module))]
|
|
212
|
-
const entry = retainRouteEntry(nativePath, () => printNativeEntrySource(modules, base), routeEntrySources, routeEntryPaths, outputDirectory)
|
|
213
|
-
entries.native = entry.path
|
|
214
|
-
html = html.replaceAll(assetPath(base, `assets/${nativePath}`), assetPath(base, `assets/${entry.path}`))
|
|
254
|
+
const entries = {
|
|
255
|
+
...(result.hasParams ? { param: paramPath } : {}),
|
|
256
|
+
...(result.hasEffects ? { effect: effectPath } : {}),
|
|
257
|
+
...(plan.events.some(event => event.native) ? { native: nativePath } : {})
|
|
215
258
|
}
|
|
216
|
-
|
|
259
|
+
const record = createRouteBuildRecord({
|
|
217
260
|
route: routePath,
|
|
218
261
|
output: route,
|
|
219
|
-
html,
|
|
262
|
+
html: inlineQueryFormCarry(result.html, plan),
|
|
220
263
|
plan,
|
|
221
264
|
handlerReferences: result.handlerReferences,
|
|
222
265
|
styles: styleUrls,
|
|
@@ -233,8 +276,17 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
233
276
|
},
|
|
234
277
|
entries,
|
|
235
278
|
runtimeSchema
|
|
236
|
-
})
|
|
279
|
+
})
|
|
280
|
+
routeRecords.push(record)
|
|
281
|
+
if (navigationGroup) navigationGroup.buildRecords.push(record)
|
|
282
|
+
routeDrafts.push({ record, result, runtimeSchema, navigationGroup, applicationRoute, effectPath, nativePath, paramPath })
|
|
237
283
|
}
|
|
284
|
+
pageRenders.set(pageFile, {
|
|
285
|
+
drafts: routeDrafts.slice(draftOffset).map(({ navigationGroup: _, ...draft }) => draft),
|
|
286
|
+
layout: module.layout,
|
|
287
|
+
navigationRecords: emittedNavigationRecords.slice(navigationOffset).map(({ group: _, ...record }) => record),
|
|
288
|
+
rewrites: rewrites.slice(rewriteOffset)
|
|
289
|
+
})
|
|
238
290
|
}
|
|
239
291
|
|
|
240
292
|
for (const group of navigationGroups) for (const route of group.routes) if (!emittedApplicationRoutes.has(route)) throw new Error(`${group.label} route ${JSON.stringify(route)} is not an emitted route`)
|
|
@@ -245,12 +297,65 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
245
297
|
group.records.sort((left, right) => (right.segments?.filter(segment => segment !== null).length ?? 0) - (left.segments?.filter(segment => segment !== null).length ?? 0) || left.id.localeCompare(right.id))
|
|
246
298
|
}
|
|
247
299
|
|
|
300
|
+
const runtimePlan = planRuntimeFamilies(routeRecords, navigationGroups)
|
|
301
|
+
for (const group of navigationGroups) {
|
|
302
|
+
group.runtimeFamily = runtimePlan.familyByRecord.get(group.buildRecords[0])
|
|
303
|
+
group.assetPath = assetPath(base, `assets/runtime/${group.runtimeFamily.id}/${group.assetName}`)
|
|
304
|
+
}
|
|
305
|
+
const runtimeFamilyByRecord = new Map()
|
|
306
|
+
routeRecords = routeDrafts.map(draft => {
|
|
307
|
+
const { record, result, runtimeSchema, navigationGroup, effectPath, nativePath, paramPath } = draft
|
|
308
|
+
const family = runtimePlan.familyByRecord.get(record)
|
|
309
|
+
if (record.capabilities.hasBehaviors && !family) throw new Error(`Interactive route has no runtime family: ${record.route}`)
|
|
310
|
+
const runtimeDirectory = family ? join(outputDirectory, "assets", "runtime", family.id) : undefined
|
|
311
|
+
const routeRuntimeName = record.capabilities.usesDependencyRuntime ? "kudzu-deps.js" : "kudzu.js"
|
|
312
|
+
const entries = {}
|
|
313
|
+
let html = record.html
|
|
314
|
+
if (record.capabilities.hasParams) {
|
|
315
|
+
const entry = retainRouteEntry(paramPath, output => printParamEntry(runtimeSchema, record.plan.params, record.plan.searchParams, record.plan.searchParamsWritable, output, runtimeDirectory, base, routeRuntimeName, record.capabilities.navigable), routeEntrySources, routeEntryPaths, outputDirectory)
|
|
316
|
+
entries.param = entry.path
|
|
317
|
+
html = html.replaceAll(assetPath(base, `assets/${paramPath}`), assetPath(base, `assets/${entry.path}`))
|
|
318
|
+
}
|
|
319
|
+
if (record.capabilities.hasEffects) {
|
|
320
|
+
const entry = retainRouteEntry(effectPath, output => printEffectEntry(runtimeEffects(record.plan.effects, record.capabilities.navigable), output, handlerModules, join(outputDirectory, "assets"), base, entries.param, routeRuntimeName, record.capabilities.navigable, runtimeDirectory), routeEntrySources, routeEntryPaths, outputDirectory)
|
|
321
|
+
entries.effect = entry.path
|
|
322
|
+
html = html.replaceAll(assetPath(base, `assets/${effectPath}`), assetPath(base, `assets/${entry.path}`))
|
|
323
|
+
}
|
|
324
|
+
if (record.plan.events.some(event => event.native)) {
|
|
325
|
+
const modules = [...new Set(record.plan.events.filter(event => event.native).map(event => event.native.module))]
|
|
326
|
+
const nativeRuntime = assetPath(base, `assets/runtime/${family.id}/kudzu-native.js`)
|
|
327
|
+
const entry = retainRouteEntry(nativePath, () => printNativeEntrySource(modules, nativeRuntime), routeEntrySources, routeEntryPaths, outputDirectory)
|
|
328
|
+
entries.native = entry.path
|
|
329
|
+
html = html.replaceAll(assetPath(base, `assets/${nativePath}`), assetPath(base, `assets/${entry.path}`))
|
|
330
|
+
}
|
|
331
|
+
if (family) {
|
|
332
|
+
html = html.replaceAll(runtimePlaceholder, escapeAttribute(assetPath(base, `assets/runtime/${family.id}/${routeRuntimeName}`)))
|
|
333
|
+
html = html.replaceAll(bindingPlaceholder, escapeAttribute(assetPath(base, `assets/runtime/${family.id}/kudzu-binding.js`)))
|
|
334
|
+
html = html.replaceAll(listPlaceholder, escapeAttribute(assetPath(base, `assets/runtime/${family.id}/kudzu-list.js`)))
|
|
335
|
+
}
|
|
336
|
+
if (navigationGroup) html = html.replaceAll(escapeAttribute(navigationAssets.get(record.route)), escapeAttribute(navigationGroup.assetPath))
|
|
337
|
+
const finalRecord = createRouteBuildRecord({
|
|
338
|
+
route: record.route,
|
|
339
|
+
output: record.output,
|
|
340
|
+
html,
|
|
341
|
+
plan: record.plan,
|
|
342
|
+
handlerReferences: result.handlerReferences,
|
|
343
|
+
styles: record.artifacts.styles,
|
|
344
|
+
capabilities: record.capabilities,
|
|
345
|
+
entries,
|
|
346
|
+
runtimeSchema
|
|
347
|
+
})
|
|
348
|
+
if (family) runtimeFamilyByRecord.set(finalRecord, family)
|
|
349
|
+
if (navigationGroup) navigationAssets.set(finalRecord.route, navigationGroup.assetPath)
|
|
350
|
+
return finalRecord
|
|
351
|
+
})
|
|
352
|
+
|
|
248
353
|
const assetsDirectory = join(outputDirectory, "assets")
|
|
249
354
|
await mkdir(assetsDirectory, { recursive: true })
|
|
250
355
|
const { handlerModules: emittedHandlerModules, workerReferences: renderedWorkerReferences, styles: renderedStyles } = planRouteArtifacts(routeRecords, handlerModules, workerReferences, module => assetPath(base, `assets/${module.path}`))
|
|
251
356
|
const renderedStyleUrls = new Set(renderedStyles)
|
|
252
357
|
if (renderedWorkerReferences.length && await exists(join(publicDirectory, "assets", "workers"))) throw new Error("public/assets/workers collides with Kudzu's generated Worker asset namespace")
|
|
253
|
-
const workerAssets = await project.workerCompiler.emit(renderedWorkerReferences, sourceFileSet, assetsDirectory, base, minify)
|
|
358
|
+
const { assets: workerAssets, outputs: workerOutputs } = await project.workerCompiler.emit(renderedWorkerReferences, sourceFileSet, assetsDirectory, base, minify)
|
|
254
359
|
for (const module of emittedHandlerModules) {
|
|
255
360
|
for (const reference of workerReferences) {
|
|
256
361
|
if (reference.module !== assetPath(base, `assets/${module.path}`)) continue
|
|
@@ -260,63 +365,55 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
260
365
|
if (module.code.includes("/__kudzu_worker_")) throw new Error(`Worker URL placeholder survived in ${module.path}`)
|
|
261
366
|
}
|
|
262
367
|
const plans = routeRecords.map(record => record.plan)
|
|
263
|
-
const
|
|
264
|
-
const {
|
|
265
|
-
routes: { behaviors: behaviorCount, regularBehaviors: regularBehaviorCount, dependencyStateSeeds: dependencyStateSeedCount },
|
|
266
|
-
events: { command: commandEvents, hasNativeHandlers },
|
|
267
|
-
bindings: { count: bindingCount },
|
|
268
|
-
lists,
|
|
269
|
-
effects: { any: hasEffects, derivedDependencies: hasDerivedEffectDependencies, captures: hasEffectCaptures },
|
|
270
|
-
captures: { nestedState: hasNestedStateCaptures, setter: hasSetterCaptures },
|
|
271
|
-
runtime: { shared: hasSharedRuntime, dependency: hasDependencyRuntime }
|
|
272
|
-
} = capabilityIR
|
|
273
|
-
const runtimeName = usesDependencyRuntime => usesDependencyRuntime ? "kudzu-deps.js" : "kudzu.js"
|
|
368
|
+
const behaviorCount = routeRecords.filter(record => record.capabilities.hasBehaviors).length
|
|
274
369
|
for (let offset = 0; offset < routeRecords.length; offset += 64) {
|
|
275
370
|
await Promise.all(routeRecords.slice(offset, offset + 64).map(async record => {
|
|
276
371
|
const routeDirectory = join(outputDirectory, record.output)
|
|
277
372
|
await mkdir(routeDirectory, { recursive: true })
|
|
278
|
-
|
|
279
|
-
await writeFile(join(routeDirectory, "index.html"), html)
|
|
373
|
+
if ([runtimePlaceholder, bindingPlaceholder, listPlaceholder].some(placeholder => record.html.includes(placeholder))) throw new Error(`Runtime family placeholder survived in ${record.route}`)
|
|
374
|
+
await writeFile(join(routeDirectory, "index.html"), preloadModules(record.html))
|
|
280
375
|
}))
|
|
281
376
|
}
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
await
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
377
|
+
for (const family of runtimePlan.families) {
|
|
378
|
+
const capabilityIR = family.capability
|
|
379
|
+
const { bindings, captures, effects, events, lists, routes, runtime } = capabilityIR
|
|
380
|
+
const familyDirectory = join(assetsDirectory, "runtime", family.id)
|
|
381
|
+
await mkdir(familyDirectory, { recursive: true })
|
|
382
|
+
if (runtime.dependency) {
|
|
383
|
+
const source = await readFile(new URL("./dependency-runtime.js", import.meta.url), "utf8")
|
|
384
|
+
await writeJavaScript(join(familyDirectory, "kudzu-deps.js"), specializeRuntime(source, events.command, routes.dependencyStateSeeds > 0), minify)
|
|
385
|
+
} else {
|
|
386
|
+
const source = await readFile(new URL(runtime.shared ? "./shared-runtime.js" : "./runtime.js", import.meta.url), "utf8")
|
|
387
|
+
await writeJavaScript(join(familyDirectory, "kudzu.js"), generateCoreRuntime(source, capabilityIR), minify)
|
|
388
|
+
}
|
|
389
|
+
if (bindings.count || events.hasNativeHandlers || effects.captures) await writeJavaScript(join(familyDirectory, "kudzu-serialization.js"), await readFile(new URL("./serialization.js", import.meta.url), "utf8"), minify, {
|
|
390
|
+
"globalThis.__KUDZU_CAPTURE_STATE__": String(captures.nestedState),
|
|
391
|
+
"globalThis.__KUDZU_CAPTURE_SETTER__": String(captures.setter)
|
|
392
|
+
})
|
|
393
|
+
if (effects.any) {
|
|
394
|
+
const generated = generateEffectRuntime(await readFile(new URL("./effect-runtime.js", import.meta.url), "utf8"), capabilityIR)
|
|
395
|
+
await writeBundledJavaScript(join(familyDirectory, "kudzu-effect.js"), generated.source, minify, generated.define)
|
|
396
|
+
}
|
|
397
|
+
if (bindings.count || lists.styleCount) await writeJavaScript(join(familyDirectory, "kudzu-style.js"), await readFile(new URL("./style.js", import.meta.url), "utf8"), minify)
|
|
398
|
+
if (bindings.count) {
|
|
399
|
+
const generated = generateBindingRuntime(await readFile(new URL("./binding-runtime.js", import.meta.url), "utf8"), capabilityIR, family.navigation)
|
|
400
|
+
await writeBundledJavaScript(join(familyDirectory, "kudzu-binding.js"), generated.source, minify, generated.define)
|
|
401
|
+
}
|
|
402
|
+
if (effects.derivedDependencies || lists.selectors) await writeJavaScript(join(familyDirectory, "kudzu-collection-selector.js"), await readFile(new URL("./collection-selector.js", import.meta.url), "utf8"), minify)
|
|
403
|
+
if (lists.count) {
|
|
404
|
+
const generated = generateListRuntime(await readFile(new URL("./list-runtime.js", import.meta.url), "utf8"), capabilityIR)
|
|
405
|
+
await writeBundledJavaScript(join(familyDirectory, "kudzu-list.js"), generated.source, minify, generated.define)
|
|
406
|
+
}
|
|
407
|
+
if (events.hasNativeHandlers) {
|
|
408
|
+
const generated = generateNativeRuntime(await readFile(new URL("./native-runtime.js", import.meta.url), "utf8"), capabilityIR)
|
|
409
|
+
await writeJavaScript(join(familyDirectory, "kudzu-native.js"), generated.source, minify, generated.define)
|
|
410
|
+
}
|
|
315
411
|
}
|
|
412
|
+
for (const [path, source] of routeEntrySources) if (path.startsWith("native/")) await writeRetainedRouteEntry(path, source, assetsDirectory, minify, routeEntryTransforms)
|
|
316
413
|
if (navigationGroups.length) {
|
|
317
414
|
const navigationSource = await readFile(new URL("./navigation-runtime.js", import.meta.url), "utf8")
|
|
318
415
|
for (const group of navigationGroups) {
|
|
319
|
-
await writeJavaScript(join(assetsDirectory, group.assetName), generateNavigationRuntime(navigationSource, group), minify)
|
|
416
|
+
await writeJavaScript(join(assetsDirectory, "runtime", group.runtimeFamily.id, group.assetName), generateNavigationRuntime(navigationSource, group), minify)
|
|
320
417
|
}
|
|
321
418
|
}
|
|
322
419
|
for (const handlerModule of emittedHandlerModules) {
|
|
@@ -333,8 +430,9 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
333
430
|
await mkdir(dirname(output), { recursive: true })
|
|
334
431
|
await writeJavaScript(output, module.code, minify)
|
|
335
432
|
}
|
|
433
|
+
let handlerMetafile
|
|
336
434
|
if (clientModules.length || emittedHandlerModules.some(module => module.hasPackageImports)) {
|
|
337
|
-
await bundle({
|
|
435
|
+
const result = await bundle({
|
|
338
436
|
entryPoints: emittedHandlerModules.map(module => join(assetsDirectory, module.path)),
|
|
339
437
|
outbase: join(assetsDirectory, "handlers"),
|
|
340
438
|
outdir: join(assetsDirectory, "handlers"),
|
|
@@ -347,12 +445,16 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
347
445
|
target: "es2022",
|
|
348
446
|
minify,
|
|
349
447
|
legalComments: "none",
|
|
448
|
+
metafile: true,
|
|
350
449
|
logLevel: "silent"
|
|
351
450
|
})
|
|
451
|
+
handlerMetafile = result.metafile
|
|
352
452
|
await rm(join(assetsDirectory, "modules"), { recursive: true, force: true })
|
|
353
453
|
}
|
|
354
454
|
const sortedRewrites = rewrites.sort((left, right) => runtimeSpecificity(right) - runtimeSpecificity(left) || left.pattern.localeCompare(right.pattern))
|
|
355
455
|
await writeFile(join(workDirectory, "kudzu-plan.json"), JSON.stringify({ routes: plans, rewrites: sortedRewrites }, null, 2))
|
|
456
|
+
const artifacts = createRouteArtifactReport(routeRecords, { base, handlerMetafile, outputDirectory, navigationAssets, runtimeFamilies: runtimePlan.families, runtimeFamilyByRecord, workerReferences: renderedWorkerReferences, workerOutputs })
|
|
457
|
+
await writeFile(join(workDirectory, "kudzu-artifacts.json"), JSON.stringify(artifacts, null, 2))
|
|
356
458
|
const emittedCssFiles = new Set()
|
|
357
459
|
for (const file of cssFiles.filter(file => renderedStyleUrls.has(assetPath(base, `assets/${relative(sourceDirectory, file).replaceAll(sep, "/")}`)))) {
|
|
358
460
|
const output = join(assetsDirectory, relative(sourceDirectory, file))
|
|
@@ -384,10 +486,81 @@ async function buildInto(project, outputDirectory, { minify }) {
|
|
|
384
486
|
}
|
|
385
487
|
if (config.afterBuild !== undefined) {
|
|
386
488
|
if (typeof config.afterBuild !== "function") throw new Error("kudzu.config afterBuild must be a function")
|
|
387
|
-
await config.afterBuild({ root, outDir: outputDirectory, sourceDir: sourceDirectory, base, routes: plans.map(plan => plan.route), plans, rewrites: sortedRewrites })
|
|
489
|
+
await config.afterBuild({ root, outDir: outputDirectory, sourceDir: sourceDirectory, base, routes: plans.map(plan => plan.route), plans, rewrites: sortedRewrites, artifacts })
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const incremental = { compiledModules, renderedPages }
|
|
493
|
+
return {
|
|
494
|
+
result: { sourceResults, incremental },
|
|
495
|
+
pageCount: plans.length,
|
|
496
|
+
behaviorCount,
|
|
497
|
+
cache: { pageRenders, pageSources, placeholders, sourceResults: sourceResultsByFile }
|
|
388
498
|
}
|
|
499
|
+
}
|
|
389
500
|
|
|
390
|
-
|
|
501
|
+
function affectedPageFiles({ changedFiles, pageFiles, pageSources, previous, sourceDirectory }) {
|
|
502
|
+
if (!previous || changedFiles === undefined) return new Set(pageFiles)
|
|
503
|
+
if (changedFiles.some(file => typeof file !== "string")) return new Set(pageFiles)
|
|
504
|
+
const changes = new Set(changedFiles.map(file => isAbsolute(file) ? file : resolve(sourceDirectory, file)))
|
|
505
|
+
if ([...changes].some(file => !/\.(?:ts|tsx)$/.test(file))) return new Set(pageFiles)
|
|
506
|
+
const affected = new Set()
|
|
507
|
+
for (const page of pageFiles) {
|
|
508
|
+
const current = pageSources.get(page)
|
|
509
|
+
const prior = previous.pageSources.get(page)
|
|
510
|
+
if (!prior || [...changes].some(file => current.has(file) || prior.has(file))) affected.add(page)
|
|
511
|
+
}
|
|
512
|
+
return affected
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function expandAffectedNavigationGroups(affected, pageRenders, groups) {
|
|
516
|
+
if (!affected.size || !pageRenders || !groups.length) return
|
|
517
|
+
const groupRoutes = groups.map(group => new Set(group.routes))
|
|
518
|
+
const groupIndexes = new Set()
|
|
519
|
+
for (const page of affected) {
|
|
520
|
+
const render = pageRenders.get(page)
|
|
521
|
+
if (!render) {
|
|
522
|
+
for (let index = 0; index < groups.length; index++) groupIndexes.add(index)
|
|
523
|
+
continue
|
|
524
|
+
}
|
|
525
|
+
for (const draft of render.drafts) for (let index = 0; index < groups.length; index++) if (groupRoutes[index].has(draft.applicationRoute)) groupIndexes.add(index)
|
|
526
|
+
}
|
|
527
|
+
for (const [page, render] of pageRenders) {
|
|
528
|
+
if (render.drafts.some(draft => [...groupIndexes].some(index => groupRoutes[index].has(draft.applicationRoute)))) affected.add(page)
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function replayPageRender(cached, state) {
|
|
533
|
+
for (const rewrite of cached.rewrites) {
|
|
534
|
+
const conflicting = state.rewrites.find(entry => sameRuntimePrecedence(entry, rewrite))
|
|
535
|
+
if (conflicting) throw new Error(`Ambiguous runtime routes: ${conflicting.route} and ${rewrite.route}`)
|
|
536
|
+
state.rewrites.push(rewrite)
|
|
537
|
+
}
|
|
538
|
+
for (const entry of cached.navigationRecords) {
|
|
539
|
+
const group = state.navigationByRoute.get(entry.route)
|
|
540
|
+
const routeRecord = { ...entry, group }
|
|
541
|
+
state.emittedNavigationRecords.push(routeRecord)
|
|
542
|
+
state.emittedApplicationRoutes.add(entry.route)
|
|
543
|
+
if (!group) continue
|
|
544
|
+
if (typeof cached.layout !== "function") throw new Error(`${group.label} emitted route ${JSON.stringify(entry.record.path ?? entry.record.id)} must export a layout function so Kudzu can emit route markers`)
|
|
545
|
+
if (group.layoutIdentity && group.layoutIdentity !== cached.layout) throw new Error(`${group.label} routes ${JSON.stringify(group.layoutRoute)} and ${JSON.stringify(entry.route)} must export the same layout function identity`)
|
|
546
|
+
group.layoutIdentity = cached.layout
|
|
547
|
+
group.layoutRoute ??= entry.route
|
|
548
|
+
group.records.push(entry.record)
|
|
549
|
+
group.routeRecords.push(routeRecord)
|
|
550
|
+
}
|
|
551
|
+
for (const draft of cached.drafts) {
|
|
552
|
+
const navigationGroup = state.navigationByRoute.get(draft.applicationRoute)
|
|
553
|
+
if (state.emittedRoutes.has(draft.record.route)) throw new Error(`Duplicate route: ${draft.record.route}`)
|
|
554
|
+
state.emittedRoutes.add(draft.record.route)
|
|
555
|
+
if (navigationGroup) {
|
|
556
|
+
state.navigationAssets.set(draft.record.route, navigationGroup.assetPath)
|
|
557
|
+
navigationGroup.buildRecords.push(draft.record)
|
|
558
|
+
navigationGroup.hasEffects ||= draft.result.hasEffects
|
|
559
|
+
navigationGroup.hasParams ||= draft.result.hasParams
|
|
560
|
+
}
|
|
561
|
+
state.routeRecords.push(draft.record)
|
|
562
|
+
state.routeDrafts.push({ ...draft, navigationGroup })
|
|
563
|
+
}
|
|
391
564
|
}
|
|
392
565
|
|
|
393
566
|
async function acquireBuildLock(lockPath, root = dirname(lockPath)) {
|
|
@@ -489,10 +662,9 @@ function inlineQueryFormCarry(html, plan) {
|
|
|
489
662
|
return html.replace("</body>", `${script}</body>`)
|
|
490
663
|
}
|
|
491
664
|
|
|
492
|
-
function printNativeEntrySource(modules,
|
|
665
|
+
function printNativeEntrySource(modules, runtime) {
|
|
493
666
|
const imports = modules.map((module, index) => `import * as __kNativeModule${index} from ${JSON.stringify(module)}`).join("\n")
|
|
494
667
|
const registrations = modules.map((module, index) => `[${JSON.stringify(module)}, __kNativeModule${index}]`).join(",")
|
|
495
|
-
const runtime = assetPath(base, "assets/kudzu-native.js")
|
|
496
668
|
return `import { registerNativeModules } from ${JSON.stringify(runtime)}\n${imports}\nregisterNativeModules([${registrations}])`
|
|
497
669
|
}
|
|
498
670
|
|
|
@@ -545,7 +717,7 @@ async function writeBundledJavaScript(file, source, minify, define) {
|
|
|
545
717
|
stdin: { contents: source, resolveDir: dirname(file), sourcefile: file },
|
|
546
718
|
bundle: true,
|
|
547
719
|
write: false,
|
|
548
|
-
external: ["./kudzu.js", "./kudzu-binding.js", "./kudzu-serialization.js", "./kudzu-style.js"],
|
|
720
|
+
external: ["./kudzu.js", "./kudzu-binding.js", "./kudzu-collection-selector.js", "./kudzu-serialization.js", "./kudzu-style.js"],
|
|
549
721
|
define,
|
|
550
722
|
format: "esm",
|
|
551
723
|
target: "es2022",
|
|
@@ -562,7 +734,7 @@ export async function dev({ port = parseDevPort(process.env.PORT), host = parseD
|
|
|
562
734
|
const project = createProjectSession(projectRoot)
|
|
563
735
|
const { root, sourceDirectory, workDirectory, outputDirectory } = project
|
|
564
736
|
const base = normalizeBase((await loadConfig(root)).base)
|
|
565
|
-
return startDevServer({ build: options =>
|
|
737
|
+
return startDevServer({ build: options => buildWithSession(project, options), port, host, base, sourceDirectory, workDirectory, outputDirectory })
|
|
566
738
|
}
|
|
567
739
|
|
|
568
740
|
function inlineJson(value) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from "node:path"
|
|
2
2
|
|
|
3
3
|
export function createEffectCodegen({ assetPath, inlineJson, relativeModulePath }) {
|
|
4
|
-
function printEffectEntry(effects, output, handlerModules, assetsDirectory, base, paramPath, runtimeName) {
|
|
4
|
+
function printEffectEntry(effects, output, handlerModules, assetsDirectory, runtimeDirectory, base, paramPath, runtimeName) {
|
|
5
5
|
const hasCleanup = effects.some(effect => effect.cleanup)
|
|
6
6
|
const hasDependencies = effects.some(effect => effect.dependencies?.length || effect.itemDependencies?.length)
|
|
7
7
|
const hasOwners = effects.some(effect => effect.owner)
|
|
@@ -14,10 +14,10 @@ function printEffectEntry(effects, output, handlerModules, assetsDirectory, base
|
|
|
14
14
|
})
|
|
15
15
|
const imports = [
|
|
16
16
|
hasCleanup || hasDependencies || hasOwners
|
|
17
|
-
? `import * as __kRuntime from ${JSON.stringify(relativeModulePath(output, join(
|
|
18
|
-
: `import { browserState, commitDom } from ${JSON.stringify(relativeModulePath(output, join(
|
|
19
|
-
`import { createEffectContext } from ${JSON.stringify(relativeModulePath(output, join(
|
|
20
|
-
...(hasDependencyExpressions ? [`import { evaluateCollectionExpression as __kEvaluateDependency } from ${JSON.stringify(relativeModulePath(output, join(
|
|
17
|
+
? `import * as __kRuntime from ${JSON.stringify(relativeModulePath(output, join(runtimeDirectory, runtimeName)))}\nconst { browserState, commitDom } = __kRuntime`
|
|
18
|
+
: `import { browserState, commitDom } from ${JSON.stringify(relativeModulePath(output, join(runtimeDirectory, runtimeName)))}`,
|
|
19
|
+
`import { createEffectContext } from ${JSON.stringify(relativeModulePath(output, join(runtimeDirectory, "kudzu-effect.js")))}`,
|
|
20
|
+
...(hasDependencyExpressions ? [`import { evaluateCollectionExpression as __kEvaluateDependency } from ${JSON.stringify(relativeModulePath(output, join(runtimeDirectory, "kudzu-collection-selector.js")))}`] : []),
|
|
21
21
|
...(paramPath ? [`import ${JSON.stringify(relativeModulePath(output, join(assetsDirectory, paramPath)))}`] : []),
|
|
22
22
|
...modules.map((module, index) => `import * as __kEffectModule${index} from ${JSON.stringify(relativeModulePath(output, join(assetsDirectory, module.path)))}`)
|
|
23
23
|
]
|
|
@@ -178,7 +178,7 @@ addEventListener("pagehide", event => {
|
|
|
178
178
|
})`
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
function printNavigableEffectEntry(effects, output, handlerModules, assetsDirectory, base) {
|
|
181
|
+
function printNavigableEffectEntry(effects, output, handlerModules, assetsDirectory, runtimeDirectory, base) {
|
|
182
182
|
const hasDependencyExpressions = effects.some(effect => effect.dependencyExpressions?.length)
|
|
183
183
|
const moduleUrls = [...new Set(effects.map(effect => effect.module))]
|
|
184
184
|
const modules = moduleUrls.map(url => {
|
|
@@ -187,9 +187,9 @@ function printNavigableEffectEntry(effects, output, handlerModules, assetsDirect
|
|
|
187
187
|
return module
|
|
188
188
|
})
|
|
189
189
|
const imports = [
|
|
190
|
-
`import * as __kRuntime from ${JSON.stringify(relativeModulePath(output, join(
|
|
191
|
-
`import { createEffectContext } from ${JSON.stringify(relativeModulePath(output, join(
|
|
192
|
-
...(hasDependencyExpressions ? [`import { evaluateCollectionExpression as __kEvaluateDependency } from ${JSON.stringify(relativeModulePath(output, join(
|
|
190
|
+
`import * as __kRuntime from ${JSON.stringify(relativeModulePath(output, join(runtimeDirectory, "kudzu.js")))}`,
|
|
191
|
+
`import { createEffectContext } from ${JSON.stringify(relativeModulePath(output, join(runtimeDirectory, "kudzu-effect.js")))}`,
|
|
192
|
+
...(hasDependencyExpressions ? [`import { evaluateCollectionExpression as __kEvaluateDependency } from ${JSON.stringify(relativeModulePath(output, join(runtimeDirectory, "kudzu-collection-selector.js")))}`] : []),
|
|
193
193
|
...modules.map((module, index) => `import * as __kEffectModule${index} from ${JSON.stringify(relativeModulePath(output, join(assetsDirectory, module.path)))}`)
|
|
194
194
|
]
|
|
195
195
|
const entries = moduleUrls.map((url, index) => `[${JSON.stringify(url)}, __kEffectModule${index}]`).join(",")
|
|
@@ -300,7 +300,7 @@ ${hasDependencyExpressions ? printDerivedDependencyRead("__kRuntime.browserState
|
|
|
300
300
|
}`
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
function printOwnedNavigableEffectEntry(effects, output, handlerModules, assetsDirectory, base) {
|
|
303
|
+
function printOwnedNavigableEffectEntry(effects, output, handlerModules, assetsDirectory, runtimeDirectory, base) {
|
|
304
304
|
const hasItemDependencies = effects.some(effect => effect.itemDependencies?.length)
|
|
305
305
|
const hasDependencyExpressions = effects.some(effect => effect.dependencyExpressions?.length)
|
|
306
306
|
const moduleUrls = [...new Set(effects.map(effect => effect.module))]
|
|
@@ -310,9 +310,9 @@ function printOwnedNavigableEffectEntry(effects, output, handlerModules, assetsD
|
|
|
310
310
|
return module
|
|
311
311
|
})
|
|
312
312
|
const imports = [
|
|
313
|
-
`import * as __kRuntime from ${JSON.stringify(relativeModulePath(output, join(
|
|
314
|
-
`import { createEffectContext } from ${JSON.stringify(relativeModulePath(output, join(
|
|
315
|
-
...(hasDependencyExpressions ? [`import { evaluateCollectionExpression as __kEvaluateDependency } from ${JSON.stringify(relativeModulePath(output, join(
|
|
313
|
+
`import * as __kRuntime from ${JSON.stringify(relativeModulePath(output, join(runtimeDirectory, "kudzu.js")))}`,
|
|
314
|
+
`import { createEffectContext } from ${JSON.stringify(relativeModulePath(output, join(runtimeDirectory, "kudzu-effect.js")))}`,
|
|
315
|
+
...(hasDependencyExpressions ? [`import { evaluateCollectionExpression as __kEvaluateDependency } from ${JSON.stringify(relativeModulePath(output, join(runtimeDirectory, "kudzu-collection-selector.js")))}`] : []),
|
|
316
316
|
...modules.map((module, index) => `import * as __kEffectModule${index} from ${JSON.stringify(relativeModulePath(output, join(assetsDirectory, module.path)))}`)
|
|
317
317
|
]
|
|
318
318
|
const entries = moduleUrls.map((url, index) => `[${JSON.stringify(url)}, __kEffectModule${index}]`).join(",")
|
|
@@ -876,9 +876,9 @@ async function invokeCleanup() {
|
|
|
876
876
|
}${disposal}`
|
|
877
877
|
}
|
|
878
878
|
|
|
879
|
-
return (effects, output, handlerModules, assetsDirectory, base, paramPath, runtimeName, navigable) => navigable
|
|
879
|
+
return (effects, output, handlerModules, assetsDirectory, base, paramPath, runtimeName, navigable, runtimeDirectory = assetsDirectory) => navigable
|
|
880
880
|
? effects.some(effect => effect.owner)
|
|
881
|
-
? printOwnedNavigableEffectEntry(effects, output, handlerModules, assetsDirectory, base)
|
|
882
|
-
: printNavigableEffectEntry(effects, output, handlerModules, assetsDirectory, base)
|
|
883
|
-
: printEffectEntry(effects, output, handlerModules, assetsDirectory, base, paramPath, runtimeName)
|
|
881
|
+
? printOwnedNavigableEffectEntry(effects, output, handlerModules, assetsDirectory, runtimeDirectory, base)
|
|
882
|
+
: printNavigableEffectEntry(effects, output, handlerModules, assetsDirectory, runtimeDirectory, base)
|
|
883
|
+
: printEffectEntry(effects, output, handlerModules, assetsDirectory, runtimeDirectory, base, paramPath, runtimeName)
|
|
884
884
|
}
|