@jsenv/core 38.2.11 → 38.3.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 +3 -3
- package/dist/jsenv_core.js +1388 -1371
- package/package.json +1 -1
- package/src/dev/file_service.js +1 -1
- package/src/kitchen/url_graph/references.js +2 -2
- package/src/kitchen/url_graph/url_graph.js +14 -4
- package/src/plugins/autoreload/jsenv_plugin_autoreload_server.js +10 -2
- package/src/plugins/plugins.js +0 -2
- package/src/plugins/reference_analysis/html/jsenv_plugin_html_reference_analysis.js +505 -310
- package/src/plugins/ribbon/jsenv_plugin_ribbon.js +2 -2
- package/src/plugins/importmap/jsenv_plugin_importmap.js +0 -205
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
parseHtmlString,
|
|
3
3
|
stringifyHtmlAst,
|
|
4
4
|
createHtmlNode,
|
|
5
|
-
|
|
5
|
+
injectHtmlNodeAsEarlyAsPossible,
|
|
6
6
|
} from "@jsenv/ast";
|
|
7
7
|
import { URL_META } from "@jsenv/url-meta";
|
|
8
8
|
import { asUrlWithoutSearch } from "@jsenv/urls";
|
|
@@ -54,7 +54,7 @@ export const jsenvPluginRibbon = ({
|
|
|
54
54
|
null,
|
|
55
55
|
" ",
|
|
56
56
|
);
|
|
57
|
-
|
|
57
|
+
injectHtmlNodeAsEarlyAsPossible(
|
|
58
58
|
htmlAst,
|
|
59
59
|
createHtmlNode({
|
|
60
60
|
tagName: "script",
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Plugin to read and apply importmap files found in html files.
|
|
3
|
-
* - feeds importmap files to jsenv kitchen
|
|
4
|
-
* - use importmap to resolve import (when there is one + fallback to other resolution mecanism)
|
|
5
|
-
* - inline importmap with [src=""]
|
|
6
|
-
*
|
|
7
|
-
* A correct importmap resolution should scope importmap resolution per html file.
|
|
8
|
-
* It would be doable by adding ?html_id to each js file in order to track
|
|
9
|
-
* the html file importing it.
|
|
10
|
-
* Considering it happens only when all the following conditions are met:
|
|
11
|
-
* - 2+ html files are using an importmap
|
|
12
|
-
* - the importmap used is not the same
|
|
13
|
-
* - the importmap contain conflicting mappings
|
|
14
|
-
* - these html files are both executed during the same scenario (dev, test, build)
|
|
15
|
-
* And that it would be ugly to see ?html_id all over the place
|
|
16
|
-
* -> The importmap resolution implemented here takes a shortcut and does the following:
|
|
17
|
-
* - All importmap found are merged into a single one that is applied to every import specifiers
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
resolveImport,
|
|
22
|
-
composeTwoImportMaps,
|
|
23
|
-
normalizeImportMap,
|
|
24
|
-
} from "@jsenv/importmap";
|
|
25
|
-
import {
|
|
26
|
-
parseHtmlString,
|
|
27
|
-
stringifyHtmlAst,
|
|
28
|
-
findHtmlNode,
|
|
29
|
-
getHtmlNodeAttribute,
|
|
30
|
-
setHtmlNodeAttributes,
|
|
31
|
-
getHtmlNodePosition,
|
|
32
|
-
getHtmlNodeText,
|
|
33
|
-
setHtmlNodeText,
|
|
34
|
-
removeHtmlNode,
|
|
35
|
-
getUrlForContentInsideHtml,
|
|
36
|
-
} from "@jsenv/ast";
|
|
37
|
-
|
|
38
|
-
export const jsenvPluginImportmap = () => {
|
|
39
|
-
let finalImportmap = null;
|
|
40
|
-
const importmaps = {};
|
|
41
|
-
const onHtmlImportmapParsed = (importmap, htmlUrl) => {
|
|
42
|
-
importmaps[htmlUrl] = importmap
|
|
43
|
-
? normalizeImportMap(importmap, htmlUrl)
|
|
44
|
-
: null;
|
|
45
|
-
finalImportmap = Object.keys(importmaps).reduce((previous, url) => {
|
|
46
|
-
const importmap = importmaps[url];
|
|
47
|
-
if (!previous) {
|
|
48
|
-
return importmap;
|
|
49
|
-
}
|
|
50
|
-
if (!importmap) {
|
|
51
|
-
return previous;
|
|
52
|
-
}
|
|
53
|
-
return composeTwoImportMaps(previous, importmap);
|
|
54
|
-
}, null);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
name: "jsenv:importmap",
|
|
59
|
-
appliesDuring: "*",
|
|
60
|
-
resolveReference: {
|
|
61
|
-
js_import: (reference) => {
|
|
62
|
-
if (!finalImportmap) {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
let fromMapping = false;
|
|
67
|
-
const result = resolveImport({
|
|
68
|
-
specifier: reference.specifier,
|
|
69
|
-
importer: reference.ownerUrlInfo.url,
|
|
70
|
-
importMap: finalImportmap,
|
|
71
|
-
onImportMapping: () => {
|
|
72
|
-
fromMapping = true;
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
if (fromMapping) {
|
|
76
|
-
return result;
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
79
|
-
} catch (e) {
|
|
80
|
-
if (e.message.includes("bare specifier")) {
|
|
81
|
-
// in theory we should throw to be compliant with web behaviour
|
|
82
|
-
// but for now it's simpler to return null
|
|
83
|
-
// and let a chance to other plugins to handle the bare specifier
|
|
84
|
-
// (node esm resolution)
|
|
85
|
-
// and we want importmap to be prio over node esm so we cannot put this plugin after
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
throw e;
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
transformUrlContent: {
|
|
93
|
-
html: async (htmlUrlInfo) => {
|
|
94
|
-
const htmlAst = parseHtmlString(htmlUrlInfo.content);
|
|
95
|
-
const importmap = findHtmlNode(htmlAst, (node) => {
|
|
96
|
-
if (node.nodeName !== "script") {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
const type = getHtmlNodeAttribute(node, "type");
|
|
100
|
-
if (type === undefined || type !== "importmap") {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
return true;
|
|
104
|
-
});
|
|
105
|
-
if (!importmap) {
|
|
106
|
-
onHtmlImportmapParsed(null, htmlUrlInfo.url);
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
const handleInlineImportmap = async (importmap, htmlNodeText) => {
|
|
110
|
-
const { line, column, isOriginal } = getHtmlNodePosition(importmap, {
|
|
111
|
-
preferOriginal: true,
|
|
112
|
-
});
|
|
113
|
-
const inlineImportmapUrl = getUrlForContentInsideHtml(importmap, {
|
|
114
|
-
htmlUrl: htmlUrlInfo.url,
|
|
115
|
-
});
|
|
116
|
-
const inlineImportmapReference = htmlUrlInfo.dependencies.foundInline(
|
|
117
|
-
{
|
|
118
|
-
type: "script",
|
|
119
|
-
isOriginalPosition: isOriginal,
|
|
120
|
-
specifierLine: line - 1,
|
|
121
|
-
specifierColumn: column,
|
|
122
|
-
specifier: inlineImportmapUrl,
|
|
123
|
-
contentType: "application/importmap+json",
|
|
124
|
-
content: htmlNodeText,
|
|
125
|
-
},
|
|
126
|
-
);
|
|
127
|
-
const inlineImportmapUrlInfo = inlineImportmapReference.urlInfo;
|
|
128
|
-
await inlineImportmapUrlInfo.cook();
|
|
129
|
-
setHtmlNodeText(importmap, inlineImportmapUrlInfo.content, {
|
|
130
|
-
indentation: "auto",
|
|
131
|
-
});
|
|
132
|
-
setHtmlNodeAttributes(importmap, {
|
|
133
|
-
"jsenv-cooked-by": "jsenv:importmap",
|
|
134
|
-
});
|
|
135
|
-
onHtmlImportmapParsed(
|
|
136
|
-
JSON.parse(inlineImportmapUrlInfo.content),
|
|
137
|
-
htmlUrlInfo.url,
|
|
138
|
-
);
|
|
139
|
-
};
|
|
140
|
-
const handleImportmapWithSrc = async (importmap, src) => {
|
|
141
|
-
// Browser would throw on remote importmap
|
|
142
|
-
// and won't sent a request to the server for it
|
|
143
|
-
// We must precook the importmap to know its content and inline it into the HTML
|
|
144
|
-
// In this situation the ref to the importmap was already discovered
|
|
145
|
-
// when parsing the HTML
|
|
146
|
-
let importmapReference = null;
|
|
147
|
-
for (const referenceToOther of htmlUrlInfo.referenceToOthersSet) {
|
|
148
|
-
if (referenceToOther.generatedSpecifier === src) {
|
|
149
|
-
importmapReference = referenceToOther;
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
const { line, column, isOriginal } = getHtmlNodePosition(importmap, {
|
|
154
|
-
preferOriginal: true,
|
|
155
|
-
});
|
|
156
|
-
const importmapInlineUrl = getUrlForContentInsideHtml(importmap, {
|
|
157
|
-
htmlUrl: htmlUrlInfo.url,
|
|
158
|
-
});
|
|
159
|
-
const importmapReferenceInlined = importmapReference.inline({
|
|
160
|
-
line: line - 1,
|
|
161
|
-
column,
|
|
162
|
-
isOriginal,
|
|
163
|
-
specifier: importmapInlineUrl,
|
|
164
|
-
contentType: "application/importmap+json",
|
|
165
|
-
});
|
|
166
|
-
const importmapInlineUrlInfo = importmapReferenceInlined.urlInfo;
|
|
167
|
-
await importmapInlineUrlInfo.cook();
|
|
168
|
-
onHtmlImportmapParsed(
|
|
169
|
-
JSON.parse(importmapInlineUrlInfo.content),
|
|
170
|
-
htmlUrlInfo.url,
|
|
171
|
-
);
|
|
172
|
-
setHtmlNodeText(importmap, importmapInlineUrlInfo.content, {
|
|
173
|
-
indentation: "auto",
|
|
174
|
-
});
|
|
175
|
-
setHtmlNodeAttributes(importmap, {
|
|
176
|
-
"src": undefined,
|
|
177
|
-
"jsenv-inlined-by": "jsenv:importmap",
|
|
178
|
-
"inlined-from-src": src,
|
|
179
|
-
});
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const src = getHtmlNodeAttribute(importmap, "src");
|
|
183
|
-
if (src) {
|
|
184
|
-
await handleImportmapWithSrc(importmap, src);
|
|
185
|
-
} else {
|
|
186
|
-
const htmlNodeText = getHtmlNodeText(importmap);
|
|
187
|
-
if (htmlNodeText) {
|
|
188
|
-
await handleInlineImportmap(importmap, htmlNodeText);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
// once this plugin knows the importmap, it will use it
|
|
192
|
-
// to map imports. These import specifiers will be normalized
|
|
193
|
-
// by "formatReferencedUrl" making the importmap presence useless.
|
|
194
|
-
// In dev/test we keep importmap into the HTML to see it even if useless
|
|
195
|
-
// Duing build we get rid of it
|
|
196
|
-
if (htmlUrlInfo.context.build) {
|
|
197
|
-
removeHtmlNode(importmap);
|
|
198
|
-
}
|
|
199
|
-
return {
|
|
200
|
-
content: stringifyHtmlAst(htmlAst),
|
|
201
|
-
};
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
};
|
|
205
|
-
};
|