@lwrjs/core 0.12.0-alpha.2 → 0.12.0-alpha.21
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/build/cjs/context/provider.cjs +7 -2
- package/build/cjs/index.cjs +19 -25
- package/build/cjs/info/route-handler.cjs +149 -0
- package/build/cjs/middleware/asset-middleware.cjs +41 -28
- package/build/cjs/middleware/bundle-middleware.cjs +7 -1
- package/build/cjs/middleware/mapping-middleware.cjs +4 -1
- package/build/cjs/middleware/module-middleware.cjs +7 -1
- package/build/cjs/middleware/request-processor-middleware.cjs +15 -1
- package/build/cjs/middleware/utils/error-handling.cjs +1 -1
- package/build/cjs/middleware/utils/identity.cjs +1 -1
- package/build/cjs/middleware/view-middleware.cjs +8 -2
- package/build/cjs/tools/static-generation.cjs +41 -6
- package/build/es/context/provider.js +7 -1
- package/build/es/index.d.ts +12 -5
- package/build/es/index.js +21 -28
- package/build/es/info/route-handler.d.ts +3 -0
- package/build/es/info/route-handler.js +123 -0
- package/build/es/middleware/asset-middleware.js +60 -33
- package/build/es/middleware/bundle-middleware.js +7 -0
- package/build/es/middleware/mapping-middleware.js +3 -0
- package/build/es/middleware/module-middleware.js +7 -0
- package/build/es/middleware/request-processor-middleware.js +17 -1
- package/build/es/middleware/utils/error-handling.js +1 -1
- package/build/es/middleware/utils/identity.js +5 -1
- package/build/es/middleware/view-middleware.js +7 -1
- package/build/es/tools/static-generation.d.ts +4 -0
- package/build/es/tools/static-generation.js +47 -11
- package/package.json +41 -35
package/build/es/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getFeatureFlags, DEFAULT_LWR_BOOTSTRAP_CONFIG } from '@lwrjs/shared-utils';
|
|
2
2
|
import { createInternalServer } from '@lwrjs/server';
|
|
3
3
|
import { LwrServerError, createSingleDiagnosticError, descriptions, logger } from '@lwrjs/diagnostics';
|
|
4
|
-
import { loadConfig, executeConfigHooks, executeStartHooks, executeInstrumentationHooks, } from '@lwrjs/config';
|
|
4
|
+
import { loadConfig, executeConfigHooks, executeStartHooks, executeInstrumentationHooks, executeContextHooks, } from '@lwrjs/config';
|
|
5
5
|
import { loadHooks, loadServices, loadRouteHandlers } from '@lwrjs/config/modules';
|
|
6
6
|
import SiteGenerator from './tools/static-generation.js';
|
|
7
7
|
import { warmupServer } from './tools/server-warmup.js';
|
|
@@ -47,12 +47,18 @@ async function initContext(appConfig, runtimeEnvironment, globalData) {
|
|
|
47
47
|
const skipValidation = true; // skip for config hook, since `executeStartHooks` hook will validate
|
|
48
48
|
await executeConfigHooks(hooks, appConfig, runtimeEnvironment, globalData, skipValidation);
|
|
49
49
|
executeInstrumentationHooks(hooks);
|
|
50
|
-
executeStartHooks(hooks, appConfig, runtimeEnvironment);
|
|
51
50
|
}
|
|
52
51
|
// load all configurable modules
|
|
53
52
|
const services = await loadServices(appConfig);
|
|
54
53
|
// create all framework components(ie. registries)
|
|
55
54
|
const serverContext = getTracer().trace({ name: CoreSpan.CreateServerContext }, () => createServerContext(appConfig, runtimeEnvironment, globalData));
|
|
55
|
+
// set routes on server context
|
|
56
|
+
const routeHandlers = await loadRouteHandlers(appConfig);
|
|
57
|
+
serverContext.routeHandlers = routeHandlers;
|
|
58
|
+
if (hooks.length) {
|
|
59
|
+
await executeContextHooks(hooks, serverContext);
|
|
60
|
+
executeStartHooks(hooks, appConfig, runtimeEnvironment);
|
|
61
|
+
}
|
|
56
62
|
// create public subset of configurations
|
|
57
63
|
const providerContext = createProviderContext(serverContext);
|
|
58
64
|
const { moduleRegistry, assetRegistry, resourceRegistry, viewRegistry, moduleBundler } = serverContext;
|
|
@@ -82,30 +88,26 @@ async function initContext(appConfig, runtimeEnvironment, globalData) {
|
|
|
82
88
|
viewRegistry.addViewTransformers(viewTransformers);
|
|
83
89
|
// invoke async initialization
|
|
84
90
|
await serverContext.viewRegistry.initializeViewProviders();
|
|
85
|
-
// set routes on server context
|
|
86
|
-
const routeHandlers = await loadRouteHandlers(appConfig);
|
|
87
|
-
serverContext.routeHandlers = routeHandlers;
|
|
88
91
|
return serverContext;
|
|
89
92
|
}
|
|
90
93
|
export class LwrApp {
|
|
91
|
-
constructor(
|
|
94
|
+
constructor(configs) {
|
|
92
95
|
this.initialized = false;
|
|
93
96
|
const span = getTracer().startSpan({ name: CoreSpan.CreateServer });
|
|
94
|
-
const { appConfig, runtimeEnvironment, globalData } =
|
|
97
|
+
const { appConfig, runtimeEnvironment, globalData } = configs;
|
|
95
98
|
this.config = appConfig;
|
|
96
99
|
this.runtimeEnvironment = runtimeEnvironment;
|
|
97
100
|
this.globalData = globalData;
|
|
98
|
-
const { basePath } = this.config;
|
|
99
|
-
this.
|
|
101
|
+
const { basePath, serverType } = this.config;
|
|
102
|
+
this.serverType = serverType;
|
|
103
|
+
this.app = createInternalServer(serverType, { basePath });
|
|
100
104
|
this.server = this.app.createHttpServer();
|
|
105
|
+
this.use = this.app.use.bind(this.app);
|
|
106
|
+
this.all = this.app.all.bind(this.app);
|
|
107
|
+
this.get = this.app.get.bind(this.app);
|
|
108
|
+
this.post = this.app.post.bind(this.app);
|
|
101
109
|
span.end();
|
|
102
110
|
}
|
|
103
|
-
setConfig(config) {
|
|
104
|
-
const { appConfig, runtimeEnvironment, globalData } = loadConfig(config);
|
|
105
|
-
this.config = appConfig;
|
|
106
|
-
this.runtimeEnvironment = runtimeEnvironment;
|
|
107
|
-
this.globalData = globalData;
|
|
108
|
-
}
|
|
109
111
|
getConfig() {
|
|
110
112
|
return this.config;
|
|
111
113
|
}
|
|
@@ -163,26 +165,17 @@ export class LwrApp {
|
|
|
163
165
|
});
|
|
164
166
|
});
|
|
165
167
|
}
|
|
166
|
-
|
|
167
|
-
this.server?.close &&
|
|
168
|
+
close() {
|
|
169
|
+
this.server?.close && this.server.close();
|
|
168
170
|
}
|
|
169
171
|
// Get the underlying server (e.g. express, koa...)
|
|
170
172
|
getInternalServer() {
|
|
171
173
|
return this.app.getImpl();
|
|
172
174
|
}
|
|
173
|
-
// Return the public server interface which is compatible with all server types
|
|
174
|
-
getServer() {
|
|
175
|
-
return {
|
|
176
|
-
use: this.app.use.bind(this.app),
|
|
177
|
-
all: this.app.all.bind(this.app),
|
|
178
|
-
get: this.app.get.bind(this.app),
|
|
179
|
-
post: this.app.post.bind(this.app),
|
|
180
|
-
getRegexWildcard: this.app.getRegexWildcard.bind(this.app),
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
175
|
}
|
|
184
176
|
export function createServer(config) {
|
|
185
|
-
|
|
177
|
+
const configs = loadConfig(config);
|
|
178
|
+
return new LwrApp(configs);
|
|
186
179
|
}
|
|
187
180
|
export async function generateStaticSite(config) {
|
|
188
181
|
config = config || {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { LWC_VERSION, LWR_VERSION, PWA_KIT_RUNTIME_VERSION, NODE_VERSION } from '@lwrjs/config';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
const baseHtml = `<!DOCTYPE html>
|
|
4
|
+
<html lang="en">
|
|
5
|
+
<head>
|
|
6
|
+
<meta charset="utf-8" />
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
8
|
+
<title>Site Information</title>
|
|
9
|
+
<style>
|
|
10
|
+
body {
|
|
11
|
+
font-family: Arial, sans-serif;
|
|
12
|
+
margin: 0;
|
|
13
|
+
padding: 20px;
|
|
14
|
+
background-color: #f9f9f9;
|
|
15
|
+
color: #333;
|
|
16
|
+
display: flex;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.container {
|
|
20
|
+
max-width: 800px;
|
|
21
|
+
width: 100%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
h1, h2 {
|
|
25
|
+
color: #555;
|
|
26
|
+
margin-bottom: 16px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
ul {
|
|
30
|
+
list-style-type: none;
|
|
31
|
+
padding: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
li {
|
|
35
|
+
background-color: #fff;
|
|
36
|
+
border: 1px solid #ddd;
|
|
37
|
+
margin-bottom: 10px;
|
|
38
|
+
padding: 10px;
|
|
39
|
+
border-radius: 4px;
|
|
40
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
41
|
+
display: flex;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.key {
|
|
45
|
+
margin-right: 10px;
|
|
46
|
+
color: #777;
|
|
47
|
+
font-weight: normal;
|
|
48
|
+
min-width: 200px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.val {
|
|
52
|
+
font-weight: bold;
|
|
53
|
+
color: steelblue;
|
|
54
|
+
font-size: 1.1em;
|
|
55
|
+
flex-grow: 1;
|
|
56
|
+
text-align: left;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.data-container {
|
|
60
|
+
padding: 20px;
|
|
61
|
+
border: 1px solid #333;
|
|
62
|
+
border-radius: 5px;
|
|
63
|
+
white-space: pre;
|
|
64
|
+
font-family: monospace;
|
|
65
|
+
text-align: justify;
|
|
66
|
+
};
|
|
67
|
+
</style>
|
|
68
|
+
</head>
|
|
69
|
+
<body>
|
|
70
|
+
<div class="container">
|
|
71
|
+
<h1>Site Information</h1>
|
|
72
|
+
<h2>Environment</h2>
|
|
73
|
+
{{ versionInfo }}
|
|
74
|
+
<h2>Metadata</h2>
|
|
75
|
+
{{ metadata }}
|
|
76
|
+
</div>
|
|
77
|
+
</body>
|
|
78
|
+
</html>
|
|
79
|
+
`;
|
|
80
|
+
export default async function siteInfoHandler(_request, context) {
|
|
81
|
+
const versionInfo = {
|
|
82
|
+
'LWR Version': LWR_VERSION,
|
|
83
|
+
'LWC Version': LWC_VERSION,
|
|
84
|
+
'Node Version': NODE_VERSION,
|
|
85
|
+
'PWA Kit Runtime Version': PWA_KIT_RUNTIME_VERSION,
|
|
86
|
+
};
|
|
87
|
+
let versionInfoString = '<ul>';
|
|
88
|
+
for (const key in versionInfo) {
|
|
89
|
+
if (versionInfo[key] !== 'note-provided') {
|
|
90
|
+
versionInfoString += `<li><span class="key">${key}:</span><span class="val">${versionInfo[key]}</span></li>\n`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
versionInfoString += '</ul>';
|
|
94
|
+
// Get any provided metadata from site/.metadata/runtime-info.json
|
|
95
|
+
const providedMetadataPath = `${context.rootDir}/site/.metadata/runtime-info.json`;
|
|
96
|
+
let providedMetadata = {};
|
|
97
|
+
if (fs.existsSync(providedMetadataPath)) {
|
|
98
|
+
providedMetadata = JSON.parse(fs.readFileSync(providedMetadataPath, 'utf-8'));
|
|
99
|
+
}
|
|
100
|
+
let providedInfoString = '<ul>';
|
|
101
|
+
for (const key in providedMetadata) {
|
|
102
|
+
if (providedMetadata[key] !== 'note-provided') {
|
|
103
|
+
const value = typeof providedMetadata[key] === 'object'
|
|
104
|
+
? `<div class="data-container val">${JSON.stringify(providedMetadata[key], null, 2)}</div>`
|
|
105
|
+
: `<span class="val">${providedMetadata[key]}</span>`;
|
|
106
|
+
providedInfoString += `<li><span class="key">${key}:</span>${value}</li>\n`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
providedInfoString += '</ul>';
|
|
110
|
+
let infoHtml = baseHtml;
|
|
111
|
+
infoHtml = infoHtml.replace('{{ versionInfo }}', versionInfoString);
|
|
112
|
+
infoHtml = infoHtml.replace('{{ metadata }}', providedInfoString);
|
|
113
|
+
return {
|
|
114
|
+
body: infoHtml,
|
|
115
|
+
headers: {
|
|
116
|
+
'content-type': `text/html; charset=utf-8`,
|
|
117
|
+
},
|
|
118
|
+
cache: {
|
|
119
|
+
ttl: '200s',
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=route-handler.js.map
|
|
@@ -3,6 +3,30 @@ import { DiagnosticsError } from '@lwrjs/diagnostics';
|
|
|
3
3
|
import { RequestHandlerSpan, getTracer } from '@lwrjs/instrumentation';
|
|
4
4
|
import { getAssetIdentity } from './utils/identity.js';
|
|
5
5
|
import { handleErrors } from './utils/error-handling.js';
|
|
6
|
+
import fs from 'fs-extra';
|
|
7
|
+
export function assetMiddleware(app, context) {
|
|
8
|
+
const paths = context.appConfig.assets.map((config) => {
|
|
9
|
+
const assetDirConfig = config;
|
|
10
|
+
let urlPath = config.urlPath;
|
|
11
|
+
// If this is a root config add a /:filename path to match any file in the root. The middleware will fall through if there is no match.
|
|
12
|
+
if (assetDirConfig.root) {
|
|
13
|
+
urlPath = '/:filename';
|
|
14
|
+
}
|
|
15
|
+
else if (assetDirConfig.dir) {
|
|
16
|
+
urlPath += app.getRegexWildcard();
|
|
17
|
+
}
|
|
18
|
+
return urlPath;
|
|
19
|
+
});
|
|
20
|
+
app.get([
|
|
21
|
+
// De-dupe paths (i.e. root path may have been added more than once)
|
|
22
|
+
...[...new Set(paths)],
|
|
23
|
+
'/:apiVersion/:assetType(asset|content-asset)/:immutable?/s/:signature/' + app.getRegexWildcard(),
|
|
24
|
+
'/:apiVersion/:assetType(asset|content-asset)/:immutable?/' + app.getRegexWildcard(),
|
|
25
|
+
], handleErrors(createAssetMiddleware(context)));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create middleware function to handle get assets requests
|
|
29
|
+
*/
|
|
6
30
|
function createAssetMiddleware(context) {
|
|
7
31
|
const { assetRegistry, runtimeEnvironment: { basePath }, } = context;
|
|
8
32
|
return async (req, res, next) => {
|
|
@@ -12,24 +36,22 @@ function createAssetMiddleware(context) {
|
|
|
12
36
|
assetId.specifier = path.join(basePath, assetId.specifier);
|
|
13
37
|
}
|
|
14
38
|
try {
|
|
15
|
-
|
|
39
|
+
let asset;
|
|
16
40
|
const assetUri = await assetRegistry.resolveAssetUri(assetId, runtimeEnvironment);
|
|
41
|
+
// Check if this asset is available externally
|
|
17
42
|
if (assetUri.external) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
43
|
+
// This asset is marked external but hit the middleware anyway
|
|
44
|
+
// Check if we have this file locally, if not, send a 302 to
|
|
45
|
+
// redirect to the external URL
|
|
46
|
+
asset = await getAssetDefinition(assetId, req, assetRegistry, signature, runtimeEnvironment);
|
|
47
|
+
// Verify the content actually exists locally
|
|
48
|
+
if (!fs.existsSync(asset.entry) && assetUri.uri.startsWith('/mobify/bundle')) {
|
|
49
|
+
return sendRedirect(res, assetUri);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
asset = await getAssetDefinition(assetId, req, assetRegistry, signature, runtimeEnvironment);
|
|
24
54
|
}
|
|
25
|
-
const asset = await getTracer().trace({
|
|
26
|
-
name: RequestHandlerSpan.GetAsset,
|
|
27
|
-
attributes: {
|
|
28
|
-
specifier: assetId.specifier,
|
|
29
|
-
},
|
|
30
|
-
}, () => {
|
|
31
|
-
return assetRegistry.getAsset({ ...assetId, signature }, runtimeEnvironment, req.isSiteGeneration());
|
|
32
|
-
});
|
|
33
55
|
if (req.isSiteGeneration()) {
|
|
34
56
|
res.setSiteGenerationMetadata({ asset });
|
|
35
57
|
}
|
|
@@ -58,24 +80,29 @@ function createAssetMiddleware(context) {
|
|
|
58
80
|
}
|
|
59
81
|
};
|
|
60
82
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return
|
|
83
|
+
/**
|
|
84
|
+
* Get the asset definition if asset is local
|
|
85
|
+
*/
|
|
86
|
+
async function getAssetDefinition(assetId, req, assetRegistry, signature, runtimeEnvironment) {
|
|
87
|
+
const asset = await getTracer().trace({
|
|
88
|
+
name: RequestHandlerSpan.GetAsset,
|
|
89
|
+
attributes: {
|
|
90
|
+
specifier: assetId.specifier,
|
|
91
|
+
url: req.originalUrl,
|
|
92
|
+
},
|
|
93
|
+
}, () => {
|
|
94
|
+
return assetRegistry.getAsset({ ...assetId, signature }, runtimeEnvironment, req.isSiteGeneration());
|
|
73
95
|
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
96
|
+
return asset;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Send a redirect (302) response
|
|
100
|
+
*/
|
|
101
|
+
function sendRedirect(res, assetUri) {
|
|
102
|
+
res.set({
|
|
103
|
+
Location: assetUri.uri,
|
|
104
|
+
'cache-control': 'public, max-age=60',
|
|
105
|
+
});
|
|
106
|
+
res.sendStatus(302);
|
|
80
107
|
}
|
|
81
108
|
//# sourceMappingURL=asset-middleware.js.map
|
|
@@ -20,6 +20,12 @@ function createBundleMiddleware(context) {
|
|
|
20
20
|
res.send(descriptions.UNRESOLVABLE.INVALID_JSON().message);
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
|
+
if (!req.validateApiVersion(appConfig)) {
|
|
24
|
+
res.status(400);
|
|
25
|
+
res.send(descriptions.UNRESOLVABLE.INVALID_API_VERSION(req.params.apiVersion, appConfig.apiVersion)
|
|
26
|
+
.message);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
23
29
|
const { runtimeEnvironment, runtimeParams } = req.getRuntimeContext(defaultRuntimeEnvironment);
|
|
24
30
|
const importer = req.query.importer
|
|
25
31
|
? await getRequestImporter(req, moduleRegistry, runtimeParams)
|
|
@@ -34,6 +40,7 @@ function createBundleMiddleware(context) {
|
|
|
34
40
|
name: RequestHandlerSpan.GetBundle,
|
|
35
41
|
attributes: {
|
|
36
42
|
specifier: moduleId.specifier,
|
|
43
|
+
url: req.originalUrl,
|
|
37
44
|
},
|
|
38
45
|
}, () => {
|
|
39
46
|
return moduleBundler.getModuleBundle(moduleId,
|
|
@@ -15,6 +15,9 @@ function createMappingMiddleware(context) {
|
|
|
15
15
|
const { moduleIds } = getMappingIdentity(req);
|
|
16
16
|
const importMetadata = await getTracer().trace({
|
|
17
17
|
name: RequestHandlerSpan.GetMapping,
|
|
18
|
+
attributes: {
|
|
19
|
+
url: req.originalUrl,
|
|
20
|
+
},
|
|
18
21
|
}, () => {
|
|
19
22
|
return getImportMetadataMappings(moduleIds, runtimeEnvironment, runtimeParams, moduleRegistry, moduleBundler);
|
|
20
23
|
});
|
|
@@ -19,6 +19,12 @@ function createModuleMiddleware(context) {
|
|
|
19
19
|
res.send(descriptions.UNRESOLVABLE.INVALID_JSON().message);
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
|
+
if (!req.validateApiVersion(appConfig)) {
|
|
23
|
+
res.status(400);
|
|
24
|
+
res.send(descriptions.UNRESOLVABLE.INVALID_API_VERSION(req.params.apiVersion, appConfig.apiVersion)
|
|
25
|
+
.message);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
22
28
|
const { runtimeEnvironment, runtimeParams } = req.getRuntimeContext(defaultRuntimeEnvironment);
|
|
23
29
|
const importer = req.query.importer
|
|
24
30
|
? await getRequestImporter(req, moduleRegistry, runtimeParams)
|
|
@@ -32,6 +38,7 @@ function createModuleMiddleware(context) {
|
|
|
32
38
|
name: RequestHandlerSpan.GetModule,
|
|
33
39
|
attributes: {
|
|
34
40
|
specifier: moduleId.specifier,
|
|
41
|
+
url: req.originalUrl,
|
|
35
42
|
},
|
|
36
43
|
}, () => {
|
|
37
44
|
return moduleRegistry.getLinkedModule(moduleId,
|
|
@@ -7,15 +7,21 @@
|
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
import { logger } from '@lwrjs/diagnostics';
|
|
10
|
+
import { parseRequestDepthHeader } from '@lwrjs/shared-utils';
|
|
10
11
|
const MRT_REQUEST_CLASS = 'X-Mobify-Request-Class';
|
|
11
12
|
const MRT_REQUEST_CLASS_KEY = MRT_REQUEST_CLASS.toLowerCase();
|
|
12
13
|
export function requestProcessorMiddleware(app, context) {
|
|
13
14
|
const { basePath } = context.runtimeEnvironment;
|
|
14
|
-
app.use(async (req,
|
|
15
|
+
app.use(async (req, res, next) => {
|
|
15
16
|
let requestClass;
|
|
17
|
+
let requestDepth;
|
|
16
18
|
if (req.headers) {
|
|
17
19
|
// If debug print log all the headers
|
|
18
20
|
if (logger.isDebugEnabled()) {
|
|
21
|
+
logger.debug({
|
|
22
|
+
label: `request-processor-middleware`,
|
|
23
|
+
message: `Request: ${req.originalUrl}`,
|
|
24
|
+
});
|
|
19
25
|
// Loop through and print each header
|
|
20
26
|
for (const headerName in req.headers) {
|
|
21
27
|
logger.debug({
|
|
@@ -25,6 +31,16 @@ export function requestProcessorMiddleware(app, context) {
|
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
33
|
requestClass = req.headers[MRT_REQUEST_CLASS_KEY];
|
|
34
|
+
requestDepth = parseRequestDepthHeader(req.headers);
|
|
35
|
+
}
|
|
36
|
+
// TODO Clean up once we have a long term solution
|
|
37
|
+
if (requestDepth && requestDepth > 0) {
|
|
38
|
+
logger.error({
|
|
39
|
+
label: 'request-processor-middleware',
|
|
40
|
+
message: `Lambda SSR request cycle detected: ${req.originalUrl}`,
|
|
41
|
+
});
|
|
42
|
+
// Return 400 Too Many Requests status
|
|
43
|
+
return res.status(400).send('Request depth limit reached');
|
|
28
44
|
}
|
|
29
45
|
if (req.headers && typeof requestClass === 'string') {
|
|
30
46
|
const parsedRequestClass = parseRequestClass(requestClass);
|
|
@@ -8,7 +8,7 @@ function createReturnStatus(error, url) {
|
|
|
8
8
|
if (error instanceof LwrUnresolvableError) {
|
|
9
9
|
return { status: 404, message: error.message };
|
|
10
10
|
}
|
|
11
|
-
return { status: 500, message: descriptions.
|
|
11
|
+
return { status: 500, message: descriptions.SERVER.SERVER_ERROR(url).message };
|
|
12
12
|
}
|
|
13
13
|
export function handleErrors(middleware) {
|
|
14
14
|
return async (req, res, next) => {
|
|
@@ -44,7 +44,11 @@ export function getResourceIdentity(req) {
|
|
|
44
44
|
}
|
|
45
45
|
export function getAssetIdentity(req) {
|
|
46
46
|
const { signature, immutable, assetType: type } = req.params;
|
|
47
|
-
const specifier = type
|
|
47
|
+
const specifier = type
|
|
48
|
+
? process.platform === 'win32'
|
|
49
|
+
? req.params[0]
|
|
50
|
+
: '/' + req.params[0]
|
|
51
|
+
: req.originalUrl.split('?')[0];
|
|
48
52
|
if (validateSpecifier(specifier) === false) {
|
|
49
53
|
throw createSingleDiagnosticError({
|
|
50
54
|
description: descriptions.UNRESOLVABLE.INVALID_SPECIFIER(specifier),
|
|
@@ -40,13 +40,16 @@ function createViewMiddleware(route, errorRoutes, context, viewHandler) {
|
|
|
40
40
|
const resolve = req.isJsonRequest() ? viewHandler.getViewJson : viewHandler.getViewContent;
|
|
41
41
|
let viewResponse;
|
|
42
42
|
let resolvedRoute;
|
|
43
|
+
let traceId;
|
|
43
44
|
try {
|
|
44
45
|
viewResponse = await getTracer().trace({
|
|
45
46
|
name: RequestHandlerSpan.GetView,
|
|
46
47
|
attributes: {
|
|
47
48
|
view: route.id,
|
|
49
|
+
url: req.originalUrl,
|
|
48
50
|
},
|
|
49
|
-
}, () => {
|
|
51
|
+
}, (span) => {
|
|
52
|
+
traceId = span.traceId;
|
|
50
53
|
return resolve.call(viewHandler, viewRequest, route, runtimeEnvironment, runtimeParams);
|
|
51
54
|
});
|
|
52
55
|
resolvedRoute = route;
|
|
@@ -84,6 +87,9 @@ function createViewMiddleware(route, errorRoutes, context, viewHandler) {
|
|
|
84
87
|
res.setHeader('cache-control', `public, max-age=${cacheTtl}`);
|
|
85
88
|
}
|
|
86
89
|
}
|
|
90
|
+
if (traceId?.length) {
|
|
91
|
+
res.setHeader('x-trace-id', traceId);
|
|
92
|
+
}
|
|
87
93
|
const status = resolvedRoute.status || viewResponse.status || 200;
|
|
88
94
|
res.status(status);
|
|
89
95
|
res.send(viewResponse.body);
|
|
@@ -40,6 +40,10 @@ export default class SiteGenerator {
|
|
|
40
40
|
* @param dispatcher - Network dispatcher
|
|
41
41
|
*/
|
|
42
42
|
private handleJavascriptResource;
|
|
43
|
+
/**
|
|
44
|
+
* If this is a file based external copy it to the site folder and add it to the bundle metadata
|
|
45
|
+
*/
|
|
46
|
+
private handleExternalBundle;
|
|
43
47
|
private addBundleToSiteMetadata;
|
|
44
48
|
private addResourceToSiteMetadata;
|
|
45
49
|
private addAssetToSiteMetadata;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { performance } from 'perf_hooks';
|
|
2
2
|
import { logger } from '@lwrjs/diagnostics';
|
|
3
|
-
import { getSpecifier, getFeatureFlags, hashContent, isSelfUrl, getModuleUriPrefix, getMappingUriPrefix, isExternalUrl, mimeLookup, getViewUri, sortLocalesByFallback, } from '@lwrjs/shared-utils';
|
|
3
|
+
import { getSpecifier, getFeatureFlags, hashContent, isSelfUrl, getModuleUriPrefix, getMappingUriPrefix, isExternalUrl, mimeLookup, getViewUri, sortLocalesByFallback, VERSION_NOT_PROVIDED, PROTOCOL_FILE, normalizeFromFileURL, } from '@lwrjs/shared-utils';
|
|
4
4
|
import { SiteMetadataImpl } from '@lwrjs/static/site-metadata';
|
|
5
5
|
import { join, dirname, extname, normalize } from 'path';
|
|
6
6
|
import fs from 'fs-extra';
|
|
7
7
|
import { writeResponse } from './utils/stream.js';
|
|
8
8
|
import { createResourceDir } from './utils/dir.js';
|
|
9
9
|
import { getRuntimeEnvironment } from '@lwrjs/config';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
10
11
|
export default class SiteGenerator {
|
|
11
12
|
/**
|
|
12
13
|
* Build a static site in the configured directory
|
|
@@ -198,6 +199,14 @@ export default class SiteGenerator {
|
|
|
198
199
|
*/
|
|
199
200
|
async handleJavascriptResource(url, context, siteConfig, dispatcher) {
|
|
200
201
|
const { outputDir } = siteConfig;
|
|
202
|
+
const moduleDefinition = context.fs?.metadata?.moduleDefinition; // LinkedModuleDefinition | BundleDefinition
|
|
203
|
+
const externals = moduleDefinition?.config?.external || {};
|
|
204
|
+
const siteBundles = siteConfig?.siteMetadata?.getSiteBundles()?.bundles;
|
|
205
|
+
const specifier = moduleDefinition?.specifier;
|
|
206
|
+
if (externals && siteBundles && externals[specifier]) {
|
|
207
|
+
this.handleExternalBundle(specifier, siteBundles, externals, outputDir);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
201
210
|
const normalizedUrl = decodeURIComponent(url);
|
|
202
211
|
createResourceDir(dirname(normalizedUrl), outputDir);
|
|
203
212
|
const ext = extname(normalizedUrl);
|
|
@@ -223,26 +232,33 @@ export default class SiteGenerator {
|
|
|
223
232
|
siteConfig.urlRewriteMap.set(url.substring(0, url.indexOf('%2Fv%2F')), normalizedUrl);
|
|
224
233
|
}
|
|
225
234
|
// Recursively traverse dependencies
|
|
226
|
-
const moduleDefinition = context.fs?.metadata?.moduleDefinition; // LinkedModuleDefinition | BundleDefinition
|
|
227
235
|
if (moduleDefinition) {
|
|
228
236
|
// Imports
|
|
229
237
|
const imports = moduleDefinition.linkedModuleRecord?.imports || moduleDefinition.bundleRecord?.imports || [];
|
|
230
238
|
// /1/module/esm/0/l/en-US/mi/lwc
|
|
231
239
|
for (const importModule of imports) {
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
240
|
+
const specifier = importModule.specifier;
|
|
241
|
+
if (externals[specifier]) {
|
|
242
|
+
this.handleExternalBundle(specifier, siteBundles, externals, outputDir);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
const jsUri = specifier.startsWith('/') ? specifier : getSpecifier(importModule);
|
|
246
|
+
dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
|
|
247
|
+
}
|
|
236
248
|
}
|
|
237
249
|
// Dynamic imports
|
|
238
250
|
const dynamicImports = moduleDefinition.linkedModuleRecord?.dynamicImports ||
|
|
239
251
|
moduleDefinition.bundleRecord?.dynamicImports ||
|
|
240
252
|
[];
|
|
241
253
|
for (const importModule of dynamicImports) {
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
254
|
+
const specifier = importModule.specifier;
|
|
255
|
+
if (externals[specifier]) {
|
|
256
|
+
this.handleExternalBundle(specifier, siteBundles, externals, outputDir);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
const jsUri = specifier.startsWith('/') ? specifier : getSpecifier(importModule);
|
|
260
|
+
dispatchRequests.push(this.dispatchJSResourceRecursive(jsUri, dispatcher, siteConfig));
|
|
261
|
+
}
|
|
246
262
|
}
|
|
247
263
|
// If this is a bundle add it to the bundle metadata
|
|
248
264
|
if (moduleDefinition.bundleRecord) {
|
|
@@ -257,6 +273,25 @@ export default class SiteGenerator {
|
|
|
257
273
|
// -- Dispatch dependencies
|
|
258
274
|
await Promise.all(dispatchRequests);
|
|
259
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* If this is a file based external copy it to the site folder and add it to the bundle metadata
|
|
278
|
+
*/
|
|
279
|
+
handleExternalBundle(specifier, siteBundles, externals, outputDir) {
|
|
280
|
+
if (siteBundles && externals[specifier]?.startsWith(PROTOCOL_FILE) && !siteBundles[specifier]) {
|
|
281
|
+
const path = normalizeFromFileURL(externals[specifier], process.cwd());
|
|
282
|
+
const normalizedPath = decodeURIComponent(path);
|
|
283
|
+
createResourceDir(dirname(normalizedPath), outputDir);
|
|
284
|
+
const ext = extname(normalizedPath);
|
|
285
|
+
const fullPath = join(outputDir, `${normalizedPath}${ext ? '' : '.js'}`);
|
|
286
|
+
fs.copyFileSync(fileURLToPath(externals[specifier]), fullPath);
|
|
287
|
+
const bundleMetadata = {
|
|
288
|
+
specifier,
|
|
289
|
+
path: normalizedPath,
|
|
290
|
+
imports: [],
|
|
291
|
+
};
|
|
292
|
+
siteBundles[specifier] = bundleMetadata;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
260
295
|
addBundleToSiteMetadata(bundleDefinition, url, siteConfig) {
|
|
261
296
|
if (siteConfig.siteMetadata) {
|
|
262
297
|
const locale = siteConfig.locale;
|
|
@@ -265,8 +300,9 @@ export default class SiteGenerator {
|
|
|
265
300
|
: `${bundleDefinition.specifier}|l/${locale}`;
|
|
266
301
|
const imports = bundleDefinition.bundleRecord.imports?.map((moduleRef) => getSpecifier(moduleRef)) || [];
|
|
267
302
|
const dynamicImports = bundleDefinition.bundleRecord.dynamicImports?.map((moduleRef) => getSpecifier(moduleRef));
|
|
303
|
+
const version = bundleDefinition.version === VERSION_NOT_PROVIDED ? undefined : bundleDefinition.version;
|
|
268
304
|
const bundleMetadata = {
|
|
269
|
-
version
|
|
305
|
+
version,
|
|
270
306
|
path: decodeURIComponent(url),
|
|
271
307
|
includedModules: bundleDefinition.bundleRecord.includedModules || [],
|
|
272
308
|
imports,
|