@tailorkit/vite 0.0.0-stage → 0.1.0-beta.31
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/LICENSE.md +74 -0
- package/README.md +17 -2
- package/dist/builder.d.mts +36 -0
- package/dist/builder.mjs +37 -0
- package/dist/config-BUmUqzEq.mjs +19 -0
- package/dist/config-CLAKd3cA.mjs +19 -0
- package/dist/config-DVRoBc48.d.mts +73 -0
- package/dist/config-loader.d.mts +9 -0
- package/dist/config-loader.mjs +21 -0
- package/dist/config-rlQLa7z2.d.mts +73 -0
- package/dist/config.d.mts +2 -0
- package/dist/config.mjs +2 -0
- package/dist/file-routes-CjOIw7fH.mjs +454 -0
- package/dist/file-routes-Dtln0K66.mjs +438 -0
- package/dist/index.d.mts +9 -0
- package/dist/index.mjs +788 -0
- package/package.json +69 -3
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { access, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { execFile } from "node:child_process";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import { parseSync } from "vite";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { existsSync, watch } from "node:fs";
|
|
10
|
+
//#region src/builder/upload-manifest.ts
|
|
11
|
+
const tailorkitUploadManifestSchema = z.object({
|
|
12
|
+
assets: z.object({
|
|
13
|
+
client: z.literal("client/client.js"),
|
|
14
|
+
server: z.literal("server/server.js").optional(),
|
|
15
|
+
logos: z.object({
|
|
16
|
+
dark: z.string().regex(/^client\/logo-dark\.(?:png|svg|webp)$/u).optional(),
|
|
17
|
+
light: z.string().regex(/^client\/logo-light\.(?:png|svg|webp)$/u).optional()
|
|
18
|
+
}).optional()
|
|
19
|
+
}),
|
|
20
|
+
views: z.array(z.object({
|
|
21
|
+
slot: z.string().min(1).max(255),
|
|
22
|
+
path: z.string().startsWith("/").max(1024),
|
|
23
|
+
instances: z.literal(true).optional(),
|
|
24
|
+
disabled: z.literal(true).optional()
|
|
25
|
+
})).max(1e3).optional(),
|
|
26
|
+
version: z.literal(1)
|
|
27
|
+
});
|
|
28
|
+
const createTailorKitUploadManifest = (logos, server = false, views) => tailorkitUploadManifestSchema.parse({
|
|
29
|
+
assets: {
|
|
30
|
+
client: "client/client.js",
|
|
31
|
+
...server ? { server: "server/server.js" } : {},
|
|
32
|
+
...logos && Object.keys(logos).length > 0 ? { logos } : {}
|
|
33
|
+
},
|
|
34
|
+
views,
|
|
35
|
+
version: 1
|
|
36
|
+
});
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/builder/database-paths.ts
|
|
39
|
+
function appDatabasePaths(root, migrations) {
|
|
40
|
+
return {
|
|
41
|
+
schema: path.join(root, "src/db/schema.ts"),
|
|
42
|
+
migrations: path.resolve(root, migrations ?? "./src/db/migrations")
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/builder/drizzle.ts
|
|
47
|
+
/** Use the app's Drizzle version with explicit flags; no Drizzle config file is needed. */
|
|
48
|
+
async function drizzleGenerateArguments(root, output, name) {
|
|
49
|
+
const require = createRequire(path.join(root, "package.json"));
|
|
50
|
+
for (const directory of require.resolve.paths("drizzle-kit") ?? []) {
|
|
51
|
+
const cli = path.join(directory, "drizzle-kit/bin.cjs");
|
|
52
|
+
if (await access(cli).then(() => true).catch(() => false)) return [
|
|
53
|
+
"--preserve-symlinks-main",
|
|
54
|
+
cli,
|
|
55
|
+
"generate",
|
|
56
|
+
"--dialect",
|
|
57
|
+
"sqlite",
|
|
58
|
+
"--schema",
|
|
59
|
+
appDatabasePaths(root).schema,
|
|
60
|
+
"--out",
|
|
61
|
+
output,
|
|
62
|
+
...name === void 0 ? [] : ["--name", name]
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
throw new Error("Install drizzle-kit and drizzle-orm as dev dependencies, then run tailorkit db generate.");
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/builder/schema-check.ts
|
|
69
|
+
const run = promisify(execFile);
|
|
70
|
+
async function snapshot(directory) {
|
|
71
|
+
const latest = (await readdir(directory, { withFileTypes: true }).catch((error) => {
|
|
72
|
+
if (error.code === "ENOENT") return [];
|
|
73
|
+
throw error;
|
|
74
|
+
})).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort().at(-1);
|
|
75
|
+
if (!latest) return;
|
|
76
|
+
const filename = path.join(directory, latest, "snapshot.json");
|
|
77
|
+
const value = JSON.parse(await readFile(filename, "utf8"));
|
|
78
|
+
if (value.version !== "7" || value.dialect !== "sqlite" || !Array.isArray(value.ddl)) throw new Error(`Unsupported app schema snapshot: ${filename}. Use Drizzle Kit v1 SQLite migrations.`);
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
function canonical(value) {
|
|
82
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
83
|
+
if (value && typeof value === "object") return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`).join(",")}}`;
|
|
84
|
+
return JSON.stringify(value);
|
|
85
|
+
}
|
|
86
|
+
/** Generate a fresh snapshot with Drizzle in disposable storage, leaving committed history unchanged. */
|
|
87
|
+
async function checkAppSchema(root, migrationsDirectory) {
|
|
88
|
+
const schema = appDatabasePaths(root).schema;
|
|
89
|
+
try {
|
|
90
|
+
await access(schema);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error.code === "ENOENT") return;
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
const temporary = await mkdtemp(path.join(tmpdir(), "tailorkit-schema-check-"));
|
|
96
|
+
try {
|
|
97
|
+
const { stdout } = await run(process.execPath, await drizzleGenerateArguments(root, temporary), {
|
|
98
|
+
cwd: root,
|
|
99
|
+
timeout: 3e4,
|
|
100
|
+
maxBuffer: 1048576,
|
|
101
|
+
env: {
|
|
102
|
+
...process.env,
|
|
103
|
+
CI: "true"
|
|
104
|
+
}
|
|
105
|
+
}).catch((error) => {
|
|
106
|
+
throw new Error(`Could not check the app database schema with Drizzle Kit. Check ${path.relative(root, schema)} and your drizzle-kit/drizzle-orm dependencies.`, { cause: error });
|
|
107
|
+
});
|
|
108
|
+
const current = await snapshot(temporary);
|
|
109
|
+
if (!current && !stdout.includes("No schema changes")) throw new Error(`Drizzle Kit did not produce a schema snapshot. Check ${path.relative(root, schema)} and run tailorkit db generate.`);
|
|
110
|
+
const saved = await snapshot(migrationsDirectory);
|
|
111
|
+
const expected = (current?.ddl ?? []).map(canonical).sort();
|
|
112
|
+
const actual = (saved?.ddl ?? []).map(canonical).sort();
|
|
113
|
+
if (canonical(expected) !== canonical(actual)) throw new Error(`App database migrations are missing or out of sync with ${path.relative(root, schema)}. Run tailorkit db generate, commit the generated files in ${path.relative(root, migrationsDirectory)}, then build again.`);
|
|
114
|
+
} finally {
|
|
115
|
+
await rm(temporary, {
|
|
116
|
+
recursive: true,
|
|
117
|
+
force: true
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/builder/define-view.ts
|
|
123
|
+
const clientImports = /* @__PURE__ */ new Set([
|
|
124
|
+
"tailorkit/client",
|
|
125
|
+
"tailorkit/app",
|
|
126
|
+
"tailorkit/app/client",
|
|
127
|
+
"@tailorkit/app/client",
|
|
128
|
+
"@tailorkit/app"
|
|
129
|
+
]);
|
|
130
|
+
function createScope(parent, functionScope = false) {
|
|
131
|
+
return {
|
|
132
|
+
parent,
|
|
133
|
+
functionScope,
|
|
134
|
+
bindings: /* @__PURE__ */ new Map()
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function bindingNames(node) {
|
|
138
|
+
if (!node) return [];
|
|
139
|
+
switch (node.type) {
|
|
140
|
+
case "Identifier": return [node.name];
|
|
141
|
+
case "RestElement": return bindingNames(node.argument);
|
|
142
|
+
case "AssignmentPattern": return bindingNames(node.left);
|
|
143
|
+
case "ArrayPattern": return node.elements.flatMap(bindingNames);
|
|
144
|
+
case "ObjectPattern": return node.properties.flatMap((property) => bindingNames(property.type === "Property" ? property.value : property.argument));
|
|
145
|
+
case "TSParameterProperty": return bindingNames(node.parameter);
|
|
146
|
+
default: return [];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function bind(node, scope) {
|
|
150
|
+
for (const name of bindingNames(node)) scope.bindings.set(name, "local");
|
|
151
|
+
}
|
|
152
|
+
function classScope(node, parent) {
|
|
153
|
+
if (node.type === "ClassDeclaration") bind(node.id, parent);
|
|
154
|
+
const scope = createScope(parent);
|
|
155
|
+
bind(node.id, scope);
|
|
156
|
+
return scope;
|
|
157
|
+
}
|
|
158
|
+
function resolve(scope, name) {
|
|
159
|
+
for (let current = scope; current; current = current.parent) if (current.bindings.has(name)) return current.bindings.get(name);
|
|
160
|
+
}
|
|
161
|
+
function childNodes(node) {
|
|
162
|
+
return Object.values(node).flatMap((value) => {
|
|
163
|
+
return (Array.isArray(value) ? value : [value]).filter((child) => !!child && typeof child === "object" && "type" in child);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function bindImports(node, scope) {
|
|
167
|
+
const sdk = node.importKind !== "type" && clientImports.has(String(node.source.value));
|
|
168
|
+
for (const specifier of node.specifiers) {
|
|
169
|
+
let binding = "local";
|
|
170
|
+
if (sdk && specifier.type === "ImportNamespaceSpecifier") binding = "namespace";
|
|
171
|
+
else if (sdk && specifier.type === "ImportSpecifier" && specifier.importKind !== "type" && specifier.imported.type === "Identifier" && specifier.imported.name === "defineView") binding = "view";
|
|
172
|
+
scope.bindings.set(specifier.local.name, binding);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Resolve SDK import bindings, including declarations hoisted past a call. */
|
|
176
|
+
function callScopes(program) {
|
|
177
|
+
const scopes = /* @__PURE__ */ new WeakMap();
|
|
178
|
+
function visitFunction(node, scope) {
|
|
179
|
+
if (node.type === "FunctionDeclaration") bind(node.id, scope);
|
|
180
|
+
const parameters = createScope(scope, true);
|
|
181
|
+
if (node.type !== "ArrowFunctionExpression") bind(node.id, parameters);
|
|
182
|
+
for (const parameter of node.params) bind(parameter, parameters);
|
|
183
|
+
for (const parameter of node.params) visit(parameter, parameters);
|
|
184
|
+
if (node.body) visit(node.body, createScope(parameters, true));
|
|
185
|
+
}
|
|
186
|
+
function bindVariables(node, scope) {
|
|
187
|
+
let target = scope;
|
|
188
|
+
if (node.kind === "var") while (!target.functionScope && target.parent) target = target.parent;
|
|
189
|
+
for (const declaration of node.declarations) bind(declaration.id, target);
|
|
190
|
+
}
|
|
191
|
+
function visit(node, scope) {
|
|
192
|
+
scopes.set(node, scope);
|
|
193
|
+
switch (node.type) {
|
|
194
|
+
case "ImportDeclaration":
|
|
195
|
+
bindImports(node, scope);
|
|
196
|
+
return;
|
|
197
|
+
case "VariableDeclaration":
|
|
198
|
+
bindVariables(node, scope);
|
|
199
|
+
break;
|
|
200
|
+
case "FunctionDeclaration":
|
|
201
|
+
case "FunctionExpression":
|
|
202
|
+
case "ArrowFunctionExpression":
|
|
203
|
+
visitFunction(node, scope);
|
|
204
|
+
return;
|
|
205
|
+
case "ClassDeclaration":
|
|
206
|
+
case "ClassExpression":
|
|
207
|
+
scope = classScope(node, scope);
|
|
208
|
+
break;
|
|
209
|
+
case "CatchClause":
|
|
210
|
+
scope = createScope(scope);
|
|
211
|
+
bind(node.param, scope);
|
|
212
|
+
break;
|
|
213
|
+
case "SwitchStatement":
|
|
214
|
+
visit(node.discriminant, scope);
|
|
215
|
+
scope = createScope(scope);
|
|
216
|
+
for (const branch of node.cases) visit(branch, scope);
|
|
217
|
+
return;
|
|
218
|
+
case "BlockStatement":
|
|
219
|
+
case "ForStatement":
|
|
220
|
+
case "ForInStatement":
|
|
221
|
+
case "ForOfStatement":
|
|
222
|
+
scope = createScope(scope);
|
|
223
|
+
break;
|
|
224
|
+
case "StaticBlock":
|
|
225
|
+
case "TSModuleBlock":
|
|
226
|
+
scope = createScope(scope, true);
|
|
227
|
+
break;
|
|
228
|
+
case "TSEnumDeclaration":
|
|
229
|
+
case "TSModuleDeclaration": bind(node.id, scope);
|
|
230
|
+
}
|
|
231
|
+
for (const child of childNodes(node)) visit(child, scope);
|
|
232
|
+
}
|
|
233
|
+
visit(program, createScope(void 0, true));
|
|
234
|
+
return scopes;
|
|
235
|
+
}
|
|
236
|
+
/** Recognize the same SDK calls in route inference and instance extraction. */
|
|
237
|
+
function createDefineViewMatcher(program) {
|
|
238
|
+
const scopes = callScopes(program);
|
|
239
|
+
return (node) => {
|
|
240
|
+
if (node.type !== "CallExpression") return false;
|
|
241
|
+
const { callee } = node;
|
|
242
|
+
const scope = scopes.get(node);
|
|
243
|
+
if (callee.type === "Identifier") return resolve(scope, callee.name) === "view";
|
|
244
|
+
if (callee.type !== "MemberExpression" || callee.object.type !== "Identifier" || resolve(scope, callee.object.name) !== "namespace") return false;
|
|
245
|
+
return callee.computed ? callee.property.type === "Literal" && callee.property.value === "defineView" : callee.property.type === "Identifier" && callee.property.name === "defineView";
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region src/builder/file-routes.ts
|
|
250
|
+
const clientRuntimeImport = "tailorkit:file-client-runtime";
|
|
251
|
+
function fileViewLocation(filename, root) {
|
|
252
|
+
const relative = path.relative(path.join(root, "src/slots"), filename).split(path.sep);
|
|
253
|
+
if (relative.length < 2 || relative[0] === ".." || !filename.endsWith(".view.tsx")) return;
|
|
254
|
+
const [slot, ...routeSegments] = relative;
|
|
255
|
+
if (!slot) return;
|
|
256
|
+
return {
|
|
257
|
+
slot,
|
|
258
|
+
path: routePath(routeSegments.join("/").slice(0, -9), filename)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function routePath(name, filename) {
|
|
262
|
+
const segments = name.split(/[./]/u);
|
|
263
|
+
if (segments.some((segment) => !segment || segment === "..")) throw new Error(`${filename} has an empty route segment. Use route.nested-route.view.tsx.`);
|
|
264
|
+
return `/${segments.join("/")}`;
|
|
265
|
+
}
|
|
266
|
+
/** Fill missing defineView options before instance extraction, on both build sides. */
|
|
267
|
+
function inferFileView(source, filename, root) {
|
|
268
|
+
const location = fileViewLocation(filename, root);
|
|
269
|
+
if (!location) return source;
|
|
270
|
+
const { slot, path: viewPath } = location;
|
|
271
|
+
const parsed = parseSync(filename, source);
|
|
272
|
+
if (parsed.errors.length) throw new Error(`${filename}: ${parsed.errors[0]?.message}`);
|
|
273
|
+
const isDefineViewCall = createDefineViewMatcher(parsed.program);
|
|
274
|
+
const edits = [];
|
|
275
|
+
function visit(node) {
|
|
276
|
+
if (isDefineViewCall(node)) {
|
|
277
|
+
const options = node.arguments[0];
|
|
278
|
+
if (options?.type !== "ObjectExpression") throw new Error(`${filename}: file views require defineView with an inline options object.`);
|
|
279
|
+
const properties = new Set(options.properties.flatMap((property) => {
|
|
280
|
+
if (property.type !== "Property" || property.computed) return [];
|
|
281
|
+
if (property.key.type === "Identifier") return [property.key.name];
|
|
282
|
+
if (property.key.type === "Literal") return [String(property.key.value)];
|
|
283
|
+
return [];
|
|
284
|
+
}));
|
|
285
|
+
const defaults = [!properties.has("slot") ? `slot: ${JSON.stringify(slot)},` : "", !properties.has("view") ? `view: ${JSON.stringify(viewPath)},` : ""].join("");
|
|
286
|
+
if (defaults) edits.push({
|
|
287
|
+
position: options.start + 1,
|
|
288
|
+
text: defaults
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
for (const value of Object.values(node)) {
|
|
292
|
+
const children = Array.isArray(value) ? value : [value];
|
|
293
|
+
for (const child of children) if (child && typeof child === "object" && "type" in child) visit(child);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
visit(parsed.program);
|
|
297
|
+
for (const edit of edits.toSorted((a, b) => b.position - a.position)) source = source.slice(0, edit.position) + edit.text + source.slice(edit.position);
|
|
298
|
+
return source;
|
|
299
|
+
}
|
|
300
|
+
async function discoverFileRoutes(root) {
|
|
301
|
+
const views = [];
|
|
302
|
+
const roots = [];
|
|
303
|
+
const layouts = [];
|
|
304
|
+
const slotsDir = path.join(root, "src/slots");
|
|
305
|
+
async function scan(directory, slot, prefix = "") {
|
|
306
|
+
const entries = await readdir(directory, { withFileTypes: true }).catch((error) => {
|
|
307
|
+
if (error.code === "ENOENT") return [];
|
|
308
|
+
throw error;
|
|
309
|
+
});
|
|
310
|
+
for (const entry of entries.toSorted((a, b) => a.name.localeCompare(b.name))) {
|
|
311
|
+
const filename = path.join(directory, entry.name);
|
|
312
|
+
if (entry.isDirectory()) await scan(filename, slot ?? entry.name, slot ? `${prefix}${entry.name}/` : "");
|
|
313
|
+
else if (entry.isFile() && slot) {
|
|
314
|
+
if (entry.name.endsWith(".view.tsx")) {
|
|
315
|
+
fileViewLocation(filename, root);
|
|
316
|
+
views.push({
|
|
317
|
+
filename,
|
|
318
|
+
slot
|
|
319
|
+
});
|
|
320
|
+
} else if (entry.name === "root.tsx" && !prefix) roots.push({
|
|
321
|
+
filename,
|
|
322
|
+
slot
|
|
323
|
+
});
|
|
324
|
+
else if (entry.name === "layout.tsx" || entry.name.endsWith(".layout.tsx")) {
|
|
325
|
+
const name = entry.name === "layout.tsx" ? prefix.replace(/\/$/u, "") : prefix + entry.name.slice(0, -11);
|
|
326
|
+
layouts.push({
|
|
327
|
+
filename,
|
|
328
|
+
slot,
|
|
329
|
+
path: name ? routePath(name, filename) : "/"
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
await scan(slotsDir);
|
|
336
|
+
const rootFile = path.join(root, "src/root.tsx");
|
|
337
|
+
return {
|
|
338
|
+
root: existsSync(rootFile) ? { filename: rootFile } : void 0,
|
|
339
|
+
roots,
|
|
340
|
+
layouts,
|
|
341
|
+
views
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/** Root, layout, view and binding sources to check before deployment. */
|
|
345
|
+
async function getClientSourceFiles(root) {
|
|
346
|
+
const discovered = await discoverFileRoutes(root);
|
|
347
|
+
const bindings = path.join(root, "src/tailorkit.gen.ts");
|
|
348
|
+
return [
|
|
349
|
+
...discovered.root ? [discovered.root.filename] : [],
|
|
350
|
+
...discovered.roots.map((module) => module.filename),
|
|
351
|
+
...discovered.layouts.map((module) => module.filename),
|
|
352
|
+
...discovered.views.map((module) => module.filename),
|
|
353
|
+
...existsSync(bindings) ? [bindings] : []
|
|
354
|
+
];
|
|
355
|
+
}
|
|
356
|
+
async function fileClientSource(root) {
|
|
357
|
+
const discovered = await discoverFileRoutes(root);
|
|
358
|
+
const imports = [`import { createFileClient } from ${JSON.stringify(clientRuntimeImport)};`];
|
|
359
|
+
let index = 0;
|
|
360
|
+
function module(entry) {
|
|
361
|
+
const binding = `module${index++}`;
|
|
362
|
+
imports.push(`import ${binding} from ${JSON.stringify(entry.filename)};`);
|
|
363
|
+
return `{ filename: ${JSON.stringify(path.relative(root, entry.filename))}, definition: ${binding}`;
|
|
364
|
+
}
|
|
365
|
+
const rootModule = discovered.root ? `${module(discovered.root)} }` : "undefined";
|
|
366
|
+
const roots = discovered.roots.map((entry) => `${module(entry)}, slot: ${JSON.stringify(entry.slot)} }`);
|
|
367
|
+
const layouts = discovered.layouts.map((entry) => `${module(entry)}, slot: ${JSON.stringify(entry.slot)}, path: ${JSON.stringify(entry.path)} }`);
|
|
368
|
+
const views = discovered.views.map((entry) => `${module(entry)}, slot: ${JSON.stringify(entry.slot)} }`);
|
|
369
|
+
return `${imports.join("\n")}\nexport default createFileClient({ root: ${rootModule}, roots: [${roots.join(",")}], layouts: [${layouts.join(",")}], views: [${views.join(",")}] });`;
|
|
370
|
+
}
|
|
371
|
+
/** A generated entry in build output lets the bundler track imports normally.
|
|
372
|
+
* Directory watching also covers routes that did not exist at the first build. */
|
|
373
|
+
async function createFileRouteEntry(root, output, watching) {
|
|
374
|
+
let source = await fileClientSource(root);
|
|
375
|
+
const temporaryRoot = path.join(output, "tmp");
|
|
376
|
+
await mkdir(temporaryRoot, { recursive: true });
|
|
377
|
+
const directory = await mkdtemp(path.join(temporaryRoot, "client-"));
|
|
378
|
+
const entry = path.join(directory, "entry.ts");
|
|
379
|
+
await writeFile(entry, source);
|
|
380
|
+
let pending = Promise.resolve();
|
|
381
|
+
let closed = false;
|
|
382
|
+
let timer;
|
|
383
|
+
async function refresh() {
|
|
384
|
+
if (closed) return;
|
|
385
|
+
let next;
|
|
386
|
+
try {
|
|
387
|
+
next = await fileClientSource(root);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
next = `throw new Error(${JSON.stringify(error instanceof Error ? error.message : String(error))});`;
|
|
390
|
+
}
|
|
391
|
+
if (next === source) return;
|
|
392
|
+
source = next;
|
|
393
|
+
await writeFile(entry, next);
|
|
394
|
+
}
|
|
395
|
+
const watcher = watching ? watch(path.join(root, "src"), { recursive: true }, () => {
|
|
396
|
+
if (timer) clearTimeout(timer);
|
|
397
|
+
timer = setTimeout(() => {
|
|
398
|
+
pending = pending.then(refresh);
|
|
399
|
+
}, 25);
|
|
400
|
+
}) : void 0;
|
|
401
|
+
return {
|
|
402
|
+
entry,
|
|
403
|
+
async close() {
|
|
404
|
+
closed = true;
|
|
405
|
+
if (timer) clearTimeout(timer);
|
|
406
|
+
watcher?.close();
|
|
407
|
+
try {
|
|
408
|
+
await pending;
|
|
409
|
+
} finally {
|
|
410
|
+
await rm(directory, {
|
|
411
|
+
recursive: true,
|
|
412
|
+
force: true
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function fileRoutesPlugin(root) {
|
|
419
|
+
return {
|
|
420
|
+
name: "tailorkit-file-routes",
|
|
421
|
+
enforce: "pre",
|
|
422
|
+
async resolveId(id) {
|
|
423
|
+
if (id !== clientRuntimeImport) return;
|
|
424
|
+
const importer = path.join(root(), "src/root.tsx");
|
|
425
|
+
return await this.resolve("tailorkit/client", importer, { skipSelf: true }) ?? this.resolve("@tailorkit/app/client", importer, { skipSelf: true });
|
|
426
|
+
},
|
|
427
|
+
transform(source, filename) {
|
|
428
|
+
if (!filename.endsWith(".view.tsx")) return;
|
|
429
|
+
const code = inferFileView(source, filename, root());
|
|
430
|
+
return code === source ? void 0 : {
|
|
431
|
+
code,
|
|
432
|
+
map: null
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
//#endregion
|
|
438
|
+
export { createDefineViewMatcher as a, appDatabasePaths as c, inferFileView as i, createTailorKitUploadManifest as l, fileRoutesPlugin as n, checkAppSchema as o, getClientSourceFiles as r, drizzleGenerateArguments as s, createFileRouteEntry as t, tailorkitUploadManifestSchema as u };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
//#region src/index.d.ts
|
|
3
|
+
export interface TailorKitPluginOptions {
|
|
4
|
+
/** Config file path relative to vite.config.ts. Defaults to ./tailorkit.config.ts. */
|
|
5
|
+
configPath?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Build the app client, private backend, migrations and deployment manifest. */
|
|
8
|
+
export declare const tailorkit: (options?: TailorKitPluginOptions) => Plugin[];
|
|
9
|
+
//#endregion
|