@jsenv/plugin-transpilation 1.2.2 → 1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/plugin-transpilation",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "TODO",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -53,8 +53,8 @@
53
53
  "@babel/plugin-transform-unicode-regex": "7.22.5",
54
54
  "babel-plugin-transform-async-to-promises": "0.8.18",
55
55
  "construct-style-sheets-polyfill": "3.1.0",
56
- "@jsenv/ast": "5.0.1",
57
- "@jsenv/runtime-compat": "1.1.0",
56
+ "@jsenv/ast": "5.1.0",
57
+ "@jsenv/runtime-compat": "1.2.0",
58
58
  "lightningcss": "1.21.5"
59
59
  }
60
60
  }
@@ -16,10 +16,10 @@ import { convertJsClassicToJsModule } from "./convert_js_classic_to_js_module.js
16
16
  export const jsenvPluginAsJsModule = () => {
17
17
  const markAsJsModuleProxy = (reference) => {
18
18
  reference.expectedType = "js_module";
19
- if (!reference.filename) {
19
+ if (!reference.filenameHint) {
20
20
  const filename = urlToFilename(reference.url);
21
21
  const [basename] = splitFileExtension(filename);
22
- reference.filename = `${basename}.mjs`;
22
+ reference.filenameHint = `${basename}.mjs`;
23
23
  }
24
24
  };
25
25
 
@@ -41,7 +41,7 @@ export const jsenvPluginAsJsModule = () => {
41
41
  if (!jsClassicUrlInfo) {
42
42
  return null;
43
43
  }
44
- await jsClassicUrlInfo.fetchContent();
44
+ await jsClassicUrlInfo.cook();
45
45
  const { content, sourcemap } = await convertJsClassicToJsModule({
46
46
  input: jsClassicUrlInfo.content,
47
47
  inputSourcemap: jsClassicUrlInfo.sourcemap,
@@ -19,33 +19,22 @@ export const jsenvPluginImportAssertions = ({
19
19
  text = "auto",
20
20
  }) => {
21
21
  const transpilations = { json, css, text };
22
- const shouldTranspileImportAssertion = (reference, type) => {
23
- const transpilation = transpilations[type];
24
- if (transpilation === true) {
25
- return true;
26
- }
27
- if (transpilation === "auto") {
28
- return !reference.ownerUrlInfo.context.isSupportedOnCurrentClients(
29
- `import_type_${type}`,
30
- );
31
- }
32
- return false;
33
- };
34
22
  const markAsJsModuleProxy = (reference) => {
35
23
  reference.expectedType = "js_module";
36
- if (!reference.filename) {
37
- reference.filename = `${urlToFilename(reference.url)}.js`;
24
+ if (!reference.filenameHint) {
25
+ reference.filenameHint = `${urlToFilename(reference.url)}.js`;
38
26
  }
39
27
  };
40
28
  const turnIntoJsModuleProxy = (reference, type) => {
41
29
  reference.mutation = (magicSource) => {
42
- const { importTypeAttributeNode } = reference.astInfo;
43
30
  if (reference.subtype === "import_dynamic") {
31
+ const { importTypeAttributeNode } = reference.astInfo;
44
32
  magicSource.remove({
45
33
  start: importTypeAttributeNode.start,
46
34
  end: importTypeAttributeNode.end,
47
35
  });
48
36
  } else {
37
+ const { importTypeAttributeNode } = reference.astInfo;
49
38
  const content = reference.ownerUrlInfo.content;
50
39
  const assertKeyboardStart = content.indexOf(
51
40
  "assert",
@@ -64,66 +53,86 @@ export const jsenvPluginImportAssertions = ({
64
53
  const newUrl = injectQueryParams(reference.url, {
65
54
  [`as_${type}_module`]: "",
66
55
  });
67
- markAsJsModuleProxy(reference);
56
+ markAsJsModuleProxy(reference, type);
68
57
  return newUrl;
69
58
  };
70
59
 
71
- const importAssertions = {
72
- name: "jsenv:import_assertions",
73
- appliesDuring: "*",
74
- init: (context) => {
75
- // transpilation is forced during build so that
76
- // - avoid rollup to see import assertions
77
- // We would have to tell rollup to ignore import with assertion
78
- // - means rollup can bundle more js file together
79
- // - means url versioning can work for css inlined in js
80
- if (context.build) {
81
- transpilations.json = true;
82
- transpilations.css = true;
83
- transpilations.text = true;
84
- }
85
- },
86
- redirectReference: (reference) => {
87
- if (!reference.importAttributes) {
60
+ const createImportTypePlugin = ({ type, createUrlContent }) => {
61
+ return {
62
+ name: `jsenv:import_type_${type}`,
63
+ appliesDuring: "*",
64
+ init: (context) => {
65
+ // transpilation is forced during build so that
66
+ // - avoid rollup to see import assertions
67
+ // We would have to tell rollup to ignore import with assertion
68
+ // - means rollup can bundle more js file together
69
+ // - means url versioning can work for css inlined in js
70
+ if (context.build) {
71
+ return true;
72
+ }
73
+ const transpilation = transpilations[type];
74
+ if (transpilation === "auto") {
75
+ return !context.isSupportedOnCurrentClients(`import_type_${type}`);
76
+ }
77
+ return transpilation;
78
+ },
79
+ redirectReference: (reference) => {
80
+ if (!reference.importAttributes) {
81
+ return null;
82
+ }
83
+ const { searchParams } = reference;
84
+ if (searchParams.has(`as_${type}_module`)) {
85
+ markAsJsModuleProxy(reference, type);
86
+ return null;
87
+ }
88
+ // when search param is injected, it will be removed later
89
+ // by "getWithoutSearchParam". We don't want to redirect again
90
+ // (would create infinite recursion)
91
+ if (
92
+ reference.prev &&
93
+ reference.prev.searchParams.has(`as_${type}_module`)
94
+ ) {
95
+ return null;
96
+ }
97
+ if (reference.importAttributes.type === type) {
98
+ return turnIntoJsModuleProxy(reference, type);
99
+ }
88
100
  return null;
89
- }
90
- const { searchParams } = reference;
91
- if (
92
- searchParams.has("as_json_module") ||
93
- searchParams.has("as_css_module") ||
94
- searchParams.has("as_text_module")
95
- ) {
96
- markAsJsModuleProxy(reference);
97
- return null;
98
- }
99
- const type = reference.importAttributes.type;
100
- if (shouldTranspileImportAssertion(reference, type)) {
101
- return turnIntoJsModuleProxy(reference, type);
102
- }
103
- return null;
104
- },
101
+ },
102
+ fetchUrlContent: async (urlInfo) => {
103
+ const originalUrlInfo = urlInfo.getWithoutSearchParam(
104
+ `as_${type}_module`,
105
+ {
106
+ expectedType: "json",
107
+ },
108
+ );
109
+ if (!originalUrlInfo) {
110
+ return null;
111
+ }
112
+ await originalUrlInfo.cook();
113
+ return createUrlContent(originalUrlInfo);
114
+ },
115
+ };
105
116
  };
106
- return [importAssertions, ...jsenvPluginAsModules()];
107
- };
108
117
 
109
- const jsenvPluginAsModules = () => {
110
- const asJsonModule = {
111
- name: `jsenv:as_json_module`,
112
- appliesDuring: "*",
113
- fetchUrlContent: async (urlInfo) => {
114
- const jsonUrlInfo = urlInfo.getWithoutSearchParam("as_json_module", {
115
- expectedType: "json",
116
- });
117
- if (!jsonUrlInfo) {
118
- return null;
119
- }
120
- await jsonUrlInfo.fetchContent();
118
+ const asJsonModule = createImportTypePlugin({
119
+ type: "json",
120
+ createUrlContent: (jsonUrlInfo) => {
121
121
  const jsonText = JSON.stringify(jsonUrlInfo.content.trim());
122
+ let inlineContentCall;
123
+ // here we could `export default ${jsonText}`:
124
+ // but js engine are optimized to recognize JSON.parse
125
+ // and use a faster parsing strategy
126
+ if (jsonUrlInfo.context.dev) {
127
+ inlineContentCall = `JSON.parse(
128
+ ${jsonText},
129
+ //# inlinedFromUrl=${jsonUrlInfo.url}
130
+ )`;
131
+ } else {
132
+ inlineContentCall = `JSON.parse(${jsonText})`;
133
+ }
122
134
  return {
123
- // here we could `export default ${jsonText}`:
124
- // but js engine are optimized to recognize JSON.parse
125
- // and use a faster parsing strategy
126
- content: `export default JSON.parse(${jsonText})`,
135
+ content: `export default ${inlineContentCall};`,
127
136
  contentType: "text/javascript",
128
137
  type: "js_module",
129
138
  originalUrl: jsonUrlInfo.originalUrl,
@@ -131,33 +140,35 @@ const jsenvPluginAsModules = () => {
131
140
  data: jsonUrlInfo.data,
132
141
  };
133
142
  },
134
- };
143
+ });
135
144
 
136
- const asCssModule = {
137
- name: `jsenv:as_css_module`,
138
- appliesDuring: "*",
139
- fetchUrlContent: async (urlInfo) => {
140
- const cssUrlInfo = urlInfo.getWithoutSearchParam("as_css_module", {
141
- expectedType: "css",
142
- });
143
- if (!cssUrlInfo) {
144
- return null;
145
- }
146
- await cssUrlInfo.fetchContent();
145
+ const asCssModule = createImportTypePlugin({
146
+ type: "css",
147
+ createUrlContent: (cssUrlInfo) => {
147
148
  const cssText = JS_QUOTES.escapeSpecialChars(cssUrlInfo.content, {
148
149
  // If template string is choosen and runtime do not support template literals
149
150
  // it's ok because "jsenv:new_inline_content" plugin executes after this one
150
151
  // and convert template strings into raw strings
151
152
  canUseTemplateString: true,
152
153
  });
154
+ let inlineContentCall;
155
+ if (cssUrlInfo.context.dev) {
156
+ inlineContentCall = `new __InlineContent__(
157
+ ${cssText},
158
+ { type: "text/css" },
159
+ //# inlinedFromUrl=${cssUrlInfo.url}
160
+ )`;
161
+ } else {
162
+ inlineContentCall = `new __InlineContent__(${cssText}, { type: "text/css" })`;
163
+ }
153
164
  return {
154
- content: `import ${JSON.stringify(
155
- urlInfo.context.inlineContentClientFileUrl,
156
- )};
165
+ content: `
166
+ import ${JSON.stringify(cssUrlInfo.context.inlineContentClientFileUrl)};
157
167
 
158
- const inlineContent = new __InlineContent__(${cssText}, { type: "text/css" });
168
+ const inlineContent = ${inlineContentCall};
159
169
  const stylesheet = new CSSStyleSheet();
160
170
  stylesheet.replaceSync(inlineContent.text);
171
+
161
172
  export default stylesheet;`,
162
173
  contentType: "text/javascript",
163
174
  type: "js_module",
@@ -166,31 +177,33 @@ export default stylesheet;`,
166
177
  data: cssUrlInfo.data,
167
178
  };
168
179
  },
169
- };
180
+ });
170
181
 
171
- const asTextModule = {
172
- name: `jsenv:as_text_module`,
173
- appliesDuring: "*",
174
- fetchUrlContent: async (urlInfo) => {
175
- const textUrlInfo = urlInfo.getWithoutSearchParam("as_text_module", {
176
- expectedType: "text",
177
- });
178
- if (!textUrlInfo) {
179
- return null;
180
- }
181
- await textUrlInfo.fetchContent();
182
- const textPlain = JS_QUOTES.escapeSpecialChars(urlInfo.content, {
182
+ const asTextModule = createImportTypePlugin({
183
+ type: "text",
184
+ createUrlContent: (textUrlInfo) => {
185
+ const textPlain = JS_QUOTES.escapeSpecialChars(textUrlInfo.content, {
183
186
  // If template string is choosen and runtime do not support template literals
184
187
  // it's ok because "jsenv:new_inline_content" plugin executes after this one
185
188
  // and convert template strings into raw strings
186
189
  canUseTemplateString: true,
187
190
  });
191
+ let inlineContentCall;
192
+ if (textUrlInfo.context.dev) {
193
+ inlineContentCall = `new __InlineContent__(
194
+ ${textPlain},
195
+ { type: "text/plain"},
196
+ //# inlinedFromUrl=${textUrlInfo.url}
197
+ )`;
198
+ } else {
199
+ inlineContentCall = `new __InlineContent__(${textPlain}, { type: "text/plain"})`;
200
+ }
188
201
  return {
189
- content: `import ${JSON.stringify(
190
- urlInfo.context.inlineContentClientFileUrl,
191
- )};
202
+ content: `
203
+ import ${JSON.stringify(textUrlInfo.context.inlineContentClientFileUrl)};
204
+
205
+ const inlineContent = ${inlineContentCall};
192
206
 
193
- const inlineContent = new InlineContent(${textPlain}, { type: "text/plain" });
194
207
  export default inlineContent.text;`,
195
208
  contentType: "text/javascript",
196
209
  type: "js_module",
@@ -199,7 +212,7 @@ export default inlineContent.text;`,
199
212
  data: textUrlInfo.data,
200
213
  };
201
214
  },
202
- };
215
+ });
203
216
 
204
217
  return [asJsonModule, asCssModule, asTextModule];
205
218
  };
@@ -1,6 +1,6 @@
1
1
  import { createMagicSource } from "@jsenv/sourcemap";
2
2
 
3
- export const jsenvPluginImportMetaResolve = () => {
3
+ export const jsenvPluginImportMetaResolve = ({ needJsModuleFallback }) => {
4
4
  return {
5
5
  name: "jsenv:import_meta_resolve",
6
6
  appliesDuring: "*",
@@ -8,8 +8,8 @@ export const jsenvPluginImportMetaResolve = () => {
8
8
  if (context.isSupportedOnCurrentClients("import_meta_resolve")) {
9
9
  return false;
10
10
  }
11
- // keep it untouched, systemjs will handle it
12
- if (context.systemJsTranspilation) {
11
+ if (needJsModuleFallback(context)) {
12
+ // will be handled by systemjs, keep it untouched
13
13
  return false;
14
14
  }
15
15
  return true;
@@ -9,7 +9,7 @@ import {
9
9
  systemJsClientFileUrlDefault,
10
10
  } from "@jsenv/js-module-fallback";
11
11
 
12
- export const jsenvPluginJsModuleConversion = () => {
12
+ export const jsenvPluginJsModuleConversion = ({ remapImportSpecifier }) => {
13
13
  const isReferencingJsModule = (reference) => {
14
14
  if (
15
15
  reference.type === "js_import" ||
@@ -35,17 +35,16 @@ export const jsenvPluginJsModuleConversion = () => {
35
35
 
36
36
  const markAsJsClassicProxy = (reference) => {
37
37
  reference.expectedType = "js_classic";
38
- if (!reference.filename) {
39
- reference.filename = generateJsClassicFilename(reference.url);
38
+ if (!reference.filenameHint) {
39
+ reference.filenameHint = generateJsClassicFilename(reference.url);
40
40
  }
41
41
  };
42
42
 
43
43
  const turnIntoJsClassicProxy = (reference) => {
44
- const urlTransformed = injectQueryParams(reference.url, {
44
+ markAsJsClassicProxy(reference);
45
+ return injectQueryParams(reference.url, {
45
46
  js_module_fallback: "",
46
47
  });
47
- markAsJsClassicProxy(reference);
48
- return urlTransformed;
49
48
  };
50
49
 
51
50
  return {
@@ -56,6 +55,15 @@ export const jsenvPluginJsModuleConversion = () => {
56
55
  markAsJsClassicProxy(reference);
57
56
  return null;
58
57
  }
58
+ // when search param is injected, it will be removed later
59
+ // by "getWithoutSearchParam". We don't want to redirect again
60
+ // (would create infinite recursion)
61
+ if (
62
+ reference.prev &&
63
+ reference.prev.searchParams.has(`js_module_fallback`)
64
+ ) {
65
+ return null;
66
+ }
59
67
  // We want to propagate transformation of js module to js classic to:
60
68
  // - import specifier (static/dynamic import + re-export)
61
69
  // - url specifier when inside System.register/_context.import()
@@ -80,7 +88,7 @@ export const jsenvPluginJsModuleConversion = () => {
80
88
  if (!jsModuleUrlInfo) {
81
89
  return null;
82
90
  }
83
- await jsModuleUrlInfo.fetchContent();
91
+ await jsModuleUrlInfo.cook();
84
92
  let outputFormat;
85
93
  if (urlInfo.isEntryPoint && !jsModuleUrlInfo.data.usesImport) {
86
94
  // if it's an entry point without dependency (it does not use import)
@@ -107,6 +115,7 @@ export const jsenvPluginJsModuleConversion = () => {
107
115
  inputUrl: jsModuleUrlInfo.url,
108
116
  outputUrl: urlInfo.url,
109
117
  outputFormat,
118
+ remapImportSpecifier,
110
119
  });
111
120
  return {
112
121
  content,
@@ -130,6 +139,7 @@ const generateJsClassicFilename = (url) => {
130
139
  searchParams.has("as_css_module") ||
131
140
  searchParams.has("as_text_module")
132
141
  ) {
142
+ basename += extension;
133
143
  extension = ".js";
134
144
  }
135
145
  return `${basename}.nomodule${extension}`;
@@ -1,11 +1,40 @@
1
1
  import { jsenvPluginJsModuleConversion } from "./jsenv_plugin_js_module_conversion.js";
2
2
  import { jsenvPluginJsModuleFallbackInsideHtml } from "./jsenv_plugin_js_module_fallback_inside_html.js";
3
3
  import { jsenvPluginJsModuleFallbackOnWorkers } from "./jsenv_plugin_js_module_fallback_on_workers.js";
4
+ import { jsenvPluginImportMetaResolve } from "./jsenv_plugin_import_meta_resolve.js";
5
+ import { jsenvPluginTopLevelAwait } from "./jsenv_plugin_top_level_await.js";
6
+
7
+ export const jsenvPluginJsModuleFallback = ({ remapImportSpecifier } = {}) => {
8
+ const needJsModuleFallback = (context) => {
9
+ if (Object.keys(context.clientRuntimeCompat).includes("node")) {
10
+ return false;
11
+ }
12
+ if (
13
+ context.versioning &&
14
+ context.versioningViaImportmap &&
15
+ !context.isSupportedOnCurrentClients("importmap")
16
+ ) {
17
+ return true;
18
+ }
19
+ if (
20
+ !context.isSupportedOnCurrentClients("script_type_module") ||
21
+ !context.isSupportedOnCurrentClients("import_dynamic") ||
22
+ !context.isSupportedOnCurrentClients("import_meta")
23
+ ) {
24
+ return true;
25
+ }
26
+ return false;
27
+ };
4
28
 
5
- export const jsenvPluginJsModuleFallback = () => {
6
29
  return [
7
- jsenvPluginJsModuleFallbackInsideHtml(),
30
+ jsenvPluginJsModuleFallbackInsideHtml({ needJsModuleFallback }),
8
31
  jsenvPluginJsModuleFallbackOnWorkers(),
9
- jsenvPluginJsModuleConversion(),
32
+ jsenvPluginJsModuleConversion({ remapImportSpecifier }),
33
+ // must come after jsModuleFallback because it's related to the module format
34
+ // so we want to want to know the module format before transforming things
35
+ // - top level await
36
+ // - import.meta.resolve()
37
+ jsenvPluginImportMetaResolve({ needJsModuleFallback }),
38
+ jsenvPluginTopLevelAwait({ needJsModuleFallback }),
10
39
  ];
11
40
  };
@@ -13,34 +13,32 @@ import {
13
13
  getHtmlNodeAttribute,
14
14
  setHtmlNodeAttributes,
15
15
  analyzeScriptNode,
16
+ getHtmlNodeText,
16
17
  } from "@jsenv/ast";
17
18
 
18
- export const jsenvPluginJsModuleFallbackInsideHtml = () => {
19
+ export const jsenvPluginJsModuleFallbackInsideHtml = ({
20
+ needJsModuleFallback,
21
+ }) => {
19
22
  const turnIntoJsClassicProxy = (reference) => {
20
- // not needed for now: redirections are disabled
21
- // for getWithoutSearchParam
22
- // if (
23
- // reference.prev &&
24
- // reference.prev.searchParams.has("js_module_fallback")
25
- // ) {
26
- // return null;
27
- // }
28
23
  return injectQueryParams(reference.url, { js_module_fallback: "" });
29
24
  };
30
25
 
31
26
  return {
32
27
  name: "jsenv:js_module_fallback_inside_html",
33
28
  appliesDuring: "*",
29
+ init: needJsModuleFallback,
34
30
  redirectReference: {
35
31
  link_href: (reference) => {
36
32
  if (
37
- reference.ownerUrlInfo.context.systemJsTranspilation &&
38
- reference.subtype === "modulepreload"
33
+ reference.prev &&
34
+ reference.prev.searchParams.has(`js_module_fallback`)
39
35
  ) {
36
+ return null;
37
+ }
38
+ if (reference.subtype === "modulepreload") {
40
39
  return turnIntoJsClassicProxy(reference);
41
40
  }
42
41
  if (
43
- reference.ownerUrlInfo.context.systemJsTranspilation &&
44
42
  reference.subtype === "preload" &&
45
43
  reference.expectedType === "js_module"
46
44
  ) {
@@ -50,18 +48,24 @@ export const jsenvPluginJsModuleFallbackInsideHtml = () => {
50
48
  },
51
49
  script: (reference) => {
52
50
  if (
53
- reference.ownerUrlInfo.context.systemJsTranspilation &&
54
- reference.expectedType === "js_module"
51
+ reference.prev &&
52
+ reference.prev.searchParams.has(`js_module_fallback`)
55
53
  ) {
54
+ return null;
55
+ }
56
+ if (reference.expectedType === "js_module") {
56
57
  return turnIntoJsClassicProxy(reference);
57
58
  }
58
59
  return null;
59
60
  },
60
61
  js_url: (reference) => {
61
62
  if (
62
- reference.ownerUrlInfo.context.systemJsTranspilation &&
63
- reference.expectedType === "js_module"
63
+ reference.prev &&
64
+ reference.prev.searchParams.has(`js_module_fallback`)
64
65
  ) {
66
+ return null;
67
+ }
68
+ if (reference.expectedType === "js_module") {
65
69
  return turnIntoJsClassicProxy(reference);
66
70
  }
67
71
  return null;
@@ -92,19 +96,21 @@ export const jsenvPluginJsModuleFallbackInsideHtml = () => {
92
96
  break;
93
97
  }
94
98
  }
95
- if (!wasOrWillBeConvertedToJsClassic(linkHintReference)) {
96
- return;
97
- }
98
99
  if (rel === "modulepreload") {
99
- mutations.push(() => {
100
- setHtmlNodeAttributes(node, {
101
- rel: "preload",
102
- as: "script",
103
- crossorigin: undefined,
100
+ if (linkHintReference.expectedType === "js_classic") {
101
+ mutations.push(() => {
102
+ setHtmlNodeAttributes(node, {
103
+ rel: "preload",
104
+ as: "script",
105
+ crossorigin: undefined,
106
+ });
104
107
  });
105
- });
108
+ }
106
109
  }
107
- if (rel === "preload") {
110
+ if (
111
+ rel === "preload" &&
112
+ wasConvertedFromJsModule(linkHintReference)
113
+ ) {
108
114
  mutations.push(() => {
109
115
  setHtmlNodeAttributes(node, { crossorigin: undefined });
110
116
  });
@@ -116,31 +122,33 @@ export const jsenvPluginJsModuleFallbackInsideHtml = () => {
116
122
  return;
117
123
  }
118
124
  const src = getHtmlNodeAttribute(node, "src");
119
- if (src) {
120
- let scriptTypeModuleReference = null;
121
- for (const referenceToOther of urlInfo.referenceToOthersSet) {
122
- if (
123
- referenceToOther.generatedSpecifier === src &&
124
- referenceToOther.type === "script" &&
125
- referenceToOther.subtype === "js_module"
126
- ) {
127
- scriptTypeModuleReference = referenceToOther;
128
- break;
129
- }
125
+ const text = getHtmlNodeText(node);
126
+ let scriptReference = null;
127
+ for (const referenceToOther of urlInfo.referenceToOthersSet) {
128
+ if (referenceToOther.type !== "script") {
129
+ continue;
130
130
  }
131
- if (!scriptTypeModuleReference) {
132
- return;
131
+ if (src && referenceToOther.generatedSpecifier === src) {
132
+ scriptReference = referenceToOther;
133
+ break;
133
134
  }
134
- if (scriptTypeModuleReference.expectedType === "js_classic") {
135
- mutations.push(() => {
136
- setHtmlNodeAttributes(node, { type: undefined });
137
- });
135
+ if (text) {
136
+ if (referenceToOther.content === text) {
137
+ scriptReference = referenceToOther;
138
+ break;
139
+ }
140
+ if (referenceToOther.urlInfo.content === text) {
141
+ scriptReference = referenceToOther;
142
+ break;
143
+ }
138
144
  }
139
- } else if (urlInfo.context.systemJsTranspilation) {
140
- mutations.push(() => {
141
- setHtmlNodeAttributes(node, { type: undefined });
142
- });
143
145
  }
146
+ if (!wasConvertedFromJsModule(scriptReference)) {
147
+ return;
148
+ }
149
+ mutations.push(() => {
150
+ setHtmlNodeAttributes(node, { type: undefined });
151
+ });
144
152
  },
145
153
  });
146
154
  await Promise.all(mutations.map((mutation) => mutation()));
@@ -152,30 +160,14 @@ export const jsenvPluginJsModuleFallbackInsideHtml = () => {
152
160
  };
153
161
  };
154
162
 
155
- const wasOrWillBeConvertedToJsClassic = (reference) => {
156
- if (reference.expectedType !== "js_module") {
157
- return false;
158
- }
159
- if (willBeConvertedToJsClassic(reference)) {
160
- return true;
161
- }
162
- let prev = reference.prev;
163
- while (prev) {
164
- if (prev.expectedType === "js_classic") {
165
- return true;
166
- }
167
- if (willBeConvertedToJsClassic(prev)) {
168
- return true;
163
+ const wasConvertedFromJsModule = (reference) => {
164
+ if (reference.expectedType === "js_classic") {
165
+ // check if a prev version was using js module
166
+ if (reference.original) {
167
+ if (reference.original.expectedType === "js_module") {
168
+ return true;
169
+ }
169
170
  }
170
- prev = prev.prev;
171
171
  }
172
-
173
172
  return false;
174
173
  };
175
-
176
- const willBeConvertedToJsClassic = (reference) => {
177
- return (
178
- reference.searchParams.has("js_module_fallback") ||
179
- reference.searchParams.has("as_js_classic")
180
- );
181
- };
@@ -26,46 +26,33 @@ export const jsenvPluginJsModuleFallbackOnWorkers = () => {
26
26
  return injectQueryParams(reference.url, { js_module_fallback: "" });
27
27
  };
28
28
 
29
- return {
30
- name: "jsenv:js_module_fallback_on_workers",
31
- appliesDuring: "*",
32
- redirectReference: {
33
- js_url: (reference) => {
34
- if (reference.expectedType !== "js_module") {
35
- return null;
29
+ const createWorkerPlugin = (subtype) => {
30
+ return {
31
+ name: `jsenv:js_module_fallback_on_${subtype}`,
32
+ appliesDuring: "*",
33
+ init: (context) => {
34
+ if (context.isSupportedOnCurrentClients(`${subtype}_type_module`)) {
35
+ return false;
36
36
  }
37
- if (reference.expectedSubtype === "worker") {
38
- if (
39
- reference.ownerUrlInfo.context.isSupportedOnCurrentClients(
40
- "worker_type_module",
41
- )
42
- ) {
43
- return null;
44
- }
45
- return turnIntoJsClassicProxy(reference);
46
- }
47
- if (reference.expectedSubtype === "service_worker") {
48
- if (
49
- reference.ownerUrlInfo.context.isSupportedOnCurrentClients(
50
- "service_worker_type_module",
51
- )
52
- ) {
37
+ return true;
38
+ },
39
+ redirectReference: {
40
+ js_url: (reference) => {
41
+ if (reference.expectedType !== "js_module") {
53
42
  return null;
54
43
  }
55
- return turnIntoJsClassicProxy(reference);
56
- }
57
- if (reference.expectedSubtype === "shared_worker") {
58
- if (
59
- reference.ownerUrlInfo.context.isSupportedOnCurrentClients(
60
- "shared_worker_type_module",
61
- )
62
- ) {
44
+ if (reference.expectedSubtype !== subtype) {
63
45
  return null;
64
46
  }
65
47
  return turnIntoJsClassicProxy(reference);
66
- }
67
- return null;
48
+ },
68
49
  },
69
- },
50
+ };
70
51
  };
52
+
53
+ return [
54
+ createWorkerPlugin("worker"),
55
+ createWorkerPlugin("service_worker"),
56
+ createWorkerPlugin("shared_worker"),
57
+ ];
71
58
  };
@@ -3,7 +3,7 @@ import { applyBabelPlugins } from "@jsenv/ast";
3
3
 
4
4
  const require = createRequire(import.meta.url);
5
5
 
6
- export const jsenvPluginTopLevelAwait = () => {
6
+ export const jsenvPluginTopLevelAwait = ({ needJsModuleFallback }) => {
7
7
  return {
8
8
  name: "jsenv:top_level_await",
9
9
  appliesDuring: "*",
@@ -11,8 +11,8 @@ export const jsenvPluginTopLevelAwait = () => {
11
11
  if (context.isSupportedOnCurrentClients("top_level_await")) {
12
12
  return false;
13
13
  }
14
- // keep it untouched, systemjs will handle it
15
- if (context.systemJsTranspilation) {
14
+ if (needJsModuleFallback(context)) {
15
+ // will be handled by systemjs, keep it untouched
16
16
  return false;
17
17
  }
18
18
  return true;
@@ -11,44 +11,34 @@ import { jsenvPluginImportAssertions } from "./import_assertions/jsenv_plugin_im
11
11
  import { jsenvPluginBabel } from "./babel/jsenv_plugin_babel.js";
12
12
  import { jsenvPluginJsModuleFallback } from "./js_module_fallback/jsenv_plugin_js_module_fallback.js";
13
13
  import { jsenvPluginAsJsModule } from "./as_js_module/jsenv_plugin_as_js_module.js";
14
- import { jsenvPluginTopLevelAwait } from "./top_level_await/jsenv_plugin_top_level_await.js";
15
- import { jsenvPluginImportMetaResolve } from "./import_meta_resolve/jsenv_plugin_import_meta_resolve.js";
16
14
  import { jsenvPluginCssTranspilation } from "./css/jsenv_plugin_css_transpilation.js";
17
15
 
18
16
  export const jsenvPluginTranspilation = ({
19
17
  importAssertions = true,
20
18
  css = true, // TODO
21
- // build sets jsModuleFallbackOnJsClassic: false during first step of the build
19
+ // build sets jsModuleFallback: false during first step of the build
22
20
  // and re-enable it in the second phase (when performing the bundling)
23
21
  // so that bundling is applied on js modules THEN it is converted to js classic if needed
24
- jsModuleFallbackOnJsClassic = true,
25
- topLevelAwait = true,
26
- importMetaResolve = true,
22
+ jsModuleFallback = true,
27
23
  babelHelpersAsImport = true,
28
24
  }) => {
29
25
  if (importAssertions === true) {
30
26
  importAssertions = {};
31
27
  }
32
- if (jsModuleFallbackOnJsClassic === true) {
33
- jsModuleFallbackOnJsClassic = {};
28
+ if (jsModuleFallback === true) {
29
+ jsModuleFallback = {};
34
30
  }
35
31
  return [
36
- ...(importMetaResolve ? [jsenvPluginImportMetaResolve()] : []),
37
- ...(importAssertions
38
- ? [jsenvPluginImportAssertions(importAssertions)]
39
- : []),
40
32
  // babel also so that rollup can bundle babel helpers for instance
41
33
  jsenvPluginBabel({
42
- topLevelAwait,
43
34
  babelHelpersAsImport,
44
35
  }),
45
- ...(jsModuleFallbackOnJsClassic
46
- ? [jsenvPluginJsModuleFallback(jsModuleFallbackOnJsClassic)]
47
- : []),
48
36
  jsenvPluginAsJsModule(),
49
- // topLevelAwait must come after jsModuleFallback because it's related to the module format
50
- // so we want to wait to know the module format before transforming things related to top level await
51
- ...(topLevelAwait ? [jsenvPluginTopLevelAwait(topLevelAwait)] : []),
37
+ ...(jsModuleFallback ? [jsenvPluginJsModuleFallback()] : []),
38
+ ...(importAssertions
39
+ ? [jsenvPluginImportAssertions(importAssertions)]
40
+ : []),
41
+
52
42
  ...(css ? [jsenvPluginCssTranspilation()] : []),
53
43
  ];
54
44
  };