@kuratchi/js 0.0.14 → 0.0.16

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.
Files changed (65) hide show
  1. package/README.md +135 -68
  2. package/dist/cli.js +80 -47
  3. package/dist/compiler/api-route-pipeline.d.ts +8 -0
  4. package/dist/compiler/api-route-pipeline.js +23 -0
  5. package/dist/compiler/asset-pipeline.d.ts +7 -0
  6. package/dist/compiler/asset-pipeline.js +33 -0
  7. package/dist/compiler/client-module-pipeline.d.ts +25 -0
  8. package/dist/compiler/client-module-pipeline.js +257 -0
  9. package/dist/compiler/compiler-shared.d.ts +55 -0
  10. package/dist/compiler/compiler-shared.js +4 -0
  11. package/dist/compiler/component-pipeline.d.ts +15 -0
  12. package/dist/compiler/component-pipeline.js +163 -0
  13. package/dist/compiler/config-reading.d.ts +11 -0
  14. package/dist/compiler/config-reading.js +323 -0
  15. package/dist/compiler/convention-discovery.d.ts +9 -0
  16. package/dist/compiler/convention-discovery.js +83 -0
  17. package/dist/compiler/durable-object-pipeline.d.ts +9 -0
  18. package/dist/compiler/durable-object-pipeline.js +255 -0
  19. package/dist/compiler/error-page-pipeline.d.ts +1 -0
  20. package/dist/compiler/error-page-pipeline.js +16 -0
  21. package/dist/compiler/import-linking.d.ts +36 -0
  22. package/dist/compiler/import-linking.js +139 -0
  23. package/dist/compiler/index.d.ts +3 -3
  24. package/dist/compiler/index.js +137 -3265
  25. package/dist/compiler/layout-pipeline.d.ts +31 -0
  26. package/dist/compiler/layout-pipeline.js +155 -0
  27. package/dist/compiler/page-route-pipeline.d.ts +16 -0
  28. package/dist/compiler/page-route-pipeline.js +62 -0
  29. package/dist/compiler/parser.d.ts +4 -0
  30. package/dist/compiler/parser.js +433 -51
  31. package/dist/compiler/root-layout-pipeline.d.ts +10 -0
  32. package/dist/compiler/root-layout-pipeline.js +517 -0
  33. package/dist/compiler/route-discovery.d.ts +7 -0
  34. package/dist/compiler/route-discovery.js +87 -0
  35. package/dist/compiler/route-pipeline.d.ts +57 -0
  36. package/dist/compiler/route-pipeline.js +296 -0
  37. package/dist/compiler/route-state-pipeline.d.ts +25 -0
  38. package/dist/compiler/route-state-pipeline.js +139 -0
  39. package/dist/compiler/routes-module-feature-blocks.d.ts +2 -0
  40. package/dist/compiler/routes-module-feature-blocks.js +330 -0
  41. package/dist/compiler/routes-module-pipeline.d.ts +2 -0
  42. package/dist/compiler/routes-module-pipeline.js +6 -0
  43. package/dist/compiler/routes-module-runtime-shell.d.ts +2 -0
  44. package/dist/compiler/routes-module-runtime-shell.js +81 -0
  45. package/dist/compiler/routes-module-types.d.ts +44 -0
  46. package/dist/compiler/routes-module-types.js +1 -0
  47. package/dist/compiler/script-transform.d.ts +16 -0
  48. package/dist/compiler/script-transform.js +218 -0
  49. package/dist/compiler/server-module-pipeline.d.ts +13 -0
  50. package/dist/compiler/server-module-pipeline.js +124 -0
  51. package/dist/compiler/template.d.ts +13 -1
  52. package/dist/compiler/template.js +323 -60
  53. package/dist/compiler/worker-output-pipeline.d.ts +13 -0
  54. package/dist/compiler/worker-output-pipeline.js +37 -0
  55. package/dist/compiler/wrangler-sync.d.ts +14 -0
  56. package/dist/compiler/wrangler-sync.js +185 -0
  57. package/dist/runtime/app.js +15 -3
  58. package/dist/runtime/generated-worker.d.ts +33 -0
  59. package/dist/runtime/generated-worker.js +412 -0
  60. package/dist/runtime/index.d.ts +2 -1
  61. package/dist/runtime/index.js +1 -0
  62. package/dist/runtime/router.d.ts +2 -1
  63. package/dist/runtime/router.js +12 -3
  64. package/dist/runtime/types.d.ts +8 -2
  65. package/package.json +5 -1
@@ -0,0 +1,296 @@
1
+ import { stripTopLevelImports } from './parser.js';
2
+ import { transpileTypeScript } from './transpile.js';
3
+ import { buildDevAliasDeclarations, buildSegmentedScriptBody, rewriteImportedFunctionCalls, rewriteWorkerEnvAliases, } from './script-transform.js';
4
+ function dedupe(items) {
5
+ return Array.from(new Set(items));
6
+ }
7
+ function extractDeclaredConstName(statement) {
8
+ const match = statement.trim().match(/^const\s+([A-Za-z_$][\w$]*)\s*=/);
9
+ return match ? match[1] : null;
10
+ }
11
+ function buildLoadQueryStateCode(opts) {
12
+ const lines = [];
13
+ for (const query of opts.queries) {
14
+ const fnName = query.fnName;
15
+ const rpcId = query.rpcId || opts.rpcNameMap?.get(fnName) || fnName;
16
+ const argsExpr = (query.argsExpr || '').trim();
17
+ const asName = query.asName;
18
+ const defaultArgs = argsExpr ? `[${argsExpr}]` : '[]';
19
+ const moduleId = opts.fnToModule[fnName];
20
+ const qualifiedFn = moduleId ? `${moduleId}.${fnName}` : fnName;
21
+ lines.push(`let ${asName} = { state: 'loading', loading: true, error: null, data: null, empty: false, success: false };`);
22
+ lines.push(`const __qOverride_${asName} = __getLocals().__queryOverride;`);
23
+ lines.push(`const __qArgs_${asName} = ${defaultArgs};`);
24
+ lines.push(`const __qShouldRun_${asName} = !!(__qOverride_${asName} && __qOverride_${asName}.fn === '${rpcId}' && Array.isArray(__qOverride_${asName}.args) && JSON.stringify(__qOverride_${asName}.args) === JSON.stringify(__qArgs_${asName}));`);
25
+ lines.push(`if (__qShouldRun_${asName}) {`);
26
+ lines.push(` try {`);
27
+ lines.push(` const __qData_${asName} = await ${qualifiedFn}(...__qArgs_${asName});`);
28
+ lines.push(` const __qEmpty_${asName} = Array.isArray(__qData_${asName}) ? __qData_${asName}.length === 0 : (__qData_${asName} == null);`);
29
+ lines.push(` ${asName} = { state: __qEmpty_${asName} ? 'empty' : 'success', loading: false, error: null, data: __qData_${asName}, empty: __qEmpty_${asName}, success: !__qEmpty_${asName} };`);
30
+ lines.push(` } catch (err) {`);
31
+ lines.push(` const __qErr_${asName} = (err && err.message) ? String(err.message) : String(err);`);
32
+ lines.push(` ${asName} = { state: 'error', loading: false, error: __qErr_${asName}, data: null, empty: false, success: false };`);
33
+ lines.push(` }`);
34
+ lines.push(`}`);
35
+ }
36
+ return lines.join('\n');
37
+ }
38
+ function buildGeneratedLoadPlan(opts) {
39
+ const loadSections = [];
40
+ if (opts.scriptBody && opts.scriptUsesAwait) {
41
+ loadSections.push(opts.scriptBody);
42
+ }
43
+ if (opts.queries.length > 0) {
44
+ loadSections.push(buildLoadQueryStateCode({
45
+ queries: opts.queries,
46
+ fnToModule: opts.fnToModule,
47
+ rpcNameMap: opts.rpcNameMap,
48
+ }));
49
+ }
50
+ const queryVars = opts.queries.map((query) => query.asName);
51
+ const returnVars = dedupe([...opts.scriptReturnVars, ...queryVars]);
52
+ const loadLines = [];
53
+ if (loadSections.length > 0) {
54
+ loadLines.push(loadSections.join('\n'));
55
+ }
56
+ if (returnVars.length > 0) {
57
+ if (opts.hasSegmentedScripts && opts.scriptUsesAwait) {
58
+ const segmentReturnEntries = opts.scriptReturnVars.map((name) => `${name}: __segmentData.${name}`);
59
+ const queryReturnEntries = queryVars
60
+ .filter((name) => !opts.scriptReturnVars.includes(name))
61
+ .map((name) => name);
62
+ loadLines.push(`return { ${[...segmentReturnEntries, ...queryReturnEntries].join(', ')} };`);
63
+ }
64
+ else {
65
+ loadLines.push(`return { ${returnVars.join(', ')} };`);
66
+ }
67
+ }
68
+ return {
69
+ mode: 'generated',
70
+ code: `async load(__routeParams = {}) {\n ${loadLines.join('\n ')}\n }`,
71
+ returnVars,
72
+ scriptUsesAwait: opts.scriptUsesAwait,
73
+ };
74
+ }
75
+ function assertRoutePlanInvariants(opts) {
76
+ const loadReturnSet = new Set(opts.loadReturnVars);
77
+ const leakedActions = opts.actionNames.filter((name) => loadReturnSet.has(name));
78
+ if (leakedActions.length > 0) {
79
+ throw new Error(`[kuratchi compiler] ${opts.pattern}\nGenerated load data cannot include action bindings: ${leakedActions.join(', ')}`);
80
+ }
81
+ const reservedQueryNames = opts.queryVars.filter((name) => opts.actionNames.includes(name));
82
+ if (reservedQueryNames.length > 0) {
83
+ throw new Error(`[kuratchi compiler] ${opts.pattern}\nQuery aliases cannot collide with action bindings: ${reservedQueryNames.join(', ')}`);
84
+ }
85
+ }
86
+ export function analyzeRouteBuild(opts) {
87
+ const { pattern, renderBody, renderHeadBody, isDev, parsed, fnToModule, rpcNameMap, componentStyles, clientModuleHref } = opts;
88
+ const hasFns = Object.keys(fnToModule).length > 0;
89
+ const queryDefs = parsed.dataGetQueries ?? [];
90
+ const queryVars = queryDefs.map((query) => query.asName);
91
+ const scriptSegments = (parsed.scriptSegments ?? []).filter((segment) => !!segment.script);
92
+ const hasSegmentedScripts = scriptSegments.length > 1;
93
+ const routeDevDecls = buildDevAliasDeclarations(parsed.devAliases, isDev);
94
+ const routeImportDeclLines = parsed.scriptImportDecls ?? [];
95
+ const routeImportDecls = routeImportDeclLines.join('\n');
96
+ const importedBindingNames = new Set(Object.keys(fnToModule));
97
+ const renderScopeActionNames = new Set(parsed.actionFunctions);
98
+ const renderImportPrelude = routeImportDeclLines
99
+ .filter((statement) => {
100
+ const declaredName = extractDeclaredConstName(statement);
101
+ return declaredName ? !renderScopeActionNames.has(declaredName) : true;
102
+ })
103
+ .join('\n');
104
+ let scriptBody = '';
105
+ let renderPreludeSource = '';
106
+ let scriptUsesAwait = false;
107
+ if (hasSegmentedScripts) {
108
+ const combinedScript = scriptSegments.map((segment) => stripTopLevelImports(segment.script)).join('\n');
109
+ scriptUsesAwait = /\bawait\b/.test(combinedScript);
110
+ scriptBody = buildSegmentedScriptBody({
111
+ segments: scriptSegments,
112
+ fnToModule,
113
+ importDecls: routeImportDecls,
114
+ workerEnvAliases: parsed.workerEnvAliases,
115
+ devAliases: parsed.devAliases,
116
+ isDev,
117
+ asyncMode: scriptUsesAwait,
118
+ });
119
+ renderPreludeSource = buildSegmentedScriptBody({
120
+ segments: scriptSegments,
121
+ fnToModule,
122
+ importDecls: renderImportPrelude,
123
+ workerEnvAliases: parsed.workerEnvAliases,
124
+ devAliases: parsed.devAliases,
125
+ isDev,
126
+ asyncMode: false,
127
+ });
128
+ }
129
+ else {
130
+ const strippedScriptBody = parsed.script
131
+ ? stripTopLevelImports(parsed.script)
132
+ : '';
133
+ scriptBody = [routeDevDecls, routeImportDecls, strippedScriptBody].filter(Boolean).join('\n');
134
+ renderPreludeSource = [routeDevDecls, renderImportPrelude, strippedScriptBody].filter(Boolean).join('\n');
135
+ scriptBody = rewriteImportedFunctionCalls(scriptBody, fnToModule);
136
+ scriptBody = rewriteWorkerEnvAliases(scriptBody, parsed.workerEnvAliases);
137
+ renderPreludeSource = rewriteImportedFunctionCalls(renderPreludeSource, fnToModule);
138
+ renderPreludeSource = rewriteWorkerEnvAliases(renderPreludeSource, parsed.workerEnvAliases);
139
+ scriptUsesAwait = /\bawait\b/.test(scriptBody);
140
+ }
141
+ let explicitLoadFunction = parsed.loadFunction
142
+ ? parsed.loadFunction.replace(/^export\s+/, '').trim()
143
+ : '';
144
+ if (explicitLoadFunction) {
145
+ explicitLoadFunction = [routeDevDecls, explicitLoadFunction].filter(Boolean).join('\n');
146
+ explicitLoadFunction = rewriteImportedFunctionCalls(explicitLoadFunction, fnToModule);
147
+ explicitLoadFunction = rewriteWorkerEnvAliases(explicitLoadFunction, parsed.workerEnvAliases);
148
+ if (routeImportDecls)
149
+ explicitLoadFunction = explicitLoadFunction.replace('{', `{\n${routeImportDecls}\n`);
150
+ }
151
+ if (explicitLoadFunction && scriptUsesAwait) {
152
+ throw new Error(`[kuratchi compiler] ${pattern}\nTop-level await cannot be mixed with export async function load(). Move async server work into load().`);
153
+ }
154
+ if (scriptBody) {
155
+ scriptBody = transpileTypeScript(scriptBody, `route-script:${pattern}.ts`);
156
+ }
157
+ if (renderPreludeSource) {
158
+ renderPreludeSource = transpileTypeScript(renderPreludeSource, `route-render:${pattern}.ts`);
159
+ }
160
+ if (explicitLoadFunction) {
161
+ explicitLoadFunction = transpileTypeScript(explicitLoadFunction, `route-load:${pattern}.ts`);
162
+ }
163
+ const scriptReturnVars = parsed.script
164
+ ? parsed.dataVars.filter((name) => !queryVars.includes(name) &&
165
+ !importedBindingNames.has(name) &&
166
+ !parsed.actionFunctions.includes(name) &&
167
+ !parsed.pollFunctions.includes(name))
168
+ : [];
169
+ const loadPlan = explicitLoadFunction
170
+ ? {
171
+ mode: 'explicit',
172
+ code: `load: ${explicitLoadFunction}`,
173
+ returnVars: [...parsed.loadReturnVars],
174
+ scriptUsesAwait: false,
175
+ }
176
+ : ((scriptBody && scriptUsesAwait) || queryDefs.length > 0)
177
+ ? buildGeneratedLoadPlan({
178
+ pattern,
179
+ scriptBody,
180
+ scriptUsesAwait,
181
+ scriptReturnVars,
182
+ queries: queryDefs,
183
+ hasSegmentedScripts,
184
+ fnToModule,
185
+ rpcNameMap,
186
+ })
187
+ : {
188
+ mode: 'none',
189
+ code: '',
190
+ returnVars: [],
191
+ scriptUsesAwait,
192
+ };
193
+ const actions = hasFns
194
+ ? parsed.actionFunctions.map((name) => {
195
+ const moduleId = fnToModule[name];
196
+ return { name, expression: moduleId ? `${moduleId}.${name}` : name };
197
+ })
198
+ : [];
199
+ const rpc = hasFns
200
+ ? parsed.pollFunctions.map((name) => {
201
+ const moduleId = fnToModule[name];
202
+ const rpcId = rpcNameMap?.get(name) || name;
203
+ return { name, rpcId, expression: moduleId ? `${moduleId}.${name}` : name };
204
+ })
205
+ : [];
206
+ assertRoutePlanInvariants({
207
+ pattern,
208
+ loadReturnVars: loadPlan.returnVars,
209
+ actionNames: actions.map((action) => action.name),
210
+ queryVars,
211
+ });
212
+ const renderDataVars = dedupe([
213
+ ...queryVars,
214
+ ...(scriptUsesAwait ? scriptReturnVars : []),
215
+ ...parsed.actionFunctions,
216
+ 'params',
217
+ 'breadcrumbs',
218
+ ]);
219
+ const renderPrelude = !scriptUsesAwait
220
+ ? renderPreludeSource
221
+ : renderImportPrelude;
222
+ return {
223
+ pattern,
224
+ load: loadPlan,
225
+ actions,
226
+ rpc,
227
+ render: {
228
+ prelude: renderPrelude,
229
+ dataVars: renderDataVars,
230
+ body: renderBody,
231
+ headBody: renderHeadBody,
232
+ componentStyles,
233
+ clientModuleHref: clientModuleHref ?? null,
234
+ },
235
+ };
236
+ }
237
+ export function emitRouteObject(plan) {
238
+ const parts = [];
239
+ parts.push(` pattern: '${plan.pattern}'`);
240
+ if (plan.load.mode === 'explicit' || plan.load.mode === 'generated') {
241
+ parts.push(` ${plan.load.code}`);
242
+ }
243
+ if (plan.actions.length > 0) {
244
+ const actionEntries = plan.actions
245
+ .map((action) => `'${action.name}': ${action.expression}`)
246
+ .join(', ');
247
+ parts.push(` actions: { ${actionEntries} }`);
248
+ }
249
+ if (plan.rpc.length > 0) {
250
+ const rpcEntries = plan.rpc
251
+ .map((rpc) => `'${rpc.rpcId}': ${rpc.expression}`)
252
+ .join(', ');
253
+ parts.push(` rpc: { ${rpcEntries} }`);
254
+ }
255
+ const destructure = `const { ${plan.render.dataVars.join(', ')} } = data;\n `;
256
+ let finalHeadRenderBody = plan.render.headBody;
257
+ if (plan.render.componentStyles.length > 0) {
258
+ const lines = plan.render.headBody.split('\n');
259
+ const styleLines = plan.render.componentStyles.map((css) => `__html += \`${css}\\n\`;`);
260
+ finalHeadRenderBody = [lines[0], ...styleLines, ...lines.slice(1)].join('\n');
261
+ }
262
+ if (plan.render.clientModuleHref) {
263
+ finalHeadRenderBody += `\n__html += \`<script type="module" src="${plan.render.clientModuleHref}"></script>\\n\`;`;
264
+ }
265
+ parts.push(` render(data) {
266
+ ${destructure}${plan.render.prelude ? plan.render.prelude + '\n ' : ''}const __head = (() => {
267
+ ${finalHeadRenderBody}
268
+ return __html;
269
+ })();
270
+ const __rendered = (() => {
271
+ const __fragments = Object.create(null);
272
+ const __fragmentStack = [];
273
+ const __emit = (chunk) => {
274
+ const __value = chunk == null ? '' : String(chunk);
275
+ __html += __value;
276
+ for (const __fragmentId of __fragmentStack) {
277
+ __fragments[__fragmentId] = (__fragments[__fragmentId] || '') + __value;
278
+ }
279
+ };
280
+ const __pushFragment = (id) => {
281
+ const __key = String(id);
282
+ if (!Object.prototype.hasOwnProperty.call(__fragments, __key)) {
283
+ __fragments[__key] = '';
284
+ }
285
+ __fragmentStack.push(__key);
286
+ };
287
+ const __popFragment = () => {
288
+ __fragmentStack.pop();
289
+ };
290
+ ${plan.render.body}
291
+ return { html: __html, fragments: __fragments };
292
+ })();
293
+ return { html: __rendered.html, head: __head, fragments: __rendered.fragments };
294
+ }`);
295
+ return ` {\n${parts.join(',\n')}\n }`;
296
+ }
@@ -0,0 +1,25 @@
1
+ import { type ParsedFile } from './parser.js';
2
+ import type { RouteImportEntry } from './import-linking.js';
3
+ export interface RouteScriptSegment {
4
+ script: string;
5
+ dataVars: string[];
6
+ }
7
+ export interface MergedRouteParsed extends ParsedFile {
8
+ scriptImportDecls: string[];
9
+ scriptSegments: RouteScriptSegment[];
10
+ }
11
+ export interface RouteStatePlan {
12
+ effectiveTemplate: string;
13
+ routeImportDecls: string[];
14
+ routeScriptReferenceSource: string;
15
+ routeServerImportEntries: RouteImportEntry[];
16
+ routeClientImportEntries: RouteImportEntry[];
17
+ routeBrowserImportEntries: RouteImportEntry[];
18
+ mergedParsed: MergedRouteParsed;
19
+ }
20
+ export declare function assembleRouteState(opts: {
21
+ parsed: ParsedFile;
22
+ fullPath: string;
23
+ routesDir: string;
24
+ layoutRelativePaths: string[];
25
+ }): RouteStatePlan;
@@ -0,0 +1,139 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { parseFile, stripTopLevelImports } from './parser.js';
4
+ function pushUnique(target, value) {
5
+ if (!target.includes(value))
6
+ target.push(value);
7
+ }
8
+ function isSharedImport(line) {
9
+ return /\bfrom\s+['"]\$shared\//.test(line);
10
+ }
11
+ export function assembleRouteState(opts) {
12
+ const { parsed, fullPath, routesDir, layoutRelativePaths } = opts;
13
+ let effectiveTemplate = parsed.template;
14
+ const routeScriptParts = [];
15
+ const routeScriptSegments = [];
16
+ const routeServerImportEntries = parsed.serverImports.map((line) => ({
17
+ line,
18
+ importerDir: path.dirname(fullPath),
19
+ }));
20
+ const routeClientImportEntries = parsed.clientImports.map((line) => ({
21
+ line,
22
+ importerDir: path.dirname(fullPath),
23
+ }));
24
+ const routeBrowserImportEntries = [
25
+ ...parsed.routeClientImports.map((line) => ({
26
+ line,
27
+ importerDir: path.dirname(fullPath),
28
+ })),
29
+ ...parsed.serverImports.filter(isSharedImport).map((line) => ({
30
+ line,
31
+ importerDir: path.dirname(fullPath),
32
+ })),
33
+ ];
34
+ const mergedActionFunctions = [...parsed.actionFunctions];
35
+ const mergedDataVars = [...parsed.dataVars];
36
+ const mergedPollFunctions = [...parsed.pollFunctions];
37
+ const mergedDataGetQueries = parsed.dataGetQueries.map((query) => ({ ...query }));
38
+ const mergedComponentImports = { ...parsed.componentImports };
39
+ const mergedWorkerEnvAliases = [...parsed.workerEnvAliases];
40
+ const mergedDevAliases = [...parsed.devAliases];
41
+ const mergedRouteClientImports = [...parsed.routeClientImports];
42
+ const mergedRouteClientImportBindings = [...parsed.routeClientImportBindings];
43
+ for (const layoutRelPath of layoutRelativePaths) {
44
+ if (layoutRelPath === 'layout.html')
45
+ continue;
46
+ const layoutPath = path.join(routesDir, layoutRelPath);
47
+ if (!fs.existsSync(layoutPath))
48
+ continue;
49
+ const layoutSource = fs.readFileSync(layoutPath, 'utf-8');
50
+ const layoutParsed = parseFile(layoutSource, { kind: 'layout', filePath: layoutPath });
51
+ if (layoutParsed.loadFunction) {
52
+ throw new Error(`${layoutRelPath} cannot export load(); nested layouts currently share the child route load lifecycle.`);
53
+ }
54
+ const layoutSlot = layoutParsed.template.match(/<slot\s*><\/slot>|<slot\s*\/>/);
55
+ if (!layoutSlot) {
56
+ throw new Error(`${layoutRelPath} must contain <slot></slot> or <slot />`);
57
+ }
58
+ if (layoutParsed.script) {
59
+ routeScriptParts.push(layoutParsed.script);
60
+ routeScriptSegments.push({ script: layoutParsed.script, dataVars: [...layoutParsed.dataVars] });
61
+ }
62
+ for (const line of layoutParsed.serverImports) {
63
+ routeServerImportEntries.push({ line, importerDir: path.dirname(layoutPath) });
64
+ }
65
+ for (const line of layoutParsed.clientImports) {
66
+ routeClientImportEntries.push({ line, importerDir: path.dirname(layoutPath) });
67
+ }
68
+ for (const line of layoutParsed.routeClientImports) {
69
+ routeBrowserImportEntries.push({ line, importerDir: path.dirname(layoutPath) });
70
+ pushUnique(mergedRouteClientImports, line);
71
+ }
72
+ for (const line of layoutParsed.serverImports.filter(isSharedImport)) {
73
+ routeBrowserImportEntries.push({ line, importerDir: path.dirname(layoutPath) });
74
+ }
75
+ for (const binding of layoutParsed.routeClientImportBindings) {
76
+ pushUnique(mergedRouteClientImportBindings, binding);
77
+ }
78
+ for (const fnName of layoutParsed.actionFunctions) {
79
+ pushUnique(mergedActionFunctions, fnName);
80
+ }
81
+ for (const varName of layoutParsed.dataVars) {
82
+ pushUnique(mergedDataVars, varName);
83
+ }
84
+ for (const fnName of layoutParsed.pollFunctions) {
85
+ pushUnique(mergedPollFunctions, fnName);
86
+ }
87
+ for (const query of layoutParsed.dataGetQueries) {
88
+ if (!mergedDataGetQueries.some((existing) => existing.asName === query.asName)) {
89
+ mergedDataGetQueries.push({ ...query });
90
+ }
91
+ }
92
+ for (const [pascalName, fileName] of Object.entries(layoutParsed.componentImports)) {
93
+ mergedComponentImports[pascalName] = fileName;
94
+ }
95
+ for (const alias of layoutParsed.workerEnvAliases) {
96
+ pushUnique(mergedWorkerEnvAliases, alias);
97
+ }
98
+ for (const alias of layoutParsed.devAliases) {
99
+ pushUnique(mergedDevAliases, alias);
100
+ }
101
+ effectiveTemplate = layoutParsed.template.replace(layoutSlot[0], effectiveTemplate);
102
+ }
103
+ if (parsed.script) {
104
+ routeScriptParts.push(parsed.script);
105
+ routeScriptSegments.push({ script: parsed.script, dataVars: [...parsed.dataVars] });
106
+ }
107
+ const routeImportDecls = [];
108
+ const routeScriptReferenceSource = [
109
+ ...routeScriptParts.map((script) => stripTopLevelImports(script)),
110
+ parsed.loadFunction || '',
111
+ ].join('\n');
112
+ const mergedParsed = {
113
+ ...parsed,
114
+ template: effectiveTemplate,
115
+ script: routeScriptParts.length > 0 ? routeScriptParts.join('\n\n') : parsed.script,
116
+ serverImports: routeServerImportEntries.map((entry) => entry.line),
117
+ clientImports: routeClientImportEntries.map((entry) => entry.line),
118
+ routeClientImports: mergedRouteClientImports,
119
+ routeClientImportBindings: mergedRouteClientImportBindings,
120
+ actionFunctions: mergedActionFunctions,
121
+ dataVars: mergedDataVars,
122
+ componentImports: mergedComponentImports,
123
+ pollFunctions: mergedPollFunctions,
124
+ dataGetQueries: mergedDataGetQueries,
125
+ workerEnvAliases: mergedWorkerEnvAliases,
126
+ devAliases: mergedDevAliases,
127
+ scriptImportDecls: routeImportDecls,
128
+ scriptSegments: routeScriptSegments,
129
+ };
130
+ return {
131
+ effectiveTemplate,
132
+ routeImportDecls,
133
+ routeScriptReferenceSource,
134
+ routeServerImportEntries,
135
+ routeClientImportEntries,
136
+ routeBrowserImportEntries,
137
+ mergedParsed,
138
+ };
139
+ }
@@ -0,0 +1,2 @@
1
+ import type { GenerateRoutesModuleOptions, RoutesModuleFeatureBlocks } from './routes-module-types.js';
2
+ export declare function buildRoutesModuleFeatureBlocks(opts: GenerateRoutesModuleOptions): RoutesModuleFeatureBlocks;