@jsenv/core 35.0.5 → 36.0.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/README.md +1 -1
- package/dist/js/inline_content.js +5 -4
- package/dist/jsenv_core.js +1127 -1497
- package/package.json +8 -8
- package/src/build/build.js +49 -41
- package/src/dev/file_service.js +7 -17
- package/src/dev/start_dev_server.js +12 -7
- package/src/kitchen/kitchen.js +38 -19
- package/src/kitchen/url_graph.js +1 -1
- package/src/plugins/autoreload/jsenv_plugin_hmr.js +2 -2
- package/src/plugins/file_urls/jsenv_plugin_file_urls.js +4 -4
- package/src/plugins/http_urls/jsenv_plugin_http_urls.js +1 -1
- package/src/plugins/importmap/jsenv_plugin_importmap.js +1 -1
- package/src/plugins/inlining/jsenv_plugin_inlining.js +1 -1
- package/src/plugins/inlining/jsenv_plugin_inlining_as_data_url.js +13 -2
- package/src/plugins/plugin_controller.js +19 -10
- package/src/plugins/plugins.js +21 -25
- package/src/plugins/{url_analysis/css/css_urls.js → reference_analysis/css/jsenv_plugin_css_reference_analysis.js} +11 -1
- package/src/plugins/{inline_content_analysis/jsenv_plugin_data_urls.js → reference_analysis/data_urls/jsenv_plugin_data_urls_analysis.js} +19 -19
- package/src/plugins/reference_analysis/directory/jsenv_plugin_directory_reference_analysis.js +51 -0
- package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +429 -0
- package/src/plugins/reference_analysis/inline_content.js +7 -0
- package/src/plugins/reference_analysis/js/jsenv_plugin_js_reference_analysis.js +161 -0
- package/src/plugins/reference_analysis/jsenv_plugin_reference_analysis.js +120 -0
- package/src/plugins/{url_analysis → reference_analysis}/jsenv_plugin_reference_expected_types.js +19 -12
- package/src/plugins/{url_analysis/webmanifest/webmanifest_urls.js → reference_analysis/webmanifest/jsenv_plugin_webmanifest_reference_analysis.js} +13 -1
- package/src/plugins/resolution_node_esm/jsenv_plugin_node_esm_resolution.js +74 -0
- package/src/plugins/{url_resolution → resolution_node_esm}/node_esm_resolver.js +8 -0
- package/src/plugins/resolution_web/jsenv_plugin_web_resolution.js +45 -0
- package/src/plugins/transpilation/as_js_module/jsenv_plugin_as_js_module.js +1 -1
- package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +1 -1
- package/src/plugins/transpilation/import_assertions/jsenv_plugin_import_assertions.js +4 -6
- package/src/plugins/transpilation/js_module_fallback/jsenv_plugin_js_module_conversion.js +1 -1
- package/src/plugins/transpilation/js_module_fallback/jsenv_plugin_js_module_fallback_inside_html.js +4 -6
- package/src/plugins/transpilation/js_module_fallback/jsenv_plugin_js_module_fallback_on_workers.js +1 -1
- package/src/plugins/url_type_from_reference.js +13 -0
- package/src/plugins/{url_version/jsenv_plugin_url_version.js → version_search_param/jsenv_plugin_version_search_param.js} +4 -4
- package/dist/html/explorer.html +0 -559
- package/dist/other/jsenv.png +0 -0
- package/src/plugins/explorer/client/explorer.html +0 -608
- package/src/plugins/explorer/client/jsenv.png +0 -0
- package/src/plugins/explorer/jsenv_plugin_explorer.js +0 -86
- package/src/plugins/inline_content_analysis/client/inline_content.js +0 -6
- package/src/plugins/inline_content_analysis/jsenv_plugin_html_inline_content_analysis.js +0 -206
- package/src/plugins/inline_content_analysis/jsenv_plugin_inline_content_analysis.js +0 -34
- package/src/plugins/inline_content_analysis/jsenv_plugin_js_inline_content_analysis.js +0 -314
- package/src/plugins/url_analysis/html/html_urls.js +0 -313
- package/src/plugins/url_analysis/js/js_urls.js +0 -65
- package/src/plugins/url_analysis/jsenv_plugin_url_analysis.js +0 -116
- package/src/plugins/url_resolution/jsenv_plugin_url_resolution.js +0 -140
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseHtmlString,
|
|
3
|
+
visitHtmlNodes,
|
|
4
|
+
getHtmlNodeText,
|
|
5
|
+
setHtmlNodeText,
|
|
6
|
+
removeHtmlNodeText,
|
|
7
|
+
getHtmlNodeAttribute,
|
|
8
|
+
getHtmlNodePosition,
|
|
9
|
+
setHtmlNodeAttributes,
|
|
10
|
+
getHtmlNodeAttributePosition,
|
|
11
|
+
analyzeScriptNode,
|
|
12
|
+
parseSrcSet,
|
|
13
|
+
stringifyHtmlAst,
|
|
14
|
+
} from "@jsenv/ast";
|
|
15
|
+
import { generateInlineContentUrl } from "@jsenv/urls";
|
|
16
|
+
import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
|
|
17
|
+
|
|
18
|
+
export const jsenvPluginHtmlReferenceAnalysis = ({
|
|
19
|
+
inlineContent,
|
|
20
|
+
inlineConvertedScript,
|
|
21
|
+
}) => {
|
|
22
|
+
return {
|
|
23
|
+
name: "jsenv:html_reference_analysis",
|
|
24
|
+
appliesDuring: "*",
|
|
25
|
+
transformUrlContent: {
|
|
26
|
+
html: (urlInfo, context) =>
|
|
27
|
+
parseAndTransformHtmlReferences(urlInfo, context, {
|
|
28
|
+
inlineContent,
|
|
29
|
+
inlineConvertedScript,
|
|
30
|
+
}),
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const parseAndTransformHtmlReferences = async (
|
|
36
|
+
urlInfo,
|
|
37
|
+
context,
|
|
38
|
+
{ inlineContent, inlineConvertedScript },
|
|
39
|
+
) => {
|
|
40
|
+
const url = urlInfo.originalUrl;
|
|
41
|
+
const content = urlInfo.content;
|
|
42
|
+
const htmlAst = parseHtmlString(content);
|
|
43
|
+
|
|
44
|
+
const mutations = [];
|
|
45
|
+
const actions = [];
|
|
46
|
+
const finalizeCallbacks = [];
|
|
47
|
+
|
|
48
|
+
const createExternalReference = (
|
|
49
|
+
node,
|
|
50
|
+
attributeName,
|
|
51
|
+
attributeValue,
|
|
52
|
+
{ type, subtype, expectedType },
|
|
53
|
+
) => {
|
|
54
|
+
let position;
|
|
55
|
+
if (getHtmlNodeAttribute(node, "jsenv-cooked-by")) {
|
|
56
|
+
// when generated from inline content,
|
|
57
|
+
// line, column is not "src" nor "inlined-from-src" but "original-position"
|
|
58
|
+
position = getHtmlNodePosition(node);
|
|
59
|
+
} else {
|
|
60
|
+
position = getHtmlNodeAttributePosition(node, attributeName);
|
|
61
|
+
}
|
|
62
|
+
const {
|
|
63
|
+
line,
|
|
64
|
+
column,
|
|
65
|
+
// originalLine, originalColumn
|
|
66
|
+
} = position;
|
|
67
|
+
const debug = getHtmlNodeAttribute(node, "jsenv-debug") !== undefined;
|
|
68
|
+
|
|
69
|
+
const { crossorigin, integrity } = readFetchMetas(node);
|
|
70
|
+
const isResourceHint = [
|
|
71
|
+
"preconnect",
|
|
72
|
+
"dns-prefetch",
|
|
73
|
+
"prefetch",
|
|
74
|
+
"preload",
|
|
75
|
+
"modulepreload",
|
|
76
|
+
].includes(subtype);
|
|
77
|
+
const [reference] = context.referenceUtils.found({
|
|
78
|
+
type,
|
|
79
|
+
subtype,
|
|
80
|
+
expectedType,
|
|
81
|
+
specifier: attributeValue,
|
|
82
|
+
specifierLine: line,
|
|
83
|
+
specifierColumn: column,
|
|
84
|
+
isResourceHint,
|
|
85
|
+
crossorigin,
|
|
86
|
+
integrity,
|
|
87
|
+
debug,
|
|
88
|
+
});
|
|
89
|
+
actions.push(async () => {
|
|
90
|
+
await context.referenceUtils.readGeneratedSpecifier(reference);
|
|
91
|
+
mutations.push(() => {
|
|
92
|
+
setHtmlNodeAttributes(node, {
|
|
93
|
+
[attributeName]: reference.generatedSpecifier,
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
const visitHref = (node, referenceProps) => {
|
|
99
|
+
const href = getHtmlNodeAttribute(node, "href");
|
|
100
|
+
if (href) {
|
|
101
|
+
return createExternalReference(node, "href", href, referenceProps);
|
|
102
|
+
}
|
|
103
|
+
const inlinedFromHref = getHtmlNodeAttribute(node, "inlined-from-href");
|
|
104
|
+
if (inlinedFromHref) {
|
|
105
|
+
return createExternalReference(
|
|
106
|
+
node,
|
|
107
|
+
"inlined-from-href",
|
|
108
|
+
new URL(inlinedFromHref, url).href,
|
|
109
|
+
referenceProps,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
};
|
|
114
|
+
const visitSrc = (node, referenceProps) => {
|
|
115
|
+
const src = getHtmlNodeAttribute(node, "src");
|
|
116
|
+
if (src) {
|
|
117
|
+
return createExternalReference(node, "src", src, referenceProps);
|
|
118
|
+
}
|
|
119
|
+
const inlinedFromSrc = getHtmlNodeAttribute(node, "inlined-from-src");
|
|
120
|
+
if (inlinedFromSrc) {
|
|
121
|
+
return createExternalReference(
|
|
122
|
+
node,
|
|
123
|
+
"inlined-from-src",
|
|
124
|
+
new URL(inlinedFromSrc, url).href,
|
|
125
|
+
referenceProps,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
};
|
|
130
|
+
const visitSrcset = (node, referenceProps) => {
|
|
131
|
+
const srcset = getHtmlNodeAttribute(node, "srcset");
|
|
132
|
+
if (srcset) {
|
|
133
|
+
const srcCandidates = parseSrcSet(srcset);
|
|
134
|
+
return srcCandidates.map((srcCandidate) => {
|
|
135
|
+
return createExternalReference(
|
|
136
|
+
node,
|
|
137
|
+
"srcset",
|
|
138
|
+
srcCandidate.specifier,
|
|
139
|
+
referenceProps,
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const createInlineReference = (
|
|
147
|
+
node,
|
|
148
|
+
inlineContent,
|
|
149
|
+
{ extension, type, expectedType, contentType },
|
|
150
|
+
) => {
|
|
151
|
+
const hotAccept = getHtmlNodeAttribute(node, "hot-accept") !== undefined;
|
|
152
|
+
const { line, column, lineEnd, columnEnd, isOriginal } =
|
|
153
|
+
getHtmlNodePosition(node, { preferOriginal: true });
|
|
154
|
+
const inlineContentUrl = generateInlineContentUrl({
|
|
155
|
+
url: urlInfo.url,
|
|
156
|
+
extension,
|
|
157
|
+
line,
|
|
158
|
+
column,
|
|
159
|
+
lineEnd,
|
|
160
|
+
columnEnd,
|
|
161
|
+
});
|
|
162
|
+
const debug = getHtmlNodeAttribute(node, "jsenv-debug") !== undefined;
|
|
163
|
+
const [inlineReference, inlineUrlInfo] = context.referenceUtils.foundInline(
|
|
164
|
+
{
|
|
165
|
+
node,
|
|
166
|
+
type,
|
|
167
|
+
expectedType,
|
|
168
|
+
isOriginalPosition: isOriginal,
|
|
169
|
+
// we remove 1 to the line because imagine the following html:
|
|
170
|
+
// <style>body { color: red; }</style>
|
|
171
|
+
// -> content starts same line as <style> (same for <script>)
|
|
172
|
+
specifierLine: line - 1,
|
|
173
|
+
specifierColumn: column,
|
|
174
|
+
specifier: inlineContentUrl,
|
|
175
|
+
contentType,
|
|
176
|
+
content: inlineContent,
|
|
177
|
+
debug,
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
actions.push(async () => {
|
|
181
|
+
await cookInlineContent({
|
|
182
|
+
context,
|
|
183
|
+
inlineContentUrlInfo: inlineUrlInfo,
|
|
184
|
+
inlineContentReference: inlineReference,
|
|
185
|
+
});
|
|
186
|
+
mutations.push(() => {
|
|
187
|
+
if (hotAccept) {
|
|
188
|
+
removeHtmlNodeText(node);
|
|
189
|
+
setHtmlNodeAttributes(node, {
|
|
190
|
+
"jsenv-cooked-by": "jsenv:html_inline_content_analysis",
|
|
191
|
+
});
|
|
192
|
+
} else {
|
|
193
|
+
setHtmlNodeText(node, inlineUrlInfo.content, {
|
|
194
|
+
indentation: false, // indentation would decrease stack trace precision
|
|
195
|
+
});
|
|
196
|
+
setHtmlNodeAttributes(node, {
|
|
197
|
+
"jsenv-cooked-by": "jsenv:html_inline_content_analysis",
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return inlineReference;
|
|
204
|
+
};
|
|
205
|
+
const visitTextContent = (
|
|
206
|
+
node,
|
|
207
|
+
{ extension, type, expectedType, contentType },
|
|
208
|
+
) => {
|
|
209
|
+
const inlineContent = getHtmlNodeText(node);
|
|
210
|
+
if (!inlineContent) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return createInlineReference(node, inlineContent, {
|
|
214
|
+
extension,
|
|
215
|
+
type,
|
|
216
|
+
expectedType,
|
|
217
|
+
contentType,
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
visitHtmlNodes(htmlAst, {
|
|
222
|
+
link: (linkNode) => {
|
|
223
|
+
const rel = getHtmlNodeAttribute(linkNode, "rel");
|
|
224
|
+
const type = getHtmlNodeAttribute(linkNode, "type");
|
|
225
|
+
const ref = visitHref(linkNode, {
|
|
226
|
+
type: "link_href",
|
|
227
|
+
subtype: rel,
|
|
228
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload#including_a_mime_type
|
|
229
|
+
expectedContentType: type,
|
|
230
|
+
});
|
|
231
|
+
if (ref) {
|
|
232
|
+
finalizeCallbacks.push(() => {
|
|
233
|
+
ref.expectedType = decideLinkExpectedType(ref, context);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
style: inlineContent
|
|
238
|
+
? (styleNode) => {
|
|
239
|
+
visitTextContent(styleNode, {
|
|
240
|
+
extension: ".css",
|
|
241
|
+
type: "style",
|
|
242
|
+
expectedType: "css",
|
|
243
|
+
contentType: "text/css",
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
: null,
|
|
247
|
+
script: (scriptNode) => {
|
|
248
|
+
// during build the importmap is inlined
|
|
249
|
+
// and shoud not be considered as a dependency anymore
|
|
250
|
+
if (
|
|
251
|
+
getHtmlNodeAttribute(scriptNode, "jsenv-inlined-by") ===
|
|
252
|
+
"jsenv:importmap"
|
|
253
|
+
) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const { type, contentType, extension } = analyzeScriptNode(scriptNode);
|
|
258
|
+
// ignore <script type="whatever">foobar</script>
|
|
259
|
+
// per HTML spec https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type
|
|
260
|
+
if (type !== "text") {
|
|
261
|
+
const externalRef = visitSrc(scriptNode, {
|
|
262
|
+
type: "script",
|
|
263
|
+
subtype: type,
|
|
264
|
+
expectedType: type,
|
|
265
|
+
});
|
|
266
|
+
if (externalRef) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// now visit the content, if any
|
|
272
|
+
if (!inlineContent) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
// If the inline script was already handled by an other plugin, ignore it
|
|
276
|
+
// - we want to preserve inline scripts generated by html supervisor during dev
|
|
277
|
+
// - we want to avoid cooking twice a script during build
|
|
278
|
+
if (
|
|
279
|
+
!inlineConvertedScript &&
|
|
280
|
+
getHtmlNodeAttribute(scriptNode, "jsenv-injected-by") ===
|
|
281
|
+
"jsenv:js_module_fallback"
|
|
282
|
+
) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const inlineRef = visitTextContent(scriptNode, {
|
|
287
|
+
extension: extension || CONTENT_TYPE.asFileExtension(contentType),
|
|
288
|
+
type: "script",
|
|
289
|
+
expectedType: type,
|
|
290
|
+
contentType,
|
|
291
|
+
});
|
|
292
|
+
if (inlineRef && extension) {
|
|
293
|
+
// 1. <script type="jsx"> becomes <script>
|
|
294
|
+
// 2. <script type="module/jsx"> becomes <script type="module">
|
|
295
|
+
mutations.push(() => {
|
|
296
|
+
setHtmlNodeAttributes(scriptNode, {
|
|
297
|
+
type: type === "js_module" ? "module" : undefined,
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
a: (aNode) => {
|
|
303
|
+
visitHref(aNode, {
|
|
304
|
+
type: "a_href",
|
|
305
|
+
});
|
|
306
|
+
},
|
|
307
|
+
iframe: (iframeNode) => {
|
|
308
|
+
visitSrc(iframeNode, {
|
|
309
|
+
type: "iframe_src",
|
|
310
|
+
});
|
|
311
|
+
},
|
|
312
|
+
img: (imgNode) => {
|
|
313
|
+
visitSrc(imgNode, {
|
|
314
|
+
type: "img_src",
|
|
315
|
+
});
|
|
316
|
+
visitSrcset(imgNode, {
|
|
317
|
+
type: "img_srcset",
|
|
318
|
+
});
|
|
319
|
+
},
|
|
320
|
+
source: (sourceNode) => {
|
|
321
|
+
visitSrc(sourceNode, {
|
|
322
|
+
type: "source_src",
|
|
323
|
+
});
|
|
324
|
+
visitSrcset(sourceNode, {
|
|
325
|
+
type: "source_srcset",
|
|
326
|
+
});
|
|
327
|
+
},
|
|
328
|
+
// svg <image> tag
|
|
329
|
+
image: (imageNode) => {
|
|
330
|
+
visitHref(imageNode, {
|
|
331
|
+
type: "image_href",
|
|
332
|
+
});
|
|
333
|
+
},
|
|
334
|
+
use: (useNode) => {
|
|
335
|
+
visitHref(useNode, {
|
|
336
|
+
type: "use_href",
|
|
337
|
+
});
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
finalizeCallbacks.forEach((finalizeCallback) => {
|
|
341
|
+
finalizeCallback();
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
if (actions.length > 0) {
|
|
345
|
+
await Promise.all(actions.map((action) => action()));
|
|
346
|
+
}
|
|
347
|
+
if (mutations.length === 0) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
mutations.forEach((mutation) => mutation());
|
|
351
|
+
return stringifyHtmlAst(htmlAst);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const cookInlineContent = async ({
|
|
355
|
+
context,
|
|
356
|
+
inlineContentUrlInfo,
|
|
357
|
+
inlineContentReference,
|
|
358
|
+
}) => {
|
|
359
|
+
try {
|
|
360
|
+
await context.cook(inlineContentUrlInfo, {
|
|
361
|
+
reference: inlineContentReference,
|
|
362
|
+
});
|
|
363
|
+
} catch (e) {
|
|
364
|
+
if (e.code === "PARSE_ERROR") {
|
|
365
|
+
// When something like <style> or <script> contains syntax error
|
|
366
|
+
// the HTML in itself it still valid
|
|
367
|
+
// keep the syntax error and continue with the HTML
|
|
368
|
+
const messageStart =
|
|
369
|
+
inlineContentUrlInfo.type === "css"
|
|
370
|
+
? `Syntax error on css declared inside <style>`
|
|
371
|
+
: `Syntax error on js declared inside <script>`;
|
|
372
|
+
|
|
373
|
+
context.logger.error(`${messageStart}: ${e.cause.reasonCode}
|
|
374
|
+
${e.traceMessage}`);
|
|
375
|
+
} else {
|
|
376
|
+
throw e;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const crossOriginCompatibleTagNames = ["script", "link", "img", "source"];
|
|
382
|
+
const integrityCompatibleTagNames = ["script", "link", "img", "source"];
|
|
383
|
+
const readFetchMetas = (node) => {
|
|
384
|
+
const meta = {};
|
|
385
|
+
if (crossOriginCompatibleTagNames.includes(node.nodeName)) {
|
|
386
|
+
const crossorigin = getHtmlNodeAttribute(node, "crossorigin") !== undefined;
|
|
387
|
+
meta.crossorigin = crossorigin;
|
|
388
|
+
}
|
|
389
|
+
if (integrityCompatibleTagNames.includes(node.nodeName)) {
|
|
390
|
+
const integrity = getHtmlNodeAttribute(node, "integrity");
|
|
391
|
+
meta.integrity = integrity;
|
|
392
|
+
}
|
|
393
|
+
return meta;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const decideLinkExpectedType = (linkReference, context) => {
|
|
397
|
+
const rel = getHtmlNodeAttribute(linkReference.node, "rel");
|
|
398
|
+
if (rel === "webmanifest") {
|
|
399
|
+
return "webmanifest";
|
|
400
|
+
}
|
|
401
|
+
if (rel === "modulepreload") {
|
|
402
|
+
return "js_module";
|
|
403
|
+
}
|
|
404
|
+
if (rel === "stylesheet") {
|
|
405
|
+
return "css";
|
|
406
|
+
}
|
|
407
|
+
if (rel === "preload") {
|
|
408
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload#what_types_of_content_can_be_preloaded
|
|
409
|
+
const as = getHtmlNodeAttribute(linkReference.node, "as");
|
|
410
|
+
if (as === "document") {
|
|
411
|
+
return "html";
|
|
412
|
+
}
|
|
413
|
+
if (as === "style") {
|
|
414
|
+
return "css";
|
|
415
|
+
}
|
|
416
|
+
if (as === "script") {
|
|
417
|
+
const firstScriptOnThisUrl = context.referenceUtils.find(
|
|
418
|
+
(refCandidate) =>
|
|
419
|
+
refCandidate.url === linkReference.url &&
|
|
420
|
+
refCandidate.type === "script",
|
|
421
|
+
);
|
|
422
|
+
if (firstScriptOnThisUrl) {
|
|
423
|
+
return firstScriptOnThisUrl.expectedType;
|
|
424
|
+
}
|
|
425
|
+
return undefined;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return undefined;
|
|
429
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { createMagicSource } from "@jsenv/sourcemap";
|
|
2
|
+
import { parseJsUrls } from "@jsenv/ast";
|
|
3
|
+
import { generateInlineContentUrl } from "@jsenv/urls";
|
|
4
|
+
import { JS_QUOTES } from "@jsenv/utils/src/string/js_quotes.js";
|
|
5
|
+
import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
|
|
6
|
+
|
|
7
|
+
import { isWebWorkerUrlInfo } from "@jsenv/core/src/kitchen/web_workers.js";
|
|
8
|
+
|
|
9
|
+
export const jsenvPluginJsReferenceAnalysis = ({
|
|
10
|
+
inlineContent,
|
|
11
|
+
allowEscapeForVersioning,
|
|
12
|
+
}) => {
|
|
13
|
+
return [
|
|
14
|
+
{
|
|
15
|
+
name: "jsenv:js_reference_analysis",
|
|
16
|
+
appliesDuring: "*",
|
|
17
|
+
transformUrlContent: {
|
|
18
|
+
js_classic: (urlInfo, context) =>
|
|
19
|
+
parseAndTransformJsReferences(urlInfo, context, {
|
|
20
|
+
inlineContent,
|
|
21
|
+
allowEscapeForVersioning,
|
|
22
|
+
}),
|
|
23
|
+
js_module: (urlInfo, context) =>
|
|
24
|
+
parseAndTransformJsReferences(urlInfo, context, {
|
|
25
|
+
inlineContent,
|
|
26
|
+
allowEscapeForVersioning,
|
|
27
|
+
}),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const parseAndTransformJsReferences = async (
|
|
34
|
+
urlInfo,
|
|
35
|
+
context,
|
|
36
|
+
{ inlineContent, allowEscapeForVersioning },
|
|
37
|
+
) => {
|
|
38
|
+
const magicSource = createMagicSource(urlInfo.content);
|
|
39
|
+
const parallelActions = [];
|
|
40
|
+
const sequentialActions = [];
|
|
41
|
+
|
|
42
|
+
const onInlineReference = (inlineReferenceInfo) => {
|
|
43
|
+
const inlineUrl = generateInlineContentUrl({
|
|
44
|
+
url: urlInfo.url,
|
|
45
|
+
extension: CONTENT_TYPE.asFileExtension(inlineReferenceInfo.contentType),
|
|
46
|
+
line: inlineReferenceInfo.line,
|
|
47
|
+
column: inlineReferenceInfo.column,
|
|
48
|
+
lineEnd: inlineReferenceInfo.lineEnd,
|
|
49
|
+
columnEnd: inlineReferenceInfo.columnEnd,
|
|
50
|
+
});
|
|
51
|
+
let { quote } = inlineReferenceInfo;
|
|
52
|
+
if (
|
|
53
|
+
quote === "`" &&
|
|
54
|
+
!context.isSupportedOnCurrentClients("template_literals")
|
|
55
|
+
) {
|
|
56
|
+
// if quote is "`" and template literals are not supported
|
|
57
|
+
// we'll use a regular string (single or double quote)
|
|
58
|
+
// when rendering the string
|
|
59
|
+
quote = JS_QUOTES.pickBest(inlineReferenceInfo.content);
|
|
60
|
+
}
|
|
61
|
+
const [inlineReference, inlineUrlInfo] = context.referenceUtils.foundInline(
|
|
62
|
+
{
|
|
63
|
+
type: "js_inline_content",
|
|
64
|
+
subtype: inlineReferenceInfo.type, // "new_blob_first_arg", "new_inline_content_first_arg", "json_parse_first_arg"
|
|
65
|
+
isOriginalPosition: urlInfo.content === urlInfo.originalContent,
|
|
66
|
+
specifierLine: inlineReferenceInfo.line,
|
|
67
|
+
specifierColumn: inlineReferenceInfo.column,
|
|
68
|
+
specifier: inlineUrl,
|
|
69
|
+
contentType: inlineReferenceInfo.contentType,
|
|
70
|
+
content: inlineReferenceInfo.content,
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
inlineUrlInfo.jsQuote = quote;
|
|
74
|
+
inlineReference.escape = (value) =>
|
|
75
|
+
JS_QUOTES.escapeSpecialChars(value.slice(1, -1), { quote });
|
|
76
|
+
|
|
77
|
+
sequentialActions.push(async () => {
|
|
78
|
+
await context.cook(inlineUrlInfo, { reference: inlineReference });
|
|
79
|
+
const replacement = JS_QUOTES.escapeSpecialChars(inlineUrlInfo.content, {
|
|
80
|
+
quote,
|
|
81
|
+
allowEscapeForVersioning,
|
|
82
|
+
});
|
|
83
|
+
magicSource.replace({
|
|
84
|
+
start: inlineReferenceInfo.start,
|
|
85
|
+
end: inlineReferenceInfo.end,
|
|
86
|
+
replacement,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
const onExternalReference = (externalReferenceInfo) => {
|
|
91
|
+
if (
|
|
92
|
+
externalReferenceInfo.subtype === "import_static" ||
|
|
93
|
+
externalReferenceInfo.subtype === "import_dynamic"
|
|
94
|
+
) {
|
|
95
|
+
urlInfo.data.usesImport = true;
|
|
96
|
+
}
|
|
97
|
+
const [reference] = context.referenceUtils.found({
|
|
98
|
+
node: externalReferenceInfo.node,
|
|
99
|
+
type: externalReferenceInfo.type,
|
|
100
|
+
subtype: externalReferenceInfo.subtype,
|
|
101
|
+
expectedType: externalReferenceInfo.expectedType,
|
|
102
|
+
expectedSubtype: externalReferenceInfo.expectedSubtype || urlInfo.subtype,
|
|
103
|
+
specifier: externalReferenceInfo.specifier,
|
|
104
|
+
specifierStart: externalReferenceInfo.start,
|
|
105
|
+
specifierEnd: externalReferenceInfo.end,
|
|
106
|
+
specifierLine: externalReferenceInfo.line,
|
|
107
|
+
specifierColumn: externalReferenceInfo.column,
|
|
108
|
+
data: externalReferenceInfo.data,
|
|
109
|
+
baseUrl: {
|
|
110
|
+
"StringLiteral": externalReferenceInfo.baseUrl,
|
|
111
|
+
"window.location": urlInfo.url,
|
|
112
|
+
"window.origin": context.rootDirectoryUrl,
|
|
113
|
+
"import.meta.url": urlInfo.url,
|
|
114
|
+
"context.meta.url": urlInfo.url,
|
|
115
|
+
"document.currentScript.src": urlInfo.url,
|
|
116
|
+
}[externalReferenceInfo.baseUrlType],
|
|
117
|
+
assert: externalReferenceInfo.assert,
|
|
118
|
+
assertNode: externalReferenceInfo.assertNode,
|
|
119
|
+
typePropertyNode: externalReferenceInfo.typePropertyNode,
|
|
120
|
+
});
|
|
121
|
+
parallelActions.push(async () => {
|
|
122
|
+
const replacement = await context.referenceUtils.readGeneratedSpecifier(
|
|
123
|
+
reference,
|
|
124
|
+
);
|
|
125
|
+
magicSource.replace({
|
|
126
|
+
start: externalReferenceInfo.start,
|
|
127
|
+
end: externalReferenceInfo.end,
|
|
128
|
+
replacement,
|
|
129
|
+
});
|
|
130
|
+
if (reference.mutation) {
|
|
131
|
+
reference.mutation(magicSource);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
const jsReferenceInfos = await parseJsUrls({
|
|
136
|
+
js: urlInfo.content,
|
|
137
|
+
url: urlInfo.originalUrl,
|
|
138
|
+
isJsModule: urlInfo.type === "js_module",
|
|
139
|
+
isWebWorker: isWebWorkerUrlInfo(urlInfo),
|
|
140
|
+
inlineContent,
|
|
141
|
+
});
|
|
142
|
+
for (const jsReferenceInfo of jsReferenceInfos) {
|
|
143
|
+
if (jsReferenceInfo.isInline) {
|
|
144
|
+
onInlineReference(jsReferenceInfo);
|
|
145
|
+
} else {
|
|
146
|
+
onExternalReference(jsReferenceInfo);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (parallelActions.length > 0) {
|
|
150
|
+
await Promise.all(parallelActions.map((action) => action()));
|
|
151
|
+
}
|
|
152
|
+
if (sequentialActions.length > 0) {
|
|
153
|
+
await sequentialActions.reduce(async (previous, action) => {
|
|
154
|
+
await previous;
|
|
155
|
+
await action();
|
|
156
|
+
}, Promise.resolve());
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const { content, sourcemap } = magicSource.toContentAndSourcemap();
|
|
160
|
+
return { content, sourcemap };
|
|
161
|
+
};
|