@tailorkit/app 0.1.0-beta.23 → 0.1.0-beta.25

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/dist/builder.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  loadTailorKitConfig
3
- } from "./chunk-DEU6RITM.js";
4
- import "./chunk-GVB2PAK7.js";
3
+ } from "./chunk-L5ECQGR7.js";
4
+ import "./chunk-DA2TE2FW.js";
5
5
 
6
6
  // src/builder/index.ts
7
- import path7 from "path";
7
+ import path9 from "path";
8
8
  import { createRequire as createRequire2 } from "module";
9
- import { readFile as readFile4, realpath, writeFile as writeFile2 } from "fs/promises";
9
+ import { readFile as readFile4, realpath, writeFile as writeFile3 } from "fs/promises";
10
10
 
11
11
  // ../asset-delivery/dist/logo-validation.mjs
12
12
  var maxLogoBytes = 262144;
@@ -136,7 +136,7 @@ var inspectClient = `
136
136
  import { readFile } from "node:fs/promises";
137
137
  const source = await readFile(process.argv[1], "utf8");
138
138
  const { default: client } = await import("data:text/javascript;base64," + Buffer.from(source).toString("base64"));
139
- if (!client?.slots || typeof client.slots !== "object") throw new Error("Client must default-export defineClient(...)");
139
+ if (!client?.slots || typeof client.slots !== "object") throw new Error("Invalid generated app client: missing slots");
140
140
  const instanceResolvers = [];
141
141
  const views = Object.entries(client.slots).flatMap(([slot, definitions]) =>
142
142
  Object.entries(definitions).flatMap(([path, view]) => {
@@ -169,15 +169,15 @@ async function readClientManifest(filename) {
169
169
  return { views, instanceResolvers: manifest.instanceResolvers };
170
170
  } catch (error) {
171
171
  throw new Error(
172
- "Unable to read app views during the build. Keep browser-only side effects inside components or effects, and default-export defineClient(...).",
172
+ "Unable to read app views during the build. Keep browser-only side effects inside components or effects. Check the default exports of your roots, layouts and .view.tsx files.",
173
173
  { cause: error }
174
174
  );
175
175
  }
176
176
  }
177
177
 
178
178
  // src/builder/server.ts
179
- import { mkdir, mkdtemp as mkdtemp2, writeFile, cp, rm as rm2 } from "fs/promises";
180
- import path5 from "path";
179
+ import { mkdir as mkdir2, mkdtemp as mkdtemp3, writeFile as writeFile2, cp, rm as rm3 } from "fs/promises";
180
+ import path7 from "path";
181
181
  import { build } from "vite";
182
182
 
183
183
  // src/builder/migrations.ts
@@ -219,24 +219,35 @@ async function readAppMigrations(directory, optional = false) {
219
219
  // src/server/bundle.ts
220
220
  var runtimeManifest = {
221
221
  apiVersion: 1,
222
- requires: ["database", "actions"]
222
+ requires: ["database", "actions", "database-relations"]
223
223
  };
224
224
 
225
225
  // src/builder/schema-check.ts
226
226
  import { execFile as execFile2 } from "child_process";
227
227
  import { access as access2, mkdtemp, readFile as readFile2, readdir as readdir2, rm } from "fs/promises";
228
228
  import { tmpdir } from "os";
229
- import path3 from "path";
229
+ import path4 from "path";
230
230
  import { promisify as promisify2 } from "util";
231
231
 
232
232
  // src/builder/drizzle.ts
233
233
  import { access } from "fs/promises";
234
234
  import { createRequire } from "module";
235
+ import path3 from "path";
236
+
237
+ // src/builder/database-paths.ts
235
238
  import path2 from "path";
239
+ function appDatabasePaths(root, migrations) {
240
+ return {
241
+ schema: path2.join(root, "src/db/schema.ts"),
242
+ migrations: path2.resolve(root, migrations ?? "./src/db/migrations")
243
+ };
244
+ }
245
+
246
+ // src/builder/drizzle.ts
236
247
  async function drizzleGenerateArguments(root, output, name) {
237
- const require2 = createRequire(path2.join(root, "package.json"));
248
+ const require2 = createRequire(path3.join(root, "package.json"));
238
249
  for (const directory of require2.resolve.paths("drizzle-kit") ?? []) {
239
- const cli = path2.join(directory, "drizzle-kit/bin.cjs");
250
+ const cli = path3.join(directory, "drizzle-kit/bin.cjs");
240
251
  if (await access(cli).then(() => true).catch(() => false)) {
241
252
  return [
242
253
  "--preserve-symlinks-main",
@@ -245,7 +256,7 @@ async function drizzleGenerateArguments(root, output, name) {
245
256
  "--dialect",
246
257
  "sqlite",
247
258
  "--schema",
248
- path2.join(root, "src/schema.ts"),
259
+ appDatabasePaths(root).schema,
249
260
  "--out",
250
261
  output,
251
262
  ...name === void 0 ? [] : ["--name", name]
@@ -266,7 +277,7 @@ async function snapshot(directory) {
266
277
  });
267
278
  const latest = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort().at(-1);
268
279
  if (!latest) return;
269
- const filename = path3.join(directory, latest, "snapshot.json");
280
+ const filename = path4.join(directory, latest, "snapshot.json");
270
281
  const value = JSON.parse(await readFile2(filename, "utf8"));
271
282
  if (value.version !== "7" || value.dialect !== "sqlite" || !Array.isArray(value.ddl)) {
272
283
  throw new Error(
@@ -283,14 +294,14 @@ function canonical(value) {
283
294
  return JSON.stringify(value);
284
295
  }
285
296
  async function checkAppSchema(root, migrationsDirectory) {
286
- const schema = path3.join(root, "src/schema.ts");
297
+ const schema = appDatabasePaths(root).schema;
287
298
  try {
288
299
  await access2(schema);
289
300
  } catch (error) {
290
301
  if (error.code === "ENOENT") return;
291
302
  throw error;
292
303
  }
293
- const temporary = await mkdtemp(path3.join(tmpdir(), "tailorkit-schema-check-"));
304
+ const temporary = await mkdtemp(path4.join(tmpdir(), "tailorkit-schema-check-"));
294
305
  try {
295
306
  const { stdout } = await run(
296
307
  process.execPath,
@@ -303,14 +314,14 @@ async function checkAppSchema(root, migrationsDirectory) {
303
314
  }
304
315
  ).catch((error) => {
305
316
  throw new Error(
306
- "Could not check the app database schema with Drizzle Kit. Check src/schema.ts and your drizzle-kit/drizzle-orm dependencies.",
317
+ `Could not check the app database schema with Drizzle Kit. Check ${path4.relative(root, schema)} and your drizzle-kit/drizzle-orm dependencies.`,
307
318
  { cause: error }
308
319
  );
309
320
  });
310
321
  const current = await snapshot(temporary);
311
322
  if (!current && !stdout.includes("No schema changes")) {
312
323
  throw new Error(
313
- "Drizzle Kit did not produce a schema snapshot. Check src/schema.ts and run tailorkit db generate."
324
+ `Drizzle Kit did not produce a schema snapshot. Check ${path4.relative(root, schema)} and run tailorkit db generate.`
314
325
  );
315
326
  }
316
327
  const saved = await snapshot(migrationsDirectory);
@@ -318,7 +329,7 @@ async function checkAppSchema(root, migrationsDirectory) {
318
329
  const actual = (saved?.ddl ?? []).map(canonical).sort();
319
330
  if (canonical(expected) !== canonical(actual)) {
320
331
  throw new Error(
321
- `App database migrations are missing or out of sync with src/schema.ts. Run tailorkit db generate, commit the generated files in ${path3.relative(root, migrationsDirectory)}, then build again.`
332
+ `App database migrations are missing or out of sync with ${path4.relative(root, schema)}. Run tailorkit db generate, commit the generated files in ${path4.relative(root, migrationsDirectory)}, then build again.`
322
333
  );
323
334
  }
324
335
  } finally {
@@ -329,14 +340,432 @@ async function checkAppSchema(root, migrationsDirectory) {
329
340
  // src/builder/instances.ts
330
341
  import { createHash as createHash2 } from "crypto";
331
342
  import { readFile as readFile3 } from "fs/promises";
332
- import path4 from "path";
333
- import { parseSync, transformWithOxc } from "vite";
334
- var suffix = "?tailorkit-instances";
343
+ import path6 from "path";
344
+ import { parseSync as parseSync2, transformWithOxc } from "vite";
345
+
346
+ // src/builder/file-routes.ts
347
+ import { readdir as readdir3, mkdir, mkdtemp as mkdtemp2, writeFile, rm as rm2 } from "fs/promises";
348
+ import { existsSync, watch } from "fs";
349
+ import path5 from "path";
350
+ import { parseSync } from "vite";
351
+
352
+ // src/builder/define-view.ts
335
353
  var clientImports = /* @__PURE__ */ new Set([
336
354
  "tailorkit/client",
355
+ "tailorkit/app",
337
356
  "tailorkit/app/client",
338
- "@tailorkit/app/client"
357
+ "@tailorkit/app/client",
358
+ "@tailorkit/app"
339
359
  ]);
360
+ function createScope(parent, functionScope = false) {
361
+ return { parent, functionScope, bindings: /* @__PURE__ */ new Map() };
362
+ }
363
+ function bindingNames(node) {
364
+ if (!node) {
365
+ return [];
366
+ }
367
+ switch (node.type) {
368
+ case "Identifier": {
369
+ return [node.name];
370
+ }
371
+ case "RestElement": {
372
+ return bindingNames(node.argument);
373
+ }
374
+ case "AssignmentPattern": {
375
+ return bindingNames(node.left);
376
+ }
377
+ case "ArrayPattern": {
378
+ return node.elements.flatMap(bindingNames);
379
+ }
380
+ case "ObjectPattern": {
381
+ return node.properties.flatMap(
382
+ (property2) => bindingNames(property2.type === "Property" ? property2.value : property2.argument)
383
+ );
384
+ }
385
+ case "TSParameterProperty": {
386
+ return bindingNames(node.parameter);
387
+ }
388
+ default: {
389
+ return [];
390
+ }
391
+ }
392
+ }
393
+ function bind(node, scope) {
394
+ for (const name of bindingNames(node)) {
395
+ scope.bindings.set(name, "local");
396
+ }
397
+ }
398
+ function classScope(node, parent) {
399
+ if (node.type === "ClassDeclaration") {
400
+ bind(node.id, parent);
401
+ }
402
+ const scope = createScope(parent);
403
+ bind(node.id, scope);
404
+ return scope;
405
+ }
406
+ function resolve(scope, name) {
407
+ for (let current = scope; current; current = current.parent) {
408
+ if (current.bindings.has(name)) {
409
+ return current.bindings.get(name);
410
+ }
411
+ }
412
+ }
413
+ function childNodes(node) {
414
+ return Object.values(node).flatMap((value) => {
415
+ const children2 = Array.isArray(value) ? value : [value];
416
+ return children2.filter(
417
+ (child) => !!child && typeof child === "object" && "type" in child
418
+ );
419
+ });
420
+ }
421
+ function bindImports(node, scope) {
422
+ const sdk = node.importKind !== "type" && clientImports.has(String(node.source.value));
423
+ for (const specifier of node.specifiers) {
424
+ let binding = "local";
425
+ if (sdk && specifier.type === "ImportNamespaceSpecifier") {
426
+ binding = "namespace";
427
+ } else if (sdk && specifier.type === "ImportSpecifier" && specifier.importKind !== "type" && specifier.imported.type === "Identifier" && specifier.imported.name === "defineView") {
428
+ binding = "view";
429
+ }
430
+ scope.bindings.set(specifier.local.name, binding);
431
+ }
432
+ }
433
+ function callScopes(program) {
434
+ const scopes = /* @__PURE__ */ new WeakMap();
435
+ function visitFunction(node, scope) {
436
+ if (node.type === "FunctionDeclaration") {
437
+ bind(node.id, scope);
438
+ }
439
+ const parameters = createScope(scope, true);
440
+ if (node.type !== "ArrowFunctionExpression") {
441
+ bind(node.id, parameters);
442
+ }
443
+ for (const parameter of node.params) {
444
+ bind(parameter, parameters);
445
+ }
446
+ for (const parameter of node.params) {
447
+ visit(parameter, parameters);
448
+ }
449
+ if (node.body) {
450
+ visit(node.body, createScope(parameters, true));
451
+ }
452
+ }
453
+ function bindVariables(node, scope) {
454
+ let target = scope;
455
+ if (node.kind === "var") {
456
+ while (!target.functionScope && target.parent) {
457
+ target = target.parent;
458
+ }
459
+ }
460
+ for (const declaration of node.declarations) {
461
+ bind(declaration.id, target);
462
+ }
463
+ }
464
+ function visit(node, scope) {
465
+ scopes.set(node, scope);
466
+ switch (node.type) {
467
+ case "ImportDeclaration": {
468
+ bindImports(node, scope);
469
+ return;
470
+ }
471
+ case "VariableDeclaration": {
472
+ bindVariables(node, scope);
473
+ break;
474
+ }
475
+ case "FunctionDeclaration":
476
+ case "FunctionExpression":
477
+ case "ArrowFunctionExpression": {
478
+ visitFunction(node, scope);
479
+ return;
480
+ }
481
+ case "ClassDeclaration":
482
+ case "ClassExpression": {
483
+ scope = classScope(node, scope);
484
+ break;
485
+ }
486
+ case "CatchClause": {
487
+ scope = createScope(scope);
488
+ bind(node.param, scope);
489
+ break;
490
+ }
491
+ case "SwitchStatement": {
492
+ visit(node.discriminant, scope);
493
+ scope = createScope(scope);
494
+ for (const branch of node.cases) {
495
+ visit(branch, scope);
496
+ }
497
+ return;
498
+ }
499
+ case "BlockStatement":
500
+ case "ForStatement":
501
+ case "ForInStatement":
502
+ case "ForOfStatement": {
503
+ scope = createScope(scope);
504
+ break;
505
+ }
506
+ case "StaticBlock":
507
+ case "TSModuleBlock": {
508
+ scope = createScope(scope, true);
509
+ break;
510
+ }
511
+ case "TSEnumDeclaration":
512
+ case "TSModuleDeclaration": {
513
+ bind(node.id, scope);
514
+ break;
515
+ }
516
+ default: {
517
+ break;
518
+ }
519
+ }
520
+ for (const child of childNodes(node)) {
521
+ visit(child, scope);
522
+ }
523
+ }
524
+ visit(program, createScope(void 0, true));
525
+ return scopes;
526
+ }
527
+ function createDefineViewMatcher(program) {
528
+ const scopes = callScopes(program);
529
+ return (node) => {
530
+ if (node.type !== "CallExpression") {
531
+ return false;
532
+ }
533
+ const { callee } = node;
534
+ const scope = scopes.get(node);
535
+ if (callee.type === "Identifier") {
536
+ return resolve(scope, callee.name) === "view";
537
+ }
538
+ if (callee.type !== "MemberExpression" || callee.object.type !== "Identifier" || resolve(scope, callee.object.name) !== "namespace") {
539
+ return false;
540
+ }
541
+ return callee.computed ? callee.property.type === "Literal" && callee.property.value === "defineView" : callee.property.type === "Identifier" && callee.property.name === "defineView";
542
+ };
543
+ }
544
+
545
+ // src/builder/file-routes.ts
546
+ var clientRuntimeImport = "tailorkit:file-client-runtime";
547
+ function fileViewLocation(filename, root) {
548
+ const relative = path5.relative(path5.join(root, "src/slots"), filename).split(path5.sep);
549
+ if (relative.length < 2 || relative[0] === ".." || !filename.endsWith(".view.tsx")) {
550
+ return;
551
+ }
552
+ const [slot, ...routeSegments] = relative;
553
+ if (!slot) {
554
+ return;
555
+ }
556
+ const basename = routeSegments.join("/").slice(0, -".view.tsx".length);
557
+ return { slot, path: routePath(basename, filename) };
558
+ }
559
+ function routePath(name, filename) {
560
+ const segments = name.split(/[./]/u);
561
+ if (segments.some((segment) => !segment || segment === "..")) {
562
+ throw new Error(`${filename} has an empty route segment. Use route.nested-route.view.tsx.`);
563
+ }
564
+ return `/${segments.join("/")}`;
565
+ }
566
+ function inferFileView(source, filename, root) {
567
+ const location = fileViewLocation(filename, root);
568
+ if (!location) {
569
+ return source;
570
+ }
571
+ const { slot, path: viewPath } = location;
572
+ const parsed = parseSync(filename, source);
573
+ if (parsed.errors.length) {
574
+ throw new Error(`${filename}: ${parsed.errors[0]?.message}`);
575
+ }
576
+ const isDefineViewCall = createDefineViewMatcher(parsed.program);
577
+ const edits = [];
578
+ function visit(node) {
579
+ if (isDefineViewCall(node)) {
580
+ const options = node.arguments[0];
581
+ if (options?.type !== "ObjectExpression") {
582
+ throw new Error(
583
+ `${filename}: file views require defineView with an inline options object.`
584
+ );
585
+ }
586
+ const properties = new Set(
587
+ options.properties.flatMap((property2) => {
588
+ if (property2.type !== "Property" || property2.computed) {
589
+ return [];
590
+ }
591
+ if (property2.key.type === "Identifier") {
592
+ return [property2.key.name];
593
+ }
594
+ if (property2.key.type === "Literal") {
595
+ return [String(property2.key.value)];
596
+ }
597
+ return [];
598
+ })
599
+ );
600
+ const defaults = [
601
+ !properties.has("slot") ? `slot: ${JSON.stringify(slot)},` : "",
602
+ !properties.has("view") ? `view: ${JSON.stringify(viewPath)},` : ""
603
+ ].join("");
604
+ if (defaults) {
605
+ edits.push({ position: options.start + 1, text: defaults });
606
+ }
607
+ }
608
+ for (const value of Object.values(node)) {
609
+ const children2 = Array.isArray(value) ? value : [value];
610
+ for (const child of children2) {
611
+ if (child && typeof child === "object" && "type" in child) {
612
+ visit(child);
613
+ }
614
+ }
615
+ }
616
+ }
617
+ visit(parsed.program);
618
+ for (const edit of edits.toSorted((a, b) => b.position - a.position)) {
619
+ source = source.slice(0, edit.position) + edit.text + source.slice(edit.position);
620
+ }
621
+ return source;
622
+ }
623
+ async function discoverFileRoutes(root) {
624
+ const views = [];
625
+ const roots = [];
626
+ const layouts = [];
627
+ const slotsDir = path5.join(root, "src/slots");
628
+ async function scan(directory, slot, prefix = "") {
629
+ const entries = await readdir3(directory, { withFileTypes: true }).catch(
630
+ (error) => {
631
+ if (error.code === "ENOENT") {
632
+ return [];
633
+ }
634
+ throw error;
635
+ }
636
+ );
637
+ for (const entry of entries.toSorted((a, b) => a.name.localeCompare(b.name))) {
638
+ const filename = path5.join(directory, entry.name);
639
+ if (entry.isDirectory()) {
640
+ await scan(filename, slot ?? entry.name, slot ? `${prefix}${entry.name}/` : "");
641
+ } else if (entry.isFile() && slot) {
642
+ if (entry.name.endsWith(".view.tsx")) {
643
+ fileViewLocation(filename, root);
644
+ views.push({ filename, slot });
645
+ } else if (entry.name === "root.tsx" && !prefix) {
646
+ roots.push({ filename, slot });
647
+ } else if (entry.name === "layout.tsx" || entry.name.endsWith(".layout.tsx")) {
648
+ const name = entry.name === "layout.tsx" ? prefix.replace(/\/$/u, "") : prefix + entry.name.slice(0, -".layout.tsx".length);
649
+ layouts.push({ filename, slot, path: name ? routePath(name, filename) : "/" });
650
+ }
651
+ }
652
+ }
653
+ }
654
+ await scan(slotsDir);
655
+ const rootFile = path5.join(root, "src/root.tsx");
656
+ return {
657
+ root: existsSync(rootFile) ? { filename: rootFile } : void 0,
658
+ roots,
659
+ layouts,
660
+ views
661
+ };
662
+ }
663
+ async function getClientSourceFiles(root) {
664
+ const discovered = await discoverFileRoutes(root);
665
+ const bindings = path5.join(root, "src/tailorkit.gen.ts");
666
+ return [
667
+ ...discovered.root ? [discovered.root.filename] : [],
668
+ ...discovered.roots.map((module) => module.filename),
669
+ ...discovered.layouts.map((module) => module.filename),
670
+ ...discovered.views.map((module) => module.filename),
671
+ ...existsSync(bindings) ? [bindings] : []
672
+ ];
673
+ }
674
+ async function fileClientSource(root) {
675
+ const discovered = await discoverFileRoutes(root);
676
+ const imports = [`import { createFileClient } from ${JSON.stringify(clientRuntimeImport)};`];
677
+ let index = 0;
678
+ function module(entry) {
679
+ const binding = `module${index++}`;
680
+ imports.push(`import ${binding} from ${JSON.stringify(entry.filename)};`);
681
+ return `{ filename: ${JSON.stringify(path5.relative(root, entry.filename))}, definition: ${binding}`;
682
+ }
683
+ const rootModule = discovered.root ? `${module(discovered.root)} }` : "undefined";
684
+ const roots = discovered.roots.map(
685
+ (entry) => `${module(entry)}, slot: ${JSON.stringify(entry.slot)} }`
686
+ );
687
+ const layouts = discovered.layouts.map(
688
+ (entry) => `${module(entry)}, slot: ${JSON.stringify(entry.slot)}, path: ${JSON.stringify(entry.path)} }`
689
+ );
690
+ const views = discovered.views.map(
691
+ (entry) => `${module(entry)}, slot: ${JSON.stringify(entry.slot)} }`
692
+ );
693
+ return `${imports.join("\n")}
694
+ export default createFileClient({ root: ${rootModule}, roots: [${roots.join(",")}], layouts: [${layouts.join(",")}], views: [${views.join(",")}] });`;
695
+ }
696
+ async function createFileRouteEntry(root, output, watching) {
697
+ let source = await fileClientSource(root);
698
+ const temporaryRoot = path5.join(output, "tmp");
699
+ await mkdir(temporaryRoot, { recursive: true });
700
+ const directory = await mkdtemp2(path5.join(temporaryRoot, "client-"));
701
+ const entry = path5.join(directory, "entry.ts");
702
+ await writeFile(entry, source);
703
+ let pending = Promise.resolve();
704
+ let closed = false;
705
+ let timer;
706
+ async function refresh() {
707
+ if (closed) {
708
+ return;
709
+ }
710
+ let next;
711
+ try {
712
+ next = await fileClientSource(root);
713
+ } catch (error) {
714
+ next = `throw new Error(${JSON.stringify(error instanceof Error ? error.message : String(error))});`;
715
+ }
716
+ if (next === source) {
717
+ return;
718
+ }
719
+ source = next;
720
+ await writeFile(entry, next);
721
+ }
722
+ const watcher = watching ? watch(path5.join(root, "src"), { recursive: true }, () => {
723
+ if (timer) {
724
+ clearTimeout(timer);
725
+ }
726
+ timer = setTimeout(() => {
727
+ pending = pending.then(refresh);
728
+ }, 25);
729
+ }) : void 0;
730
+ return {
731
+ entry,
732
+ async close() {
733
+ closed = true;
734
+ if (timer) {
735
+ clearTimeout(timer);
736
+ }
737
+ watcher?.close();
738
+ try {
739
+ await pending;
740
+ } finally {
741
+ await rm2(directory, { recursive: true, force: true });
742
+ }
743
+ }
744
+ };
745
+ }
746
+ function fileRoutesPlugin(root) {
747
+ return {
748
+ name: "tailorkit-file-routes",
749
+ enforce: "pre",
750
+ async resolveId(id) {
751
+ if (id !== clientRuntimeImport) {
752
+ return;
753
+ }
754
+ const importer = path5.join(root, "src/root.tsx");
755
+ return await this.resolve("tailorkit/client", importer, { skipSelf: true }) ?? this.resolve("@tailorkit/app/client", importer, { skipSelf: true });
756
+ },
757
+ transform(source, filename) {
758
+ if (!filename.endsWith(".view.tsx")) {
759
+ return;
760
+ }
761
+ const code = inferFileView(source, filename, root);
762
+ return code === source ? void 0 : { code, map: null };
763
+ }
764
+ };
765
+ }
766
+
767
+ // src/builder/instances.ts
768
+ var suffix = "?tailorkit-instances";
340
769
  var browserGlobals = /* @__PURE__ */ new Set([
341
770
  "window",
342
771
  "document",
@@ -487,7 +916,7 @@ function property(node, name) {
487
916
  );
488
917
  }
489
918
  function parse(filename, source) {
490
- const result = parseSync(filename, source);
919
+ const result = parseSync2(filename, source);
491
920
  if (result.errors.length)
492
921
  throw new Error(`Unable to parse ${filename}: ${result.errors[0]?.message}`);
493
922
  return result.program;
@@ -552,25 +981,19 @@ function renderImports(imports, needed, source, sideEffects) {
552
981
  }).join("\n");
553
982
  }
554
983
  async function extractInstances(filename, source, root, includedNames) {
984
+ source = inferFileView(source, filename, root);
555
985
  const transformed = await transformWithOxc(source, filename, {
556
986
  jsx: { importSource: "preact" },
557
987
  sourcemap: false
558
988
  });
559
989
  source = transformed.code;
560
990
  const program = parse(filename.replace(/\.[^.]+$/u, ".js"), source);
561
- const aliases = /* @__PURE__ */ new Set();
562
- for (const statement of program.body) {
563
- if (statement.type !== "ImportDeclaration" || !clientImports.has(String(statement.source.value)))
564
- continue;
565
- for (const s of statement.specifiers) {
566
- if (s.type === "ImportSpecifier" && s.imported.type === "Identifier" && s.imported.name === "defineView")
567
- aliases.add(s.local.name);
568
- }
569
- }
991
+ const isDefineViewCall = createDefineViewMatcher(program);
570
992
  let resolvers = [];
571
993
  walk(program, (node, ancestors) => {
572
- if (node.type !== "CallExpression" || node.callee.type !== "Identifier" || !aliases.has(node.callee.name))
994
+ if (!isDefineViewCall(node)) {
573
995
  return;
996
+ }
574
997
  const options = node.arguments[0];
575
998
  if (options?.type !== "ObjectExpression") return;
576
999
  const instances = property(options, "instances");
@@ -590,12 +1013,12 @@ async function extractInstances(filename, source, root, includedNames) {
590
1013
  const config = instances.value;
591
1014
  if (config.properties.some((p) => p.type !== "Property" || p.computed || p.kind !== "init"))
592
1015
  fail("Instance options cannot contain spreads, getters or computed properties.");
593
- const resolve = property(config, "resolve");
1016
+ const resolve2 = property(config, "resolve");
594
1017
  const schema = property(config, "dataSchema");
595
- if (!resolve || !schema || resolve.method || !["ArrowFunctionExpression", "FunctionExpression"].includes(resolve.value.type))
1018
+ if (!resolve2 || !schema || resolve2.method || !["ArrowFunctionExpression", "FunctionExpression"].includes(resolve2.value.type))
596
1019
  fail("instances requires dataSchema and an inline resolve function.");
597
1020
  const name = `i${createHash2("sha256").update(
598
- `${path4.relative(root, filename).split(path4.sep).join("/")}\0${viewName}\0${view.value}`
1021
+ `${path6.relative(root, filename).split(path6.sep).join("/")}\0${viewName}\0${view.value}`
599
1022
  ).digest("hex").slice(0, 24)}`;
600
1023
  if (resolvers.some((r) => r.name === name))
601
1024
  fail("Instance resolvers must have distinct module-level variable names.");
@@ -604,7 +1027,7 @@ async function extractInstances(filename, source, root, includedNames) {
604
1027
  view: view.value,
605
1028
  call: node,
606
1029
  instances,
607
- resolve,
1030
+ resolve: resolve2,
608
1031
  schema
609
1032
  });
610
1033
  });
@@ -720,34 +1143,34 @@ function instanceServerBindings(modules, registrations) {
720
1143
  }
721
1144
 
722
1145
  // src/builder/server.ts
723
- async function buildServer(loaded, watch = false, buildOutDir, instances = [], registrations = []) {
1146
+ async function buildServer(loaded, watch2 = false, buildOutDir, instances = [], registrations = []) {
724
1147
  const config = loaded.config.server;
725
1148
  if (!config) {
726
1149
  return;
727
1150
  }
728
- const output = buildOutDir ?? path5.resolve(loaded.root, loaded.config.build?.outDir ?? ".tailorkit");
729
- const directory = path5.join(output, "server");
730
- const migrationsDirectory = path5.resolve(loaded.root, config.migrations ?? "./migrations");
731
- const migrationsOutput = path5.join(output, "migrations");
732
- const temporaryRoot = path5.join(output, "tmp");
1151
+ const output = buildOutDir ?? path7.resolve(loaded.root, loaded.config.build?.outDir ?? ".tailorkit");
1152
+ const directory = path7.join(output, "server");
1153
+ const migrationsDirectory = appDatabasePaths(loaded.root, config.migrations).migrations;
1154
+ const migrationsOutput = path7.join(output, "migrations");
1155
+ const temporaryRoot = path7.join(output, "tmp");
733
1156
  validateServerBuildOutput(loaded, output);
734
- await rm2(directory, { recursive: true, force: true });
735
- await mkdir(directory, { recursive: true });
736
- await mkdir(temporaryRoot, { recursive: true });
737
- const temporary = await mkdtemp2(path5.join(temporaryRoot, "server-"));
738
- const entry = path5.join(temporary, "entry.mjs");
1157
+ await rm3(directory, { recursive: true, force: true });
1158
+ await mkdir2(directory, { recursive: true });
1159
+ await mkdir2(temporaryRoot, { recursive: true });
1160
+ const temporary = await mkdtemp3(path7.join(temporaryRoot, "server-"));
1161
+ const entry = path7.join(temporary, "entry.mjs");
739
1162
  let watching = false;
740
1163
  let ready;
741
- const initialBuild = watch ? new Promise((resolve) => {
742
- ready = resolve;
1164
+ const initialBuild = watch2 ? new Promise((resolve2) => {
1165
+ ready = resolve2;
743
1166
  }) : void 0;
744
1167
  try {
745
- await writeFile(
1168
+ await writeFile2(
746
1169
  entry,
747
- `${instances.length ? `import app from ${JSON.stringify(path5.resolve(loaded.root, config.entry ?? "src/server.ts"))};
1170
+ `${instances.length ? `import app from ${JSON.stringify(path7.resolve(loaded.root, config.entry ?? "src/server.ts"))};
748
1171
  import { withInstanceResolvers } from "tailorkit/server";
749
1172
  ${instanceServerImports(instances)}
750
- export default withInstanceResolvers(app, ${instanceServerBindings(instances, registrations)});` : `export { default } from ${JSON.stringify(path5.resolve(loaded.root, config.entry ?? "src/server.ts"))};`}
1173
+ export default withInstanceResolvers(app, ${instanceServerBindings(instances, registrations)});` : `export { default } from ${JSON.stringify(path7.resolve(loaded.root, config.entry ?? "src/server.ts"))};`}
751
1174
  export { default as migrations } from "tailorkit:migrations";
752
1175
  export const runtimeManifest = ${JSON.stringify(runtimeManifest)};
753
1176
  `
@@ -759,7 +1182,7 @@ export const runtimeManifest = ${JSON.stringify(runtimeManifest)};
759
1182
  instanceServerPlugin(loaded.root, instances),
760
1183
  {
761
1184
  name: "tailorkit-app-migrations",
762
- closeWatcher: () => rm2(temporary, { recursive: true, force: true }),
1185
+ closeWatcher: () => rm3(temporary, { recursive: true, force: true }),
763
1186
  resolveId(id) {
764
1187
  if (id === "tailorkit:migrations") {
765
1188
  return "\0tailorkit:migrations";
@@ -774,12 +1197,12 @@ export const runtimeManifest = ${JSON.stringify(runtimeManifest)};
774
1197
  config.migrations === void 0
775
1198
  );
776
1199
  await checkAppSchema(loaded.root, migrationsDirectory);
777
- this.addWatchFile(path5.join(loaded.root, "src/schema.ts"));
1200
+ this.addWatchFile(appDatabasePaths(loaded.root).schema);
778
1201
  for (const file of files) {
779
1202
  this.addWatchFile(file);
780
1203
  }
781
- await rm2(migrationsOutput, { recursive: true, force: true });
782
- await mkdir(migrationsOutput, { recursive: true });
1204
+ await rm3(migrationsOutput, { recursive: true, force: true });
1205
+ await mkdir2(migrationsOutput, { recursive: true });
783
1206
  await cp(migrationsDirectory, migrationsOutput, { recursive: true }).catch(
784
1207
  (error) => {
785
1208
  if (config.migrations === void 0 && error.code === "ENOENT") {
@@ -797,7 +1220,7 @@ export const runtimeManifest = ${JSON.stringify(runtimeManifest)};
797
1220
  outDir: directory,
798
1221
  emptyOutDir: false,
799
1222
  target: "es2022",
800
- watch: watch ? {} : null,
1223
+ watch: watch2 ? {} : null,
801
1224
  minify: "oxc",
802
1225
  rollupOptions: {
803
1226
  external: [],
@@ -811,8 +1234,8 @@ export const runtimeManifest = ${JSON.stringify(runtimeManifest)};
811
1234
  },
812
1235
  ssr: { noExternal: true }
813
1236
  });
814
- watching = watch;
815
- if (watch && "on" in result && initialBuild) {
1237
+ watching = watch2;
1238
+ if (watch2 && "on" in result && initialBuild) {
816
1239
  try {
817
1240
  await Promise.race([
818
1241
  initialBuild,
@@ -830,25 +1253,25 @@ export const runtimeManifest = ${JSON.stringify(runtimeManifest)};
830
1253
  }
831
1254
  return result;
832
1255
  } finally {
833
- if (!watching) await rm2(temporary, { recursive: true, force: true });
1256
+ if (!watching) await rm3(temporary, { recursive: true, force: true });
834
1257
  }
835
1258
  }
836
1259
  function validateServerBuildOutput(loaded, output) {
837
1260
  if (!loaded.config.server) return;
838
- const migrationsDirectory = path5.resolve(
1261
+ const migrationsDirectory = appDatabasePaths(
839
1262
  loaded.root,
840
- loaded.config.server.migrations ?? "./migrations"
841
- );
1263
+ loaded.config.server.migrations
1264
+ ).migrations;
842
1265
  for (const name of ["server", "client", "migrations", "tmp"]) {
843
- if (overlaps(migrationsDirectory, path5.join(output, name))) {
1266
+ if (overlaps(migrationsDirectory, path7.join(output, name))) {
844
1267
  throw new Error("App build output must not overlap migration source");
845
1268
  }
846
1269
  }
847
1270
  }
848
1271
  function overlaps(source, destination) {
849
1272
  const contains = (parent, child) => {
850
- const relative = path5.relative(parent, child);
851
- return relative === "" || !path5.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path5.sep}`);
1273
+ const relative = path7.relative(parent, child);
1274
+ return relative === "" || !path7.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path7.sep}`);
852
1275
  };
853
1276
  return contains(source, destination) || contains(destination, source);
854
1277
  }
@@ -856,7 +1279,7 @@ function overlaps(source, destination) {
856
1279
  // src/builder/generate-migrations.ts
857
1280
  import { spawn } from "child_process";
858
1281
  import { access as access3 } from "fs/promises";
859
- import path6 from "path";
1282
+ import path8 from "path";
860
1283
  async function generateAppMigrations(options = {}) {
861
1284
  const loaded = await loadTailorKitConfig(options.configPath, options.cwd);
862
1285
  if (!loaded.config.server) {
@@ -864,19 +1287,20 @@ async function generateAppMigrations(options = {}) {
864
1287
  "Enable server: {} in tailorkit.config.ts before generating database migrations."
865
1288
  );
866
1289
  }
867
- await access3(path6.join(loaded.root, "src/schema.ts")).catch((error) => {
1290
+ const paths = appDatabasePaths(loaded.root, loaded.config.server.migrations);
1291
+ await access3(paths.schema).catch((error) => {
868
1292
  throw new Error(
869
- "Define the app database schema in src/schema.ts before generating migrations.",
1293
+ `Define the app database schema in ${path8.relative(loaded.root, paths.schema)} before generating migrations.`,
870
1294
  { cause: error }
871
1295
  );
872
1296
  });
873
- const directory = path6.resolve(loaded.root, loaded.config.server.migrations ?? "./migrations");
1297
+ const directory = paths.migrations;
874
1298
  const args = await drizzleGenerateArguments(loaded.root, directory, options.name);
875
- await new Promise((resolve, reject) => {
1299
+ await new Promise((resolve2, reject) => {
876
1300
  const child = spawn(process.execPath, args, { cwd: loaded.root, stdio: "inherit" });
877
1301
  child.once("error", reject);
878
1302
  child.once("close", (code, signal) => {
879
- if (code === 0) resolve();
1303
+ if (code === 0) resolve2();
880
1304
  else reject(new Error(`Drizzle migration generation failed (${signal ?? code}).`));
881
1305
  });
882
1306
  });
@@ -890,19 +1314,20 @@ var preactPackageJsonModuleId = "\0tailorkit-preact-package-json";
890
1314
  var buildApp = async (options = {}) => {
891
1315
  const loaded = await loadTailorKitConfig(options.configPath, options.cwd);
892
1316
  loaded.root = await realpath(loaded.root);
893
- const entry = path7.resolve(loaded.root, loaded.config.client?.entry ?? "src/client.ts");
894
- const serverEntry = path7.resolve(loaded.root, loaded.config.server?.entry ?? "src/server.ts");
1317
+ const serverEntry = path9.resolve(loaded.root, loaded.config.server?.entry ?? "src/server.ts");
895
1318
  const outDir = options.outDir ?? loaded.config.build?.outDir ?? ".tailorkit";
896
1319
  const preactVersion = getInstalledPreactVersion(loaded.root);
897
1320
  assertSupportedPreactVersion(preactVersion);
898
- const resolvedOutDir = path7.resolve(loaded.root, outDir);
1321
+ const resolvedOutDir = path9.resolve(loaded.root, outDir);
899
1322
  validateServerBuildOutput(loaded, resolvedOutDir);
900
- const clientOutDir = path7.join(resolvedOutDir, "client");
1323
+ const clientOutDir = path9.join(resolvedOutDir, "client");
1324
+ const fileEntry = await createFileRouteEntry(loaded.root, resolvedOutDir, Boolean(options.watch));
1325
+ const entry = fileEntry.entry;
901
1326
  const instanceModules = /* @__PURE__ */ new Map();
902
1327
  let serverWatcher;
903
1328
  const writeBuildExtras = async () => {
904
1329
  const { views, instanceResolvers } = await readClientManifest(
905
- path7.join(clientOutDir, "client.js")
1330
+ path9.join(clientOutDir, "client.js")
906
1331
  );
907
1332
  const resolverNames = new Set(instanceResolvers.map((registration) => registration.resolver));
908
1333
  const activeModules = [...instanceModules.values()].map((module) => ({
@@ -928,21 +1353,21 @@ var buildApp = async (options = {}) => {
928
1353
  if (!configuredPath) {
929
1354
  continue;
930
1355
  }
931
- const extension = path7.extname(configuredPath).toLowerCase().slice(1);
1356
+ const extension = path9.extname(configuredPath).toLowerCase().slice(1);
932
1357
  const contentType = { png: "image/png", svg: "image/svg+xml", webp: "image/webp" }[extension];
933
1358
  if (!contentType) {
934
1359
  throw new Error(`The ${variant} logo must be an SVG, PNG, or WebP file.`);
935
1360
  }
936
- const content = await readFile4(path7.resolve(loaded.root, configuredPath));
1361
+ const content = await readFile4(path9.resolve(loaded.root, configuredPath));
937
1362
  validateLogoAsset(content, contentType);
938
1363
  const filename = `logo-${variant}.${extension}`;
939
- await writeFile2(path7.join(clientOutDir, filename), content);
1364
+ await writeFile3(path9.join(clientOutDir, filename), content);
940
1365
  logoManifest[variant] = `client/${filename}`;
941
1366
  }
942
- await writeFile2(path7.join(clientOutDir, "views.json"), `${JSON.stringify(views)}
1367
+ await writeFile3(path9.join(clientOutDir, "views.json"), `${JSON.stringify(views)}
943
1368
  `);
944
- await writeFile2(
945
- path7.join(resolvedOutDir, "tailorkit-upload.json"),
1369
+ await writeFile3(
1370
+ path9.join(resolvedOutDir, "tailorkit-upload.json"),
946
1371
  `${JSON.stringify(createTailorKitUploadManifest(logoManifest, Boolean(loaded.config.server), views), null, 2)}
947
1372
  `,
948
1373
  "utf-8"
@@ -950,8 +1375,8 @@ var buildApp = async (options = {}) => {
950
1375
  };
951
1376
  let firstBuildDone;
952
1377
  let firstBuildFailed;
953
- const firstBuild = new Promise((resolve, reject) => {
954
- firstBuildDone = resolve;
1378
+ const firstBuild = new Promise((resolve2, reject) => {
1379
+ firstBuildDone = resolve2;
955
1380
  firstBuildFailed = reject;
956
1381
  });
957
1382
  if (!options.watch) {
@@ -994,6 +1419,7 @@ var buildApp = async (options = {}) => {
994
1419
  mode: options.mode,
995
1420
  oxc: { jsx: { importSource: "preact" } },
996
1421
  plugins: [
1422
+ fileRoutesPlugin(loaded.root),
997
1423
  instanceExtractionPlugin(loaded.root, Boolean(loaded.config.server), instanceModules),
998
1424
  {
999
1425
  name: "tailorkit-browser-server-boundary",
@@ -1001,7 +1427,7 @@ var buildApp = async (options = {}) => {
1001
1427
  async resolveId(id, importer) {
1002
1428
  if (/^(?:@tailorkit\/app\/server$|tailorkit\/(?:server|app\/server)$|effect(?:\/|$))/u.test(
1003
1429
  id
1004
- ) || importer && path7.resolve(path7.dirname(importer), id) === serverEntry) {
1430
+ ) || importer && path9.resolve(path9.dirname(importer), id) === serverEntry) {
1005
1431
  throw new Error(
1006
1432
  "App server implementations cannot be imported into a browser bundle. Import api from #tailorkit and server types with import type."
1007
1433
  );
@@ -1043,6 +1469,7 @@ var buildApp = async (options = {}) => {
1043
1469
  ],
1044
1470
  root: loaded.root
1045
1471
  }).catch(async (error) => {
1472
+ await fileEntry.close();
1046
1473
  if (serverWatcher && "close" in serverWatcher) await serverWatcher.close();
1047
1474
  throw error;
1048
1475
  });
@@ -1061,23 +1488,26 @@ var buildApp = async (options = {}) => {
1061
1488
  await result.close();
1062
1489
  }
1063
1490
  if (serverWatcher && "close" in serverWatcher) await serverWatcher.close();
1491
+ await fileEntry.close();
1064
1492
  throw error;
1065
1493
  }
1066
- if (result && "close" in result && serverWatcher && "close" in serverWatcher) {
1494
+ if (result && "close" in result) {
1067
1495
  const clientWatcher = result;
1068
1496
  return {
1069
1497
  on: clientWatcher.on.bind(clientWatcher),
1070
1498
  async close() {
1071
1499
  await clientWatcher.close();
1072
1500
  if (serverWatcher && "close" in serverWatcher) await serverWatcher.close();
1501
+ await fileEntry.close();
1073
1502
  }
1074
1503
  };
1075
1504
  }
1076
1505
  }
1506
+ await fileEntry.close();
1077
1507
  return result;
1078
1508
  };
1079
1509
  function getInstalledPreactVersion(root) {
1080
- const require2 = createRequire2(path7.join(root, "package.json"));
1510
+ const require2 = createRequire2(path9.join(root, "package.json"));
1081
1511
  try {
1082
1512
  const packageJsonPath = require2.resolve("preact/package.json");
1083
1513
  const packageJson = require2(packageJsonPath);
@@ -1096,5 +1526,6 @@ export {
1096
1526
  buildApp,
1097
1527
  createTailorKitUploadManifest,
1098
1528
  generateAppMigrations,
1529
+ getClientSourceFiles,
1099
1530
  tailorkitUploadManifestSchema
1100
1531
  };