@jsenv/core 27.0.0-alpha.38 → 27.0.0-alpha.40

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.
@@ -1,61 +1,81 @@
1
- import { applyBabelPlugins } from "@jsenv/utils/js_ast/apply_babel_plugins.js"
2
1
  import { injectQueryParamsIntoSpecifier } from "@jsenv/utils/urls/url_utils.js"
3
2
  import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
4
- import {
5
- analyzeNewWorkerOrNewSharedWorker,
6
- analyzeServiceWorkerRegisterCall,
7
- } from "@jsenv/utils/js_ast/js_static_analysis.js"
3
+ import { parseJsUrls } from "@jsenv/core/packages/utils/js_ast/parse_js_urls.js"
8
4
 
9
- // TODO: handle also service worker and shared worker in this plugin
10
5
  export const jsenvPluginWorkersTypeModuleAsClassic = ({
11
6
  generateJsClassicFilename,
12
7
  }) => {
13
8
  const transformJsWorkerTypes = async (urlInfo, context) => {
14
- const workersToTranspile = getWorkersToTranspile(urlInfo, context)
15
- if (
16
- !workersToTranspile.worker &&
17
- !workersToTranspile.serviceWorker &&
18
- !workersToTranspile.sharedServiceWorker
19
- ) {
20
- return null
21
- }
22
- const { metadata } = await applyBabelPlugins({
23
- babelPlugins: [
24
- [
25
- babelPluginMetadataWorkerMentions,
26
- {
27
- workersToTranspile,
28
- isJsModule: urlInfo.type === "js_module",
29
- },
30
- ],
31
- ],
32
- urlInfo,
9
+ const toUpdate = []
10
+ let workerTypeModuleIsSupported
11
+ let serviceWorkerTypeModuleIsSupported
12
+ let sharedWorkerTypeModuleIsSupported
13
+ const jsUrls = parseJsUrls({
14
+ js: urlInfo.content,
15
+ url: (urlInfo.data && urlInfo.data.rawUrl) || urlInfo.url,
16
+ isJsModule: urlInfo.type === "js_module",
33
17
  })
34
- const { workerMentions } = metadata
35
- const magicSource = createMagicSource(urlInfo.content)
36
- workerMentions.forEach((workerMention) => {
37
- if (workerMention.expectedType !== "js_module") {
18
+ jsUrls.forEach((jsUrlMention) => {
19
+ if (jsUrlMention.expectedType !== "js_module") {
38
20
  return
39
21
  }
40
- const specifier = workerMention.specifier
22
+ if (jsUrlMention.expectedSubtype === "worker") {
23
+ if (workerTypeModuleIsSupported === undefined) {
24
+ workerTypeModuleIsSupported =
25
+ context.isSupportedOnCurrentClients("worker_type_module")
26
+ }
27
+ if (workerTypeModuleIsSupported) {
28
+ return
29
+ }
30
+ toUpdate.push(jsUrlMention)
31
+ return
32
+ }
33
+ if (jsUrlMention.expectedSubtype === "service_worker") {
34
+ if (serviceWorkerTypeModuleIsSupported === undefined) {
35
+ serviceWorkerTypeModuleIsSupported =
36
+ context.isSupportedOnCurrentClients("service_worker_type_module")
37
+ }
38
+ if (serviceWorkerTypeModuleIsSupported) {
39
+ return
40
+ }
41
+ toUpdate.push(jsUrlMention)
42
+ return
43
+ }
44
+ if (jsUrlMention.expectedSubtype === "shared_worker") {
45
+ if (sharedWorkerTypeModuleIsSupported === undefined) {
46
+ sharedWorkerTypeModuleIsSupported =
47
+ context.isSupportedOnCurrentClients("shared_worker_type_module")
48
+ }
49
+ if (sharedWorkerTypeModuleIsSupported) {
50
+ return
51
+ }
52
+ toUpdate.push(jsUrlMention)
53
+ return
54
+ }
55
+ })
56
+ if (toUpdate.length === 0) {
57
+ return null
58
+ }
59
+ const magicSource = createMagicSource(urlInfo.content)
60
+ toUpdate.forEach((jsUrlMention) => {
41
61
  const reference = context.referenceUtils.findByGeneratedSpecifier(
42
- JSON.stringify(specifier),
62
+ JSON.stringify(jsUrlMention.specifier),
43
63
  )
44
64
  const [newReference] = context.referenceUtils.update(reference, {
45
65
  expectedType: "js_classic",
46
- specifier: injectQueryParamsIntoSpecifier(specifier, {
66
+ specifier: injectQueryParamsIntoSpecifier(reference.specifier, {
47
67
  as_js_classic: "",
48
68
  }),
49
69
  filename: generateJsClassicFilename(reference.url),
50
70
  })
51
71
  magicSource.replace({
52
- start: workerMention.start,
53
- end: workerMention.end,
72
+ start: jsUrlMention.start,
73
+ end: jsUrlMention.end,
54
74
  replacement: newReference.generatedSpecifier,
55
75
  })
56
76
  magicSource.replace({
57
- start: workerMention.typeArgNode.value.start,
58
- end: workerMention.typeArgNode.value.end,
77
+ start: jsUrlMention.typePropertyNode.value.start,
78
+ end: jsUrlMention.typePropertyNode.value.end,
59
79
  replacement: JSON.stringify("classic"),
60
80
  })
61
81
  })
@@ -71,69 +91,3 @@ export const jsenvPluginWorkersTypeModuleAsClassic = ({
71
91
  },
72
92
  }
73
93
  }
74
-
75
- const getWorkersToTranspile = (urlInfo, context) => {
76
- let worker = false
77
- let serviceWorker = false
78
- let sharedWorker = false
79
- for (const reference of urlInfo.references) {
80
- if (reference.expectedType !== "js_module") {
81
- continue
82
- }
83
- if (
84
- reference.expectedSubtype === "worker" &&
85
- !context.isSupportedOnCurrentClients("worker_type_module")
86
- ) {
87
- worker = true
88
- }
89
- if (
90
- reference.expectedSubtype === "service_worker" &&
91
- !context.isSupportedOnCurrentClients("service_worker_type_module")
92
- ) {
93
- serviceWorker = true
94
- }
95
- if (
96
- reference.expectedSubtype === "shared_worker" &&
97
- !context.isSupportedOnCurrentClients("shared_worker_type_module")
98
- ) {
99
- sharedWorker = true
100
- }
101
- }
102
- return { worker, serviceWorker, sharedWorker }
103
- }
104
-
105
- const babelPluginMetadataWorkerMentions = (
106
- _,
107
- { workersToTranspile, isJsModule },
108
- ) => {
109
- return {
110
- name: "metadata-worker-mentions",
111
- visitor: {
112
- Program(programPath, state) {
113
- const workerMentions = []
114
- const visitors = {
115
- NewExpression: (path) => {
116
- if (workersToTranspile.worker || workersToTranspile.sharedWorker) {
117
- const mentions = analyzeNewWorkerOrNewSharedWorker(path, {
118
- isJsModule,
119
- })
120
- if (mentions) {
121
- workerMentions.push(...mentions)
122
- }
123
- }
124
- },
125
- }
126
- if (workersToTranspile.serviceWorker) {
127
- visitors.CallExpression = (path) => {
128
- const mentions = analyzeServiceWorkerRegisterCall(path)
129
- if (mentions) {
130
- workerMentions.push(...mentions)
131
- }
132
- }
133
- }
134
- programPath.traverse(visitors)
135
- state.file.metadata.workerMentions = workerMentions
136
- },
137
- },
138
- }
139
- }
@@ -1,12 +1,11 @@
1
1
  import { urlToFilename } from "@jsenv/filesystem"
2
2
 
3
- import { applyBabelPlugins } from "@jsenv/utils/js_ast/apply_babel_plugins.js"
3
+ import { parseJsImportAssertions } from "@jsenv/utils/js_ast/parse_js_import_assertions.js"
4
4
  import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
5
5
  import { injectQueryParamsIntoSpecifier } from "@jsenv/utils/urls/url_utils.js"
6
6
  import { JS_QUOTES } from "@jsenv/utils/string/js_quotes.js"
7
7
 
8
8
  import { fetchOriginalUrlInfo } from "../fetch_original_url_info.js"
9
- import { babelPluginMetadataImportAssertions } from "./helpers/babel_plugin_metadata_import_assertions.js"
10
9
 
11
10
  export const jsenvPluginImportAssertions = () => {
12
11
  const importAssertions = {
@@ -15,18 +14,17 @@ export const jsenvPluginImportAssertions = () => {
15
14
  transformUrlContent: {
16
15
  js_module: async (urlInfo, context) => {
17
16
  // "usesImportAssertion" is set by "jsenv:imports_analysis"
18
- if (!urlInfo.data.usesImportAssertion) {
17
+ if (urlInfo.data.usesImportAssertion === false) {
19
18
  return null
20
19
  }
21
20
  const importTypesToTranspile = getImportTypesToTranspile(context)
22
21
  if (importTypesToTranspile.length === 0) {
23
22
  return null
24
23
  }
25
- const { metadata } = await applyBabelPlugins({
26
- babelPlugins: [babelPluginMetadataImportAssertions],
27
- urlInfo,
24
+ const importAssertions = await parseJsImportAssertions({
25
+ js: urlInfo.content,
26
+ url: (urlInfo.data && urlInfo.data.rawUrl) || urlInfo.url,
28
27
  })
29
- const { importAssertions } = metadata
30
28
  const magicSource = createMagicSource(urlInfo.content)
31
29
  importAssertions.forEach((importAssertion) => {
32
30
  const assertType = importAssertion.assert.type
@@ -34,54 +32,27 @@ export const jsenvPluginImportAssertions = () => {
34
32
  return
35
33
  }
36
34
  const { searchParam } = importAsInfos[assertType]
37
- const { path } = importAssertion
38
- const { node } = path
39
- if (node.type === "CallExpression") {
40
- const importSpecifierPath = path.get("arguments")[0]
41
- const specifier = importSpecifierPath.node.value
42
- const reference = context.referenceUtils.findByGeneratedSpecifier(
43
- JSON.stringify(specifier),
44
- )
45
- const [newReference] = context.referenceUtils.update(reference, {
46
- expectedType: "js_module",
47
- specifier: injectQueryParamsIntoSpecifier(specifier, {
48
- [searchParam]: "",
49
- }),
50
- filename: `${urlToFilename(reference.url)}.js`,
51
- })
52
- magicSource.replace({
53
- start: importSpecifierPath.node.start,
54
- end: importSpecifierPath.node.end,
55
- replacement: newReference.generatedSpecifier,
56
- })
57
- const secondArgPath = path.get("arguments")[1]
58
- magicSource.remove({
59
- start: secondArgPath.node.start,
60
- end: secondArgPath.node.end,
61
- })
62
- return
63
- }
64
- const importSpecifierPath = path.get("source")
65
- const specifier = importSpecifierPath.node.value
66
35
  const reference = context.referenceUtils.findByGeneratedSpecifier(
67
- JSON.stringify(specifier),
36
+ JSON.stringify(importAssertion.specifier),
68
37
  )
69
38
  const [newReference] = context.referenceUtils.update(reference, {
70
39
  expectedType: "js_module",
71
- specifier: injectQueryParamsIntoSpecifier(specifier, {
72
- [searchParam]: "",
73
- }),
40
+ specifier: injectQueryParamsIntoSpecifier(
41
+ importAssertion.specifier,
42
+ {
43
+ [searchParam]: "",
44
+ },
45
+ ),
74
46
  filename: `${urlToFilename(reference.url)}.js`,
75
47
  })
76
48
  magicSource.replace({
77
- start: importSpecifierPath.node.start,
78
- end: importSpecifierPath.node.end,
49
+ start: importAssertion.specifierStart,
50
+ end: importAssertion.specifierEnd,
79
51
  replacement: newReference.generatedSpecifier,
80
52
  })
81
- const assertionsPath = path.get("assertions")[0]
82
53
  magicSource.remove({
83
- start: assertionsPath.node.start,
84
- end: assertionsPath.node.end,
54
+ start: importAssertion.assertNode.start,
55
+ end: importAssertion.assertNode.end,
85
56
  })
86
57
  })
87
58
  return magicSource.toContentAndSourcemap()
@@ -208,10 +179,10 @@ const getImportTypesToTranspile = ({
208
179
  isSupportedOnCurrentClients,
209
180
  }) => {
210
181
  // during build always replace import assertions with the js:
211
- // - means rollup can bundle more js file together
212
- // - means url versioning can work for css inlined in js
213
182
  // - avoid rollup to see import assertions
214
183
  // We would have to tell rollup to ignore import with assertion
184
+ // - means rollup can bundle more js file together
185
+ // - means url versioning can work for css inlined in js
215
186
  if (scenario === "build") {
216
187
  return ["json", "css", "text"]
217
188
  }
@@ -1,17 +1,14 @@
1
- import { applyBabelPlugins } from "@jsenv/utils/js_ast/apply_babel_plugins.js"
2
- import {
3
- analyzeNewUrlCall,
4
- analyzeNewWorkerOrNewSharedWorker,
5
- analyzeImportScriptCalls,
6
- analyzeSystemRegisterCall,
7
- analyzeSystemImportCall,
8
- analyzeServiceWorkerRegisterCall,
9
- } from "@jsenv/utils/js_ast/js_static_analysis.js"
1
+ import { parseJsUrls } from "@jsenv/utils/js_ast/parse_js_urls.js"
10
2
  import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
11
3
  import { isWebWorkerUrlInfo } from "@jsenv/core/src/omega/web_workers.js"
12
4
 
13
5
  export const parseAndTransformJsUrls = async (urlInfo, context) => {
14
- const jsMentions = await performJsUrlsStaticAnalysis(urlInfo)
6
+ const jsMentions = await parseJsUrls({
7
+ js: urlInfo.content,
8
+ url: (urlInfo.data && urlInfo.data.rawUrl) || urlInfo.url,
9
+ isJsModule: urlInfo.type === "js_module",
10
+ isWebWorker: isWebWorkerUrlInfo(urlInfo),
11
+ })
15
12
  const { rootDirectoryUrl, referenceUtils } = context
16
13
  const actions = []
17
14
  const magicSource = createMagicSource(urlInfo.content)
@@ -29,6 +26,8 @@ export const parseAndTransformJsUrls = async (urlInfo, context) => {
29
26
  "StringLiteral": jsMention.baseUrl,
30
27
  "window.origin": rootDirectoryUrl,
31
28
  "import.meta.url": urlInfo.url,
29
+ "context.meta.url": urlInfo.url,
30
+ "document.currentScript.src": urlInfo.url,
32
31
  }[jsMention.baseUrlType],
33
32
  })
34
33
  actions.push(async () => {
@@ -42,107 +41,3 @@ export const parseAndTransformJsUrls = async (urlInfo, context) => {
42
41
  await Promise.all(actions.map((action) => action()))
43
42
  return magicSource.toContentAndSourcemap()
44
43
  }
45
-
46
- const performJsUrlsStaticAnalysis = async (urlInfo) => {
47
- const isJsModule = urlInfo.type === "js_module"
48
- const isWebWorker = isWebWorkerUrlInfo(urlInfo)
49
- if (canSkipStaticAnalysis(urlInfo, { isJsModule, isWebWorker })) {
50
- return []
51
- }
52
- const { metadata } = await applyBabelPlugins({
53
- babelPlugins: [
54
- [babelPluginMetadataJsUrlMentions, { isJsModule, isWebWorker }],
55
- ],
56
- urlInfo,
57
- })
58
- const { jsMentions } = metadata
59
- return jsMentions
60
- }
61
-
62
- const canSkipStaticAnalysis = (urlInfo, { isJsModule, isWebWorker }) => {
63
- const js = urlInfo.content
64
- if (isJsModule) {
65
- if (
66
- js.includes("new URL(") ||
67
- js.includes("new Worker(") ||
68
- js.includes("new SharedWorker(") ||
69
- js.includes("serviceWorker.register(")
70
- ) {
71
- return false
72
- }
73
- }
74
- if (!isJsModule) {
75
- if (
76
- js.includes("System.") ||
77
- js.includes("new URL(") ||
78
- js.includes("new Worker(") ||
79
- js.includes("new SharedWorker(") ||
80
- js.includes("serviceWorker.register(")
81
- ) {
82
- return false
83
- }
84
- }
85
- if (isWebWorker && js.includes("importScripts(")) {
86
- return false
87
- }
88
- return true
89
- }
90
-
91
- /*
92
- * see also
93
- * https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md
94
- * https://github.com/mjackson/babel-plugin-import-visitor
95
- *
96
- */
97
- const babelPluginMetadataJsUrlMentions = (_, { isJsModule, isWebWorker }) => {
98
- return {
99
- name: "metadata-js-mentions",
100
- visitor: {
101
- Program(programPath, state) {
102
- const jsMentions = []
103
- const callOneStaticAnalyzer = (path, analyzer) => {
104
- const returnValue = analyzer(path)
105
- if (returnValue === null) {
106
- return false
107
- }
108
- if (Array.isArray(returnValue)) {
109
- jsMentions.push(...returnValue)
110
- return true
111
- }
112
- if (typeof returnValue === "object") {
113
- jsMentions.push(returnValue)
114
- return true
115
- }
116
- return false
117
- }
118
- const callStaticAnalyzers = (path, analysers) => {
119
- for (const analyzer of analysers) {
120
- if (callOneStaticAnalyzer(path, analyzer)) {
121
- break
122
- }
123
- }
124
- }
125
- const visitors = {
126
- NewExpression: (path) => {
127
- callStaticAnalyzers(path, [
128
- (path) => analyzeNewWorkerOrNewSharedWorker(path, { isJsModule }),
129
- (path) => analyzeNewUrlCall(path, { isJsModule }),
130
- ])
131
- },
132
- }
133
- const callExpressionStaticAnalysers = [
134
- ...(isJsModule
135
- ? []
136
- : [analyzeSystemRegisterCall, analyzeSystemImportCall]),
137
- ...(isWebWorker ? [analyzeImportScriptCalls] : []),
138
- analyzeServiceWorkerRegisterCall,
139
- ]
140
- visitors.CallExpression = (path) => {
141
- callStaticAnalyzers(path, callExpressionStaticAnalysers)
142
- }
143
- programPath.traverse(visitors)
144
- state.file.metadata.jsMentions = jsMentions
145
- },
146
- },
147
- }
148
- }
@@ -7,31 +7,31 @@ export const jsenvPluginImportsAnalysis = () => {
7
7
  appliesDuring: "*",
8
8
  transformUrlContent: {
9
9
  js_module: async (urlInfo, context) => {
10
- const [imports, exports] = await parseJsModuleImports(
11
- urlInfo.content,
12
- (urlInfo.data && urlInfo.data.rawUrl) || urlInfo.url,
13
- )
10
+ const [imports, exports] = await parseJsModuleImports({
11
+ js: urlInfo.content,
12
+ url: (urlInfo.data && urlInfo.data.rawUrl) || urlInfo.url,
13
+ })
14
14
  const actions = []
15
15
  const magicSource = createMagicSource(urlInfo.content)
16
16
  urlInfo.data.usesImport = imports.length > 0
17
17
  urlInfo.data.usesExport = exports.length > 0
18
18
  urlInfo.data.usesImportAssertion = imports.some(
19
- (importInfo) => importInfo.usesAssert,
19
+ (importInfo) => typeof importInfo.usesAssert,
20
20
  )
21
21
  imports.forEach((importInfo) => {
22
22
  const [reference] = context.referenceUtils.found({
23
23
  type: "js_import_export",
24
24
  subtype: importInfo.subtype,
25
- expectedType: "js_module",
25
+ expectedType: importInfo.expectedType, // can be json for import assertions
26
26
  expectedSubtype: urlInfo.subtype,
27
- line: importInfo.line,
28
- column: importInfo.column,
27
+ line: importInfo.specifierLine,
28
+ column: importInfo.specifierColumn,
29
29
  specifier: importInfo.specifier,
30
30
  })
31
31
  actions.push(async () => {
32
32
  magicSource.replace({
33
- start: importInfo.start,
34
- end: importInfo.end,
33
+ start: importInfo.specifierStart,
34
+ end: importInfo.specifierEnd,
35
35
  replacement: await context.referenceUtils.readGeneratedSpecifier(
36
36
  reference,
37
37
  ),
@@ -1,98 +0,0 @@
1
- export const babelPluginMetadataImportAssertions = () => {
2
- return {
3
- name: "metadata-import-assertions",
4
- visitor: {
5
- Program: (programPath, state) => {
6
- const importAssertions = collectProgramImportAssertions(programPath)
7
- state.file.metadata.importAssertions = importAssertions
8
- },
9
- },
10
- }
11
- }
12
-
13
- /*
14
- * Jsenv wont touch code where "specifier" or "type" is dynamic (see code below)
15
- * ```js
16
- * const file ="./style.css"
17
- * const type = "css"
18
- * import(file, { assert: { type }})
19
- * ```
20
- * Jsenv could throw an error because we know browser will fail to execute the code
21
- * because import assertions are not supported.
22
- * But for now (as it is simpler to do so) we let the browser throw the error
23
- */
24
- const collectProgramImportAssertions = (programPath) => {
25
- const importAssertions = []
26
- programPath.traverse({
27
- CallExpression: (path) => {
28
- if (path.node.callee.type !== "Import") {
29
- // Some other function call, not import();
30
- return
31
- }
32
- if (path.node.arguments[0].type !== "StringLiteral") {
33
- // Non-string argument, probably a variable or expression, e.g.
34
- // import(moduleId)
35
- // import('./' + moduleName)
36
- return
37
- }
38
- const args = path.node.arguments
39
- const secondArg = args[1]
40
- if (!secondArg) {
41
- return
42
- }
43
- const { properties } = secondArg
44
- const assertProperty = properties.find((property) => {
45
- return property.key.name === "assert"
46
- })
47
- if (!assertProperty) {
48
- return
49
- }
50
- const assertProperties = assertProperty.value.properties
51
- const typePropertyNode = assertProperties.find((property) => {
52
- return property.key.name === "type"
53
- })
54
- if (!typePropertyNode) {
55
- return
56
- }
57
- const typePropertyValue = typePropertyNode.value
58
- if (typePropertyValue.type !== "StringLiteral") {
59
- return
60
- }
61
- const type = typePropertyValue.value
62
- importAssertions.push({
63
- path,
64
- assert: {
65
- type,
66
- },
67
- })
68
- },
69
-
70
- ImportDeclaration: (path) => {
71
- const { assertions } = path.node
72
- if (!assertions) {
73
- return
74
- }
75
- if (assertions.length === 0) {
76
- return
77
- }
78
- const typeAssertion = assertions.find(
79
- (assertion) => assertion.key.name === "type",
80
- )
81
- if (!typeAssertion) {
82
- return
83
- }
84
- const typeNode = typeAssertion.value
85
- if (typeNode.type !== "StringLiteral") {
86
- return
87
- }
88
- const type = typeNode.value
89
- importAssertions.push({
90
- path,
91
- assert: {
92
- type,
93
- },
94
- })
95
- },
96
- })
97
- return importAssertions
98
- }