@jsenv/core 38.2.11 → 38.3.0

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.
@@ -2,7 +2,7 @@ import {
2
2
  parseHtmlString,
3
3
  stringifyHtmlAst,
4
4
  createHtmlNode,
5
- injectHtmlNode,
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
- injectHtmlNode(
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
- };