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.
- package/README.md +94 -0
- package/client.d.ts +90 -16
- package/dist/build-XJIP4GNT.js +5 -0
- package/dist/{chunk-34RGOZFA.js → chunk-4D56Z7G3.js} +49 -50
- package/dist/{chunk-JMEHF3KN.js → chunk-7ZB5MAM6.js} +4 -4
- package/dist/chunk-DF67LADH.js +735 -0
- package/dist/{chunk-LHZGX5ML.js → chunk-HS7NGZLM.js} +307 -196
- package/dist/chunk-LIKVHWB4.js +231 -0
- package/dist/chunk-QOVBLN7A.js +241 -0
- package/dist/chunk-RTWJ5GFB.js +729 -0
- package/dist/chunk-TONYIKCN.js +57 -0
- package/dist/cli.d.ts +12 -13
- package/dist/cli.js +86 -63
- package/dist/{commands-AHMXBWQQ.js → commands-GYA7NTCX.js} +124 -124
- package/dist/commands-UOWN3IK4.js +181 -0
- package/dist/{create-CYVSVDAD.js → create-B2TLV6XI.js} +198 -160
- package/dist/index.d.ts +603 -151
- package/dist/index.js +6 -6
- package/dist/{package-EIDLKAA6.js → package-3ZJOEXM4.js} +6 -5
- package/dist/preview-3IRG7MHV.js +4 -0
- package/dist/registry/index.d.ts +75 -76
- package/dist/registry/index.js +1 -1
- package/dist/{server-4UGN3SFS.js → server-ZB3P3EGS.js} +3 -3
- package/package.json +6 -5
- package/templates/react-ts/README.md +71 -35
- package/templates/react-ts/_env.example +9 -0
- package/templates/react-ts/_variants/with-engine/src/background.tsx +118 -0
- package/templates/react-ts/_variants/without-libs/src/App.tsx +380 -0
- package/templates/react-ts/_variants/without-libs/src/background.tsx +24 -0
- package/templates/react-ts/_variants/without-libs/src/entry-server.tsx +39 -0
- package/templates/react-ts/_variants/without-libs/src/main.tsx +26 -0
- package/templates/{react-ts-server/_variantes/sans-libs/client → react-ts/_variants/without-libs}/src/styles.css +137 -137
- package/templates/react-ts/{_variantes/sans-routeur → _variants/without-router}/src/App.tsx +141 -141
- package/templates/react-ts/_variants/without-router/src/entry-server.tsx +39 -0
- package/templates/react-ts/index.html +4 -4
- package/templates/react-ts/odoro.config.ts +5 -0
- package/templates/react-ts/src/App.tsx +219 -209
- package/templates/react-ts/src/background.tsx +66 -0
- package/templates/react-ts/src/entry-server.tsx +85 -0
- package/templates/react-ts/src/main.tsx +13 -4
- package/templates/react-ts/src/router.tsx +65 -45
- package/templates/react-ts/src/styles.css +5 -5
- package/templates/react-ts-server/Dockerfile +9 -9
- package/templates/react-ts-server/README.md +97 -45
- package/templates/react-ts-server/_env.example +31 -33
- package/templates/react-ts-server/_variants/with-engine/client/src/background.tsx +118 -0
- package/templates/react-ts-server/_variants/without-libs/client/src/App.tsx +380 -0
- package/templates/react-ts-server/_variants/without-libs/client/src/background.tsx +24 -0
- package/templates/react-ts-server/_variants/without-libs/client/src/entry-server.tsx +39 -0
- package/templates/react-ts-server/_variants/without-libs/client/src/main.tsx +26 -0
- package/templates/{react-ts/_variantes/sans-libs → react-ts-server/_variants/without-libs/client}/src/styles.css +137 -137
- package/templates/react-ts-server/{_variantes/sans-routeur → _variants/without-router}/client/src/App.tsx +141 -141
- package/templates/react-ts-server/_variants/without-router/client/src/entry-server.tsx +39 -0
- package/templates/react-ts-server/client/index.html +4 -4
- package/templates/react-ts-server/client/src/App.tsx +228 -210
- package/templates/react-ts-server/client/src/account.tsx +267 -0
- package/templates/react-ts-server/client/src/auth.tsx +139 -0
- package/templates/react-ts-server/client/src/background.tsx +66 -0
- package/templates/react-ts-server/client/src/entry-server.tsx +92 -0
- package/templates/react-ts-server/client/src/main.tsx +17 -5
- package/templates/react-ts-server/client/src/router.tsx +77 -45
- package/templates/react-ts-server/client/src/styles.css +5 -5
- package/templates/react-ts-server/odoro.config.ts +7 -4
- package/templates/react-ts-server/package.json +2 -0
- package/templates/react-ts-server/scripts/dev.mjs +14 -14
- package/templates/react-ts-server/server/src/main.ts +119 -45
- package/templates/react-ts-server/server/src/modules/auth/index.ts +238 -0
- package/templates/react-ts-server/server/src/modules/auth/password.ts +122 -0
- package/templates/react-ts-server/server/src/modules/auth/store.ts +193 -0
- package/templates/react-ts-server/server/src/modules/health/index.ts +45 -46
- package/dist/build-JFQHODAT.js +0 -5
- package/dist/chunk-22KJTV2R.js +0 -380
- package/dist/chunk-3SZIN6VG.js +0 -64
- package/dist/chunk-DL3NPC4H.js +0 -113
- package/dist/chunk-TQUJ3MFS.js +0 -268
- package/dist/chunk-ZVL7EXJO.js +0 -66
- package/dist/commands-4JRBD55Z.js +0 -245
- package/dist/preview-7DKEAQOQ.js +0 -4
- package/templates/react-ts/_variantes/avec-moteur/src/fond.tsx +0 -118
- package/templates/react-ts/_variantes/sans-libs/src/App.tsx +0 -382
- package/templates/react-ts/_variantes/sans-libs/src/fond.tsx +0 -23
- package/templates/react-ts/_variantes/sans-libs/src/main.tsx +0 -17
- package/templates/react-ts/src/fond.tsx +0 -66
- package/templates/react-ts-server/_variantes/avec-moteur/client/src/fond.tsx +0 -118
- package/templates/react-ts-server/_variantes/sans-libs/client/src/App.tsx +0 -382
- package/templates/react-ts-server/_variantes/sans-libs/client/src/fond.tsx +0 -23
- package/templates/react-ts-server/_variantes/sans-libs/client/src/main.tsx +0 -17
- 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,
|
|
3
|
-
import { info,
|
|
4
|
-
import { existsSync, readFileSync, statSync, watch
|
|
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
|
|
16
|
-
var
|
|
17
|
-
function
|
|
18
|
-
if (
|
|
19
|
-
if (typeof
|
|
20
|
-
return
|
|
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
|
|
23
|
+
async function cssProviderFor(root) {
|
|
23
24
|
try {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
const module_ = await import(pathToFileURL(resolve(
|
|
31
|
-
if (typeof module_.
|
|
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
|
-
|
|
35
|
-
// `generate`
|
|
36
|
-
//
|
|
37
|
-
//
|
|
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
|
-
|
|
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
|
-
/**
|
|
107
|
+
/** Hot contexts, by module URL. */
|
|
54
108
|
const contexts = new Map()
|
|
55
109
|
|
|
56
110
|
/**
|
|
57
|
-
*
|
|
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
|
-
//
|
|
64
|
-
//
|
|
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]
|
|
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
|
-
*
|
|
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]
|
|
168
|
+
console.log('[odoro] updated', url)
|
|
115
169
|
} catch (cause) {
|
|
116
|
-
console.error('[odoro]
|
|
170
|
+
console.error('[odoro] update failed for', url, cause)
|
|
117
171
|
location.reload()
|
|
118
172
|
}
|
|
119
173
|
}
|
|
120
174
|
|
|
121
|
-
/**
|
|
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
|
-
*
|
|
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 ? '
|
|
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 = '
|
|
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]
|
|
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
|
|
244
|
+
// EventSource reconnects on its own; we only report a prolonged loss.
|
|
191
245
|
if (source.readyState === EventSource.CLOSED) {
|
|
192
|
-
console.warn('[odoro]
|
|
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
|
-
//
|
|
334
|
-
//
|
|
335
|
-
// `react/jsx-dev-runtime`,
|
|
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
|
-
|
|
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
|
|
407
|
+
return await buildDeps(config, specifiers, force);
|
|
344
408
|
} catch (cause) {
|
|
345
|
-
if (!
|
|
346
|
-
await new Promise((
|
|
347
|
-
return
|
|
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
|
|
351
|
-
function
|
|
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
|
|
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
|
-
//
|
|
385
|
-
//
|
|
386
|
-
//
|
|
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 ?? "
|
|
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 = "
|
|
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 "
|
|
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 "
|
|
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 "
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
583
|
+
/** Gets a module already known. */
|
|
520
584
|
get(file) {
|
|
521
585
|
return this.nodes.get(file);
|
|
522
586
|
}
|
|
523
|
-
/**
|
|
587
|
+
/** Number of known modules. */
|
|
524
588
|
get size() {
|
|
525
589
|
return this.nodes.size;
|
|
526
590
|
}
|
|
527
591
|
/**
|
|
528
|
-
*
|
|
529
|
-
*
|
|
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
|
-
*
|
|
548
|
-
*
|
|
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
|
|
551
|
-
*
|
|
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('/
|
|
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
|
-
/**
|
|
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
|
-
//
|
|
598
|
-
//
|
|
599
|
-
// code
|
|
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`
|
|
602
|
-
// —
|
|
603
|
-
//
|
|
604
|
-
//
|
|
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`
|
|
608
|
-
//
|
|
609
|
-
// point
|
|
610
|
-
//
|
|
611
|
-
//
|
|
612
|
-
//
|
|
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
|
|
639
|
-
if (
|
|
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
|
-
//
|
|
651
|
-
//
|
|
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
|
-
//
|
|
655
|
-
//
|
|
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
|
-
*
|
|
727
|
+
* Registers the exports of a module that look like components.
|
|
664
728
|
*
|
|
665
|
-
*
|
|
666
|
-
* module
|
|
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
|
-
*
|
|
742
|
+
* Checks that a module can be replaced hot.
|
|
680
743
|
*
|
|
681
|
-
* @returns null
|
|
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 '
|
|
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 '
|
|
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
|
|
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
|
-
//
|
|
700
|
-
//
|
|
701
|
-
if (previous[key] !== value) return '
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
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
|
|
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,
|
|
843
|
+
function injectClient(html, utilities = false) {
|
|
794
844
|
const tags = [
|
|
795
|
-
//
|
|
796
|
-
//
|
|
797
|
-
...
|
|
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 =
|
|
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]
|
|
869
|
+
throw new Error(`[odoro] No "index.html" at the project root (${config.root}).`);
|
|
814
870
|
}
|
|
815
871
|
const refreshRuntime = await bundleRefreshRuntime();
|
|
816
|
-
const
|
|
817
|
-
const
|
|
818
|
-
if (
|
|
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
|
-
|
|
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}
|
|
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
|
|
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
|
-
|
|
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
|
|
878
|
-
if (!existsSync(
|
|
879
|
-
for (const
|
|
880
|
-
const
|
|
881
|
-
if (existsSync(
|
|
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
|
-
|
|
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
|
-
|
|
893
|
-
|
|
894
|
-
injectClient(html,
|
|
895
|
-
|
|
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
|
|
916
|
-
send(response, `Proxy
|
|
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
|
|
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 ===
|
|
936
|
-
if (
|
|
937
|
-
send(response, "
|
|
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,
|
|
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]
|
|
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, "
|
|
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,
|
|
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 (
|
|
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 =
|
|
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) && !
|
|
1027
|
-
await serveHtml(response);
|
|
1116
|
+
if (!extname(path) && !isAssetRequest(incoming.headers)) {
|
|
1117
|
+
await serveHtml(response, path);
|
|
1028
1118
|
return;
|
|
1029
1119
|
}
|
|
1030
|
-
send(response, "
|
|
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(`
|
|
1123
|
+
error(`failed to handle ${path}`, cause);
|
|
1034
1124
|
broadcast({ type: "error", message, file: path });
|
|
1035
|
-
send(response, `
|
|
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("
|
|
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,
|
|
1085
|
-
|
|
1086
|
-
|
|
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
|
|
1089
|
-
|
|
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,
|
|
1215
|
+
export { ModuleGraph, cssProviderFor, detectSelfAccepting, extractEntries, injectClient, optimizeDeps, scanDependencies, startDevServer };
|