odoro 1.0.9 → 2.1.0

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 (88) hide show
  1. package/README.md +94 -0
  2. package/client.d.ts +90 -16
  3. package/dist/build-XJIP4GNT.js +5 -0
  4. package/dist/{chunk-34RGOZFA.js → chunk-4D56Z7G3.js} +49 -50
  5. package/dist/{chunk-JMEHF3KN.js → chunk-7ZB5MAM6.js} +4 -4
  6. package/dist/chunk-DF67LADH.js +735 -0
  7. package/dist/{chunk-LHZGX5ML.js → chunk-HS7NGZLM.js} +307 -196
  8. package/dist/chunk-LIKVHWB4.js +231 -0
  9. package/dist/chunk-QOVBLN7A.js +241 -0
  10. package/dist/chunk-RTWJ5GFB.js +729 -0
  11. package/dist/chunk-TONYIKCN.js +57 -0
  12. package/dist/cli.d.ts +12 -13
  13. package/dist/cli.js +86 -63
  14. package/dist/{commands-AHMXBWQQ.js → commands-GYA7NTCX.js} +124 -124
  15. package/dist/commands-UOWN3IK4.js +181 -0
  16. package/dist/{create-CYVSVDAD.js → create-B2TLV6XI.js} +198 -160
  17. package/dist/index.d.ts +603 -151
  18. package/dist/index.js +6 -6
  19. package/dist/{package-EIDLKAA6.js → package-3ZJOEXM4.js} +6 -5
  20. package/dist/preview-3IRG7MHV.js +4 -0
  21. package/dist/registry/index.d.ts +75 -76
  22. package/dist/registry/index.js +1 -1
  23. package/dist/{server-4UGN3SFS.js → server-ZB3P3EGS.js} +3 -3
  24. package/package.json +6 -5
  25. package/templates/react-ts/README.md +71 -35
  26. package/templates/react-ts/_env.example +9 -0
  27. package/templates/react-ts/_variants/with-engine/src/background.tsx +118 -0
  28. package/templates/react-ts/_variants/without-libs/src/App.tsx +380 -0
  29. package/templates/react-ts/_variants/without-libs/src/background.tsx +24 -0
  30. package/templates/react-ts/_variants/without-libs/src/entry-server.tsx +39 -0
  31. package/templates/react-ts/_variants/without-libs/src/main.tsx +26 -0
  32. package/templates/{react-ts-server/_variantes/sans-libs/client → react-ts/_variants/without-libs}/src/styles.css +137 -137
  33. package/templates/react-ts/{_variantes/sans-routeur → _variants/without-router}/src/App.tsx +141 -141
  34. package/templates/react-ts/_variants/without-router/src/entry-server.tsx +39 -0
  35. package/templates/react-ts/index.html +4 -4
  36. package/templates/react-ts/odoro.config.ts +5 -0
  37. package/templates/react-ts/src/App.tsx +219 -209
  38. package/templates/react-ts/src/background.tsx +66 -0
  39. package/templates/react-ts/src/entry-server.tsx +85 -0
  40. package/templates/react-ts/src/main.tsx +13 -4
  41. package/templates/react-ts/src/router.tsx +65 -45
  42. package/templates/react-ts/src/styles.css +5 -5
  43. package/templates/react-ts-server/Dockerfile +9 -9
  44. package/templates/react-ts-server/README.md +97 -45
  45. package/templates/react-ts-server/_env.example +31 -33
  46. package/templates/react-ts-server/_variants/with-engine/client/src/background.tsx +118 -0
  47. package/templates/react-ts-server/_variants/without-libs/client/src/App.tsx +380 -0
  48. package/templates/react-ts-server/_variants/without-libs/client/src/background.tsx +24 -0
  49. package/templates/react-ts-server/_variants/without-libs/client/src/entry-server.tsx +39 -0
  50. package/templates/react-ts-server/_variants/without-libs/client/src/main.tsx +26 -0
  51. package/templates/{react-ts/_variantes/sans-libs → react-ts-server/_variants/without-libs/client}/src/styles.css +137 -137
  52. package/templates/react-ts-server/{_variantes/sans-routeur → _variants/without-router}/client/src/App.tsx +141 -141
  53. package/templates/react-ts-server/_variants/without-router/client/src/entry-server.tsx +39 -0
  54. package/templates/react-ts-server/client/index.html +4 -4
  55. package/templates/react-ts-server/client/src/App.tsx +228 -210
  56. package/templates/react-ts-server/client/src/account.tsx +267 -0
  57. package/templates/react-ts-server/client/src/auth.tsx +139 -0
  58. package/templates/react-ts-server/client/src/background.tsx +66 -0
  59. package/templates/react-ts-server/client/src/entry-server.tsx +92 -0
  60. package/templates/react-ts-server/client/src/main.tsx +17 -5
  61. package/templates/react-ts-server/client/src/router.tsx +77 -45
  62. package/templates/react-ts-server/client/src/styles.css +5 -5
  63. package/templates/react-ts-server/odoro.config.ts +7 -4
  64. package/templates/react-ts-server/package.json +2 -0
  65. package/templates/react-ts-server/scripts/dev.mjs +14 -14
  66. package/templates/react-ts-server/server/src/main.ts +119 -45
  67. package/templates/react-ts-server/server/src/modules/auth/index.ts +238 -0
  68. package/templates/react-ts-server/server/src/modules/auth/password.ts +122 -0
  69. package/templates/react-ts-server/server/src/modules/auth/store.ts +193 -0
  70. package/templates/react-ts-server/server/src/modules/health/index.ts +45 -46
  71. package/dist/build-JFQHODAT.js +0 -5
  72. package/dist/chunk-22KJTV2R.js +0 -380
  73. package/dist/chunk-3SZIN6VG.js +0 -64
  74. package/dist/chunk-DL3NPC4H.js +0 -113
  75. package/dist/chunk-TQUJ3MFS.js +0 -268
  76. package/dist/chunk-ZVL7EXJO.js +0 -66
  77. package/dist/commands-4JRBD55Z.js +0 -245
  78. package/dist/preview-7DKEAQOQ.js +0 -4
  79. package/templates/react-ts/_variantes/avec-moteur/src/fond.tsx +0 -118
  80. package/templates/react-ts/_variantes/sans-libs/src/App.tsx +0 -382
  81. package/templates/react-ts/_variantes/sans-libs/src/fond.tsx +0 -23
  82. package/templates/react-ts/_variantes/sans-libs/src/main.tsx +0 -17
  83. package/templates/react-ts/src/fond.tsx +0 -66
  84. package/templates/react-ts-server/_variantes/avec-moteur/client/src/fond.tsx +0 -118
  85. package/templates/react-ts-server/_variantes/sans-libs/client/src/App.tsx +0 -382
  86. package/templates/react-ts-server/_variantes/sans-libs/client/src/fond.tsx +0 -23
  87. package/templates/react-ts-server/_variantes/sans-libs/client/src/main.tsx +0 -17
  88. package/templates/react-ts-server/client/src/fond.tsx +0 -66
@@ -0,0 +1,729 @@
1
+ #!/usr/bin/env node
2
+ import { statSync, createReadStream, existsSync, readdirSync } from 'fs';
3
+ import { resolve, dirname, extname, relative, join } from 'path';
4
+ import { spawn } from 'child_process';
5
+ import { readFile } from 'fs/promises';
6
+ import { build } from 'esbuild';
7
+
8
+ // src/plugins.ts
9
+ async function transformWith(plugins, code, context) {
10
+ let current = code;
11
+ let touched = false;
12
+ for (const plugin of plugins) {
13
+ if (plugin.transform === void 0) continue;
14
+ try {
15
+ const rendered = await plugin.transform(current, context);
16
+ if (rendered === null || rendered === void 0) continue;
17
+ current = rendered;
18
+ touched = true;
19
+ } catch (cause) {
20
+ throw new Error(
21
+ `[odoro] Plugin "${plugin.name}" failed on ${context.id}: ` + (cause instanceof Error ? cause.message : String(cause)),
22
+ { cause }
23
+ );
24
+ }
25
+ }
26
+ return touched ? current : void 0;
27
+ }
28
+ async function transformHtmlWith(plugins, html, context) {
29
+ let current = html;
30
+ for (const plugin of plugins) {
31
+ if (plugin.transformIndexHtml === void 0) continue;
32
+ try {
33
+ current = await plugin.transformIndexHtml(current, context);
34
+ } catch (cause) {
35
+ throw new Error(
36
+ `[odoro] Plugin "${plugin.name}" failed on the document: ` + (cause instanceof Error ? cause.message : String(cause)),
37
+ { cause }
38
+ );
39
+ }
40
+ }
41
+ return current;
42
+ }
43
+ function esbuildPluginsFrom(plugins) {
44
+ return plugins.flatMap((plugin) => plugin.esbuild ?? []);
45
+ }
46
+
47
+ // src/shared/listen.ts
48
+ var ATTEMPTS = 10;
49
+ async function listen(server, port, host, attempts = ATTEMPTS) {
50
+ for (let offset = 0; offset < attempts; offset += 1) {
51
+ const candidate = port + offset;
52
+ const taken = await new Promise((resolve3, reject) => {
53
+ const onError = (cause) => {
54
+ server.removeListener("listening", onListening);
55
+ if (cause.code === "EADDRINUSE") {
56
+ resolve3(true);
57
+ return;
58
+ }
59
+ reject(cause);
60
+ };
61
+ const onListening = () => {
62
+ server.removeListener("error", onError);
63
+ resolve3(false);
64
+ };
65
+ server.once("error", onError);
66
+ server.once("listening", onListening);
67
+ server.listen(candidate, host);
68
+ });
69
+ if (!taken) {
70
+ const address = server.address();
71
+ const obtained = typeof address === "object" && address !== null ? address.port : candidate;
72
+ return offset === 0 ? { port: obtained } : { port: obtained, requested: port };
73
+ }
74
+ }
75
+ throw new Error(
76
+ `[odoro] No free port between ${String(port)} and ${String(port + attempts - 1)}.`
77
+ );
78
+ }
79
+ var MIME = {
80
+ ".html": "text/html; charset=utf-8",
81
+ ".js": "text/javascript; charset=utf-8",
82
+ ".mjs": "text/javascript; charset=utf-8",
83
+ ".css": "text/css; charset=utf-8",
84
+ ".json": "application/json; charset=utf-8",
85
+ ".map": "application/json; charset=utf-8",
86
+ ".txt": "text/plain; charset=utf-8",
87
+ ".md": "text/markdown; charset=utf-8",
88
+ ".markdown": "text/markdown; charset=utf-8",
89
+ ".xml": "application/xml; charset=utf-8",
90
+ ".webmanifest": "application/manifest+json",
91
+ ".svg": "image/svg+xml",
92
+ ".png": "image/png",
93
+ ".jpg": "image/jpeg",
94
+ ".jpeg": "image/jpeg",
95
+ ".gif": "image/gif",
96
+ ".webp": "image/webp",
97
+ ".avif": "image/avif",
98
+ ".ico": "image/x-icon",
99
+ ".woff": "font/woff",
100
+ ".woff2": "font/woff2",
101
+ ".ttf": "font/ttf",
102
+ ".otf": "font/otf",
103
+ ".mp4": "video/mp4",
104
+ ".m4v": "video/mp4",
105
+ ".webm": "video/webm",
106
+ ".mov": "video/quicktime",
107
+ ".ogv": "video/ogg",
108
+ ".mp3": "audio/mpeg",
109
+ ".m4a": "audio/mp4",
110
+ ".wav": "audio/wav",
111
+ ".oga": "audio/ogg",
112
+ ".glb": "model/gltf-binary",
113
+ ".gltf": "model/gltf+json",
114
+ ".pdf": "application/pdf",
115
+ ".wasm": "application/wasm"
116
+ };
117
+ function mimeOf(file) {
118
+ return MIME[extname(file).toLowerCase()] ?? "application/octet-stream";
119
+ }
120
+ function parseRange(header, size) {
121
+ if (header === void 0) return null;
122
+ const match = /^bytes=(\d*)-(\d*)$/.exec(header.trim());
123
+ if (match === null) return null;
124
+ const [, rawStart, rawEnd] = match;
125
+ if (rawStart === "" && rawEnd === "") return null;
126
+ if (rawStart === "") {
127
+ const length = Number(rawEnd);
128
+ if (!Number.isFinite(length) || length <= 0) return "unsatisfiable";
129
+ return { start: Math.max(0, size - length), end: size - 1 };
130
+ }
131
+ const start = Number(rawStart);
132
+ if (!Number.isFinite(start) || start >= size) return "unsatisfiable";
133
+ const end = rawEnd === "" ? size - 1 : Math.min(Number(rawEnd), size - 1);
134
+ if (!Number.isFinite(end) || end < start) return "unsatisfiable";
135
+ return { start, end };
136
+ }
137
+ function sendFile(response, file, options = {}) {
138
+ const size = statSync(file).size;
139
+ const type = mimeOf(file);
140
+ const cacheControl = options.cacheControl ?? "no-cache";
141
+ const range = parseRange(options.range, size);
142
+ if (range === "unsatisfiable") {
143
+ response.writeHead(416, {
144
+ "Content-Range": `bytes */${String(size)}`,
145
+ "Accept-Ranges": "bytes",
146
+ "Content-Type": "text/plain; charset=utf-8"
147
+ });
148
+ response.end("Range not satisfiable");
149
+ return;
150
+ }
151
+ if (range === null) {
152
+ response.writeHead(200, {
153
+ "Content-Type": type,
154
+ "Content-Length": String(size),
155
+ "Accept-Ranges": "bytes",
156
+ "Cache-Control": cacheControl
157
+ });
158
+ if (options.headOnly === true) {
159
+ response.end();
160
+ return;
161
+ }
162
+ createReadStream(file).pipe(response);
163
+ return;
164
+ }
165
+ response.writeHead(206, {
166
+ "Content-Type": type,
167
+ "Content-Length": String(range.end - range.start + 1),
168
+ "Content-Range": `bytes ${String(range.start)}-${String(range.end)}/${String(size)}`,
169
+ "Accept-Ranges": "bytes",
170
+ "Cache-Control": cacheControl
171
+ });
172
+ if (options.headOnly === true) {
173
+ response.end();
174
+ return;
175
+ }
176
+ createReadStream(file, { start: range.start, end: range.end }).pipe(response);
177
+ }
178
+ function openBrowser(url) {
179
+ const [command, ...arguments_] = process.platform === "win32" ? (
180
+ // `start` is a built-in command: it goes through the interpreter. The
181
+ // empty title that follows is mandatory, otherwise a quoted address
182
+ // would be taken for the window title.
183
+ ["cmd", "/c", "start", '""', url.replace(/&/g, "^&")]
184
+ ) : process.platform === "darwin" ? ["open", url] : ["xdg-open", url];
185
+ if (command === void 0) return;
186
+ try {
187
+ const child = spawn(command, arguments_, { stdio: "ignore", detached: true });
188
+ child.on("error", () => void 0);
189
+ child.unref();
190
+ } catch {
191
+ }
192
+ }
193
+
194
+ // src/shared/suffixes.ts
195
+ var MARKER = /\?(worker|raw|url)$/;
196
+ function readSuffix(specifier) {
197
+ const found = MARKER.exec(specifier);
198
+ const suffix = found?.[1];
199
+ if (suffix === void 0) return void 0;
200
+ return {
201
+ suffix,
202
+ path: specifier.slice(0, specifier.length - suffix.length - 1)
203
+ };
204
+ }
205
+ function workerModule(url) {
206
+ return `const source = ${JSON.stringify(url)}
207
+
208
+ export default class OdoroWorker extends Worker {
209
+ constructor(options) {
210
+ super(source, { type: 'module', ...options })
211
+ }
212
+ }
213
+ `;
214
+ }
215
+ function textModule(content) {
216
+ return `export default ${JSON.stringify(content)}
217
+ `;
218
+ }
219
+ function urlModule(url) {
220
+ return `export default ${JSON.stringify(url)}
221
+ `;
222
+ }
223
+ var IGNORED = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".odoro"]);
224
+ var SPECIALS = /[.*+?^${}()|[\]\\]/g;
225
+ function toRegex(pattern) {
226
+ let source = "";
227
+ for (let index = 0; index < pattern.length; index += 1) {
228
+ const letter = pattern[index];
229
+ if (letter === void 0) continue;
230
+ if (letter === "*") {
231
+ if (pattern[index + 1] === "*") {
232
+ if (pattern[index + 2] === "/") {
233
+ source += "(?:.*/)?";
234
+ index += 2;
235
+ continue;
236
+ }
237
+ source += ".*";
238
+ index += 1;
239
+ continue;
240
+ }
241
+ source += "[^/]*";
242
+ continue;
243
+ }
244
+ if (letter === "?") {
245
+ source += "[^/]";
246
+ continue;
247
+ }
248
+ if (letter === "{") {
249
+ const closing = pattern.indexOf("}", index);
250
+ if (closing !== -1) {
251
+ const choices = pattern.slice(index + 1, closing).split(",").map((part) => part.replace(SPECIALS, "\\$&"));
252
+ source += `(?:${choices.join("|")})`;
253
+ index = closing;
254
+ continue;
255
+ }
256
+ }
257
+ source += letter.replace(SPECIALS, "\\$&");
258
+ }
259
+ return new RegExp(`^${source}$`);
260
+ }
261
+ function groupEnd(code, opening) {
262
+ let depth = 0;
263
+ let quote;
264
+ for (let index = opening; index < code.length; index += 1) {
265
+ const letter = code[index];
266
+ if (quote !== void 0) {
267
+ if (letter === "\\") index += 1;
268
+ else if (letter === quote) quote = void 0;
269
+ continue;
270
+ }
271
+ if (letter === '"' || letter === "'" || letter === "`") {
272
+ quote = letter;
273
+ continue;
274
+ }
275
+ if (letter === "(") depth += 1;
276
+ else if (letter === ")") {
277
+ depth -= 1;
278
+ if (depth === 0) return index;
279
+ }
280
+ }
281
+ return -1;
282
+ }
283
+ function stringsIn(fragment) {
284
+ const rendered = [];
285
+ for (const found of fragment.matchAll(/(['"`])((?:\\.|(?!\1).)*)\1/g)) {
286
+ const value = found[2];
287
+ if (value !== void 0) rendered.push(value);
288
+ }
289
+ return rendered;
290
+ }
291
+ function patternsIn(code) {
292
+ const patterns = [];
293
+ for (const found of code.matchAll(/import\.meta\.glob\s*\(/g)) {
294
+ const start = found.index;
295
+ const opening = start + found[0].length - 1;
296
+ const end = groupEnd(code, opening);
297
+ if (end === -1) continue;
298
+ const arguments_ = code.slice(opening + 1, end);
299
+ const brace = arguments_.indexOf("{");
300
+ const cut = brace === -1 ? -1 : arguments_.slice(0, brace).lastIndexOf(",");
301
+ const first = cut === -1 ? arguments_ : arguments_.slice(0, cut);
302
+ const second = cut === -1 ? "" : arguments_.slice(cut + 1);
303
+ const all = stringsIn(first);
304
+ if (all.length === 0) continue;
305
+ patterns.push({
306
+ start,
307
+ end: end + 1,
308
+ included: all.filter((pattern) => !pattern.startsWith("!")),
309
+ excluded: all.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1)),
310
+ options: {
311
+ eager: /["']?eager["']?\s*:\s*true/.test(second),
312
+ named: /["']?import["']?\s*:\s*["']([^"']+)["']/.exec(second)?.[1]
313
+ }
314
+ });
315
+ }
316
+ return patterns;
317
+ }
318
+ function fixedRootOf(pattern) {
319
+ const fixed = [];
320
+ for (const segment of pattern.split("/")) {
321
+ if (/[*?{}[\]]/.test(segment)) break;
322
+ fixed.push(segment);
323
+ }
324
+ return fixed.join("/") || ".";
325
+ }
326
+ function filesUnder(directory) {
327
+ if (!existsSync(directory) || !statSync(directory).isDirectory()) return [];
328
+ const rendered = [];
329
+ const stack = [directory];
330
+ while (stack.length > 0) {
331
+ const current = stack.pop();
332
+ if (current === void 0) continue;
333
+ for (const entry of readdirSync(current)) {
334
+ if (IGNORED.has(entry) || entry.startsWith(".")) continue;
335
+ const path = join(current, entry);
336
+ if (statSync(path).isDirectory()) stack.push(path);
337
+ else rendered.push(path);
338
+ }
339
+ }
340
+ return rendered;
341
+ }
342
+ function toUrlPath(path) {
343
+ return path.split("\\").join("/");
344
+ }
345
+ function resolvePattern(pattern, file, projectRoot) {
346
+ const sourceDirectory = dirname(file);
347
+ const found = /* @__PURE__ */ new Set();
348
+ for (const included of pattern.included) {
349
+ const absolute = included.startsWith("/");
350
+ const base = absolute ? join(projectRoot, fixedRootOf(included.slice(1))) : resolve(sourceDirectory, fixedRootOf(included));
351
+ const regex = toRegex(included);
352
+ for (const path of filesUnder(base)) {
353
+ const relativePath = absolute ? `/${toUrlPath(relative(projectRoot, path))}` : toUrlPath(relative(sourceDirectory, path)).replace(/^(?!\.\.\/)/, "./");
354
+ if (!regex.test(relativePath)) continue;
355
+ if (pattern.excluded.some((excluded) => toRegex(excluded).test(relativePath)))
356
+ continue;
357
+ if (path === resolve(file)) continue;
358
+ found.add(relativePath);
359
+ }
360
+ }
361
+ return [...found].sort();
362
+ }
363
+ function hasGlob(code) {
364
+ return code.includes("import.meta.glob");
365
+ }
366
+ function transformGlob(code, file, projectRoot) {
367
+ if (!hasGlob(code)) return void 0;
368
+ const patterns = patternsIn(code);
369
+ if (patterns.length === 0) return void 0;
370
+ const prelude = [];
371
+ const files = [];
372
+ let rendered = "";
373
+ let cursor = 0;
374
+ let counter = 0;
375
+ for (const pattern of patterns) {
376
+ const entries = [];
377
+ for (const path of resolvePattern(pattern, file, projectRoot)) {
378
+ files.push(resolve(dirname(file), path));
379
+ const key = JSON.stringify(path);
380
+ const target = JSON.stringify(path);
381
+ if (pattern.options.eager) {
382
+ const alias = `__odoro_glob_${String(counter)}`;
383
+ counter += 1;
384
+ prelude.push(`import * as ${alias} from ${target}`);
385
+ entries.push(
386
+ `${key}: ${pattern.options.named === void 0 ? alias : `${alias}[${JSON.stringify(pattern.options.named)}]`}`
387
+ );
388
+ continue;
389
+ }
390
+ const importation = `import(${target})`;
391
+ entries.push(
392
+ `${key}: ${pattern.options.named === void 0 ? `() => ${importation}` : `() => ${importation}.then((m) => m[${JSON.stringify(pattern.options.named)}])`}`
393
+ );
394
+ }
395
+ rendered += code.slice(cursor, pattern.start);
396
+ rendered += entries.length === 0 ? "{}" : `{${entries.join(", ")}}`;
397
+ cursor = pattern.end;
398
+ }
399
+ rendered += code.slice(cursor);
400
+ return {
401
+ code: prelude.length === 0 ? rendered : `${prelude.join("\n")}
402
+ ${rendered}`,
403
+ files
404
+ };
405
+ }
406
+ var LOADERS = {
407
+ ".ts": "ts",
408
+ ".tsx": "tsx",
409
+ ".js": "js",
410
+ ".jsx": "jsx",
411
+ ".mjs": "js",
412
+ ".cjs": "js"
413
+ };
414
+ function sourcePlugin(options) {
415
+ const transforms = options.plugins.some((plugin) => plugin.transform !== void 0);
416
+ return {
417
+ name: "odoro-source",
418
+ setup(builder) {
419
+ builder.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, async (args) => {
420
+ if (args.path.includes("node_modules")) return null;
421
+ const source = await readFile(args.path, "utf8");
422
+ let code = source;
423
+ if (hasGlob(code)) {
424
+ const resolved = transformGlob(code, args.path, options.root);
425
+ if (resolved !== void 0) {
426
+ code = resolved.code;
427
+ for (const file of resolved.files) options.onMatch?.(file);
428
+ }
429
+ }
430
+ if (transforms) {
431
+ const rendered = await transformWith(options.plugins, code, {
432
+ id: args.path,
433
+ dev: options.dev,
434
+ ssr: options.ssr ?? false
435
+ });
436
+ if (rendered !== void 0) code = rendered;
437
+ }
438
+ if (code === source) return null;
439
+ return { contents: code, loader: LOADERS[extname(args.path).toLowerCase()] };
440
+ });
441
+ }
442
+ };
443
+ }
444
+ var DEPS_PREFIX = "/@deps/";
445
+ var INTERNAL_PREFIX = "/@odoro/";
446
+ var SOURCE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
447
+ var STYLE_EXTENSIONS = [".css"];
448
+ var ASSET_EXTENSIONS = [
449
+ // Images
450
+ ".svg",
451
+ ".png",
452
+ ".jpg",
453
+ ".jpeg",
454
+ ".gif",
455
+ ".webp",
456
+ ".avif",
457
+ ".ico",
458
+ /*
459
+ * Polices — les quatre formats, pas seulement le moderne.
460
+ *
461
+ * Un gabarit qui embarque ses coupes statiques les livre souvent en `.ttf` :
462
+ * la version variable est en `woff2`, les instances ne le sont pas toujours.
463
+ * Sans le `.ttf` ici, la construction s arrete sur « No loader is configured
464
+ * for ".ttf" files » — ce qui au moins se voit, contrairement a la plupart
465
+ * des lacunes de ce fichier.
466
+ */
467
+ ".woff",
468
+ ".woff2",
469
+ ".ttf",
470
+ ".otf",
471
+ ".eot",
472
+ // Video et audio
473
+ ".mp4",
474
+ ".m4v",
475
+ ".webm",
476
+ ".mov",
477
+ ".ogv",
478
+ ".mp3",
479
+ ".m4a",
480
+ ".wav",
481
+ ".oga",
482
+ // Ce qu une scene demande
483
+ ".glb",
484
+ ".gltf",
485
+ ".hdr",
486
+ ".pdf"
487
+ ];
488
+ function depFileName(specifier) {
489
+ return `${specifier.replace(/^@/, "").split("/").join("_")}.js`;
490
+ }
491
+ function isBareSpecifier(specifier) {
492
+ return !specifier.startsWith(".") && !specifier.startsWith("/") && !specifier.startsWith("\\") && !/^[a-zA-Z]:[\\/]/.test(specifier) && !specifier.startsWith("data:") && !specifier.startsWith("http:") && !specifier.startsWith("https:");
493
+ }
494
+ function hasExtension(path, extensions) {
495
+ const clean = path.split("?")[0] ?? path;
496
+ return extensions.some((extension) => clean.toLowerCase().endsWith(extension));
497
+ }
498
+ function fileToUrl(file, root) {
499
+ const relativePath = relative(root, file).split("\\").join("/");
500
+ if (!relativePath.startsWith("..")) return `/${relativePath}`;
501
+ return `/@fs/${file.split("\\").join("/").replace(/^\//, "")}`;
502
+ }
503
+ function urlToFile(url, root) {
504
+ const path = (url.split("?")[0] ?? url).split("#")[0] ?? url;
505
+ if (path.startsWith("/@fs/")) {
506
+ const absolute = path.slice("/@fs/".length);
507
+ return /^[a-zA-Z]:/.test(absolute) ? absolute : `/${absolute}`;
508
+ }
509
+ return resolve(root, `.${path}`);
510
+ }
511
+ function applyAlias(specifier, config) {
512
+ for (const [prefix, target] of Object.entries(config.alias)) {
513
+ if (specifier === prefix || specifier.startsWith(`${prefix}/`)) {
514
+ return resolve(
515
+ config.root,
516
+ target,
517
+ specifier.slice(prefix.length).replace(/^\//, "")
518
+ );
519
+ }
520
+ }
521
+ return specifier;
522
+ }
523
+ function externalizeImports(config, dependencies) {
524
+ return {
525
+ name: "odoro-externalize",
526
+ setup(builder) {
527
+ builder.onResolve({ filter: /.*/ }, async (args) => {
528
+ if (args.kind === "entry-point") return null;
529
+ if (args.pluginData?.resolving === true) {
530
+ return null;
531
+ }
532
+ if (args.path.startsWith(INTERNAL_PREFIX) || args.path.startsWith(DEPS_PREFIX)) {
533
+ return { path: args.path, external: true };
534
+ }
535
+ const suffixed = readSuffix(args.path);
536
+ if (suffixed !== void 0) {
537
+ const target = await builder.resolve(applyAlias(suffixed.path, config), {
538
+ kind: "import-statement",
539
+ resolveDir: args.resolveDir,
540
+ importer: args.importer,
541
+ pluginData: { resolving: true }
542
+ });
543
+ if (target.errors.length === 0) {
544
+ dependencies.add(target.path);
545
+ return {
546
+ path: `${fileToUrl(target.path, config.root)}?${suffixed.suffix}`,
547
+ external: true
548
+ };
549
+ }
550
+ }
551
+ const aliased = applyAlias(args.path, config);
552
+ const isFileLike = hasExtension(aliased, STYLE_EXTENSIONS) || hasExtension(aliased, ASSET_EXTENSIONS);
553
+ if (isBareSpecifier(aliased) && !isFileLike) {
554
+ return { path: `${DEPS_PREFIX}${depFileName(aliased)}`, external: true };
555
+ }
556
+ const resolved = await builder.resolve(aliased, {
557
+ kind: "import-statement",
558
+ resolveDir: args.resolveDir,
559
+ importer: args.importer,
560
+ pluginData: { resolving: true }
561
+ });
562
+ if (resolved.errors.length > 0) {
563
+ return { path: args.path, external: true };
564
+ }
565
+ dependencies.add(resolved.path);
566
+ const url = fileToUrl(resolved.path, config.root);
567
+ return {
568
+ path: hasExtension(url, ASSET_EXTENSIONS) ? `${url}?import` : url,
569
+ external: true
570
+ };
571
+ });
572
+ }
573
+ };
574
+ }
575
+ async function transformModule(file, config, env) {
576
+ const dependencies = /* @__PURE__ */ new Set();
577
+ const onMatch = (matched) => {
578
+ dependencies.add(matched);
579
+ };
580
+ const result = await build({
581
+ entryPoints: [file],
582
+ bundle: true,
583
+ write: false,
584
+ format: "esm",
585
+ platform: "browser",
586
+ target: "es2022",
587
+ sourcemap: "inline",
588
+ jsx: "automatic",
589
+ jsxDev: true,
590
+ logLevel: "silent",
591
+ absWorkingDir: config.root,
592
+ define: {
593
+ "import.meta.env": JSON.stringify(env),
594
+ "process.env.NODE_ENV": JSON.stringify("development"),
595
+ ...config.define
596
+ },
597
+ plugins: [
598
+ externalizeImports(config, dependencies),
599
+ sourcePlugin({
600
+ root: config.root,
601
+ plugins: config.plugins,
602
+ dev: true,
603
+ onMatch
604
+ }),
605
+ ...esbuildPluginsFrom(config.plugins)
606
+ ]
607
+ });
608
+ const code = result.outputFiles[0]?.text;
609
+ if (code === void 0) {
610
+ throw new Error(`[odoro] The build of "${file}" produced no code.`);
611
+ }
612
+ return { code, dependencies: [...dependencies] };
613
+ }
614
+ var IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
615
+ var RESERVED = /* @__PURE__ */ new Set([
616
+ "arguments",
617
+ "await",
618
+ "break",
619
+ "case",
620
+ "catch",
621
+ "class",
622
+ "const",
623
+ "continue",
624
+ "debugger",
625
+ "default",
626
+ "delete",
627
+ "do",
628
+ "else",
629
+ "enum",
630
+ "eval",
631
+ "export",
632
+ "extends",
633
+ "false",
634
+ "finally",
635
+ "for",
636
+ "function",
637
+ "if",
638
+ "implements",
639
+ "import",
640
+ "in",
641
+ "instanceof",
642
+ "interface",
643
+ "let",
644
+ "new",
645
+ "null",
646
+ "package",
647
+ "private",
648
+ "protected",
649
+ "public",
650
+ "return",
651
+ "static",
652
+ "super",
653
+ "switch",
654
+ "this",
655
+ "throw",
656
+ "true",
657
+ "try",
658
+ "typeof",
659
+ "var",
660
+ "void",
661
+ "while",
662
+ "with",
663
+ "yield"
664
+ ]);
665
+ function wrapJson(json) {
666
+ let value;
667
+ try {
668
+ value = JSON.parse(json);
669
+ } catch {
670
+ return 'throw new SyntaxError("unreadable JSON")';
671
+ }
672
+ const lines = ["export default " + json.trim()];
673
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
674
+ for (const [key, content] of Object.entries(value)) {
675
+ if (!IDENTIFIER.test(key) || RESERVED.has(key)) continue;
676
+ lines.push("export const " + key + " = " + JSON.stringify(content));
677
+ }
678
+ }
679
+ return lines.join("\n");
680
+ }
681
+ var ASSET_DESTINATIONS = /* @__PURE__ */ new Set([
682
+ "script",
683
+ "style",
684
+ "image",
685
+ "font",
686
+ "audio",
687
+ "video",
688
+ "track",
689
+ "manifest",
690
+ "worker",
691
+ "sharedworker",
692
+ "serviceworker"
693
+ ]);
694
+ function isAssetRequest(headers) {
695
+ const destination = headers["sec-fetch-dest"];
696
+ return typeof destination === "string" && ASSET_DESTINATIONS.has(destination);
697
+ }
698
+ function wantsStylesheet(headers, url) {
699
+ if (url.includes("?direct")) return true;
700
+ if (headers["sec-fetch-dest"] === "style") return true;
701
+ const accepted = headers["accept"];
702
+ return typeof accepted === "string" && accepted.includes("text/css");
703
+ }
704
+ function wrapStyle(url, css) {
705
+ return `const id = ${JSON.stringify(`odoro-style:${url}`)}
706
+ const css = ${JSON.stringify(css)}
707
+
708
+ let element = document.querySelector(\`style[data-odoro-id="\${id}"]\`)
709
+ if (element === null) {
710
+ element = document.createElement('style')
711
+ element.setAttribute('data-odoro-id', id)
712
+ document.head.appendChild(element)
713
+ }
714
+ element.textContent = css
715
+
716
+ import.meta.hot?.accept()
717
+ import.meta.hot?.dispose(() => {
718
+ // The next stylesheet will recreate the element: removing it avoids stacking
719
+ // dead rules on every reload.
720
+ element?.remove()
721
+ })
722
+ `;
723
+ }
724
+ function wrapAsset(url) {
725
+ return `export default ${JSON.stringify(url)}
726
+ `;
727
+ }
728
+
729
+ export { ASSET_EXTENSIONS, DEPS_PREFIX, INTERNAL_PREFIX, MIME, SOURCE_EXTENSIONS, STYLE_EXTENSIONS, applyAlias, depFileName, esbuildPluginsFrom, fileToUrl, hasExtension, hasGlob, isAssetRequest, isBareSpecifier, listen, openBrowser, readSuffix, sendFile, sourcePlugin, textModule, transformGlob, transformHtmlWith, transformModule, urlModule, urlToFile, wantsStylesheet, workerModule, wrapAsset, wrapJson, wrapStyle };