@foxford/den 3.1.4 → 3.2.1
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/bin/den.cjs +4 -3
- package/bin/den.js +2 -1
- package/builder/index.cjs +3 -2
- package/builder/index.d.cts +22 -2
- package/builder/index.d.ts +22 -2
- package/builder/index.js +2 -1
- package/chunk-KQAB6C6Q.cjs +15 -0
- package/chunk-RG7ZV5BD.js +15 -0
- package/{chunk-ZBB5SBEV.cjs → chunk-UB3GCX73.cjs} +134 -92
- package/{chunk-YECHY3NR.js → chunk-URSWYSZF.js} +134 -92
- package/package.json +8 -6
- package/serve/index.cjs +55 -1
- package/serve/index.d.cts +33 -2
- package/serve/index.d.ts +33 -2
- package/serve/index.js +54 -0
package/bin/den.cjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunkUB3GCX73cjs = require('../chunk-UB3GCX73.cjs');
|
|
5
|
+
require('../chunk-KQAB6C6Q.cjs');
|
|
5
6
|
require('../chunk-A6ZPAM6Z.cjs');
|
|
6
7
|
|
|
7
8
|
|
|
@@ -69,11 +70,11 @@ function run(argv) {
|
|
|
69
70
|
}
|
|
70
71
|
const target = { base: options["base"], root: (_a = options["root"]) != null ? _a : process.cwd() };
|
|
71
72
|
if (command === "build") {
|
|
72
|
-
yield
|
|
73
|
+
yield _chunkUB3GCX73cjs.buildApp.call(void 0, target);
|
|
73
74
|
return;
|
|
74
75
|
}
|
|
75
76
|
if (command === "dev") {
|
|
76
|
-
yield
|
|
77
|
+
yield _chunkUB3GCX73cjs.devApp.call(void 0, target);
|
|
77
78
|
return;
|
|
78
79
|
}
|
|
79
80
|
fail(`\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430 \xAB${command}\xBB`);
|
package/bin/den.js
CHANGED
package/builder/index.cjs
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunkUB3GCX73cjs = require('../chunk-UB3GCX73.cjs');
|
|
6
|
+
require('../chunk-KQAB6C6Q.cjs');
|
|
6
7
|
require('../chunk-A6ZPAM6Z.cjs');
|
|
7
8
|
require('../chunk-T5QVCXVB.cjs');
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
|
|
12
|
-
exports.buildApp =
|
|
13
|
+
exports.buildApp = _chunkUB3GCX73cjs.buildApp; exports.defineAppConfig = _chunkUB3GCX73cjs.defineAppConfig; exports.devApp = _chunkUB3GCX73cjs.devApp;
|
package/builder/index.d.cts
CHANGED
|
@@ -53,6 +53,14 @@ interface AppServerConfig {
|
|
|
53
53
|
* по parent-chain на любом рендере.
|
|
54
54
|
*/
|
|
55
55
|
container?: (container: Container) => void;
|
|
56
|
+
/**
|
|
57
|
+
* Порт процесса приложения.
|
|
58
|
+
*
|
|
59
|
+
* `PORT` в окружении важнее: порт назначает тот, кто процесс поднимает, — в проде оркестратор.
|
|
60
|
+
* Объявление здесь — умолчание самого приложения, чтобы адрес не приходилось повторять в каждой
|
|
61
|
+
* команде запуска и в префиксе артефактов.
|
|
62
|
+
*/
|
|
63
|
+
port?: number;
|
|
56
64
|
/**
|
|
57
65
|
* Расширение сетевого инстанса до начала прослушивания: Sentry, метрики, cors.
|
|
58
66
|
*
|
|
@@ -99,18 +107,30 @@ type BuildMode = 'production' | 'development';
|
|
|
99
107
|
interface BuildAppOptions {
|
|
100
108
|
/** Корень приложения. */
|
|
101
109
|
root: string;
|
|
102
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Префикс адресов артефактов: в проде CDN.
|
|
112
|
+
*
|
|
113
|
+
* Не задан — в разработке подставляется адрес самого приложения (артефакты отдаёт оно),
|
|
114
|
+
* в проде корень.
|
|
115
|
+
*/
|
|
103
116
|
base?: string;
|
|
104
117
|
/** Режим сборки; по умолчанию `production`. */
|
|
105
118
|
mode?: BuildMode;
|
|
106
119
|
}
|
|
120
|
+
/** Чем сборка закончилась — то, что нужно знать поднимающему процесс. */
|
|
121
|
+
interface AppBuildResult {
|
|
122
|
+
/** Префикс адресов артефактов, с которым собран клиентский выход. */
|
|
123
|
+
base: string;
|
|
124
|
+
/** Порт, который процесс будет слушать: окружение, затем умолчание приложения. */
|
|
125
|
+
port: number;
|
|
126
|
+
}
|
|
107
127
|
/**
|
|
108
128
|
* Собирает приложение целиком.
|
|
109
129
|
*
|
|
110
130
|
* @param options - Корень приложения и префикс адресов артефактов
|
|
111
131
|
* @throws Если конфига нет либо пресет сообщил, что ему не хватает объявленного
|
|
112
132
|
*/
|
|
113
|
-
declare function buildApp(options: BuildAppOptions): Promise<
|
|
133
|
+
declare function buildApp(options: BuildAppOptions): Promise<AppBuildResult>;
|
|
114
134
|
|
|
115
135
|
/** Наблюдение берёт те же опции, что сборка: путь у них один. */
|
|
116
136
|
type DevAppOptions = BuildAppOptions;
|
package/builder/index.d.ts
CHANGED
|
@@ -53,6 +53,14 @@ interface AppServerConfig {
|
|
|
53
53
|
* по parent-chain на любом рендере.
|
|
54
54
|
*/
|
|
55
55
|
container?: (container: Container) => void;
|
|
56
|
+
/**
|
|
57
|
+
* Порт процесса приложения.
|
|
58
|
+
*
|
|
59
|
+
* `PORT` в окружении важнее: порт назначает тот, кто процесс поднимает, — в проде оркестратор.
|
|
60
|
+
* Объявление здесь — умолчание самого приложения, чтобы адрес не приходилось повторять в каждой
|
|
61
|
+
* команде запуска и в префиксе артефактов.
|
|
62
|
+
*/
|
|
63
|
+
port?: number;
|
|
56
64
|
/**
|
|
57
65
|
* Расширение сетевого инстанса до начала прослушивания: Sentry, метрики, cors.
|
|
58
66
|
*
|
|
@@ -99,18 +107,30 @@ type BuildMode = 'production' | 'development';
|
|
|
99
107
|
interface BuildAppOptions {
|
|
100
108
|
/** Корень приложения. */
|
|
101
109
|
root: string;
|
|
102
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Префикс адресов артефактов: в проде CDN.
|
|
112
|
+
*
|
|
113
|
+
* Не задан — в разработке подставляется адрес самого приложения (артефакты отдаёт оно),
|
|
114
|
+
* в проде корень.
|
|
115
|
+
*/
|
|
103
116
|
base?: string;
|
|
104
117
|
/** Режим сборки; по умолчанию `production`. */
|
|
105
118
|
mode?: BuildMode;
|
|
106
119
|
}
|
|
120
|
+
/** Чем сборка закончилась — то, что нужно знать поднимающему процесс. */
|
|
121
|
+
interface AppBuildResult {
|
|
122
|
+
/** Префикс адресов артефактов, с которым собран клиентский выход. */
|
|
123
|
+
base: string;
|
|
124
|
+
/** Порт, который процесс будет слушать: окружение, затем умолчание приложения. */
|
|
125
|
+
port: number;
|
|
126
|
+
}
|
|
107
127
|
/**
|
|
108
128
|
* Собирает приложение целиком.
|
|
109
129
|
*
|
|
110
130
|
* @param options - Корень приложения и префикс адресов артефактов
|
|
111
131
|
* @throws Если конфига нет либо пресет сообщил, что ему не хватает объявленного
|
|
112
132
|
*/
|
|
113
|
-
declare function buildApp(options: BuildAppOptions): Promise<
|
|
133
|
+
declare function buildApp(options: BuildAppOptions): Promise<AppBuildResult>;
|
|
114
134
|
|
|
115
135
|
/** Наблюдение берёт те же опции, что сборка: путь у них один. */
|
|
116
136
|
type DevAppOptions = BuildAppOptions;
|
package/builder/index.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// src/serve/env.ts
|
|
2
|
+
var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);
|
|
3
|
+
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
4
|
+
var ENV_FILE = ".env";
|
|
5
|
+
function loadAppEnv(root) {
|
|
6
|
+
const file = _path2.default.join(root, ENV_FILE);
|
|
7
|
+
if (!_fs2.default.existsSync(file)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
process.loadEnvFile(file);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
exports.loadAppEnv = loadAppEnv;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/serve/env.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
var ENV_FILE = ".env";
|
|
5
|
+
function loadAppEnv(root) {
|
|
6
|
+
const file = path.join(root, ENV_FILE);
|
|
7
|
+
if (!fs.existsSync(file)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
process.loadEnvFile(file);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
loadAppEnv
|
|
15
|
+
};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
2
2
|
|
|
3
|
+
var _chunkKQAB6C6Qcjs = require('./chunk-KQAB6C6Q.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
3
6
|
var _chunkA6ZPAM6Zcjs = require('./chunk-A6ZPAM6Z.cjs');
|
|
4
7
|
|
|
5
8
|
|
|
@@ -35,7 +38,7 @@ function collectAssets(outDir, base) {
|
|
|
35
38
|
const css = [];
|
|
36
39
|
const js = [];
|
|
37
40
|
const seen = /* @__PURE__ */ new Set();
|
|
38
|
-
const prefix = base.endsWith("/") ? base : `${base}/`;
|
|
41
|
+
const prefix = base === "" ? "" : base.endsWith("/") ? base : `${base}/`;
|
|
39
42
|
function walk(key) {
|
|
40
43
|
var _a, _b;
|
|
41
44
|
if (seen.has(key)) {
|
|
@@ -64,6 +67,10 @@ function collectAssets(outDir, base) {
|
|
|
64
67
|
|
|
65
68
|
// src/serve/port.ts
|
|
66
69
|
var DEFAULT_PORT = 3e3;
|
|
70
|
+
function resolvePort(declared) {
|
|
71
|
+
var _a, _b;
|
|
72
|
+
return Number((_b = (_a = process.env["PORT"]) != null ? _a : declared) != null ? _b : DEFAULT_PORT);
|
|
73
|
+
}
|
|
67
74
|
|
|
68
75
|
// src/builder/entries.ts
|
|
69
76
|
var VIRTUAL_CLIENT = "virtual:den/client";
|
|
@@ -102,27 +109,34 @@ if (server.render) {
|
|
|
102
109
|
export default descriptor
|
|
103
110
|
`;
|
|
104
111
|
}
|
|
105
|
-
function generateServerProcess(configPath) {
|
|
106
|
-
|
|
112
|
+
function generateServerProcess(configPath, base, mode) {
|
|
113
|
+
const assets = mode === "development" ? ` assets: { base: ${JSON.stringify(base)}, dir: fileURLToPath(new URL('./client', import.meta.url)) },
|
|
114
|
+
` : "";
|
|
115
|
+
return `import { fileURLToPath } from 'node:url'
|
|
116
|
+
|
|
117
|
+
import { loadAppEnv, serveApp } from '@foxford/den/serve'
|
|
107
118
|
|
|
108
119
|
import app from ${JSON.stringify(VIRTUAL_SERVER_APP)}
|
|
109
120
|
import config from ${JSON.stringify(configPath)}
|
|
110
121
|
|
|
122
|
+
// \u0423\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044F \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u2014 \u0434\u043E \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0447\u0442\u0435\u043D\u0438\u044F: \u043F\u043E\u0440\u0442 \u0438 \u0430\u0434\u0440\u0435\u0441\u0430 \u0447\u0438\u0442\u0430\u044E\u0442\u0441\u044F \u043D\u0438\u0436\u0435
|
|
123
|
+
loadAppEnv(fileURLToPath(new URL('..', import.meta.url)))
|
|
124
|
+
|
|
111
125
|
await serveApp(app, {
|
|
112
|
-
configure: config.server?.configure,
|
|
126
|
+
${assets} configure: config.server?.configure,
|
|
113
127
|
host: process.env.HOST,
|
|
114
|
-
port: Number(process.env.PORT ?? ${String(DEFAULT_PORT)}),
|
|
128
|
+
port: Number(process.env.PORT ?? config.server?.port ?? ${String(DEFAULT_PORT)}),
|
|
115
129
|
})
|
|
116
130
|
`;
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
// src/builder/plugin.ts
|
|
120
134
|
function denAppModules(options) {
|
|
121
|
-
const { assets, config, configPath, entry } = options;
|
|
135
|
+
const { assets, base, config, configPath, entry, mode } = options;
|
|
122
136
|
const sources = {
|
|
123
137
|
[VIRTUAL_CLIENT]: () => generateClientEntry(entry, config.preset.clientEntry),
|
|
124
138
|
[VIRTUAL_SERVER_APP]: () => generateServerApp(entry, configPath, assets),
|
|
125
|
-
[VIRTUAL_SERVER_PROCESS]: () => generateServerProcess(configPath)
|
|
139
|
+
[VIRTUAL_SERVER_PROCESS]: () => generateServerProcess(configPath, base, mode)
|
|
126
140
|
};
|
|
127
141
|
return {
|
|
128
142
|
load: (id) => {
|
|
@@ -184,7 +198,9 @@ function viteConfigFor(options) {
|
|
|
184
198
|
const { assets, base, config, configPath, entryFileNames, input, mode, outDir, root, target } = options;
|
|
185
199
|
const entry = (_a = config.entry) != null ? _a : "src/index.ts";
|
|
186
200
|
const composed = {
|
|
187
|
-
|
|
201
|
+
// Сборщику относительный префикс, если своего нет: ленивые чанки тогда грузятся от адреса
|
|
202
|
+
// уже загруженного чанка, и подмена CDN уводит за собой и их
|
|
203
|
+
base: base === "" ? "./" : base,
|
|
188
204
|
build: {
|
|
189
205
|
// Клиентский выход чистит свой каталог сам, серверные — нет: они пишут рядом с уже
|
|
190
206
|
// собранным клиентским, и очистка снесла бы его вместе с манифестом
|
|
@@ -202,7 +218,10 @@ function viteConfigFor(options) {
|
|
|
202
218
|
// Режим объявляем явно: плагины сборщика выбирают по нему рантайм фреймворка, а через
|
|
203
219
|
// программный вызов он иначе не доедет.
|
|
204
220
|
mode,
|
|
205
|
-
plugins: [
|
|
221
|
+
plugins: [
|
|
222
|
+
...config.preset.plugins,
|
|
223
|
+
denAppModules({ assets, base, config, configPath, entry: _path2.default.join(root, entry), mode })
|
|
224
|
+
],
|
|
206
225
|
root,
|
|
207
226
|
ssr: { noExternal: (_c = (_b = config.server) == null ? void 0 : _b.noExternal) != null ? _c : [] }
|
|
208
227
|
};
|
|
@@ -210,12 +229,15 @@ function viteConfigFor(options) {
|
|
|
210
229
|
}
|
|
211
230
|
function buildApp(options) {
|
|
212
231
|
return _chunkT5QVCXVBcjs.__async.call(void 0, this, null, function* () {
|
|
213
|
-
var _a, _b;
|
|
214
|
-
const {
|
|
232
|
+
var _a, _b, _c, _d;
|
|
233
|
+
const { mode = "production" } = options;
|
|
215
234
|
process.env["NODE_ENV"] = mode;
|
|
216
235
|
const root = _path2.default.resolve(options.root);
|
|
236
|
+
_chunkKQAB6C6Qcjs.loadAppEnv.call(void 0, root);
|
|
217
237
|
const { config, configPath } = yield loadAppConfig(root);
|
|
218
|
-
const
|
|
238
|
+
const port = resolvePort((_a = config.server) == null ? void 0 : _a.port);
|
|
239
|
+
const base = (_b = options.base) != null ? _b : "";
|
|
240
|
+
const refusal = (_d = (_c = config.preset).validate) == null ? void 0 : _d.call(_c, dependenciesOf(root));
|
|
219
241
|
if (refusal !== void 0 && refusal !== null) {
|
|
220
242
|
throw new Error(`den: ${refusal}`);
|
|
221
243
|
}
|
|
@@ -246,11 +268,11 @@ function buildApp(options) {
|
|
|
246
268
|
target: "server"
|
|
247
269
|
}))
|
|
248
270
|
);
|
|
271
|
+
return { base, port };
|
|
249
272
|
});
|
|
250
273
|
}
|
|
251
274
|
|
|
252
275
|
// src/builder/dev.ts
|
|
253
|
-
var _child_process = require('child_process');
|
|
254
276
|
|
|
255
277
|
|
|
256
278
|
var _url = require('url');
|
|
@@ -284,7 +306,7 @@ function assetsSummary(assets) {
|
|
|
284
306
|
}
|
|
285
307
|
function bannerLines(facts2) {
|
|
286
308
|
var _a;
|
|
287
|
-
const port = (_a =
|
|
309
|
+
const port = String((_a = facts2.port) != null ? _a : "");
|
|
288
310
|
const network = networkAddress();
|
|
289
311
|
const label = (title) => ` \u2192 ${title.padEnd(PAD)}`;
|
|
290
312
|
return [
|
|
@@ -296,7 +318,10 @@ function bannerLines(facts2) {
|
|
|
296
318
|
`${label("\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E")}http://localhost:${port}`,
|
|
297
319
|
...network === void 0 ? [] : [`${label("\u0432 \u0441\u0435\u0442\u0438")}http://${network}:${port}`],
|
|
298
320
|
`${label("\u0440\u0443\u0447\u043A\u0438")}${ENDPOINTS.join(" ")}`,
|
|
299
|
-
|
|
321
|
+
// Префикс печатается, только когда он есть: при относительной сборке артефакты идут с
|
|
322
|
+
// адреса самого приложения, а он уже назван строкой выше
|
|
323
|
+
...facts2.base === void 0 || facts2.base === "" ? [] : [`${label("\u043F\u0440\u0435\u0444\u0438\u043A\u0441")}${facts2.base}`],
|
|
324
|
+
`${label("\u0447\u0430\u043D\u043A\u0438")}${assetsSummary(facts2.assets)}`,
|
|
300
325
|
`${label("\u0432\u044B\u0445\u043E\u0434\u044B")}${OUTPUTS.join(", ")}`,
|
|
301
326
|
`${label("\u043F\u0440\u0430\u0432\u043A\u0438")}${facts2.watched.join(", ")}`,
|
|
302
327
|
`${label("\u043A\u043B\u0430\u0432\u0438\u0448\u0438")}${SHORTCUTS.map(({ key, title }) => `${key} \u2014 ${title}`).join(", ")}`,
|
|
@@ -307,6 +332,63 @@ function helpLines() {
|
|
|
307
332
|
return ["", " \u043A\u043B\u0430\u0432\u0438\u0448\u0430 + enter:", ...SHORTCUTS.map(({ key, title }) => ` ${key} ${title}`), ""];
|
|
308
333
|
}
|
|
309
334
|
|
|
335
|
+
// src/builder/process.ts
|
|
336
|
+
var _child_process = require('child_process');
|
|
337
|
+
var SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
338
|
+
function signalGroup(pid, signal) {
|
|
339
|
+
try {
|
|
340
|
+
process.kill(-pid, signal);
|
|
341
|
+
} catch (e) {
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function appProcess(entry) {
|
|
345
|
+
let child = null;
|
|
346
|
+
function stop() {
|
|
347
|
+
return _chunkT5QVCXVBcjs.__async.call(void 0, this, null, function* () {
|
|
348
|
+
const running = child;
|
|
349
|
+
child = null;
|
|
350
|
+
if (!running || running.exitCode !== null || running.pid === void 0) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const { pid } = running;
|
|
354
|
+
yield new Promise((resolve) => {
|
|
355
|
+
const forced = setTimeout(() => signalGroup(pid, "SIGKILL"), SHUTDOWN_TIMEOUT_MS);
|
|
356
|
+
running.once("exit", () => {
|
|
357
|
+
clearTimeout(forced);
|
|
358
|
+
resolve();
|
|
359
|
+
});
|
|
360
|
+
signalGroup(pid, "SIGTERM");
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
function start(env) {
|
|
365
|
+
const started = _child_process.spawn.call(void 0, process.execPath, [...process.execArgv, entry], {
|
|
366
|
+
detached: true,
|
|
367
|
+
env: _chunkT5QVCXVBcjs.__spreadValues.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {}, process.env), env),
|
|
368
|
+
stdio: "inherit"
|
|
369
|
+
});
|
|
370
|
+
child = started;
|
|
371
|
+
started.on("exit", (code, signal) => {
|
|
372
|
+
if (child !== started) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
child = null;
|
|
376
|
+
_chunkA6ZPAM6Zcjs.logger.error(`\u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0441\u0430\u043C (\u043A\u043E\u0434 ${String(code)}, \u0441\u0438\u0433\u043D\u0430\u043B ${String(signal)})`);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
380
|
+
process.once(signal, () => {
|
|
381
|
+
void stop().then(() => process.exit(0));
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
process.on("exit", () => {
|
|
385
|
+
if ((child == null ? void 0 : child.pid) !== void 0) {
|
|
386
|
+
signalGroup(child.pid, "SIGTERM");
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
return { start, stop };
|
|
390
|
+
}
|
|
391
|
+
|
|
310
392
|
// src/builder/dev.ts
|
|
311
393
|
var SERVER_ENTRY = "build/server.mjs";
|
|
312
394
|
var WATCHED = ["src", "den.config.ts"];
|
|
@@ -315,7 +397,21 @@ function debugPattern(name) {
|
|
|
315
397
|
return (name === void 0 ? DEBUG_SCOPES : [`${name}:*`, ...DEBUG_SCOPES]).join(",");
|
|
316
398
|
}
|
|
317
399
|
var DEBOUNCE_MS = 150;
|
|
318
|
-
|
|
400
|
+
function watchSources(root, onChange) {
|
|
401
|
+
let pending = null;
|
|
402
|
+
for (const target of WATCHED) {
|
|
403
|
+
const watched = _path2.default.join(root, target);
|
|
404
|
+
if (!_fs2.default.existsSync(watched)) {
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
_fs2.default.watch(watched, { recursive: true }, () => {
|
|
408
|
+
if (pending) {
|
|
409
|
+
clearTimeout(pending);
|
|
410
|
+
}
|
|
411
|
+
pending = setTimeout(onChange, DEBOUNCE_MS);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
319
415
|
function facts(root) {
|
|
320
416
|
return _chunkT5QVCXVBcjs.__async.call(void 0, this, null, function* () {
|
|
321
417
|
try {
|
|
@@ -338,52 +434,10 @@ function devApp(options) {
|
|
|
338
434
|
return _chunkT5QVCXVBcjs.__async.call(void 0, this, null, function* () {
|
|
339
435
|
_chunkA6ZPAM6Zcjs.logger.setDebug(true);
|
|
340
436
|
const root = _path2.default.resolve(options.root);
|
|
341
|
-
const entry = _path2.default.join(root, SERVER_ENTRY);
|
|
342
|
-
let child = null;
|
|
343
|
-
let pending = null;
|
|
344
437
|
let building = false;
|
|
345
438
|
let dirty = false;
|
|
346
439
|
const describe = describer(root);
|
|
347
|
-
|
|
348
|
-
try {
|
|
349
|
-
process.kill(-pid, signal);
|
|
350
|
-
} catch (e) {
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
function stop() {
|
|
354
|
-
return _chunkT5QVCXVBcjs.__async.call(void 0, this, null, function* () {
|
|
355
|
-
const running = child;
|
|
356
|
-
child = null;
|
|
357
|
-
if (!running || running.exitCode !== null || running.pid === void 0) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
const { pid } = running;
|
|
361
|
-
yield new Promise((resolve) => {
|
|
362
|
-
const forced = setTimeout(() => signalGroup(pid, "SIGKILL"), SHUTDOWN_TIMEOUT_MS);
|
|
363
|
-
running.once("exit", () => {
|
|
364
|
-
clearTimeout(forced);
|
|
365
|
-
resolve();
|
|
366
|
-
});
|
|
367
|
-
signalGroup(pid, "SIGTERM");
|
|
368
|
-
});
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
function start(name) {
|
|
372
|
-
var _a;
|
|
373
|
-
const started = _child_process.spawn.call(void 0, process.execPath, [...process.execArgv, entry], {
|
|
374
|
-
detached: true,
|
|
375
|
-
env: _chunkT5QVCXVBcjs.__spreadProps.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {}, process.env), { DEBUG: (_a = process.env["DEBUG"]) != null ? _a : debugPattern(name) }),
|
|
376
|
-
stdio: "inherit"
|
|
377
|
-
});
|
|
378
|
-
child = started;
|
|
379
|
-
started.on("exit", (code, signal) => {
|
|
380
|
-
if (child !== started) {
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
child = null;
|
|
384
|
-
_chunkA6ZPAM6Zcjs.logger.error(`\u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0441\u0430\u043C (\u043A\u043E\u0434 ${String(code)}, \u0441\u0438\u0433\u043D\u0430\u043B ${String(signal)})`);
|
|
385
|
-
});
|
|
386
|
-
}
|
|
440
|
+
const app = appProcess(_path2.default.join(root, SERVER_ENTRY));
|
|
387
441
|
function show(lines) {
|
|
388
442
|
process.stdout.write(`${lines.join("\n")}
|
|
389
443
|
`);
|
|
@@ -402,26 +456,27 @@ function devApp(options) {
|
|
|
402
456
|
show(helpLines());
|
|
403
457
|
}
|
|
404
458
|
if (key === "q") {
|
|
405
|
-
void stop().then(() => process.exit(0));
|
|
459
|
+
void app.stop().then(() => process.exit(0));
|
|
406
460
|
}
|
|
407
461
|
});
|
|
408
462
|
}
|
|
409
463
|
function rebuild() {
|
|
410
464
|
return _chunkT5QVCXVBcjs.__async.call(void 0, this, null, function* () {
|
|
465
|
+
var _a;
|
|
411
466
|
if (building) {
|
|
412
467
|
dirty = true;
|
|
413
|
-
return
|
|
468
|
+
return null;
|
|
414
469
|
}
|
|
415
470
|
building = true;
|
|
416
|
-
let
|
|
471
|
+
let built = null;
|
|
417
472
|
try {
|
|
418
473
|
do {
|
|
419
474
|
dirty = false;
|
|
420
|
-
yield stop();
|
|
475
|
+
yield app.stop();
|
|
421
476
|
try {
|
|
422
|
-
yield buildApp(_chunkT5QVCXVBcjs.__spreadProps.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {}, options), { mode: "development" }));
|
|
423
|
-
start((yield describe()).name);
|
|
424
|
-
|
|
477
|
+
const result = yield buildApp(_chunkT5QVCXVBcjs.__spreadProps.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {}, options), { mode: "development" }));
|
|
478
|
+
app.start({ DEBUG: (_a = process.env["DEBUG"]) != null ? _a : debugPattern((yield describe()).name) });
|
|
479
|
+
built = result;
|
|
425
480
|
} catch (error) {
|
|
426
481
|
_chunkA6ZPAM6Zcjs.logger.error("\u0441\u0431\u043E\u0440\u043A\u0430 \u043D\u0435 \u043F\u0440\u043E\u0448\u043B\u0430 \u2014 \u0441\u0435\u0440\u0432\u0438\u0441 \u041D\u0415 \u041F\u041E\u0414\u041D\u042F\u0422, \u0436\u0434\u0443 \u043F\u0440\u0430\u0432\u043A\u0438:", error);
|
|
427
482
|
}
|
|
@@ -429,38 +484,25 @@ function devApp(options) {
|
|
|
429
484
|
} finally {
|
|
430
485
|
building = false;
|
|
431
486
|
}
|
|
432
|
-
return
|
|
487
|
+
return built;
|
|
433
488
|
});
|
|
434
489
|
}
|
|
435
490
|
const startedAt = Date.now();
|
|
436
|
-
const
|
|
491
|
+
const address = yield rebuild();
|
|
437
492
|
const elapsed = Date.now() - startedAt;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
if (built) {
|
|
451
|
-
show(bannerLines(_chunkT5QVCXVBcjs.__spreadValues.call(void 0, { elapsedMs: elapsed, mode: "development", watched: WATCHED }, yield describe())));
|
|
493
|
+
watchSources(root, () => void rebuild());
|
|
494
|
+
if (address !== null) {
|
|
495
|
+
show(
|
|
496
|
+
bannerLines(_chunkT5QVCXVBcjs.__spreadValues.call(void 0, {
|
|
497
|
+
base: address.base,
|
|
498
|
+
elapsedMs: elapsed,
|
|
499
|
+
mode: "development",
|
|
500
|
+
port: address.port,
|
|
501
|
+
watched: WATCHED
|
|
502
|
+
}, yield describe()))
|
|
503
|
+
);
|
|
452
504
|
}
|
|
453
505
|
listenForKeys();
|
|
454
|
-
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
455
|
-
process.once(signal, () => {
|
|
456
|
-
void stop().then(() => process.exit(0));
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
process.on("exit", () => {
|
|
460
|
-
if ((child == null ? void 0 : child.pid) !== void 0) {
|
|
461
|
-
signalGroup(child.pid, "SIGTERM");
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
506
|
});
|
|
465
507
|
}
|
|
466
508
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadAppEnv
|
|
3
|
+
} from "./chunk-RG7ZV5BD.js";
|
|
1
4
|
import {
|
|
2
5
|
logger
|
|
3
6
|
} from "./chunk-TPBI6TOU.js";
|
|
@@ -35,7 +38,7 @@ function collectAssets(outDir, base) {
|
|
|
35
38
|
const css = [];
|
|
36
39
|
const js = [];
|
|
37
40
|
const seen = /* @__PURE__ */ new Set();
|
|
38
|
-
const prefix = base.endsWith("/") ? base : `${base}/`;
|
|
41
|
+
const prefix = base === "" ? "" : base.endsWith("/") ? base : `${base}/`;
|
|
39
42
|
function walk(key) {
|
|
40
43
|
var _a, _b;
|
|
41
44
|
if (seen.has(key)) {
|
|
@@ -64,6 +67,10 @@ function collectAssets(outDir, base) {
|
|
|
64
67
|
|
|
65
68
|
// src/serve/port.ts
|
|
66
69
|
var DEFAULT_PORT = 3e3;
|
|
70
|
+
function resolvePort(declared) {
|
|
71
|
+
var _a, _b;
|
|
72
|
+
return Number((_b = (_a = process.env["PORT"]) != null ? _a : declared) != null ? _b : DEFAULT_PORT);
|
|
73
|
+
}
|
|
67
74
|
|
|
68
75
|
// src/builder/entries.ts
|
|
69
76
|
var VIRTUAL_CLIENT = "virtual:den/client";
|
|
@@ -102,27 +109,34 @@ if (server.render) {
|
|
|
102
109
|
export default descriptor
|
|
103
110
|
`;
|
|
104
111
|
}
|
|
105
|
-
function generateServerProcess(configPath) {
|
|
106
|
-
|
|
112
|
+
function generateServerProcess(configPath, base, mode) {
|
|
113
|
+
const assets = mode === "development" ? ` assets: { base: ${JSON.stringify(base)}, dir: fileURLToPath(new URL('./client', import.meta.url)) },
|
|
114
|
+
` : "";
|
|
115
|
+
return `import { fileURLToPath } from 'node:url'
|
|
116
|
+
|
|
117
|
+
import { loadAppEnv, serveApp } from '@foxford/den/serve'
|
|
107
118
|
|
|
108
119
|
import app from ${JSON.stringify(VIRTUAL_SERVER_APP)}
|
|
109
120
|
import config from ${JSON.stringify(configPath)}
|
|
110
121
|
|
|
122
|
+
// \u0423\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044F \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u2014 \u0434\u043E \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0447\u0442\u0435\u043D\u0438\u044F: \u043F\u043E\u0440\u0442 \u0438 \u0430\u0434\u0440\u0435\u0441\u0430 \u0447\u0438\u0442\u0430\u044E\u0442\u0441\u044F \u043D\u0438\u0436\u0435
|
|
123
|
+
loadAppEnv(fileURLToPath(new URL('..', import.meta.url)))
|
|
124
|
+
|
|
111
125
|
await serveApp(app, {
|
|
112
|
-
configure: config.server?.configure,
|
|
126
|
+
${assets} configure: config.server?.configure,
|
|
113
127
|
host: process.env.HOST,
|
|
114
|
-
port: Number(process.env.PORT ?? ${String(DEFAULT_PORT)}),
|
|
128
|
+
port: Number(process.env.PORT ?? config.server?.port ?? ${String(DEFAULT_PORT)}),
|
|
115
129
|
})
|
|
116
130
|
`;
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
// src/builder/plugin.ts
|
|
120
134
|
function denAppModules(options) {
|
|
121
|
-
const { assets, config, configPath, entry } = options;
|
|
135
|
+
const { assets, base, config, configPath, entry, mode } = options;
|
|
122
136
|
const sources = {
|
|
123
137
|
[VIRTUAL_CLIENT]: () => generateClientEntry(entry, config.preset.clientEntry),
|
|
124
138
|
[VIRTUAL_SERVER_APP]: () => generateServerApp(entry, configPath, assets),
|
|
125
|
-
[VIRTUAL_SERVER_PROCESS]: () => generateServerProcess(configPath)
|
|
139
|
+
[VIRTUAL_SERVER_PROCESS]: () => generateServerProcess(configPath, base, mode)
|
|
126
140
|
};
|
|
127
141
|
return {
|
|
128
142
|
load: (id) => {
|
|
@@ -184,7 +198,9 @@ function viteConfigFor(options) {
|
|
|
184
198
|
const { assets, base, config, configPath, entryFileNames, input, mode, outDir, root, target } = options;
|
|
185
199
|
const entry = (_a = config.entry) != null ? _a : "src/index.ts";
|
|
186
200
|
const composed = {
|
|
187
|
-
|
|
201
|
+
// Сборщику относительный префикс, если своего нет: ленивые чанки тогда грузятся от адреса
|
|
202
|
+
// уже загруженного чанка, и подмена CDN уводит за собой и их
|
|
203
|
+
base: base === "" ? "./" : base,
|
|
188
204
|
build: {
|
|
189
205
|
// Клиентский выход чистит свой каталог сам, серверные — нет: они пишут рядом с уже
|
|
190
206
|
// собранным клиентским, и очистка снесла бы его вместе с манифестом
|
|
@@ -202,7 +218,10 @@ function viteConfigFor(options) {
|
|
|
202
218
|
// Режим объявляем явно: плагины сборщика выбирают по нему рантайм фреймворка, а через
|
|
203
219
|
// программный вызов он иначе не доедет.
|
|
204
220
|
mode,
|
|
205
|
-
plugins: [
|
|
221
|
+
plugins: [
|
|
222
|
+
...config.preset.plugins,
|
|
223
|
+
denAppModules({ assets, base, config, configPath, entry: path2.join(root, entry), mode })
|
|
224
|
+
],
|
|
206
225
|
root,
|
|
207
226
|
ssr: { noExternal: (_c = (_b = config.server) == null ? void 0 : _b.noExternal) != null ? _c : [] }
|
|
208
227
|
};
|
|
@@ -210,12 +229,15 @@ function viteConfigFor(options) {
|
|
|
210
229
|
}
|
|
211
230
|
function buildApp(options) {
|
|
212
231
|
return __async(this, null, function* () {
|
|
213
|
-
var _a, _b;
|
|
214
|
-
const {
|
|
232
|
+
var _a, _b, _c, _d;
|
|
233
|
+
const { mode = "production" } = options;
|
|
215
234
|
process.env["NODE_ENV"] = mode;
|
|
216
235
|
const root = path2.resolve(options.root);
|
|
236
|
+
loadAppEnv(root);
|
|
217
237
|
const { config, configPath } = yield loadAppConfig(root);
|
|
218
|
-
const
|
|
238
|
+
const port = resolvePort((_a = config.server) == null ? void 0 : _a.port);
|
|
239
|
+
const base = (_b = options.base) != null ? _b : "";
|
|
240
|
+
const refusal = (_d = (_c = config.preset).validate) == null ? void 0 : _d.call(_c, dependenciesOf(root));
|
|
219
241
|
if (refusal !== void 0 && refusal !== null) {
|
|
220
242
|
throw new Error(`den: ${refusal}`);
|
|
221
243
|
}
|
|
@@ -246,11 +268,11 @@ function buildApp(options) {
|
|
|
246
268
|
target: "server"
|
|
247
269
|
}))
|
|
248
270
|
);
|
|
271
|
+
return { base, port };
|
|
249
272
|
});
|
|
250
273
|
}
|
|
251
274
|
|
|
252
275
|
// src/builder/dev.ts
|
|
253
|
-
import { spawn } from "child_process";
|
|
254
276
|
import fs3 from "fs";
|
|
255
277
|
import path3 from "path";
|
|
256
278
|
import { pathToFileURL } from "url";
|
|
@@ -284,7 +306,7 @@ function assetsSummary(assets) {
|
|
|
284
306
|
}
|
|
285
307
|
function bannerLines(facts2) {
|
|
286
308
|
var _a;
|
|
287
|
-
const port = (_a =
|
|
309
|
+
const port = String((_a = facts2.port) != null ? _a : "");
|
|
288
310
|
const network = networkAddress();
|
|
289
311
|
const label = (title) => ` \u2192 ${title.padEnd(PAD)}`;
|
|
290
312
|
return [
|
|
@@ -296,7 +318,10 @@ function bannerLines(facts2) {
|
|
|
296
318
|
`${label("\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E")}http://localhost:${port}`,
|
|
297
319
|
...network === void 0 ? [] : [`${label("\u0432 \u0441\u0435\u0442\u0438")}http://${network}:${port}`],
|
|
298
320
|
`${label("\u0440\u0443\u0447\u043A\u0438")}${ENDPOINTS.join(" ")}`,
|
|
299
|
-
|
|
321
|
+
// Префикс печатается, только когда он есть: при относительной сборке артефакты идут с
|
|
322
|
+
// адреса самого приложения, а он уже назван строкой выше
|
|
323
|
+
...facts2.base === void 0 || facts2.base === "" ? [] : [`${label("\u043F\u0440\u0435\u0444\u0438\u043A\u0441")}${facts2.base}`],
|
|
324
|
+
`${label("\u0447\u0430\u043D\u043A\u0438")}${assetsSummary(facts2.assets)}`,
|
|
300
325
|
`${label("\u0432\u044B\u0445\u043E\u0434\u044B")}${OUTPUTS.join(", ")}`,
|
|
301
326
|
`${label("\u043F\u0440\u0430\u0432\u043A\u0438")}${facts2.watched.join(", ")}`,
|
|
302
327
|
`${label("\u043A\u043B\u0430\u0432\u0438\u0448\u0438")}${SHORTCUTS.map(({ key, title }) => `${key} \u2014 ${title}`).join(", ")}`,
|
|
@@ -307,6 +332,63 @@ function helpLines() {
|
|
|
307
332
|
return ["", " \u043A\u043B\u0430\u0432\u0438\u0448\u0430 + enter:", ...SHORTCUTS.map(({ key, title }) => ` ${key} ${title}`), ""];
|
|
308
333
|
}
|
|
309
334
|
|
|
335
|
+
// src/builder/process.ts
|
|
336
|
+
import { spawn } from "child_process";
|
|
337
|
+
var SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
338
|
+
function signalGroup(pid, signal) {
|
|
339
|
+
try {
|
|
340
|
+
process.kill(-pid, signal);
|
|
341
|
+
} catch (e) {
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function appProcess(entry) {
|
|
345
|
+
let child = null;
|
|
346
|
+
function stop() {
|
|
347
|
+
return __async(this, null, function* () {
|
|
348
|
+
const running = child;
|
|
349
|
+
child = null;
|
|
350
|
+
if (!running || running.exitCode !== null || running.pid === void 0) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const { pid } = running;
|
|
354
|
+
yield new Promise((resolve) => {
|
|
355
|
+
const forced = setTimeout(() => signalGroup(pid, "SIGKILL"), SHUTDOWN_TIMEOUT_MS);
|
|
356
|
+
running.once("exit", () => {
|
|
357
|
+
clearTimeout(forced);
|
|
358
|
+
resolve();
|
|
359
|
+
});
|
|
360
|
+
signalGroup(pid, "SIGTERM");
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
function start(env) {
|
|
365
|
+
const started = spawn(process.execPath, [...process.execArgv, entry], {
|
|
366
|
+
detached: true,
|
|
367
|
+
env: __spreadValues(__spreadValues({}, process.env), env),
|
|
368
|
+
stdio: "inherit"
|
|
369
|
+
});
|
|
370
|
+
child = started;
|
|
371
|
+
started.on("exit", (code, signal) => {
|
|
372
|
+
if (child !== started) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
child = null;
|
|
376
|
+
logger.error(`\u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0441\u0430\u043C (\u043A\u043E\u0434 ${String(code)}, \u0441\u0438\u0433\u043D\u0430\u043B ${String(signal)})`);
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
380
|
+
process.once(signal, () => {
|
|
381
|
+
void stop().then(() => process.exit(0));
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
process.on("exit", () => {
|
|
385
|
+
if ((child == null ? void 0 : child.pid) !== void 0) {
|
|
386
|
+
signalGroup(child.pid, "SIGTERM");
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
return { start, stop };
|
|
390
|
+
}
|
|
391
|
+
|
|
310
392
|
// src/builder/dev.ts
|
|
311
393
|
var SERVER_ENTRY = "build/server.mjs";
|
|
312
394
|
var WATCHED = ["src", "den.config.ts"];
|
|
@@ -315,7 +397,21 @@ function debugPattern(name) {
|
|
|
315
397
|
return (name === void 0 ? DEBUG_SCOPES : [`${name}:*`, ...DEBUG_SCOPES]).join(",");
|
|
316
398
|
}
|
|
317
399
|
var DEBOUNCE_MS = 150;
|
|
318
|
-
|
|
400
|
+
function watchSources(root, onChange) {
|
|
401
|
+
let pending = null;
|
|
402
|
+
for (const target of WATCHED) {
|
|
403
|
+
const watched = path3.join(root, target);
|
|
404
|
+
if (!fs3.existsSync(watched)) {
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
fs3.watch(watched, { recursive: true }, () => {
|
|
408
|
+
if (pending) {
|
|
409
|
+
clearTimeout(pending);
|
|
410
|
+
}
|
|
411
|
+
pending = setTimeout(onChange, DEBOUNCE_MS);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
319
415
|
function facts(root) {
|
|
320
416
|
return __async(this, null, function* () {
|
|
321
417
|
try {
|
|
@@ -338,52 +434,10 @@ function devApp(options) {
|
|
|
338
434
|
return __async(this, null, function* () {
|
|
339
435
|
logger.setDebug(true);
|
|
340
436
|
const root = path3.resolve(options.root);
|
|
341
|
-
const entry = path3.join(root, SERVER_ENTRY);
|
|
342
|
-
let child = null;
|
|
343
|
-
let pending = null;
|
|
344
437
|
let building = false;
|
|
345
438
|
let dirty = false;
|
|
346
439
|
const describe = describer(root);
|
|
347
|
-
|
|
348
|
-
try {
|
|
349
|
-
process.kill(-pid, signal);
|
|
350
|
-
} catch (e) {
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
function stop() {
|
|
354
|
-
return __async(this, null, function* () {
|
|
355
|
-
const running = child;
|
|
356
|
-
child = null;
|
|
357
|
-
if (!running || running.exitCode !== null || running.pid === void 0) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
const { pid } = running;
|
|
361
|
-
yield new Promise((resolve) => {
|
|
362
|
-
const forced = setTimeout(() => signalGroup(pid, "SIGKILL"), SHUTDOWN_TIMEOUT_MS);
|
|
363
|
-
running.once("exit", () => {
|
|
364
|
-
clearTimeout(forced);
|
|
365
|
-
resolve();
|
|
366
|
-
});
|
|
367
|
-
signalGroup(pid, "SIGTERM");
|
|
368
|
-
});
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
function start(name) {
|
|
372
|
-
var _a;
|
|
373
|
-
const started = spawn(process.execPath, [...process.execArgv, entry], {
|
|
374
|
-
detached: true,
|
|
375
|
-
env: __spreadProps(__spreadValues({}, process.env), { DEBUG: (_a = process.env["DEBUG"]) != null ? _a : debugPattern(name) }),
|
|
376
|
-
stdio: "inherit"
|
|
377
|
-
});
|
|
378
|
-
child = started;
|
|
379
|
-
started.on("exit", (code, signal) => {
|
|
380
|
-
if (child !== started) {
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
child = null;
|
|
384
|
-
logger.error(`\u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0441\u0430\u043C (\u043A\u043E\u0434 ${String(code)}, \u0441\u0438\u0433\u043D\u0430\u043B ${String(signal)})`);
|
|
385
|
-
});
|
|
386
|
-
}
|
|
440
|
+
const app = appProcess(path3.join(root, SERVER_ENTRY));
|
|
387
441
|
function show(lines) {
|
|
388
442
|
process.stdout.write(`${lines.join("\n")}
|
|
389
443
|
`);
|
|
@@ -402,26 +456,27 @@ function devApp(options) {
|
|
|
402
456
|
show(helpLines());
|
|
403
457
|
}
|
|
404
458
|
if (key === "q") {
|
|
405
|
-
void stop().then(() => process.exit(0));
|
|
459
|
+
void app.stop().then(() => process.exit(0));
|
|
406
460
|
}
|
|
407
461
|
});
|
|
408
462
|
}
|
|
409
463
|
function rebuild() {
|
|
410
464
|
return __async(this, null, function* () {
|
|
465
|
+
var _a;
|
|
411
466
|
if (building) {
|
|
412
467
|
dirty = true;
|
|
413
|
-
return
|
|
468
|
+
return null;
|
|
414
469
|
}
|
|
415
470
|
building = true;
|
|
416
|
-
let
|
|
471
|
+
let built = null;
|
|
417
472
|
try {
|
|
418
473
|
do {
|
|
419
474
|
dirty = false;
|
|
420
|
-
yield stop();
|
|
475
|
+
yield app.stop();
|
|
421
476
|
try {
|
|
422
|
-
yield buildApp(__spreadProps(__spreadValues({}, options), { mode: "development" }));
|
|
423
|
-
start((yield describe()).name);
|
|
424
|
-
|
|
477
|
+
const result = yield buildApp(__spreadProps(__spreadValues({}, options), { mode: "development" }));
|
|
478
|
+
app.start({ DEBUG: (_a = process.env["DEBUG"]) != null ? _a : debugPattern((yield describe()).name) });
|
|
479
|
+
built = result;
|
|
425
480
|
} catch (error) {
|
|
426
481
|
logger.error("\u0441\u0431\u043E\u0440\u043A\u0430 \u043D\u0435 \u043F\u0440\u043E\u0448\u043B\u0430 \u2014 \u0441\u0435\u0440\u0432\u0438\u0441 \u041D\u0415 \u041F\u041E\u0414\u041D\u042F\u0422, \u0436\u0434\u0443 \u043F\u0440\u0430\u0432\u043A\u0438:", error);
|
|
427
482
|
}
|
|
@@ -429,38 +484,25 @@ function devApp(options) {
|
|
|
429
484
|
} finally {
|
|
430
485
|
building = false;
|
|
431
486
|
}
|
|
432
|
-
return
|
|
487
|
+
return built;
|
|
433
488
|
});
|
|
434
489
|
}
|
|
435
490
|
const startedAt = Date.now();
|
|
436
|
-
const
|
|
491
|
+
const address = yield rebuild();
|
|
437
492
|
const elapsed = Date.now() - startedAt;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
}
|
|
450
|
-
if (built) {
|
|
451
|
-
show(bannerLines(__spreadValues({ elapsedMs: elapsed, mode: "development", watched: WATCHED }, yield describe())));
|
|
493
|
+
watchSources(root, () => void rebuild());
|
|
494
|
+
if (address !== null) {
|
|
495
|
+
show(
|
|
496
|
+
bannerLines(__spreadValues({
|
|
497
|
+
base: address.base,
|
|
498
|
+
elapsedMs: elapsed,
|
|
499
|
+
mode: "development",
|
|
500
|
+
port: address.port,
|
|
501
|
+
watched: WATCHED
|
|
502
|
+
}, yield describe()))
|
|
503
|
+
);
|
|
452
504
|
}
|
|
453
505
|
listenForKeys();
|
|
454
|
-
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
455
|
-
process.once(signal, () => {
|
|
456
|
-
void stop().then(() => process.exit(0));
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
process.on("exit", () => {
|
|
460
|
-
if ((child == null ? void 0 : child.pid) !== void 0) {
|
|
461
|
-
signalGroup(child.pid, "SIGTERM");
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
506
|
});
|
|
465
507
|
}
|
|
466
508
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foxford/den",
|
|
3
|
-
"version": "3.1
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "Den — декларативный метафреймворк Foxford (core runtime)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"foxford",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"fastify": "^5.11.2",
|
|
27
|
-
"@foxford/
|
|
28
|
-
"@foxford/
|
|
27
|
+
"@foxford/ioc": "^1.1.2",
|
|
28
|
+
"@foxford/logger": "^1.2.2"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"vite": ">=5.0.0"
|
|
@@ -88,15 +88,17 @@
|
|
|
88
88
|
"chunk-A6ZPAM6Z.cjs",
|
|
89
89
|
"chunk-C7BM4DGX.cjs",
|
|
90
90
|
"chunk-E23E2VUC.js",
|
|
91
|
+
"chunk-KQAB6C6Q.cjs",
|
|
91
92
|
"chunk-MCN4GFEQ.js",
|
|
93
|
+
"chunk-RG7ZV5BD.js",
|
|
92
94
|
"chunk-T5QVCXVB.cjs",
|
|
93
95
|
"chunk-TPBI6TOU.js",
|
|
94
96
|
"chunk-TPHJUDHG.js",
|
|
97
|
+
"chunk-UB3GCX73.cjs",
|
|
98
|
+
"chunk-URSWYSZF.js",
|
|
95
99
|
"chunk-WBHHHICS.js",
|
|
96
100
|
"chunk-XOJ2VWX4.cjs",
|
|
97
|
-
"chunk-YECHY3NR.js",
|
|
98
101
|
"chunk-Z5BL4DJA.cjs",
|
|
99
|
-
"chunk-ZBB5SBEV.cjs",
|
|
100
102
|
"chunk-ZOU6W6ZQ.cjs",
|
|
101
103
|
"define-repository-CwAXed2X.d.cts",
|
|
102
104
|
"define-repository-q_T4hqeP.d.ts",
|
|
@@ -125,6 +127,6 @@
|
|
|
125
127
|
"view-adapter-B4gEb-ms.d.ts",
|
|
126
128
|
"view-adapter-DJ7yB6Ml.d.cts"
|
|
127
129
|
],
|
|
128
|
-
"sha": "
|
|
130
|
+
"sha": "57a2d9b",
|
|
129
131
|
"scripts": {}
|
|
130
132
|
}
|
package/serve/index.cjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
2
2
|
|
|
3
|
+
var _chunkKQAB6C6Qcjs = require('../chunk-KQAB6C6Q.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
3
6
|
|
|
4
7
|
|
|
5
8
|
var _chunkZOU6W6ZQcjs = require('../chunk-ZOU6W6ZQ.cjs');
|
|
@@ -16,6 +19,52 @@ var _chunkT5QVCXVBcjs = require('../chunk-T5QVCXVB.cjs');
|
|
|
16
19
|
var _fastify = require('fastify'); var _fastify2 = _interopRequireDefault(_fastify);
|
|
17
20
|
var _logger = require('@foxford/logger');
|
|
18
21
|
|
|
22
|
+
// src/serve/assets.ts
|
|
23
|
+
var _fs = require('fs');
|
|
24
|
+
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
25
|
+
var MIME = {
|
|
26
|
+
".css": "text/css",
|
|
27
|
+
".gif": "image/gif",
|
|
28
|
+
".jpeg": "image/jpeg",
|
|
29
|
+
".jpg": "image/jpeg",
|
|
30
|
+
".js": "text/javascript",
|
|
31
|
+
".json": "application/json",
|
|
32
|
+
".map": "application/json",
|
|
33
|
+
".mjs": "text/javascript",
|
|
34
|
+
".png": "image/png",
|
|
35
|
+
".svg": "image/svg+xml",
|
|
36
|
+
".ttf": "font/ttf",
|
|
37
|
+
".webp": "image/webp",
|
|
38
|
+
".woff": "font/woff",
|
|
39
|
+
".woff2": "font/woff2"
|
|
40
|
+
};
|
|
41
|
+
var HASHED_DIR = "assets/";
|
|
42
|
+
function assetsPrefix(base) {
|
|
43
|
+
const { pathname } = new URL(base, "http://assets.invalid");
|
|
44
|
+
return pathname.endsWith("/") ? pathname : `${pathname}/`;
|
|
45
|
+
}
|
|
46
|
+
function assetFile(dir, relative) {
|
|
47
|
+
const file = _path2.default.normalize(_path2.default.join(dir, relative));
|
|
48
|
+
return file.startsWith(_path2.default.normalize(dir)) ? file : null;
|
|
49
|
+
}
|
|
50
|
+
function registerAssets(instance, source) {
|
|
51
|
+
const dir = _path2.default.resolve(source.dir);
|
|
52
|
+
const prefix = assetsPrefix(source.base);
|
|
53
|
+
instance.get(`${prefix}*`, (request, reply) => {
|
|
54
|
+
var _a;
|
|
55
|
+
const relative = request.params["*"];
|
|
56
|
+
const file = assetFile(dir, relative);
|
|
57
|
+
if (file === null) {
|
|
58
|
+
return reply.code(404).send();
|
|
59
|
+
}
|
|
60
|
+
const stat = _fs.existsSync.call(void 0, file) ? _fs.statSync.call(void 0, file) : null;
|
|
61
|
+
if (stat === null || !stat.isFile()) {
|
|
62
|
+
return reply.code(404).send();
|
|
63
|
+
}
|
|
64
|
+
return reply.header("access-control-allow-origin", "*").header("cache-control", relative.startsWith(HASHED_DIR) ? "public, max-age=31536000, immutable" : "no-cache").header("content-length", stat.size).type((_a = MIME[_path2.default.extname(file)]) != null ? _a : "application/octet-stream").send(_fs.createReadStream.call(void 0, file));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
19
68
|
// src/serve/plugin.ts
|
|
20
69
|
|
|
21
70
|
var DEFAULT_SLOW_RENDER_MS = 1e3;
|
|
@@ -106,6 +155,9 @@ function serveApp(_0) {
|
|
|
106
155
|
const serveLog = (_b = appLog.getLogger("serve")) != null ? _b : appLog;
|
|
107
156
|
const instance = _fastify2.default.call(void 0, { logger: false });
|
|
108
157
|
yield instance.register(appPlugin, { app, slowRenderMs: options.slowRenderMs });
|
|
158
|
+
if (options.assets) {
|
|
159
|
+
registerAssets(instance, options.assets);
|
|
160
|
+
}
|
|
109
161
|
instance.addHook("onRequest", (req, _reply, done) => {
|
|
110
162
|
serveLog.debug(`\u2192 ${req.method} ${req.url}`);
|
|
111
163
|
done();
|
|
@@ -134,4 +186,6 @@ function serveApp(_0) {
|
|
|
134
186
|
|
|
135
187
|
|
|
136
188
|
|
|
137
|
-
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
exports.appPlugin = appPlugin; exports.loadAppEnv = _chunkKQAB6C6Qcjs.loadAppEnv; exports.registerAssets = registerAssets; exports.serveApp = serveApp;
|
package/serve/index.d.cts
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
|
+
import { FastifyInstance, FastifyPluginAsync } from 'fastify';
|
|
1
2
|
import { I as IslandDescriptor } from '../descriptor-Dujg_1on.cjs';
|
|
2
3
|
import { A as AppRoute } from '../routes-B0hDrkiF.cjs';
|
|
3
|
-
import { FastifyPluginAsync, FastifyInstance } from 'fastify';
|
|
4
4
|
import '../define-slot-DYNCLDJY.cjs';
|
|
5
5
|
import '../types-Dx5qGiwk.cjs';
|
|
6
6
|
import '@foxford/ioc';
|
|
7
7
|
|
|
8
|
+
/** Что раздаётся и по какому префиксу. */
|
|
9
|
+
interface AppAssetsSource {
|
|
10
|
+
/** Префикс адресов артефактов — тот, с которым собран клиентский выход. */
|
|
11
|
+
base: string;
|
|
12
|
+
/** Каталог клиентского выхода. */
|
|
13
|
+
dir: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Заводит раздачу артефактов на инстансе.
|
|
17
|
+
*
|
|
18
|
+
* @param instance - Инстанс fastify
|
|
19
|
+
* @param source - Префикс адресов и каталог выхода
|
|
20
|
+
*/
|
|
21
|
+
declare function registerAssets(instance: FastifyInstance, source: AppAssetsSource): void;
|
|
22
|
+
|
|
8
23
|
/** Приложение, которое можно поднять сервисом: остров плюс его заявка на адреса. */
|
|
9
24
|
type ServableApp = IslandDescriptor & {
|
|
10
25
|
routes?: ReadonlyArray<AppRoute>;
|
|
@@ -26,6 +41,15 @@ interface AppPluginOptions {
|
|
|
26
41
|
*/
|
|
27
42
|
declare const appPlugin: FastifyPluginAsync<AppPluginOptions>;
|
|
28
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Кладёт умолчания из `<root>/.env` в окружение процесса.
|
|
46
|
+
*
|
|
47
|
+
* Отсутствие файла — норма, а не отказ: он локальный и в репозиторий не попадает.
|
|
48
|
+
*
|
|
49
|
+
* @param root - Корень приложения
|
|
50
|
+
*/
|
|
51
|
+
declare function loadAppEnv(root: string): void;
|
|
52
|
+
|
|
29
53
|
/** Настройки инстанса. */
|
|
30
54
|
interface ServeAppOptions {
|
|
31
55
|
/** Порт; `0` — свободный, фактический смотри в `server.addresses()`. */
|
|
@@ -44,6 +68,13 @@ interface ServeAppOptions {
|
|
|
44
68
|
* который именно так и просит его уйти, а незакрытый инстанс рвёт запросы на середине.
|
|
45
69
|
*/
|
|
46
70
|
gracefulShutdown?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Клиентские артефакты приложения: префикс адресов и каталог выхода.
|
|
73
|
+
*
|
|
74
|
+
* Отдаёт их само приложение — адреса знает его сборка, а хост получает только ссылки. В
|
|
75
|
+
* проде перед процессом стоит CDN, и до него эти адреса не доходят.
|
|
76
|
+
*/
|
|
77
|
+
assets?: AppAssetsSource;
|
|
47
78
|
}
|
|
48
79
|
/**
|
|
49
80
|
* Поднимает приложение отдельным сервисом.
|
|
@@ -57,4 +88,4 @@ interface ServeAppOptions {
|
|
|
57
88
|
*/
|
|
58
89
|
declare function serveApp(app: ServableApp, options?: ServeAppOptions): Promise<FastifyInstance>;
|
|
59
90
|
|
|
60
|
-
export { type AppPluginOptions, type ServableApp, type ServeAppOptions, appPlugin, serveApp };
|
|
91
|
+
export { type AppAssetsSource, type AppPluginOptions, type ServableApp, type ServeAppOptions, appPlugin, loadAppEnv, registerAssets, serveApp };
|
package/serve/index.d.ts
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
|
+
import { FastifyInstance, FastifyPluginAsync } from 'fastify';
|
|
1
2
|
import { I as IslandDescriptor } from '../descriptor-DQ6Gi7A_.js';
|
|
2
3
|
import { A as AppRoute } from '../routes-0uXJ0fux.js';
|
|
3
|
-
import { FastifyPluginAsync, FastifyInstance } from 'fastify';
|
|
4
4
|
import '../define-slot-Cngzae6i.js';
|
|
5
5
|
import '../types-Dx5qGiwk.js';
|
|
6
6
|
import '@foxford/ioc';
|
|
7
7
|
|
|
8
|
+
/** Что раздаётся и по какому префиксу. */
|
|
9
|
+
interface AppAssetsSource {
|
|
10
|
+
/** Префикс адресов артефактов — тот, с которым собран клиентский выход. */
|
|
11
|
+
base: string;
|
|
12
|
+
/** Каталог клиентского выхода. */
|
|
13
|
+
dir: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Заводит раздачу артефактов на инстансе.
|
|
17
|
+
*
|
|
18
|
+
* @param instance - Инстанс fastify
|
|
19
|
+
* @param source - Префикс адресов и каталог выхода
|
|
20
|
+
*/
|
|
21
|
+
declare function registerAssets(instance: FastifyInstance, source: AppAssetsSource): void;
|
|
22
|
+
|
|
8
23
|
/** Приложение, которое можно поднять сервисом: остров плюс его заявка на адреса. */
|
|
9
24
|
type ServableApp = IslandDescriptor & {
|
|
10
25
|
routes?: ReadonlyArray<AppRoute>;
|
|
@@ -26,6 +41,15 @@ interface AppPluginOptions {
|
|
|
26
41
|
*/
|
|
27
42
|
declare const appPlugin: FastifyPluginAsync<AppPluginOptions>;
|
|
28
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Кладёт умолчания из `<root>/.env` в окружение процесса.
|
|
46
|
+
*
|
|
47
|
+
* Отсутствие файла — норма, а не отказ: он локальный и в репозиторий не попадает.
|
|
48
|
+
*
|
|
49
|
+
* @param root - Корень приложения
|
|
50
|
+
*/
|
|
51
|
+
declare function loadAppEnv(root: string): void;
|
|
52
|
+
|
|
29
53
|
/** Настройки инстанса. */
|
|
30
54
|
interface ServeAppOptions {
|
|
31
55
|
/** Порт; `0` — свободный, фактический смотри в `server.addresses()`. */
|
|
@@ -44,6 +68,13 @@ interface ServeAppOptions {
|
|
|
44
68
|
* который именно так и просит его уйти, а незакрытый инстанс рвёт запросы на середине.
|
|
45
69
|
*/
|
|
46
70
|
gracefulShutdown?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Клиентские артефакты приложения: префикс адресов и каталог выхода.
|
|
73
|
+
*
|
|
74
|
+
* Отдаёт их само приложение — адреса знает его сборка, а хост получает только ссылки. В
|
|
75
|
+
* проде перед процессом стоит CDN, и до него эти адреса не доходят.
|
|
76
|
+
*/
|
|
77
|
+
assets?: AppAssetsSource;
|
|
47
78
|
}
|
|
48
79
|
/**
|
|
49
80
|
* Поднимает приложение отдельным сервисом.
|
|
@@ -57,4 +88,4 @@ interface ServeAppOptions {
|
|
|
57
88
|
*/
|
|
58
89
|
declare function serveApp(app: ServableApp, options?: ServeAppOptions): Promise<FastifyInstance>;
|
|
59
90
|
|
|
60
|
-
export { type AppPluginOptions, type ServableApp, type ServeAppOptions, appPlugin, serveApp };
|
|
91
|
+
export { type AppAssetsSource, type AppPluginOptions, type ServableApp, type ServeAppOptions, appPlugin, loadAppEnv, registerAssets, serveApp };
|
package/serve/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadAppEnv
|
|
3
|
+
} from "../chunk-RG7ZV5BD.js";
|
|
1
4
|
import {
|
|
2
5
|
matchRoute,
|
|
3
6
|
normalizeRoutes,
|
|
@@ -16,6 +19,52 @@ import {
|
|
|
16
19
|
import Fastify from "fastify";
|
|
17
20
|
import { log as log2 } from "@foxford/logger";
|
|
18
21
|
|
|
22
|
+
// src/serve/assets.ts
|
|
23
|
+
import { createReadStream, existsSync, statSync } from "fs";
|
|
24
|
+
import path from "path";
|
|
25
|
+
var MIME = {
|
|
26
|
+
".css": "text/css",
|
|
27
|
+
".gif": "image/gif",
|
|
28
|
+
".jpeg": "image/jpeg",
|
|
29
|
+
".jpg": "image/jpeg",
|
|
30
|
+
".js": "text/javascript",
|
|
31
|
+
".json": "application/json",
|
|
32
|
+
".map": "application/json",
|
|
33
|
+
".mjs": "text/javascript",
|
|
34
|
+
".png": "image/png",
|
|
35
|
+
".svg": "image/svg+xml",
|
|
36
|
+
".ttf": "font/ttf",
|
|
37
|
+
".webp": "image/webp",
|
|
38
|
+
".woff": "font/woff",
|
|
39
|
+
".woff2": "font/woff2"
|
|
40
|
+
};
|
|
41
|
+
var HASHED_DIR = "assets/";
|
|
42
|
+
function assetsPrefix(base) {
|
|
43
|
+
const { pathname } = new URL(base, "http://assets.invalid");
|
|
44
|
+
return pathname.endsWith("/") ? pathname : `${pathname}/`;
|
|
45
|
+
}
|
|
46
|
+
function assetFile(dir, relative) {
|
|
47
|
+
const file = path.normalize(path.join(dir, relative));
|
|
48
|
+
return file.startsWith(path.normalize(dir)) ? file : null;
|
|
49
|
+
}
|
|
50
|
+
function registerAssets(instance, source) {
|
|
51
|
+
const dir = path.resolve(source.dir);
|
|
52
|
+
const prefix = assetsPrefix(source.base);
|
|
53
|
+
instance.get(`${prefix}*`, (request, reply) => {
|
|
54
|
+
var _a;
|
|
55
|
+
const relative = request.params["*"];
|
|
56
|
+
const file = assetFile(dir, relative);
|
|
57
|
+
if (file === null) {
|
|
58
|
+
return reply.code(404).send();
|
|
59
|
+
}
|
|
60
|
+
const stat = existsSync(file) ? statSync(file) : null;
|
|
61
|
+
if (stat === null || !stat.isFile()) {
|
|
62
|
+
return reply.code(404).send();
|
|
63
|
+
}
|
|
64
|
+
return reply.header("access-control-allow-origin", "*").header("cache-control", relative.startsWith(HASHED_DIR) ? "public, max-age=31536000, immutable" : "no-cache").header("content-length", stat.size).type((_a = MIME[path.extname(file)]) != null ? _a : "application/octet-stream").send(createReadStream(file));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
19
68
|
// src/serve/plugin.ts
|
|
20
69
|
import { log } from "@foxford/logger";
|
|
21
70
|
var DEFAULT_SLOW_RENDER_MS = 1e3;
|
|
@@ -106,6 +155,9 @@ function serveApp(_0) {
|
|
|
106
155
|
const serveLog = (_b = appLog.getLogger("serve")) != null ? _b : appLog;
|
|
107
156
|
const instance = Fastify({ logger: false });
|
|
108
157
|
yield instance.register(appPlugin, { app, slowRenderMs: options.slowRenderMs });
|
|
158
|
+
if (options.assets) {
|
|
159
|
+
registerAssets(instance, options.assets);
|
|
160
|
+
}
|
|
109
161
|
instance.addHook("onRequest", (req, _reply, done) => {
|
|
110
162
|
serveLog.debug(`\u2192 ${req.method} ${req.url}`);
|
|
111
163
|
done();
|
|
@@ -133,5 +185,7 @@ function serveApp(_0) {
|
|
|
133
185
|
}
|
|
134
186
|
export {
|
|
135
187
|
appPlugin,
|
|
188
|
+
loadAppEnv,
|
|
189
|
+
registerAssets,
|
|
136
190
|
serveApp
|
|
137
191
|
};
|