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
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { depFileName, INTERNAL_PREFIX, DEPS_PREFIX, urlToFile, hasExtension, STYLE_EXTENSIONS, feuilleDemandee, ASSET_EXTENSIONS, wrapAsset, estUneRessource, wrapJson, SOURCE_EXTENSIONS, ecouter, fileToUrl, transformModule, wrapStyle, applyAlias, isBareSpecifier } from './chunk-TQUJ3MFS.js';
3
- import { info, error, warn, success, duration, colors } from './chunk-JMEHF3KN.js';
4
- import { existsSync, readFileSync, statSync, watch, createReadStream } from 'fs';
2
+ import { sourcePlugin, depFileName, SOURCE_EXTENSIONS, listen, openBrowser, INTERNAL_PREFIX, applyAlias, readSuffix, hasExtension, STYLE_EXTENSIONS, ASSET_EXTENSIONS, isBareSpecifier, MIME, DEPS_PREFIX, urlToFile, wantsStylesheet, wrapAsset, isAssetRequest, wrapJson, sendFile, transformHtmlWith, textModule, urlModule, workerModule, fileToUrl, wrapStyle, transformModule } from './chunk-RTWJ5GFB.js';
3
+ import { info, warn, success, duration, colors, error } from './chunk-7ZB5MAM6.js';
4
+ import { existsSync, readFileSync, statSync, watch } from 'fs';
5
5
  import { readFile, rm, mkdir, writeFile } from 'fs/promises';
6
6
  import { createServer, request } from 'http';
7
+ import { createServer as createServer$1 } from 'https';
7
8
  import { join, dirname, resolve, extname } from 'path';
8
9
  import { createRequire } from 'module';
9
10
  import { pathToFileURL, fileURLToPath } from 'url';
@@ -12,37 +13,90 @@ import { build } from 'esbuild';
12
13
  import { transformAsync } from '@babel/core';
13
14
  import reactRefreshPlugin from 'react-refresh/babel';
14
15
 
15
- var PAQUET = "@odoro-cli/libs";
16
- var SOUS_CHEMIN = "./generateur";
17
- function fichierDe(entree) {
18
- if (entree === void 0) return void 0;
19
- if (typeof entree === "string") return entree;
20
- return entree.import ?? entree.default;
16
+ var PACKAGE = "@odoro-cli/libs";
17
+ var SUBPATH = "./generator";
18
+ function fileFor(entry) {
19
+ if (entry === void 0) return void 0;
20
+ if (typeof entry === "string") return entry;
21
+ return entry.import ?? entry.default;
21
22
  }
22
- async function fournisseurDe(root) {
23
+ async function cssProviderFor(root) {
23
24
  try {
24
- const exiger = createRequire(join(root, "package.json"));
25
- const manifeste = exiger.resolve(`${PAQUET}/package.json`);
26
- const dossier = dirname(manifeste);
27
- const contenu = JSON.parse(await readFile(manifeste, "utf8"));
28
- const relatif = fichierDe(contenu.exports?.[SOUS_CHEMIN]);
29
- if (relatif === void 0) return void 0;
30
- const module_ = await import(pathToFileURL(resolve(dossier, relatif)).href);
31
- if (typeof module_.renderUtilitairesPour !== "function") return void 0;
25
+ const require_ = createRequire(join(root, "package.json"));
26
+ const manifest = require_.resolve(`${PACKAGE}/package.json`);
27
+ const directory = dirname(manifest);
28
+ const content = JSON.parse(await readFile(manifest, "utf8"));
29
+ const relativePath = fileFor(content.exports?.[SUBPATH]);
30
+ if (relativePath === void 0) return void 0;
31
+ const module_ = await import(pathToFileURL(resolve(directory, relativePath)).href);
32
+ if (typeof module_.renderUtilitiesFor !== "function") return void 0;
32
33
  const generate = module_.generate;
33
34
  return {
34
- renderUtilitairesPour: module_.renderUtilitairesPour,
35
- // `generate` enumere les classes du palier courant. Absent d'un paquet
36
- // plus ancien : le champ reste alors indefini, et le developpement
37
- // retombe sur la feuille telle qu'elle est livree.
35
+ renderUtilitiesFor: module_.renderUtilitiesFor,
36
+ // `generate` enumerates the classes of the current tier. Absent from an
37
+ // older package: the field then stays undefined, and development falls
38
+ // back on the stylesheet as it ships.
38
39
  ...typeof generate === "function" ? {
39
- classesConnues: () => new Set(generate("core").classNames)
40
+ knownClasses: () => new Set(generate("core").classNames)
40
41
  } : {}
41
42
  };
42
43
  } catch {
43
44
  return void 0;
44
45
  }
45
46
  }
47
+ var IMPORT = /@import\s+(?:url\(\s*)?(['"])([^'"]+)\1\s*\)?\s*([^;]*);/g;
48
+ var URL_VALUE = /url\(\s*(['"]?)([^'")]+)\1\s*\)/g;
49
+ function isExternal(address) {
50
+ return address.startsWith("data:") || address.startsWith("http:") || address.startsWith("https:") || address.startsWith("//") || address.startsWith("#");
51
+ }
52
+ function resolveReference(reference, from) {
53
+ if (reference.startsWith("/")) return void 0;
54
+ if (isBareSpecifier(reference)) {
55
+ try {
56
+ return createRequire(from).resolve(reference);
57
+ } catch {
58
+ return void 0;
59
+ }
60
+ }
61
+ const path = resolve(dirname(from), reference);
62
+ return existsSync(path) ? path : void 0;
63
+ }
64
+ async function assembleStylesheet(file, root) {
65
+ const files = [];
66
+ const seen = /* @__PURE__ */ new Set();
67
+ const read = async (path) => {
68
+ if (seen.has(path)) return "";
69
+ seen.add(path);
70
+ files.push(path);
71
+ const source = await readFile(path, "utf8");
72
+ const pieces = [];
73
+ let cursor = 0;
74
+ for (const found of source.matchAll(IMPORT)) {
75
+ const reference = found[2];
76
+ const conditions = (found[3] ?? "").trim();
77
+ if (reference === void 0) continue;
78
+ pieces.push(source.slice(cursor, found.index));
79
+ cursor = found.index + found[0].length;
80
+ const target = isExternal(reference) ? void 0 : resolveReference(reference, path);
81
+ if (target === void 0) {
82
+ pieces.push(found[0]);
83
+ continue;
84
+ }
85
+ const content = await read(target);
86
+ pieces.push(conditions === "" ? content : `@media ${conditions} {
87
+ ${content}
88
+ }`);
89
+ }
90
+ pieces.push(source.slice(cursor));
91
+ return pieces.join("").replace(URL_VALUE, (whole, quote, address) => {
92
+ if (isExternal(address) || address.startsWith("/")) return whole;
93
+ const target = resolveReference(address, path);
94
+ if (target === void 0) return whole;
95
+ return `url(${quote}${fileToUrl(target, root)}${quote})`;
96
+ });
97
+ };
98
+ return { css: await read(file), files };
99
+ }
46
100
 
47
101
  // src/dev/client.ts
48
102
  var HMR_STREAM_PATH = "/@odoro/hmr";
@@ -50,23 +104,23 @@ var HMR_CLIENT_PATH = "/@odoro/client";
50
104
  var HMR_CLIENT_SOURCE = String.raw`
51
105
  const OVERLAY_ID = 'odoro-error-overlay'
52
106
 
53
- /** Contextes de rechargement, par URL de module. */
107
+ /** Hot contexts, by module URL. */
54
108
  const contexts = new Map()
55
109
 
56
110
  /**
57
- * Cree le contexte expose a un module via import.meta.hot.
111
+ * Creates the context exposed to a module through import.meta.hot.
58
112
  * @param {string} url
59
113
  */
60
114
  export function createHotContext(url) {
61
115
  const existing = contexts.get(url)
62
116
  if (existing) {
63
- // Rechargement du meme module : les rappels de la version precedente sont
64
- // executes puis oublies.
117
+ // Reload of the same module: the callbacks of the previous version are run
118
+ // then forgotten.
65
119
  for (const callback of existing.disposers) {
66
120
  try {
67
121
  callback(existing.data)
68
122
  } catch (cause) {
69
- console.error('[odoro] echec du nettoyage de', url, cause)
123
+ console.error('[odoro] cleanup failed for', url, cause)
70
124
  }
71
125
  }
72
126
  existing.disposers = []
@@ -96,7 +150,7 @@ export function createHotContext(url) {
96
150
  }
97
151
 
98
152
  /**
99
- * Recharge un module et notifie ceux qui l'acceptent.
153
+ * Reloads a module and notifies those that accept it.
100
154
  * @param {string} url
101
155
  * @param {number} timestamp
102
156
  */
@@ -111,20 +165,20 @@ async function applyUpdate(url, timestamp) {
111
165
  try {
112
166
  const module = await import(url + (url.includes('?') ? '&' : '?') + 't=' + timestamp)
113
167
  for (const accept of acceptors) accept(module)
114
- console.log('[odoro] mis a jour', url)
168
+ console.log('[odoro] updated', url)
115
169
  } catch (cause) {
116
- console.error('[odoro] echec de la mise a jour de', url, cause)
170
+ console.error('[odoro] update failed for', url, cause)
117
171
  location.reload()
118
172
  }
119
173
  }
120
174
 
121
- /** Retire la surcouche d'erreur si elle est affichee. */
175
+ /** Removes the error overlay when it is displayed. */
122
176
  function clearOverlay() {
123
177
  document.getElementById(OVERLAY_ID)?.remove()
124
178
  }
125
179
 
126
180
  /**
127
- * Affiche une erreur de compilation par-dessus la page.
181
+ * Displays a build error on top of the page.
128
182
  * @param {string} message
129
183
  * @param {string | undefined} file
130
184
  */
@@ -146,14 +200,14 @@ function showOverlay(message, file) {
146
200
  ].join(';')
147
201
 
148
202
  const title = document.createElement('div')
149
- title.textContent = file ? 'Erreur de compilation — ' + file : 'Erreur de compilation'
203
+ title.textContent = file ? 'Build error — ' + file : 'Build error'
150
204
  title.style.cssText = 'font-weight:700;margin-bottom:1rem;color:#ff9d9d'
151
205
 
152
206
  const body = document.createElement('div')
153
207
  body.textContent = message
154
208
 
155
209
  const hint = document.createElement('div')
156
- hint.textContent = 'Corrigez le fichier : cette surcouche disparaitra d elle-meme.'
210
+ hint.textContent = 'Fix the file: this overlay will disappear on its own.'
157
211
  hint.style.cssText = 'margin-top:1.5rem;opacity:0.6'
158
212
 
159
213
  overlay.append(title, body, hint)
@@ -167,7 +221,7 @@ source.addEventListener('message', (event) => {
167
221
 
168
222
  switch (payload.type) {
169
223
  case 'connected':
170
- console.log('[odoro] rechargement a chaud connecte')
224
+ console.log('[odoro] hot reloading connected')
171
225
  break
172
226
  case 'update':
173
227
  clearOverlay()
@@ -187,9 +241,9 @@ source.addEventListener('message', (event) => {
187
241
  })
188
242
 
189
243
  source.addEventListener('error', () => {
190
- // EventSource se reconnecte seul ; on ne signale que la perte prolongee.
244
+ // EventSource reconnects on its own; we only report a prolonged loss.
191
245
  if (source.readyState === EventSource.CLOSED) {
192
- console.warn('[odoro] connexion de rechargement perdue')
246
+ console.warn('[odoro] hot reloading connection lost')
193
247
  }
194
248
  })
195
249
  `.replace("__HMR_STREAM_PATH__", HMR_STREAM_PATH);
@@ -307,6 +361,9 @@ function collectBareImports(config, found) {
307
361
  builder.onResolve({ filter: /.*/ }, (args) => {
308
362
  if (args.kind === "entry-point") return null;
309
363
  const aliased = applyAlias(args.path, config);
364
+ if (readSuffix(aliased) !== void 0) {
365
+ return { path: aliased, external: true };
366
+ }
310
367
  if (hasExtension(aliased, STYLE_EXTENSIONS) || hasExtension(aliased, ASSET_EXTENSIONS)) {
311
368
  return { path: aliased, external: true };
312
369
  }
@@ -330,29 +387,36 @@ async function scanDependencies(config, entries) {
330
387
  logLevel: "silent",
331
388
  absWorkingDir: config.root,
332
389
  jsx: "automatic",
333
- // Le serveur compile en JSX de developpement : sans ce reglage, le
334
- // parcours chercherait `react/jsx-runtime` la ou le navigateur demandera
335
- // `react/jsx-dev-runtime`, et la dependance manquerait a l'appel.
390
+ // The server compiles with the development JSX: without this setting, the
391
+ // scan would look for `react/jsx-runtime` where the browser will ask for
392
+ // `react/jsx-dev-runtime`, and the dependency would be missing.
336
393
  jsxDev: true,
337
- plugins: [collectBareImports(config, found)]
394
+ // The same pass as the build one: a module reached by a pattern is a module
395
+ // of the project, and its dependencies must be prebundled like the others.
396
+ // Without that, a page loaded only through `import.meta.glob` would ask at
397
+ // runtime for a dependency the cache does not hold.
398
+ plugins: [
399
+ collectBareImports(config, found),
400
+ sourcePlugin({ root: config.root, plugins: config.plugins, dev: true })
401
+ ]
338
402
  });
339
403
  return [...found].sort();
340
404
  }
341
405
  async function optimizeDeps(config, specifiers, force = false) {
342
406
  try {
343
- return await compilerDeps(config, specifiers, force);
407
+ return await buildDeps(config, specifiers, force);
344
408
  } catch (cause) {
345
- if (!estUneCourse(cause)) throw cause;
346
- await new Promise((suite) => setTimeout(suite, DELAI_COURSE));
347
- return compilerDeps(config, specifiers, force);
409
+ if (!isRaceError(cause)) throw cause;
410
+ await new Promise((next) => setTimeout(next, RACE_DELAY));
411
+ return buildDeps(config, specifiers, force);
348
412
  }
349
413
  }
350
- var DELAI_COURSE = 400;
351
- function estUneCourse(cause) {
414
+ var RACE_DELAY = 400;
415
+ function isRaceError(cause) {
352
416
  const code = cause?.code;
353
417
  return code === "ENOENT" || code === "EPERM" || code === "EBUSY";
354
418
  }
355
- async function compilerDeps(config, specifiers, force) {
419
+ async function buildDeps(config, specifiers, force) {
356
420
  const directory = join(config.root, "node_modules", ".odoro", "deps");
357
421
  const sorted = [...specifiers].sort();
358
422
  const hash = createHash("sha256").update(JSON.stringify(sorted)).update(await lockfileFingerprint(config.root)).update(entriesFingerprint(config.root, sorted)).update(engineVersion()).digest("hex").slice(0, 16);
@@ -381,9 +445,9 @@ async function compilerDeps(config, specifiers, force) {
381
445
  entryPoints.push({ in: proxy, out });
382
446
  }
383
447
  await build({
384
- // Les noms de sortie sont imposes : un specificateur a sous-chemin
385
- // produirait sinon une arborescence, et deux paquets differents
386
- // pourraient se disputer le meme nom de fichier.
448
+ // The output names are imposed: a specifier with a subpath would
449
+ // otherwise produce a directory tree, and two different packages could
450
+ // fight over the same file name.
387
451
  entryPoints,
388
452
  bundle: true,
389
453
  format: "esm",
@@ -412,7 +476,7 @@ function engineVersion() {
412
476
  try {
413
477
  const parsed = JSON.parse(readFileSync(manifest, "utf8"));
414
478
  if (parsed.name === "odoro") {
415
- cachedVersion = parsed.version ?? "inconnue";
479
+ cachedVersion = parsed.version ?? "unknown";
416
480
  return cachedVersion;
417
481
  }
418
482
  } catch {
@@ -423,7 +487,7 @@ function engineVersion() {
423
487
  if (parent === directory) break;
424
488
  directory = parent;
425
489
  }
426
- cachedVersion = "inconnue";
490
+ cachedVersion = "unknown";
427
491
  return cachedVersion;
428
492
  }
429
493
  function entriesFingerprint(root, specifiers) {
@@ -449,13 +513,13 @@ function packageFingerprint(name, resolver) {
449
513
  try {
450
514
  manifestPath = resolver.resolve(`${name}/package.json`);
451
515
  } catch {
452
- return "introuvable";
516
+ return "not-found";
453
517
  }
454
518
  let manifest;
455
519
  try {
456
520
  manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
457
521
  } catch {
458
- return "illisible";
522
+ return "unreadable";
459
523
  }
460
524
  const directory = dirname(manifestPath);
461
525
  const targets = /* @__PURE__ */ new Set();
@@ -491,7 +555,7 @@ async function lockfileFingerprint(root) {
491
555
  if (!existsSync(file)) continue;
492
556
  return createHash("sha256").update(await readFile(file)).digest("hex");
493
557
  }
494
- return "sans-verrou";
558
+ return "no-lockfile";
495
559
  }
496
560
 
497
561
  // src/dev/graph.ts
@@ -500,7 +564,7 @@ function detectSelfAccepting(source) {
500
564
  }
501
565
  var ModuleGraph = class {
502
566
  nodes = /* @__PURE__ */ new Map();
503
- /** Recupere un module, ou le cree s'il est inconnu. */
567
+ /** Gets a module, or creates it when it is unknown. */
504
568
  ensure(file, url) {
505
569
  const existing = this.nodes.get(file);
506
570
  if (existing !== void 0) return existing;
@@ -516,17 +580,17 @@ var ModuleGraph = class {
516
580
  this.nodes.set(file, node);
517
581
  return node;
518
582
  }
519
- /** Recupere un module deja connu. */
583
+ /** Gets a module already known. */
520
584
  get(file) {
521
585
  return this.nodes.get(file);
522
586
  }
523
- /** Nombre de modules connus. */
587
+ /** Number of known modules. */
524
588
  get size() {
525
589
  return this.nodes.size;
526
590
  }
527
591
  /**
528
- * Remplace la liste des dependances d'un module, en tenant a jour les
529
- * relations inverses.
592
+ * Replaces the dependency list of a module, keeping the reverse relations up
593
+ * to date.
530
594
  */
531
595
  setDependencies(file, dependencies) {
532
596
  const node = this.nodes.get(file);
@@ -544,14 +608,14 @@ var ModuleGraph = class {
544
608
  }
545
609
  }
546
610
  /**
547
- * Invalide un module et remonte la chaine de ses importateurs jusqu'a
548
- * trouver, sur chaque branche, un module qui accepte les mises a jour.
611
+ * Invalidates a module and climbs the chain of its importers until it finds,
612
+ * on every branch, a module that accepts updates.
549
613
  *
550
- * @returns Les modules a recharger cote client. Un tableau vide signifie
551
- * qu'aucune frontiere n'accepte la mise a jour : il faut recharger la page.
614
+ * @returns The modules to reload on the client side. An empty array means
615
+ * that no boundary accepts the update: the page must be reloaded.
552
616
  *
553
617
  * @example
554
- * const boundaries = graph.invalidate('/projet/src/App.css')
618
+ * const boundaries = graph.invalidate('/project/src/App.css')
555
619
  */
556
620
  invalidate(file) {
557
621
  const boundaries = [];
@@ -577,7 +641,7 @@ var ModuleGraph = class {
577
641
  };
578
642
  return walk(file) ? boundaries : [];
579
643
  }
580
- /** Oublie tous les modules. */
644
+ /** Forgets every module. */
581
645
  clear() {
582
646
  this.nodes.clear();
583
647
  }
@@ -594,22 +658,22 @@ async function applyReactRefresh(code, file) {
594
658
  filename: file,
595
659
  babelrc: false,
596
660
  configFile: false,
597
- // La carte de source produite par la compilation precedente est reprise et
598
- // fusionnee : sans cela, les numeros de ligne du debogueur designeraient le
599
- // code instrumente plutot que la source.
661
+ // The source map produced by the previous build is picked up and merged:
662
+ // without that, the line numbers in the debugger would designate the
663
+ // instrumented code rather than the source.
600
664
  //
601
- // `true` demande a Babel de lire la carte inline du code recu. Il l'accepte
602
- // — verifie : la source reprise est bien le fichier d'origine — mais les
603
- // types de la 7.x ne decrivent que la forme objet. L'annotation est
604
- // incomplete, pas la valeur.
665
+ // `true` asks Babel to read the inline map of the received code. It accepts
666
+ // it — checked: the source picked up is indeed the original file — but the
667
+ // 7.x types only describe the object form. The annotation is incomplete,
668
+ // not the value.
605
669
  inputSourceMap: true,
606
670
  sourceMaps: "inline",
607
- // `skipEnvCheck` leve un garde-fou destine aux configurations globales,
608
- // qui refuse la transformation hors de NODE_ENV=development. Ici c'est le
609
- // point d'application qui garantit la regle : cette fonction n'est appelee
610
- // que par le serveur de developpement, jamais par la compilation de
611
- // production. Sans cela, un `NODE_ENV=production odoro dev` — ou une suite
612
- // de tests — echouerait au lieu de simplement instrumenter.
671
+ // `skipEnvCheck` lifts a guard meant for global configurations, which
672
+ // refuses the transformation outside NODE_ENV=development. Here it is the
673
+ // point of application that enforces the rule: this function is only called
674
+ // by the development server, never by the production build. Without it, a
675
+ // `NODE_ENV=production odoro dev` — or a test suite — would fail instead of
676
+ // simply instrumenting.
613
677
  plugins: [[reactRefreshPlugin, { skipEnvCheck: true }]],
614
678
  parserOpts: { sourceType: "module" }
615
679
  });
@@ -635,8 +699,8 @@ void import(import.meta.url).then((__odoroCurrent) => {
635
699
  __odoroRefresh.registerExports(${JSON.stringify(id)}, __odoroCurrent)
636
700
  import.meta.hot?.accept((__odoroNext) => {
637
701
  if (!__odoroNext) return
638
- const refus = __odoroRefresh.checkBoundary(__odoroCurrent, __odoroNext)
639
- if (refus !== null) import.meta.hot.invalidate(refus)
702
+ const refusal = __odoroRefresh.checkBoundary(__odoroCurrent, __odoroNext)
703
+ if (refusal !== null) import.meta.hot.invalidate(refusal)
640
704
  else {
641
705
  __odoroRefresh.registerExports(${JSON.stringify(id)}, __odoroNext)
642
706
  __odoroRefresh.enqueueUpdate()
@@ -647,12 +711,12 @@ void import(import.meta.url).then((__odoroCurrent) => {
647
711
  }
648
712
  var REFRESH_RUNTIME_SOURCE = `import runtime from 'react-refresh/runtime'
649
713
 
650
- // Le crochet doit etre installe **avant** que React ne soit charge : c'est par
651
- // lui que React signale les composants qu'il rend.
714
+ // The hook must be installed **before** React is loaded: it is through it that
715
+ // React reports the components it renders.
652
716
  runtime.injectIntoGlobalHook(window)
653
717
 
654
- // Valeurs neutres : un module non instrumente doit pouvoir s'evaluer sans que
655
- // ces fonctions existent vraiment.
718
+ // Neutral values: an uninstrumented module must be able to evaluate without
719
+ // those functions really existing.
656
720
  window.$RefreshReg$ = () => {}
657
721
  window.$RefreshSig$ = () => (type) => type
658
722
 
@@ -660,11 +724,10 @@ export const register = runtime.register
660
724
  export const createSignature = runtime.createSignatureFunctionForTransform
661
725
 
662
726
  /**
663
- * Enregistre les exports d'un module qui ressemblent a des composants.
727
+ * Registers the exports of a module that look like components.
664
728
  *
665
- * L'enregistrement par nom d'export complete celui pose dans le corps du
666
- * module : un composant re-exporte depuis un autre fichier n'y apparaitrait
667
- * pas.
729
+ * Registering by export name completes the one placed in the body of the
730
+ * module: a component re-exported from another file would not appear there.
668
731
  */
669
732
  export function registerExports(id, exports) {
670
733
  for (const key of Object.keys(exports)) {
@@ -676,29 +739,29 @@ export function registerExports(id, exports) {
676
739
  }
677
740
 
678
741
  /**
679
- * Verifie qu'un module peut etre remplace a chaud.
742
+ * Checks that a module can be replaced hot.
680
743
  *
681
- * @returns null si le remplacement est sur, sinon la raison du refus.
744
+ * @returns null when the replacement is safe, otherwise the reason for refusal.
682
745
  */
683
746
  export function checkBoundary(previous, next) {
684
747
  const before = Object.keys(previous)
685
748
  const after = Object.keys(next)
686
749
 
687
- if (after.length === 0) return 'le module n exporte plus rien'
750
+ if (after.length === 0) return 'the module no longer exports anything'
688
751
 
689
752
  for (const key of after) {
690
- if (!before.includes(key)) return 'nouvel export : ' + key
753
+ if (!before.includes(key)) return 'new export: ' + key
691
754
  }
692
755
  for (const key of before) {
693
- if (!after.includes(key)) return 'export retire : ' + key
756
+ if (!after.includes(key)) return 'export removed: ' + key
694
757
  }
695
758
 
696
759
  for (const key of after) {
697
760
  const value = next[key]
698
761
  if (runtime.isLikelyComponentType(value)) continue
699
- // Un export qui n'est pas un composant ne peut etre conserve que s'il n'a
700
- // pas change : sinon ses consommateurs garderaient l'ancienne valeur.
701
- if (previous[key] !== value) return 'export non-composant modifie : ' + key
762
+ // An export that is not a component can only be kept when it has not
763
+ // changed: otherwise its consumers would keep the old value.
764
+ if (previous[key] !== value) return 'non-component export changed: ' + key
702
765
  }
703
766
 
704
767
  return null
@@ -707,7 +770,7 @@ export function checkBoundary(previous, next) {
707
770
  let planned
708
771
  const DEBOUNCE = 16
709
772
 
710
- /** Regroupe les mises a jour d'une meme salve en un seul rafraichissement. */
773
+ /** Groups the updates of a same burst into a single refresh. */
711
774
  export function enqueueUpdate() {
712
775
  clearTimeout(planned)
713
776
  planned = setTimeout(() => {
@@ -740,44 +803,31 @@ async function bundleRefreshRuntime() {
740
803
  }
741
804
 
742
805
  // src/dev/server.ts
743
- var UTILITAIRES_PATH = `${INTERNAL_PREFIX}utilitaires.css`;
744
- var MIME = {
745
- ".html": "text/html; charset=utf-8",
746
- ".js": "text/javascript; charset=utf-8",
747
- ".mjs": "text/javascript; charset=utf-8",
748
- ".json": "application/json; charset=utf-8",
749
- ".css": "text/css; charset=utf-8",
750
- ".svg": "image/svg+xml",
751
- ".png": "image/png",
752
- ".jpg": "image/jpeg",
753
- ".jpeg": "image/jpeg",
754
- ".gif": "image/gif",
755
- ".webp": "image/webp",
756
- ".avif": "image/avif",
757
- ".ico": "image/x-icon",
758
- ".woff": "font/woff",
759
- ".woff2": "font/woff2",
760
- ".mp4": "video/mp4",
761
- ".webm": "video/webm",
762
- ".txt": "text/plain; charset=utf-8",
763
- ".md": "text/markdown; charset=utf-8",
764
- ".markdown": "text/markdown; charset=utf-8"
765
- };
806
+ var UTILITIES_PATH = `${INTERNAL_PREFIX}utilities.css`;
766
807
  var SCRIPT_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs"];
767
808
  function cleanUrl(url) {
768
809
  return (url.split("?")[0] ?? url).split("#")[0] ?? url;
769
810
  }
770
- function buildEnv(config) {
771
- const env = {
772
- MODE: "development",
773
- DEV: true,
774
- PROD: false,
775
- BASE_URL: config.base
776
- };
777
- for (const [key, value] of Object.entries(process.env)) {
778
- if (key.startsWith(config.envPrefix) && value !== void 0) env[key] = value;
811
+ function suffixOf(url) {
812
+ const query = url.split("?")[1];
813
+ if (query === void 0) return void 0;
814
+ for (const name of ["worker", "raw", "url"]) {
815
+ if (query.split("&").includes(name)) return name;
779
816
  }
780
- return env;
817
+ return void 0;
818
+ }
819
+ function chain(middlewares, request_, response, done) {
820
+ let index = 0;
821
+ const next = () => {
822
+ const middleware = middlewares[index];
823
+ index += 1;
824
+ if (middleware === void 0) {
825
+ done();
826
+ return;
827
+ }
828
+ void (async () => middleware(request_, response, next))();
829
+ };
830
+ next();
781
831
  }
782
832
  function extractEntries(html, root) {
783
833
  const entries = [];
@@ -790,11 +840,11 @@ function extractEntries(html, root) {
790
840
  }
791
841
  return entries;
792
842
  }
793
- function injectClient(html, utilitaires = false) {
843
+ function injectClient(html, utilities = false) {
794
844
  const tags = [
795
- // La feuille vient avant tout le reste : le socle et les styles de
796
- // l'application, importes ensuite, doivent pouvoir la surcharger.
797
- ...utilitaires ? [`<link rel="stylesheet" href="${UTILITAIRES_PATH}">`] : [],
845
+ // The stylesheet comes before everything else: the base and the application
846
+ // styles, imported afterwards, must be able to override it.
847
+ ...utilities ? [`<link rel="stylesheet" href="${UTILITIES_PATH}">`] : [],
798
848
  REFRESH_HTML_TAG,
799
849
  `<script type="module" src="${HMR_CLIENT_PATH}"></script>`
800
850
  ].join("\n ");
@@ -806,25 +856,37 @@ ${html}`;
806
856
  async function startDevServer(config) {
807
857
  const started = Date.now();
808
858
  const graph = new ModuleGraph();
809
- const env = buildEnv(config);
859
+ const env = config.envClient;
810
860
  const clients = /* @__PURE__ */ new Set();
861
+ const middlewares = [];
862
+ for (const plugin of config.plugins) {
863
+ await plugin.configureServer?.({
864
+ use: (middleware) => middlewares.push(middleware)
865
+ });
866
+ }
811
867
  const indexFile = join(config.root, "index.html");
812
868
  if (!existsSync(indexFile)) {
813
- throw new Error(`[odoro] Aucun "index.html" a la racine du projet (${config.root}).`);
869
+ throw new Error(`[odoro] No "index.html" at the project root (${config.root}).`);
814
870
  }
815
871
  const refreshRuntime = await bundleRefreshRuntime();
816
- const fournisseur = await fournisseurDe(config.root);
817
- const utilitaires = fournisseur?.classesConnues === void 0 ? void 0 : fournisseur.renderUtilitairesPour(fournisseur.classesConnues());
818
- if (utilitaires !== void 0) {
872
+ const provider = await cssProviderFor(config.root);
873
+ const utilities = provider?.knownClasses === void 0 ? void 0 : provider.renderUtilitiesFor(provider.knownClasses());
874
+ if (utilities !== void 0) {
875
+ info(`development utilities: ${String(Math.round(utilities.length / 1024))} kB`);
876
+ }
877
+ const exposed = Object.keys(config.envClient).filter(
878
+ (key) => key.startsWith(config.envPrefix)
879
+ );
880
+ if (exposed.length > 0) {
819
881
  info(
820
- `utilitaires de developpement : ${String(Math.round(utilitaires.length / 1024))} Ko`
882
+ `${String(exposed.length)} variable${exposed.length > 1 ? "s" : ""} exposed to the client: ${exposed.join(", ")}`
821
883
  );
822
884
  }
823
885
  const entries = extractEntries(await readFile(indexFile, "utf8"), config.root);
824
886
  const specifiers = await scanDependencies(config, entries);
825
887
  const deps = await optimizeDeps(config, specifiers);
826
888
  if (deps.rebuilt && specifiers.length > 0) {
827
- info(`${specifiers.length} dependances pre-compilees`);
889
+ info(`${specifiers.length} dependencies prebundled`);
828
890
  }
829
891
  const broadcast = (message) => {
830
892
  const payload = `data: ${JSON.stringify(message)}
@@ -860,40 +922,63 @@ async function startDevServer(config) {
860
922
  }
861
923
  node.code = hotPreamble(url2) + body;
862
924
  }
863
- send(response, node.code, MIME[".js"]);
925
+ send(response, node.code, MIME[".js"] ?? "text/javascript");
864
926
  };
865
927
  const serveStyle = async (response, file, direct) => {
866
- const css = await readFile(file, "utf8");
928
+ const { css, files } = await assembleStylesheet(file, config.root);
867
929
  if (direct) {
868
- send(response, css, MIME[".css"]);
930
+ send(response, css, MIME[".css"] ?? "text/css");
869
931
  return;
870
932
  }
871
933
  const url2 = fileToUrl(file, config.root);
872
934
  const node = graph.ensure(file, url2);
873
935
  node.selfAccepting = true;
874
936
  node.code = hotPreamble(url2) + wrapStyle(url2, css);
875
- send(response, node.code, MIME[".js"]);
937
+ const imported = files.filter((one) => one !== file);
938
+ if (imported.length > 0) {
939
+ graph.setDependencies(file, imported);
940
+ for (const one of imported) {
941
+ const dependency = graph.ensure(one, fileToUrl(one, config.root));
942
+ dependency.importers.add(file);
943
+ dependency.selfAccepting = false;
944
+ }
945
+ }
946
+ send(response, node.code, MIME[".js"] ?? "text/javascript");
947
+ };
948
+ const serveSuffixed = async (response, file, path, suffix) => {
949
+ if (suffix === "raw") {
950
+ send(
951
+ response,
952
+ textModule(await readFile(file, "utf8")),
953
+ MIME[".js"] ?? "text/javascript"
954
+ );
955
+ return;
956
+ }
957
+ if (suffix === "url") {
958
+ send(response, urlModule(path), MIME[".js"] ?? "text/javascript");
959
+ return;
960
+ }
961
+ send(response, workerModule(path), MIME[".js"] ?? "text/javascript");
876
962
  };
877
- const indexDeDossier = (dossier) => {
878
- if (!existsSync(dossier) || !statSync(dossier).isDirectory()) return void 0;
879
- for (const nom of ["index.html", "index.md"]) {
880
- const candidat = join(dossier, nom);
881
- if (existsSync(candidat) && statSync(candidat).isFile()) return candidat;
963
+ const directoryIndex = (directory) => {
964
+ if (!existsSync(directory) || !statSync(directory).isDirectory()) return void 0;
965
+ for (const name of ["index.html", "index.md"]) {
966
+ const candidate = join(directory, name);
967
+ if (existsSync(candidate) && statSync(candidate).isFile()) return candidate;
882
968
  }
883
969
  return void 0;
884
970
  };
885
- const serveFile = (response, file) => {
886
- const type = MIME[extname(file).toLowerCase()] ?? "application/octet-stream";
887
- response.writeHead(200, { "Content-Type": type, "Cache-Control": "no-cache" });
888
- createReadStream(file).pipe(response);
971
+ const serveFile = (response, file, incoming) => {
972
+ sendFile(response, file, { range: incoming?.headers.range });
889
973
  };
890
- const serveHtml = async (response) => {
974
+ const serveHtml = async (response, route = "/") => {
891
975
  const html = await readFile(indexFile, "utf8");
892
- send(
893
- response,
894
- injectClient(html, utilitaires !== void 0),
895
- MIME[".html"]
976
+ const rendered = await transformHtmlWith(
977
+ config.plugins,
978
+ injectClient(html, utilities !== void 0),
979
+ { dev: true, route }
896
980
  );
981
+ send(response, rendered, MIME[".html"] ?? "text/html");
897
982
  };
898
983
  const forward = (incoming, response, target) => {
899
984
  const url2 = new URL(incoming.url ?? "/", target);
@@ -912,12 +997,12 @@ async function startDevServer(config) {
912
997
  }
913
998
  );
914
999
  proxied.on("error", (cause) => {
915
- warn(`proxy indisponible : ${target}`);
916
- send(response, `Proxy indisponible : ${String(cause)}`, "text/plain", 502);
1000
+ warn(`proxy unavailable: ${target}`);
1001
+ send(response, `Proxy unavailable: ${String(cause)}`, "text/plain", 502);
917
1002
  });
918
1003
  incoming.pipe(proxied);
919
1004
  };
920
- const server = createServer((incoming, response) => {
1005
+ const handle = (incoming, response) => {
921
1006
  void (async () => {
922
1007
  const url2 = incoming.url ?? "/";
923
1008
  const path = cleanUrl(url2);
@@ -932,12 +1017,12 @@ async function startDevServer(config) {
932
1017
  send(response, refreshRuntime, MIME[".js"] ?? "text/javascript");
933
1018
  return;
934
1019
  }
935
- if (path === UTILITAIRES_PATH) {
936
- if (utilitaires === void 0) {
937
- send(response, "Introuvable", "text/plain", 404);
1020
+ if (path === UTILITIES_PATH) {
1021
+ if (utilities === void 0) {
1022
+ send(response, "Not found", "text/plain", 404);
938
1023
  return;
939
1024
  }
940
- send(response, utilitaires, MIME[".css"] ?? "text/css");
1025
+ send(response, utilities, MIME[".css"] ?? "text/css");
941
1026
  return;
942
1027
  }
943
1028
  if (path === HMR_CLIENT_PATH) {
@@ -963,13 +1048,13 @@ async function startDevServer(config) {
963
1048
  const name = last.endsWith(".js") ? last : depFileName(last);
964
1049
  const file2 = join(deps.directory, name);
965
1050
  if (existsSync(file2)) {
966
- serveFile(response, file2);
1051
+ serveFile(response, file2, incoming);
967
1052
  return;
968
1053
  }
969
1054
  send(
970
1055
  response,
971
1056
  `throw new Error(${JSON.stringify(
972
- `[odoro] Dependance non pre-compilee : "${specifier}". Relancez le serveur.`
1057
+ `[odoro] Dependency not prebundled: "${specifier}". Restart the server.`
973
1058
  )})`,
974
1059
  MIME[".js"] ?? "text/javascript"
975
1060
  );
@@ -980,20 +1065,25 @@ async function startDevServer(config) {
980
1065
  return;
981
1066
  }
982
1067
  if (path.startsWith(INTERNAL_PREFIX)) {
983
- send(response, "Introuvable", "text/plain", 404);
1068
+ send(response, "Not found", "text/plain", 404);
984
1069
  return;
985
1070
  }
986
1071
  const file = urlToFile(path, config.root);
987
1072
  if (existsSync(file) && statSync(file).isFile()) {
1073
+ const suffix = suffixOf(url2);
1074
+ if (suffix !== void 0) {
1075
+ await serveSuffixed(response, file, path, suffix);
1076
+ return;
1077
+ }
988
1078
  if (hasExtension(path, STYLE_EXTENSIONS)) {
989
- await serveStyle(response, file, feuilleDemandee(incoming.headers, url2));
1079
+ await serveStyle(response, file, wantsStylesheet(incoming.headers, url2));
990
1080
  return;
991
1081
  }
992
1082
  if (hasExtension(path, ASSET_EXTENSIONS)) {
993
1083
  if (url2.includes("?import")) {
994
1084
  send(response, wrapAsset(path), MIME[".js"] ?? "text/javascript");
995
1085
  } else {
996
- serveFile(response, file);
1086
+ serveFile(response, file, incoming);
997
1087
  }
998
1088
  return;
999
1089
  }
@@ -1002,40 +1092,56 @@ async function startDevServer(config) {
1002
1092
  return;
1003
1093
  }
1004
1094
  if (hasExtension(path, [".json"])) {
1005
- if (estUneRessource(incoming.headers) || url2.includes("?import")) {
1095
+ if (isAssetRequest(incoming.headers) || url2.includes("?import")) {
1006
1096
  const json = await readFile(file, "utf8");
1007
1097
  send(response, wrapJson(json), MIME[".js"] ?? "text/javascript");
1008
1098
  } else {
1009
- serveFile(response, file);
1099
+ serveFile(response, file, incoming);
1010
1100
  }
1011
1101
  return;
1012
1102
  }
1013
- serveFile(response, file);
1103
+ serveFile(response, file, incoming);
1014
1104
  return;
1015
1105
  }
1016
1106
  const publicFile = join(config.publicDir, path.replace(/^\//, ""));
1017
1107
  if (existsSync(publicFile) && statSync(publicFile).isFile()) {
1018
- serveFile(response, publicFile);
1108
+ serveFile(response, publicFile, incoming);
1019
1109
  return;
1020
1110
  }
1021
- const index = indexDeDossier(publicFile);
1111
+ const index = directoryIndex(publicFile);
1022
1112
  if (index !== void 0) {
1023
- serveFile(response, index);
1113
+ serveFile(response, index, incoming);
1024
1114
  return;
1025
1115
  }
1026
- if (!extname(path) && !estUneRessource(incoming.headers)) {
1027
- await serveHtml(response);
1116
+ if (!extname(path) && !isAssetRequest(incoming.headers)) {
1117
+ await serveHtml(response, path);
1028
1118
  return;
1029
1119
  }
1030
- send(response, "Introuvable", "text/plain", 404);
1120
+ send(response, "Not found", "text/plain", 404);
1031
1121
  } catch (cause) {
1032
1122
  const message = cause instanceof Error ? cause.message : String(cause);
1033
- error(`echec du traitement de ${path}`, cause);
1123
+ error(`failed to handle ${path}`, cause);
1034
1124
  broadcast({ type: "error", message, file: path });
1035
- send(response, `Erreur : ${message}`, "text/plain", 500);
1125
+ send(response, `Error: ${message}`, "text/plain", 500);
1036
1126
  }
1037
1127
  })();
1038
- });
1128
+ };
1129
+ const listener = (incoming, response) => {
1130
+ if (middlewares.length === 0) {
1131
+ handle(incoming, response);
1132
+ return;
1133
+ }
1134
+ chain(middlewares, incoming, response, () => {
1135
+ handle(incoming, response);
1136
+ });
1137
+ };
1138
+ const server = config.server.https === void 0 ? createServer(listener) : createServer$1(
1139
+ {
1140
+ cert: readFileSync(resolve(config.root, config.server.https.cert)),
1141
+ key: readFileSync(resolve(config.root, config.server.https.key))
1142
+ },
1143
+ listener
1144
+ );
1039
1145
  let pending;
1040
1146
  const changed = /* @__PURE__ */ new Set();
1041
1147
  const watcher = watch(config.root, { recursive: true }, (_event, filename) => {
@@ -1071,23 +1177,28 @@ async function startDevServer(config) {
1071
1177
  }
1072
1178
  }
1073
1179
  if (reload) {
1074
- info("rechargement de la page");
1180
+ info("page reload");
1075
1181
  broadcast({ type: "full-reload" });
1076
1182
  } else if (updates.length > 0) {
1077
- info(
1078
- `mise a jour a chaud : ${updates.map((update) => update.url).join(", ")}`
1079
- );
1183
+ info(`hot update: ${updates.map((update) => update.url).join(", ")}`);
1080
1184
  broadcast({ type: "update", updates });
1081
1185
  }
1082
1186
  }, 40);
1083
1187
  });
1084
- const { port, demande } = await ecouter(server, config.server.port, config.server.host);
1085
- if (demande !== void 0) {
1086
- warn(`port ${String(demande)} occupe \u2014 le serveur ecoute sur ${String(port)}`);
1188
+ const { port, requested } = await listen(
1189
+ server,
1190
+ config.server.port,
1191
+ config.server.host,
1192
+ config.server.strictPort ? 1 : void 0
1193
+ );
1194
+ if (requested !== void 0) {
1195
+ warn(`port ${String(requested)} in use \u2014 server listening on ${String(port)}`);
1087
1196
  }
1088
- const url = `http://${config.server.host}:${String(port)}${config.base}`;
1089
- success(`pret en ${duration(Date.now() - started)}`);
1197
+ const protocol = config.server.https === void 0 ? "http" : "https";
1198
+ const url = `${protocol}://${config.server.host}:${String(port)}${config.base}`;
1199
+ success(`ready in ${duration(Date.now() - started)}`);
1090
1200
  info(` ${colors.cyan(url)}`);
1201
+ if (config.server.open) openBrowser(url);
1091
1202
  return {
1092
1203
  url,
1093
1204
  async close() {
@@ -1101,4 +1212,4 @@ async function startDevServer(config) {
1101
1212
  };
1102
1213
  }
1103
1214
 
1104
- export { ModuleGraph, detectSelfAccepting, extractEntries, fournisseurDe, injectClient, optimizeDeps, scanDependencies, startDevServer };
1215
+ export { ModuleGraph, cssProviderFor, detectSelfAccepting, extractEntries, injectClient, optimizeDeps, scanDependencies, startDevServer };