@angular-devkit/build-angular 17.0.0-rc.1 → 17.0.0-rc.3
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/package.json +11 -10
- package/src/builders/app-shell/render-worker.d.ts +1 -1
- package/src/builders/app-shell/render-worker.js +16 -9
- package/src/builders/application/build-action.js +5 -2
- package/src/builders/application/execute-build.js +28 -18
- package/src/builders/browser-esbuild/index.js +9 -1
- package/src/builders/dev-server/vite-server.js +59 -20
- package/src/builders/extract-i18n/application-extraction.js +1 -0
- package/src/builders/prerender/routes-extractor-worker.js +1 -1
- package/src/tools/babel/plugins/elide-angular-metadata.js +14 -2
- package/src/tools/esbuild/angular/compiler-plugin.js +2 -1
- package/src/tools/esbuild/application-code-bundle.d.ts +3 -2
- package/src/tools/esbuild/application-code-bundle.js +8 -12
- package/src/tools/esbuild/bundler-context.d.ts +1 -1
- package/src/tools/esbuild/bundler-context.js +7 -2
- package/src/tools/esbuild/bundler-execution-result.d.ts +2 -0
- package/src/tools/esbuild/bundler-execution-result.js +14 -0
- package/src/tools/esbuild/commonjs-checker.js +3 -3
- package/src/tools/esbuild/global-scripts.d.ts +2 -3
- package/src/tools/esbuild/global-scripts.js +72 -69
- package/src/tools/esbuild/global-styles.d.ts +2 -3
- package/src/tools/esbuild/global-styles.js +35 -33
- package/src/tools/esbuild/javascript-transformer-worker.js +3 -2
- package/src/tools/esbuild/javascript-transformer.d.ts +1 -0
- package/src/tools/esbuild/javascript-transformer.js +25 -13
- package/src/tools/esbuild/utils.d.ts +1 -1
- package/src/tools/esbuild/utils.js +18 -4
- package/src/tools/esbuild/watcher.d.ts +1 -0
- package/src/tools/esbuild/watcher.js +3 -0
- package/src/utils/check-port.js +3 -1
- package/src/utils/environment-options.d.ts +1 -0
- package/src/utils/environment-options.js +3 -1
- package/src/utils/load-esm.d.ts +0 -2
- package/src/utils/routes-extractor/extractor.d.ts +1 -1
- package/src/utils/routes-extractor/extractor.js +2 -2
- package/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.js +3 -2
- package/src/utils/server-rendering/fetch-patch.d.ts +8 -0
- package/src/utils/server-rendering/fetch-patch.js +66 -0
- package/src/utils/server-rendering/prerender.js +25 -30
- package/src/utils/server-rendering/render-worker.d.ts +4 -2
- package/src/utils/server-rendering/render-worker.js +8 -4
- package/src/utils/server-rendering/routes-extractor-worker.d.ts +5 -3
- package/src/utils/server-rendering/routes-extractor-worker.js +10 -4
- package/src/tools/esbuild/javascript-transfomer-plugin.d.ts +0 -19
- package/src/tools/esbuild/javascript-transfomer-plugin.js +0 -51
- package/src/utils/server-rendering/prerender-server.d.ts +0 -21
- package/src/utils/server-rendering/prerender-server.js +0 -102
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.io/license
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.patchFetchToLoadInMemoryAssets = void 0;
|
|
11
|
+
const mrmime_1 = require("mrmime");
|
|
12
|
+
const promises_1 = require("node:fs/promises");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const node_worker_threads_1 = require("node:worker_threads");
|
|
15
|
+
const undici_1 = require("undici");
|
|
16
|
+
/**
|
|
17
|
+
* This is passed as workerData when setting up the worker via the `piscina` package.
|
|
18
|
+
*/
|
|
19
|
+
const { assetFiles } = node_worker_threads_1.workerData;
|
|
20
|
+
const assetsCache = new Map();
|
|
21
|
+
const RESOLVE_PROTOCOL = 'resolve:';
|
|
22
|
+
function patchFetchToLoadInMemoryAssets() {
|
|
23
|
+
const global = globalThis;
|
|
24
|
+
const originalFetch = global.fetch;
|
|
25
|
+
const patchedFetch = async (input, init) => {
|
|
26
|
+
let url;
|
|
27
|
+
if (input instanceof URL) {
|
|
28
|
+
url = input;
|
|
29
|
+
}
|
|
30
|
+
else if (typeof input === 'string') {
|
|
31
|
+
url = new URL(input, RESOLVE_PROTOCOL + '//');
|
|
32
|
+
}
|
|
33
|
+
else if (typeof input === 'object' && 'url' in input) {
|
|
34
|
+
url = new URL(input.url, RESOLVE_PROTOCOL + '//');
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return originalFetch(input, init);
|
|
38
|
+
}
|
|
39
|
+
const { pathname, protocol } = url;
|
|
40
|
+
if (protocol !== RESOLVE_PROTOCOL || !assetFiles[pathname]) {
|
|
41
|
+
// Only handle relative requests or files that are in assets.
|
|
42
|
+
return originalFetch(input, init);
|
|
43
|
+
}
|
|
44
|
+
const cachedAsset = assetsCache.get(pathname);
|
|
45
|
+
if (cachedAsset) {
|
|
46
|
+
const { content, headers } = cachedAsset;
|
|
47
|
+
return new undici_1.Response(content, {
|
|
48
|
+
headers,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const extension = (0, node_path_1.extname)(pathname);
|
|
52
|
+
const mimeType = (0, mrmime_1.lookup)(extension);
|
|
53
|
+
const content = await (0, promises_1.readFile)(assetFiles[pathname]);
|
|
54
|
+
const headers = mimeType
|
|
55
|
+
? {
|
|
56
|
+
'Content-Type': mimeType,
|
|
57
|
+
}
|
|
58
|
+
: undefined;
|
|
59
|
+
assetsCache.set(pathname, { headers, content });
|
|
60
|
+
return new undici_1.Response(content, {
|
|
61
|
+
headers,
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
global.fetch = patchedFetch;
|
|
65
|
+
}
|
|
66
|
+
exports.patchFetchToLoadInMemoryAssets = patchFetchToLoadInMemoryAssets;
|
|
@@ -16,7 +16,6 @@ const node_path_1 = require("node:path");
|
|
|
16
16
|
const piscina_1 = __importDefault(require("piscina"));
|
|
17
17
|
const bundler_context_1 = require("../../tools/esbuild/bundler-context");
|
|
18
18
|
const node_18_utils_1 = require("./esm-in-memory-loader/node-18-utils");
|
|
19
|
-
const prerender_server_1 = require("./prerender-server");
|
|
20
19
|
async function prerenderPages(workspaceRoot, appShellOptions = {}, prerenderOptions = {}, outputFiles, assets, document, sourcemap = false, inlineCriticalCss = false, maxThreads = 1, verbose = false) {
|
|
21
20
|
const outputFilesForWorker = {};
|
|
22
21
|
const serverBundlesSourceMaps = new Map();
|
|
@@ -45,37 +44,33 @@ async function prerenderPages(workspaceRoot, appShellOptions = {}, prerenderOpti
|
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
serverBundlesSourceMaps.clear();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
errors,
|
|
60
|
-
warnings,
|
|
61
|
-
output: {},
|
|
62
|
-
prerenderedRoutes: allRoutes,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
// Render routes
|
|
66
|
-
const { warnings: renderingWarnings, errors: renderingErrors, output, } = await renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, inlineCriticalCss, document, assetsServerAddress, appShellOptions);
|
|
67
|
-
errors.push(...renderingErrors);
|
|
68
|
-
warnings.push(...renderingWarnings);
|
|
47
|
+
const assetsReversed = {};
|
|
48
|
+
for (const { source, destination } of assets) {
|
|
49
|
+
assetsReversed[addLeadingSlash(destination.replace(/\\/g, node_path_1.posix.sep))] = source;
|
|
50
|
+
}
|
|
51
|
+
// Get routes to prerender
|
|
52
|
+
const { routes: allRoutes, warnings: routesWarnings } = await getAllRoutes(workspaceRoot, outputFilesForWorker, assetsReversed, document, appShellOptions, prerenderOptions, sourcemap, verbose);
|
|
53
|
+
if (routesWarnings?.length) {
|
|
54
|
+
warnings.push(...routesWarnings);
|
|
55
|
+
}
|
|
56
|
+
if (allRoutes.size < 1) {
|
|
69
57
|
return {
|
|
70
58
|
errors,
|
|
71
59
|
warnings,
|
|
72
|
-
output,
|
|
60
|
+
output: {},
|
|
73
61
|
prerenderedRoutes: allRoutes,
|
|
74
62
|
};
|
|
75
63
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
// Render routes
|
|
65
|
+
const { warnings: renderingWarnings, errors: renderingErrors, output, } = await renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetsReversed, inlineCriticalCss, document, appShellOptions);
|
|
66
|
+
errors.push(...renderingErrors);
|
|
67
|
+
warnings.push(...renderingWarnings);
|
|
68
|
+
return {
|
|
69
|
+
errors,
|
|
70
|
+
warnings,
|
|
71
|
+
output,
|
|
72
|
+
prerenderedRoutes: allRoutes,
|
|
73
|
+
};
|
|
79
74
|
}
|
|
80
75
|
exports.prerenderPages = prerenderPages;
|
|
81
76
|
class RoutesSet extends Set {
|
|
@@ -83,7 +78,7 @@ class RoutesSet extends Set {
|
|
|
83
78
|
return super.add(addLeadingSlash(value));
|
|
84
79
|
}
|
|
85
80
|
}
|
|
86
|
-
async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, inlineCriticalCss, document,
|
|
81
|
+
async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outputFilesForWorker, assetFilesForWorker, inlineCriticalCss, document, appShellOptions) {
|
|
87
82
|
const output = {};
|
|
88
83
|
const warnings = [];
|
|
89
84
|
const errors = [];
|
|
@@ -97,9 +92,9 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
|
|
|
97
92
|
workerData: {
|
|
98
93
|
workspaceRoot,
|
|
99
94
|
outputFiles: outputFilesForWorker,
|
|
95
|
+
assetFiles: assetFilesForWorker,
|
|
100
96
|
inlineCriticalCss,
|
|
101
97
|
document,
|
|
102
|
-
baseUrl,
|
|
103
98
|
},
|
|
104
99
|
execArgv: workerExecArgv,
|
|
105
100
|
});
|
|
@@ -139,7 +134,7 @@ async function renderPages(sourcemap, allRoutes, maxThreads, workspaceRoot, outp
|
|
|
139
134
|
output,
|
|
140
135
|
};
|
|
141
136
|
}
|
|
142
|
-
async function getAllRoutes(workspaceRoot, outputFilesForWorker, document, appShellOptions, prerenderOptions, sourcemap, verbose
|
|
137
|
+
async function getAllRoutes(workspaceRoot, outputFilesForWorker, assetFilesForWorker, document, appShellOptions, prerenderOptions, sourcemap, verbose) {
|
|
143
138
|
const { routesFile, discoverRoutes } = prerenderOptions;
|
|
144
139
|
const routes = new RoutesSet();
|
|
145
140
|
const { route: appShellRoute } = appShellOptions;
|
|
@@ -165,9 +160,9 @@ async function getAllRoutes(workspaceRoot, outputFilesForWorker, document, appSh
|
|
|
165
160
|
workerData: {
|
|
166
161
|
workspaceRoot,
|
|
167
162
|
outputFiles: outputFilesForWorker,
|
|
163
|
+
assetFiles: assetFilesForWorker,
|
|
168
164
|
document,
|
|
169
165
|
verbose,
|
|
170
|
-
url: assetsServerAddress,
|
|
171
166
|
},
|
|
172
167
|
execArgv: workerExecArgv,
|
|
173
168
|
});
|
|
@@ -10,11 +10,13 @@ import { RenderResult, ServerContext } from './render-page';
|
|
|
10
10
|
export interface RenderWorkerData extends ESMInMemoryFileLoaderWorkerData {
|
|
11
11
|
document: string;
|
|
12
12
|
inlineCriticalCss?: boolean;
|
|
13
|
-
|
|
13
|
+
assetFiles: Record</** Destination */ string, /** Source */ string>;
|
|
14
14
|
}
|
|
15
15
|
export interface RenderOptions {
|
|
16
16
|
route: string;
|
|
17
17
|
serverContext: ServerContext;
|
|
18
18
|
}
|
|
19
19
|
/** Renders an application based on a provided options. */
|
|
20
|
-
|
|
20
|
+
declare function render(options: RenderOptions): Promise<RenderResult>;
|
|
21
|
+
declare const _default: typeof render;
|
|
22
|
+
export default _default;
|
|
@@ -8,19 +8,23 @@
|
|
|
8
8
|
*/
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
const node_worker_threads_1 = require("node:worker_threads");
|
|
11
|
+
const fetch_patch_1 = require("./fetch-patch");
|
|
11
12
|
const render_page_1 = require("./render-page");
|
|
12
13
|
/**
|
|
13
14
|
* This is passed as workerData when setting up the worker via the `piscina` package.
|
|
14
15
|
*/
|
|
15
|
-
const { outputFiles, document, inlineCriticalCss
|
|
16
|
+
const { outputFiles, document, inlineCriticalCss } = node_worker_threads_1.workerData;
|
|
16
17
|
/** Renders an application based on a provided options. */
|
|
17
|
-
function
|
|
18
|
+
async function render(options) {
|
|
18
19
|
return (0, render_page_1.renderPage)({
|
|
19
20
|
...options,
|
|
20
|
-
route: baseUrl + options.route,
|
|
21
21
|
outputFiles,
|
|
22
22
|
document,
|
|
23
23
|
inlineCriticalCss,
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
function initialize() {
|
|
27
|
+
(0, fetch_patch_1.patchFetchToLoadInMemoryAssets)();
|
|
28
|
+
return render;
|
|
29
|
+
}
|
|
30
|
+
exports.default = initialize();
|
|
@@ -9,11 +9,13 @@ import type { ESMInMemoryFileLoaderWorkerData } from './esm-in-memory-loader/loa
|
|
|
9
9
|
export interface RoutesExtractorWorkerData extends ESMInMemoryFileLoaderWorkerData {
|
|
10
10
|
document: string;
|
|
11
11
|
verbose: boolean;
|
|
12
|
-
|
|
13
|
-
assetsServerAddress: string;
|
|
12
|
+
assetFiles: Record</** Destination */ string, /** Source */ string>;
|
|
14
13
|
}
|
|
15
14
|
export interface RoutersExtractorWorkerResult {
|
|
16
15
|
routes: string[];
|
|
17
16
|
warnings?: string[];
|
|
18
17
|
}
|
|
19
|
-
|
|
18
|
+
/** Renders an application based on a provided options. */
|
|
19
|
+
declare function extractRoutes(): Promise<RoutersExtractorWorkerResult>;
|
|
20
|
+
declare const _default: typeof extractRoutes;
|
|
21
|
+
export default _default;
|
|
@@ -9,17 +9,19 @@
|
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
const node_worker_threads_1 = require("node:worker_threads");
|
|
11
11
|
const load_esm_1 = require("../load-esm");
|
|
12
|
+
const fetch_patch_1 = require("./fetch-patch");
|
|
12
13
|
/**
|
|
13
14
|
* This is passed as workerData when setting up the worker via the `piscina` package.
|
|
14
15
|
*/
|
|
15
|
-
const { document, verbose
|
|
16
|
-
|
|
16
|
+
const { document, verbose } = node_worker_threads_1.workerData;
|
|
17
|
+
/** Renders an application based on a provided options. */
|
|
18
|
+
async function extractRoutes() {
|
|
17
19
|
const { extractRoutes } = await (0, load_esm_1.loadEsmModule)('./render-utils.server.mjs');
|
|
18
20
|
const { default: bootstrapAppFnOrModule } = await (0, load_esm_1.loadEsmModule)('./main.server.mjs');
|
|
19
21
|
const skippedRedirects = [];
|
|
20
22
|
const skippedOthers = [];
|
|
21
23
|
const routes = [];
|
|
22
|
-
for await (const { route, success, redirect } of extractRoutes(bootstrapAppFnOrModule, document
|
|
24
|
+
for await (const { route, success, redirect } of extractRoutes(bootstrapAppFnOrModule, document)) {
|
|
23
25
|
if (success) {
|
|
24
26
|
routes.push(route);
|
|
25
27
|
continue;
|
|
@@ -44,4 +46,8 @@ async function default_1() {
|
|
|
44
46
|
}
|
|
45
47
|
return { routes, warnings };
|
|
46
48
|
}
|
|
47
|
-
|
|
49
|
+
function initialize() {
|
|
50
|
+
(0, fetch_patch_1.patchFetchToLoadInMemoryAssets)();
|
|
51
|
+
return extractRoutes;
|
|
52
|
+
}
|
|
53
|
+
exports.default = initialize();
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright Google LLC All Rights Reserved.
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
-
* found in the LICENSE file at https://angular.io/license
|
|
7
|
-
*/
|
|
8
|
-
import type { Plugin } from 'esbuild';
|
|
9
|
-
import { JavaScriptTransformerOptions } from './javascript-transformer';
|
|
10
|
-
export interface JavaScriptTransformerPluginOptions extends JavaScriptTransformerOptions {
|
|
11
|
-
babelFileCache?: Map<string, Uint8Array>;
|
|
12
|
-
maxWorkers: number;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Creates a plugin that Transformers JavaScript using Babel.
|
|
16
|
-
*
|
|
17
|
-
* @returns An esbuild plugin.
|
|
18
|
-
*/
|
|
19
|
-
export declare function createJavaScriptTransformerPlugin(options: JavaScriptTransformerPluginOptions): Plugin;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @license
|
|
4
|
-
* Copyright Google LLC All Rights Reserved.
|
|
5
|
-
*
|
|
6
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
-
* found in the LICENSE file at https://angular.io/license
|
|
8
|
-
*/
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.createJavaScriptTransformerPlugin = void 0;
|
|
11
|
-
const javascript_transformer_1 = require("./javascript-transformer");
|
|
12
|
-
/**
|
|
13
|
-
* Creates a plugin that Transformers JavaScript using Babel.
|
|
14
|
-
*
|
|
15
|
-
* @returns An esbuild plugin.
|
|
16
|
-
*/
|
|
17
|
-
function createJavaScriptTransformerPlugin(options) {
|
|
18
|
-
return {
|
|
19
|
-
name: 'angular-javascript-transformer',
|
|
20
|
-
setup(build) {
|
|
21
|
-
let javascriptTransformer;
|
|
22
|
-
const { sourcemap, thirdPartySourcemaps, advancedOptimizations, jit, babelFileCache, maxWorkers, } = options;
|
|
23
|
-
build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
|
|
24
|
-
// The filename is currently used as a cache key. Since the cache is memory only,
|
|
25
|
-
// the options cannot change and do not need to be represented in the key. If the
|
|
26
|
-
// cache is later stored to disk, then the options that affect transform output
|
|
27
|
-
// would need to be added to the key as well as a check for any change of content.
|
|
28
|
-
let contents = babelFileCache?.get(args.path);
|
|
29
|
-
if (contents === undefined) {
|
|
30
|
-
// Initialize a worker pool for JavaScript transformations
|
|
31
|
-
javascriptTransformer ??= new javascript_transformer_1.JavaScriptTransformer({
|
|
32
|
-
sourcemap,
|
|
33
|
-
thirdPartySourcemaps,
|
|
34
|
-
advancedOptimizations,
|
|
35
|
-
jit,
|
|
36
|
-
}, maxWorkers);
|
|
37
|
-
contents = await javascriptTransformer.transformFile(args.path, jit);
|
|
38
|
-
babelFileCache?.set(args.path, contents);
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
contents,
|
|
42
|
-
loader: 'js',
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
build.onDispose(() => {
|
|
46
|
-
void javascriptTransformer?.close();
|
|
47
|
-
});
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
exports.createJavaScriptTransformerPlugin = createJavaScriptTransformerPlugin;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright Google LLC All Rights Reserved.
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
-
* found in the LICENSE file at https://angular.io/license
|
|
7
|
-
*/
|
|
8
|
-
import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result';
|
|
9
|
-
/**
|
|
10
|
-
* Start a server that can handle HTTP requests to assets.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* httpClient.get('/assets/content.json');
|
|
15
|
-
* ```
|
|
16
|
-
* @returns the server address.
|
|
17
|
-
*/
|
|
18
|
-
export declare function startServer(assets: Readonly<BuildOutputAsset[]>): Promise<{
|
|
19
|
-
address: string;
|
|
20
|
-
close?: () => void;
|
|
21
|
-
}>;
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @license
|
|
4
|
-
* Copyright Google LLC All Rights Reserved.
|
|
5
|
-
*
|
|
6
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
-
* found in the LICENSE file at https://angular.io/license
|
|
8
|
-
*/
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.startServer = void 0;
|
|
11
|
-
const mrmime_1 = require("mrmime");
|
|
12
|
-
const promises_1 = require("node:fs/promises");
|
|
13
|
-
const node_http_1 = require("node:http");
|
|
14
|
-
const node_path_1 = require("node:path");
|
|
15
|
-
/**
|
|
16
|
-
* Start a server that can handle HTTP requests to assets.
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```ts
|
|
20
|
-
* httpClient.get('/assets/content.json');
|
|
21
|
-
* ```
|
|
22
|
-
* @returns the server address.
|
|
23
|
-
*/
|
|
24
|
-
async function startServer(assets) {
|
|
25
|
-
if (Object.keys(assets).length === 0) {
|
|
26
|
-
return {
|
|
27
|
-
address: '',
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
const assetsReversed = {};
|
|
31
|
-
for (const { source, destination } of assets) {
|
|
32
|
-
assetsReversed[addLeadingSlash(destination.replace(/\\/g, node_path_1.posix.sep))] = source;
|
|
33
|
-
}
|
|
34
|
-
const assetsCache = new Map();
|
|
35
|
-
const server = (0, node_http_1.createServer)(requestHandler(assetsReversed, assetsCache));
|
|
36
|
-
await new Promise((resolve) => {
|
|
37
|
-
server.listen(0, '127.0.0.1', resolve);
|
|
38
|
-
});
|
|
39
|
-
const serverAddress = server.address();
|
|
40
|
-
let address;
|
|
41
|
-
if (!serverAddress) {
|
|
42
|
-
address = '';
|
|
43
|
-
}
|
|
44
|
-
else if (typeof serverAddress === 'string') {
|
|
45
|
-
address = serverAddress;
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
const { port, address: host } = serverAddress;
|
|
49
|
-
address = `http://${host}:${port}`;
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
address,
|
|
53
|
-
close: () => {
|
|
54
|
-
assetsCache.clear();
|
|
55
|
-
server.unref();
|
|
56
|
-
server.close();
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
exports.startServer = startServer;
|
|
61
|
-
function requestHandler(assetsReversed, assetsCache) {
|
|
62
|
-
return (req, res) => {
|
|
63
|
-
if (!req.url) {
|
|
64
|
-
res.destroy(new Error('Request url was empty.'));
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
const { pathname } = new URL(req.url, 'resolve://');
|
|
68
|
-
const asset = assetsReversed[pathname];
|
|
69
|
-
if (!asset) {
|
|
70
|
-
res.statusCode = 404;
|
|
71
|
-
res.statusMessage = 'Asset not found.';
|
|
72
|
-
res.end();
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const cachedAsset = assetsCache.get(pathname);
|
|
76
|
-
if (cachedAsset) {
|
|
77
|
-
const { content, mimeType } = cachedAsset;
|
|
78
|
-
if (mimeType) {
|
|
79
|
-
res.setHeader('Content-Type', mimeType);
|
|
80
|
-
}
|
|
81
|
-
res.end(content);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
(0, promises_1.readFile)(asset)
|
|
85
|
-
.then((content) => {
|
|
86
|
-
const extension = (0, node_path_1.extname)(pathname);
|
|
87
|
-
const mimeType = (0, mrmime_1.lookup)(extension);
|
|
88
|
-
assetsCache.set(pathname, {
|
|
89
|
-
mimeType,
|
|
90
|
-
content,
|
|
91
|
-
});
|
|
92
|
-
if (mimeType) {
|
|
93
|
-
res.setHeader('Content-Type', mimeType);
|
|
94
|
-
}
|
|
95
|
-
res.end(content);
|
|
96
|
-
})
|
|
97
|
-
.catch((e) => res.destroy(e));
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
function addLeadingSlash(value) {
|
|
101
|
-
return value.charAt(0) === '/' ? value : '/' + value;
|
|
102
|
-
}
|