@jsenv/plugin-transpilation 1.0.0 → 1.2.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.
Files changed (25) hide show
  1. package/package.json +26 -26
  2. package/src/as_js_module/jsenv_plugin_as_js_module.js +14 -28
  3. package/src/babel/babel_helper_directory/README.md +1 -1
  4. package/src/babel/babel_helper_directory/babel_helpers/applyDecs2301/applyDecs2301.js +547 -528
  5. package/src/babel/babel_helper_directory/babel_helpers/applyDecs2305/applyDecs2305.js +681 -0
  6. package/src/babel/babel_plugin_structure.js +1 -1
  7. package/src/babel/jsenv_plugin_babel.js +91 -19
  8. package/src/babel/new_stylesheet/babel_plugin_new_stylesheet_injector.js +11 -135
  9. package/src/babel/new_stylesheet/constructable_stylesheet_usage.js +114 -0
  10. package/src/babel/new_stylesheet/new_stylesheet_client_file_url.js +4 -0
  11. package/src/babel/regenerator_runtime/babel_plugin_regenerator_runtime_injector.js +11 -35
  12. package/src/babel/regenerator_runtime/regenerator_runtime_client_file_url.js +4 -0
  13. package/src/babel/regenerator_runtime/regenerator_runtime_usage.js +23 -0
  14. package/src/babel/{polyfill_injection_in_babel_ast.js → side_effect_injection_in_babel_ast.js} +8 -8
  15. package/src/css/jsenv_plugin_css_transpilation.js +2 -2
  16. package/src/import_assertions/jsenv_plugin_import_assertions.js +39 -78
  17. package/src/import_meta_resolve/jsenv_plugin_import_meta_resolve.js +17 -13
  18. package/src/js_module_fallback/jsenv_plugin_js_module_conversion.js +56 -48
  19. package/src/js_module_fallback/jsenv_plugin_js_module_fallback.js +3 -38
  20. package/src/js_module_fallback/jsenv_plugin_js_module_fallback_inside_html.js +63 -122
  21. package/src/js_module_fallback/jsenv_plugin_js_module_fallback_on_workers.js +15 -6
  22. package/src/babel/apply_js_transpilation.js +0 -74
  23. package/src/babel/global_this/babel_plugin_global_this_injector.js +0 -56
  24. package/src/babel/global_this/client/global_this_js_classic.js +0 -25
  25. package/src/babel/global_this/client/global_this_js_module.js +0 -21
@@ -1,29 +1,101 @@
1
- import { applyJsTranspilation } from "./apply_js_transpilation.js";
1
+ import { applyBabelPlugins } from "@jsenv/ast";
2
+
3
+ import { getBaseBabelPluginStructure } from "./babel_plugin_structure.js";
4
+ import { babelPluginBabelHelpersAsJsenvImports } from "./babel_plugin_babel_helpers_as_jsenv_imports.js";
5
+ import { analyzeRegeneratorRuntimeUsage } from "./regenerator_runtime/regenerator_runtime_usage.js";
6
+ import { babelPluginRegeneratorRuntimeInjector } from "./regenerator_runtime/babel_plugin_regenerator_runtime_injector.js";
7
+ import { regeneratorRuntimeClientFileUrl } from "./regenerator_runtime/regenerator_runtime_client_file_url.js";
8
+ import { analyzeConstructableStyleSheetUsage } from "./new_stylesheet/constructable_stylesheet_usage.js";
9
+ import { babelPluginNewStylesheetInjector } from "./new_stylesheet/babel_plugin_new_stylesheet_injector.js";
2
10
 
3
11
  export const jsenvPluginBabel = ({ babelHelpersAsImport = true } = {}) => {
4
- const transformWithBabel = async (urlInfo, context) => {
5
- return applyJsTranspilation({
6
- input: urlInfo.content,
7
- inputIsJsModule: urlInfo.type === "js_module",
8
- inputUrl: urlInfo.originalUrl,
9
- outputUrl: urlInfo.generatedUrl,
10
- babelHelpersAsImport,
11
- babelOptions: {
12
+ const transformWithBabel = async (urlInfo) => {
13
+ const isJsModule = urlInfo.type === "js_module";
14
+ const getImportSpecifier = (clientFileUrl) => {
15
+ const jsImportReference = urlInfo.dependencies.inject({
16
+ type: "js_import",
17
+ expectedType: "js_module",
18
+ specifier: clientFileUrl,
19
+ });
20
+ return JSON.parse(jsImportReference.generatedSpecifier);
21
+ };
22
+ const isSupported = urlInfo.context.isSupportedOnCurrentClients;
23
+ const babelPluginStructure = getBaseBabelPluginStructure({
24
+ url: urlInfo.originalUrl,
25
+ isSupported,
26
+ isJsModule,
27
+ getImportSpecifier,
28
+ });
29
+
30
+ if (!isSupported("async_generator_function")) {
31
+ const regeneratorRuntimeUsage = analyzeRegeneratorRuntimeUsage(urlInfo);
32
+ if (regeneratorRuntimeUsage) {
33
+ if (isJsModule && babelHelpersAsImport) {
34
+ babelPluginStructure["new-stylesheet-injector"] = [
35
+ babelPluginRegeneratorRuntimeInjector,
36
+ { babelHelpersAsImport, getImportSpecifier },
37
+ ];
38
+ } else {
39
+ urlInfo.dependencies.foundSideEffectFile({
40
+ sideEffectFileUrl: regeneratorRuntimeClientFileUrl,
41
+ expectedType: "js_classic",
42
+ specifierLine: regeneratorRuntimeUsage.line,
43
+ specifierColumn: regeneratorRuntimeUsage.column,
44
+ });
45
+ }
46
+ }
47
+ }
48
+ if (!isSupported("new_stylesheet")) {
49
+ const constructableStyleSheetUsage =
50
+ analyzeConstructableStyleSheetUsage(urlInfo);
51
+ if (constructableStyleSheetUsage) {
52
+ if (isJsModule && babelHelpersAsImport) {
53
+ babelPluginStructure["new-stylesheet-injector"] = [
54
+ babelPluginNewStylesheetInjector,
55
+ { babelHelpersAsImport, getImportSpecifier },
56
+ ];
57
+ } else {
58
+ urlInfo.dependencies.foundSideEffectFile({
59
+ sideEffectFileUrl: regeneratorRuntimeClientFileUrl,
60
+ expectedType: "js_classic",
61
+ specifierLine: constructableStyleSheetUsage.line,
62
+ specifierColumn: constructableStyleSheetUsage.column,
63
+ });
64
+ }
65
+ }
66
+ }
67
+ if (
68
+ isJsModule &&
69
+ babelHelpersAsImport &&
70
+ Object.keys(babelPluginStructure).length > 0
71
+ ) {
72
+ babelPluginStructure["babel-helper-as-jsenv-import"] = [
73
+ babelPluginBabelHelpersAsJsenvImports,
74
+ { getImportSpecifier },
75
+ ];
76
+ }
77
+
78
+ const babelPlugins = Object.keys(babelPluginStructure).map(
79
+ (babelPluginName) => babelPluginStructure[babelPluginName],
80
+ );
81
+ const { code, map } = await applyBabelPlugins({
82
+ babelPlugins,
83
+ options: {
12
84
  generatorOpts: {
13
- retainLines: context.dev,
85
+ retainLines: urlInfo.context.dev,
14
86
  },
15
87
  },
16
- runtimeCompat: context.clientRuntimeCompat,
17
- getImportSpecifier: (clientFileUrl) => {
18
- const [reference] = context.referenceUtils.inject({
19
- type: "js_import",
20
- expectedType: "js_module",
21
- specifier: clientFileUrl,
22
- });
23
- return JSON.parse(reference.generatedSpecifier);
24
- },
88
+ input: urlInfo.content,
89
+ inputIsJsModule: isJsModule,
90
+ inputUrl: urlInfo.originalUrl,
91
+ outputUrl: urlInfo.generatedUrl,
25
92
  });
93
+ return {
94
+ content: code,
95
+ sourcemap: map,
96
+ };
26
97
  };
98
+
27
99
  return {
28
100
  name: "jsenv:babel",
29
101
  appliesDuring: "*",
@@ -1,10 +1,5 @@
1
- import { pathToFileURL } from "node:url";
2
- import { injectPolyfillIntoBabelAst } from "../polyfill_injection_in_babel_ast.js";
3
-
4
- const newStylesheetClientFileUrl = new URL(
5
- "./client/new_stylesheet.js",
6
- import.meta.url,
7
- ).href;
1
+ import { injectSideEffectFileIntoBabelAst } from "../side_effect_injection_in_babel_ast.js";
2
+ import { newStylesheetClientFileUrl } from "./new_stylesheet_client_file_url.js";
8
3
 
9
4
  export const babelPluginNewStylesheetInjector = (
10
5
  babel,
@@ -14,136 +9,17 @@ export const babelPluginNewStylesheetInjector = (
14
9
  name: "new-stylesheet-injector",
15
10
  visitor: {
16
11
  Program: (path, state) => {
17
- const { filename } = state;
18
- const fileUrl = pathToFileURL(filename).href;
19
- if (fileUrl === newStylesheetClientFileUrl) {
20
- return;
21
- }
22
- let newStyleSheetDetected = false;
23
- path.traverse({
24
- NewExpression: (path) => {
25
- if (isNewCssStyleSheetCall(path.node)) {
26
- newStyleSheetDetected = true;
27
- path.stop();
28
- }
29
- },
30
- MemberExpression: (path) => {
31
- if (isDocumentAdoptedStyleSheets(path.node)) {
32
- newStyleSheetDetected = true;
33
- path.stop();
34
- }
35
- },
36
- CallExpression: (path) => {
37
- if (path.node.callee.type !== "Import") {
38
- // Some other function call, not import();
39
- return;
40
- }
41
- if (path.node.arguments[0].type !== "StringLiteral") {
42
- // Non-string argument, probably a variable or expression, e.g.
43
- // import(moduleId)
44
- // import('./' + moduleName)
45
- return;
46
- }
47
- const sourcePath = path.get("arguments")[0];
48
- if (
49
- hasCssModuleQueryParam(sourcePath) ||
50
- hasImportTypeCssAssertion(path)
51
- ) {
52
- newStyleSheetDetected = true;
53
- path.stop();
54
- }
55
- },
56
- ImportDeclaration: (path) => {
57
- const sourcePath = path.get("source");
58
- if (
59
- hasCssModuleQueryParam(sourcePath) ||
60
- hasImportTypeCssAssertion(path)
61
- ) {
62
- newStyleSheetDetected = true;
63
- path.stop();
64
- }
65
- },
66
- ExportAllDeclaration: (path) => {
67
- const sourcePath = path.get("source");
68
- if (hasCssModuleQueryParam(sourcePath)) {
69
- newStyleSheetDetected = true;
70
- path.stop();
71
- }
72
- },
73
- ExportNamedDeclaration: (path) => {
74
- if (!path.node.source) {
75
- // This export has no "source", so it's probably
76
- // a local variable or function, e.g.
77
- // export { varName }
78
- // export const constName = ...
79
- // export function funcName() {}
80
- return;
81
- }
82
- const sourcePath = path.get("source");
83
- if (hasCssModuleQueryParam(sourcePath)) {
84
- newStyleSheetDetected = true;
85
- path.stop();
86
- }
87
- },
12
+ const { sourceType } = state.file.opts.parserOpts;
13
+ const isJsModule = sourceType === "module";
14
+ injectSideEffectFileIntoBabelAst({
15
+ programPath: path,
16
+ isJsModule,
17
+ asImport: babelHelpersAsImport,
18
+ sideEffectFileUrl: newStylesheetClientFileUrl,
19
+ getSideEffectFileSpecifier: getImportSpecifier,
20
+ babel,
88
21
  });
89
- state.file.metadata.newStyleSheetDetected = newStyleSheetDetected;
90
- if (newStyleSheetDetected) {
91
- const { sourceType } = state.file.opts.parserOpts;
92
- const isJsModule = sourceType === "module";
93
- injectPolyfillIntoBabelAst({
94
- programPath: path,
95
- isJsModule,
96
- asImport: babelHelpersAsImport,
97
- polyfillFileUrl: newStylesheetClientFileUrl,
98
- getPolyfillImportSpecifier: getImportSpecifier,
99
- babel,
100
- });
101
- }
102
22
  },
103
23
  },
104
24
  };
105
25
  };
106
-
107
- const isNewCssStyleSheetCall = (node) => {
108
- return (
109
- node.type === "NewExpression" &&
110
- node.callee.type === "Identifier" &&
111
- node.callee.name === "CSSStyleSheet"
112
- );
113
- };
114
-
115
- const isDocumentAdoptedStyleSheets = (node) => {
116
- return (
117
- node.type === "MemberExpression" &&
118
- node.object.type === "Identifier" &&
119
- node.object.name === "document" &&
120
- node.property.type === "Identifier" &&
121
- node.property.name === "adoptedStyleSheets"
122
- );
123
- };
124
-
125
- const hasCssModuleQueryParam = (path) => {
126
- const { node } = path;
127
- return (
128
- node.type === "StringLiteral" &&
129
- new URL(node.value, "https://jsenv.dev").searchParams.has(`css_module`)
130
- );
131
- };
132
-
133
- const hasImportTypeCssAssertion = (path) => {
134
- const importAssertionsDescriptor = getImportAssertionsDescriptor(
135
- path.node.assertions,
136
- );
137
- return Boolean(importAssertionsDescriptor.type === "css");
138
- };
139
-
140
- const getImportAssertionsDescriptor = (importAssertions) => {
141
- const importAssertionsDescriptor = {};
142
- if (importAssertions) {
143
- importAssertions.forEach((importAssertion) => {
144
- importAssertionsDescriptor[importAssertion.key.name] =
145
- importAssertion.value.value;
146
- });
147
- }
148
- return importAssertionsDescriptor;
149
- };
@@ -0,0 +1,114 @@
1
+ import { visitJsAstUntil } from "@jsenv/ast";
2
+ import { newStylesheetClientFileUrl } from "./new_stylesheet_client_file_url.js";
3
+
4
+ export const analyzeConstructableStyleSheetUsage = (urlInfo) => {
5
+ if (urlInfo.url === newStylesheetClientFileUrl) {
6
+ return null;
7
+ }
8
+ const node = visitJsAstUntil(urlInfo.contentAst, {
9
+ NewExpression: (node) => {
10
+ return isNewCssStyleSheetCall(node);
11
+ },
12
+ MemberExpression: (node) => {
13
+ return isDocumentAdoptedStyleSheets(node);
14
+ },
15
+ ImportExpression: (node) => {
16
+ const source = node.source;
17
+ if (source.type !== "Literal" || typeof source.value === "string") {
18
+ // Non-string argument, probably a variable or expression, e.g.
19
+ // import(moduleId)
20
+ // import('./' + moduleName)
21
+ return false;
22
+ }
23
+ if (hasImportTypeCssAssertion(node)) {
24
+ return node;
25
+ }
26
+ if (hasCssModuleQueryParam(source)) {
27
+ return source;
28
+ }
29
+ return false;
30
+ },
31
+ ImportDeclaration: (node) => {
32
+ const { source } = node;
33
+ if (hasCssModuleQueryParam(source)) {
34
+ return source;
35
+ }
36
+ if (hasImportTypeCssAssertion(node)) {
37
+ return node;
38
+ }
39
+ return false;
40
+ },
41
+ ExportAllDeclaration: (node) => {
42
+ const { source } = node;
43
+ if (hasCssModuleQueryParam(source)) {
44
+ return source;
45
+ }
46
+ return false;
47
+ },
48
+ ExportNamedDeclaration: (node) => {
49
+ const { source } = node;
50
+ if (!source) {
51
+ // This export has no "source", so it's probably
52
+ // a local variable or function, e.g.
53
+ // export { varName }
54
+ // export const constName = ...
55
+ // export function funcName() {}
56
+ return false;
57
+ }
58
+ if (hasCssModuleQueryParam(source)) {
59
+ return source;
60
+ }
61
+ return false;
62
+ },
63
+ });
64
+ return node
65
+ ? {
66
+ line: node.loc.start.line,
67
+ column: node.loc.start.column,
68
+ }
69
+ : null;
70
+ };
71
+
72
+ const isNewCssStyleSheetCall = (node) => {
73
+ return (
74
+ node.type === "NewExpression" &&
75
+ node.callee.type === "Identifier" &&
76
+ node.callee.name === "CSSStyleSheet"
77
+ );
78
+ };
79
+
80
+ const isDocumentAdoptedStyleSheets = (node) => {
81
+ return (
82
+ node.type === "MemberExpression" &&
83
+ node.object.type === "Identifier" &&
84
+ node.object.name === "document" &&
85
+ node.property.type === "Identifier" &&
86
+ node.property.name === "adoptedStyleSheets"
87
+ );
88
+ };
89
+
90
+ const hasCssModuleQueryParam = (node) => {
91
+ return (
92
+ node.type === "Literal" &&
93
+ typeof node.value === "string" &&
94
+ new URL(node.value, "https://jsenv.dev").searchParams.has(`css_module`)
95
+ );
96
+ };
97
+
98
+ const hasImportTypeCssAssertion = (node) => {
99
+ const importAssertionsDescriptor = getImportAssertionsDescriptor(
100
+ node.assertions,
101
+ );
102
+ return Boolean(importAssertionsDescriptor.type === "css");
103
+ };
104
+
105
+ const getImportAssertionsDescriptor = (importAssertions) => {
106
+ const importAssertionsDescriptor = {};
107
+ if (importAssertions) {
108
+ importAssertions.forEach((importAssertion) => {
109
+ importAssertionsDescriptor[importAssertion.key.name] =
110
+ importAssertion.value.value;
111
+ });
112
+ }
113
+ return importAssertionsDescriptor;
114
+ };
@@ -0,0 +1,4 @@
1
+ export const newStylesheetClientFileUrl = new URL(
2
+ "./client/new_stylesheet.js",
3
+ import.meta.url,
4
+ ).href;
@@ -1,10 +1,5 @@
1
- import { pathToFileURL } from "node:url";
2
- import { injectPolyfillIntoBabelAst } from "../polyfill_injection_in_babel_ast.js";
3
-
4
- const regeneratorRuntimeClientFileUrl = new URL(
5
- "./client/regenerator_runtime.js",
6
- import.meta.url,
7
- ).href;
1
+ import { injectSideEffectFileIntoBabelAst } from "../side_effect_injection_in_babel_ast.js";
2
+ import { regeneratorRuntimeClientFileUrl } from "./regenerator_runtime_client_file_url.js";
8
3
 
9
4
  export const babelPluginRegeneratorRuntimeInjector = (
10
5
  babel,
@@ -14,35 +9,16 @@ export const babelPluginRegeneratorRuntimeInjector = (
14
9
  name: "regenerator-runtime-injector",
15
10
  visitor: {
16
11
  Program: (path, state) => {
17
- let regeneratorRuntimeDetected = false;
18
- const { filename } = state;
19
- const fileUrl = pathToFileURL(filename).href;
20
- if (fileUrl === regeneratorRuntimeClientFileUrl) {
21
- return;
22
- }
23
- path.traverse({
24
- Identifier(path) {
25
- const { node } = path;
26
- if (node.name === "regeneratorRuntime") {
27
- regeneratorRuntimeDetected = true;
28
- path.stop();
29
- }
30
- },
12
+ const { sourceType } = state.file.opts.parserOpts;
13
+ const isJsModule = sourceType === "module";
14
+ injectSideEffectFileIntoBabelAst({
15
+ programPath: path,
16
+ isJsModule,
17
+ asImport: babelHelpersAsImport,
18
+ sideEffectFileUrl: regeneratorRuntimeClientFileUrl,
19
+ getSideEffectFileSpecifier: getImportSpecifier,
20
+ babel,
31
21
  });
32
- state.file.metadata.regeneratorRuntimeDetected =
33
- regeneratorRuntimeDetected;
34
- if (regeneratorRuntimeDetected) {
35
- const { sourceType } = state.file.opts.parserOpts;
36
- const isJsModule = sourceType === "module";
37
- injectPolyfillIntoBabelAst({
38
- programPath: path,
39
- isJsModule,
40
- asImport: babelHelpersAsImport,
41
- polyfillFileUrl: regeneratorRuntimeClientFileUrl,
42
- getPolyfillImportSpecifier: getImportSpecifier,
43
- babel,
44
- });
45
- }
46
22
  },
47
23
  },
48
24
  };
@@ -0,0 +1,4 @@
1
+ export const regeneratorRuntimeClientFileUrl = new URL(
2
+ "./client/regenerator_runtime.js",
3
+ import.meta.url,
4
+ ).href;
@@ -0,0 +1,23 @@
1
+ import { visitJsAstUntil } from "@jsenv/ast";
2
+ import { regeneratorRuntimeClientFileUrl } from "./regenerator_runtime_client_file_url.js";
3
+
4
+ export const analyzeRegeneratorRuntimeUsage = (urlInfo) => {
5
+ if (urlInfo.url === regeneratorRuntimeClientFileUrl) {
6
+ return null;
7
+ }
8
+ const ast = urlInfo.contentAst;
9
+ const node = visitJsAstUntil(ast, {
10
+ Identifier: (node) => {
11
+ if (node.name === "regeneratorRuntime") {
12
+ return node;
13
+ }
14
+ return false;
15
+ },
16
+ });
17
+ return node
18
+ ? {
19
+ line: node.loc.start.line,
20
+ column: node.loc.start.column,
21
+ }
22
+ : null;
23
+ };
@@ -1,10 +1,10 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { injectJsImport } from "@jsenv/ast";
3
3
 
4
- export const injectPolyfillIntoBabelAst = ({
4
+ export const injectSideEffectFileIntoBabelAst = ({
5
5
  programPath,
6
- polyfillFileUrl,
7
- getPolyfillImportSpecifier,
6
+ sideEffectFileUrl,
7
+ getSideEffectFileSpecifier,
8
8
  babel,
9
9
  isJsModule,
10
10
  asImport = true,
@@ -12,19 +12,19 @@ export const injectPolyfillIntoBabelAst = ({
12
12
  if (isJsModule && asImport) {
13
13
  injectJsImport({
14
14
  programPath,
15
- from: getPolyfillImportSpecifier(polyfillFileUrl),
15
+ from: getSideEffectFileSpecifier(sideEffectFileUrl),
16
16
  sideEffect: true,
17
17
  });
18
18
  return;
19
19
  }
20
- const polyfillFileContent = readFileSync(new URL(polyfillFileUrl), "utf8");
21
- const polyfillAst = babel.parse(polyfillFileContent);
20
+ const sidEffectFileContent = readFileSync(new URL(sideEffectFileUrl), "utf8");
21
+ const sideEffectFileContentAst = babel.parse(sidEffectFileContent);
22
22
  if (isJsModule) {
23
- injectAstAfterImport(programPath, polyfillAst);
23
+ injectAstAfterImport(programPath, sideEffectFileContentAst);
24
24
  return;
25
25
  }
26
26
  const bodyNodePaths = programPath.get("body");
27
- bodyNodePaths[0].insertBefore(polyfillAst.program.body);
27
+ bodyNodePaths[0].insertBefore(sideEffectFileContentAst.program.body);
28
28
  };
29
29
 
30
30
  const injectAstAfterImport = (programPath, ast) => {
@@ -5,11 +5,11 @@ export const jsenvPluginCssTranspilation = () => {
5
5
  name: "jsenv:css_transpilation",
6
6
  appliesDuring: "*",
7
7
  transformUrlContent: {
8
- css: async (urlInfo, context) => {
8
+ css: (urlInfo) => {
9
9
  return applyCssTranspilation({
10
10
  input: urlInfo.content,
11
11
  inputUrl: urlInfo.originalUrl,
12
- runtimeCompat: context.runtimeCompat,
12
+ runtimeCompat: urlInfo.context.runtimeCompat,
13
13
  });
14
14
  },
15
15
  },