@jsenv/core 23.8.2 → 23.8.7

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 (133) hide show
  1. package/dist/jsenv_browser_system.js +46 -39
  2. package/dist/jsenv_browser_system.js.map +14 -14
  3. package/dist/jsenv_compile_proxy.js.map +6 -6
  4. package/dist/jsenv_exploring_index.js.map +5 -5
  5. package/dist/jsenv_exploring_redirector.js.map +12 -12
  6. package/dist/jsenv_toolbar.js.map +7 -7
  7. package/dist/jsenv_toolbar_injector.js.map +5 -5
  8. package/helpers/babel/.eslintrc.cjs +24 -24
  9. package/helpers/babel/AsyncGenerator/AsyncGenerator.js +81 -81
  10. package/helpers/babel/AwaitValue/AwaitValue.js +3 -3
  11. package/helpers/babel/applyDecoratorDescriptor/applyDecoratorDescriptor.js +33 -33
  12. package/helpers/babel/arrayLikeToArray/arrayLikeToArray.js +7 -7
  13. package/helpers/babel/arrayWithHoles/arrayWithHoles.js +4 -4
  14. package/helpers/babel/arrayWithoutHoles/arrayWithoutHoles.js +6 -6
  15. package/helpers/babel/assertThisInitialized/assertThisInitialized.js +7 -7
  16. package/helpers/babel/asyncGeneratorDelegate/asyncGeneratorDelegate.js +40 -40
  17. package/helpers/babel/asyncIterator/asyncIterator.js +12 -12
  18. package/helpers/babel/asyncToGenerator/asyncToGenerator.js +34 -34
  19. package/helpers/babel/awaitAsyncGenerator/awaitAsyncGenerator.js +5 -5
  20. package/helpers/babel/classApplyDescriptorDestructureSet/classApplyDescriptorDestructureSet.js +20 -20
  21. package/helpers/babel/classApplyDescriptorGet/classApplyDescriptorGet.js +6 -6
  22. package/helpers/babel/classApplyDescriptorSet/classApplyDescriptorSet.js +13 -13
  23. package/helpers/babel/classCallCheck/classCallCheck.js +5 -5
  24. package/helpers/babel/classCheckPrivateStaticAccess/classCheckPrivateStaticAccess.js +5 -5
  25. package/helpers/babel/classCheckPrivateStaticFieldDescriptor/classCheckPrivateStaticFieldDescriptor.js +6 -6
  26. package/helpers/babel/classExtractFieldDescriptor/classExtractFieldDescriptor.js +7 -7
  27. package/helpers/babel/classNameTDZError/classNameTDZError.js +4 -4
  28. package/helpers/babel/classPrivateFieldDestructureSet/classPrivateFieldDestructureSet.js +7 -7
  29. package/helpers/babel/classPrivateFieldGet/classPrivateFieldGet.js +7 -7
  30. package/helpers/babel/classPrivateFieldLooseBase/classPrivateFieldLooseBase.js +6 -6
  31. package/helpers/babel/classPrivateFieldLooseKey/classPrivateFieldLooseKey.js +5 -5
  32. package/helpers/babel/classPrivateFieldSet/classPrivateFieldSet.js +8 -8
  33. package/helpers/babel/classPrivateMethodGet/classPrivateMethodGet.js +6 -6
  34. package/helpers/babel/classPrivateMethodSet/classPrivateMethodSet.js +3 -3
  35. package/helpers/babel/classStaticPrivateFieldSpecGet/classStaticPrivateFieldSpecGet.js +9 -9
  36. package/helpers/babel/classStaticPrivateFieldSpecSet/classStaticPrivateFieldSpecSet.js +15 -15
  37. package/helpers/babel/classStaticPrivateMethodGet/classStaticPrivateMethodGet.js +6 -6
  38. package/helpers/babel/classStaticPrivateMethodSet/classStaticPrivateMethodSet.js +3 -3
  39. package/helpers/babel/construct/construct.js +16 -16
  40. package/helpers/babel/createClass/createClass.js +15 -15
  41. package/helpers/babel/createForOfIteratorHelper/createForOfIteratorHelper.js +60 -60
  42. package/helpers/babel/createForOfIteratorHelperLoose/createForOfIteratorHelperLoose.js +23 -23
  43. package/helpers/babel/createRawReactElement/createRawReactElement.js +50 -50
  44. package/helpers/babel/createSuper/createSuper.js +22 -22
  45. package/helpers/babel/decorate/decorate.js +403 -403
  46. package/helpers/babel/defaults/defaults.js +11 -11
  47. package/helpers/babel/defineEnumerableProperties/defineEnumerableProperties.js +23 -23
  48. package/helpers/babel/defineProperty/defineProperty.js +18 -18
  49. package/helpers/babel/extends/extends.js +14 -14
  50. package/helpers/babel/get/get.js +13 -13
  51. package/helpers/babel/getPrototypeOf/getPrototypeOf.js +4 -4
  52. package/helpers/babel/inherits/inherits.js +15 -15
  53. package/helpers/babel/inheritsLoose/inheritsLoose.js +7 -7
  54. package/helpers/babel/initializerDefineProperty/initializerDefineProperty.js +10 -10
  55. package/helpers/babel/initializerWarningHelper/initializerWarningHelper.js +6 -6
  56. package/helpers/babel/instanceof/instanceof.js +6 -6
  57. package/helpers/babel/interopRequireDefault/interopRequireDefault.js +3 -3
  58. package/helpers/babel/interopRequireWildcard/interopRequireWildcard.js +37 -37
  59. package/helpers/babel/isNativeFunction/isNativeFunction.js +4 -4
  60. package/helpers/babel/isNativeReflectConstruct/isNativeReflectConstruct.js +21 -21
  61. package/helpers/babel/iterableToArray/iterableToArray.js +7 -7
  62. package/helpers/babel/iterableToArrayLimit/iterableToArrayLimit.js +36 -36
  63. package/helpers/babel/iterableToArrayLimitLoose/iterableToArrayLimitLoose.js +10 -10
  64. package/helpers/babel/jsx/jsx.js +45 -45
  65. package/helpers/babel/maybeArrayLike/maybeArrayLike.js +10 -10
  66. package/helpers/babel/newArrowCheck/newArrowCheck.js +5 -5
  67. package/helpers/babel/nonIterableRest/nonIterableRest.js +5 -5
  68. package/helpers/babel/nonIterableSpread/nonIterableSpread.js +5 -5
  69. package/helpers/babel/objectDestructuringEmpty/objectDestructuringEmpty.js +3 -3
  70. package/helpers/babel/objectSpread/objectSpread.js +23 -23
  71. package/helpers/babel/objectSpread2/objectSpread2.js +33 -33
  72. package/helpers/babel/objectWithoutProperties/objectWithoutProperties.js +19 -19
  73. package/helpers/babel/objectWithoutPropertiesLoose/objectWithoutPropertiesLoose.js +13 -13
  74. package/helpers/babel/possibleConstructorReturn/possibleConstructorReturn.js +10 -10
  75. package/helpers/babel/readOnlyError/readOnlyError.js +4 -4
  76. package/helpers/babel/readme.md +9 -9
  77. package/helpers/babel/set/set.js +44 -44
  78. package/helpers/babel/setPrototypeOf/setPrototypeOf.js +6 -6
  79. package/helpers/babel/skipFirstGeneratorNext/skipFirstGeneratorNext.js +8 -8
  80. package/helpers/babel/slicedToArray/slicedToArray.js +10 -10
  81. package/helpers/babel/slicedToArrayLoose/slicedToArrayLoose.js +13 -13
  82. package/helpers/babel/superPropBase/superPropBase.js +10 -10
  83. package/helpers/babel/taggedTemplateLiteral/taggedTemplateLiteral.js +10 -10
  84. package/helpers/babel/taggedTemplateLiteralLoose/taggedTemplateLiteralLoose.js +7 -7
  85. package/helpers/babel/tdz/tdz.js +4 -4
  86. package/helpers/babel/temporalRef/temporalRef.js +6 -6
  87. package/helpers/babel/temporalUndefined/temporalUndefined.js +3 -3
  88. package/helpers/babel/toArray/toArray.js +10 -10
  89. package/helpers/babel/toConsumableArray/toConsumableArray.js +10 -10
  90. package/helpers/babel/toPrimitive/toPrimitive.js +10 -10
  91. package/helpers/babel/toPropertyKey/toPropertyKey.js +6 -6
  92. package/helpers/babel/typeof/typeof.js +14 -14
  93. package/helpers/babel/unsupportedIterableToArray/unsupportedIterableToArray.js +12 -12
  94. package/helpers/babel/wrapAsyncGenerator/wrapAsyncGenerator.js +8 -8
  95. package/helpers/babel/wrapNativeSuper/wrapNativeSuper.js +30 -30
  96. package/helpers/babel/wrapRegExp/wrapRegExp.js +63 -63
  97. package/helpers/babel/writeOnlyError/writeOnlyError.js +4 -4
  98. package/helpers/regenerator-runtime/regenerator-runtime.js +748 -748
  99. package/{LICENSE → license} +21 -21
  100. package/package.json +2 -2
  101. package/src/buildProject.js +300 -300
  102. package/src/execute.js +184 -184
  103. package/src/internal/browser-launcher/jsenv-browser-system.js +203 -199
  104. package/src/internal/building/buildUsingRollup.js +2 -10
  105. package/src/internal/compiling/babel_plugin_import_assertions.js +121 -121
  106. package/src/internal/compiling/babel_plugin_import_metadata.js +22 -22
  107. package/src/internal/compiling/babel_plugin_import_visitor.js +84 -84
  108. package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +268 -268
  109. package/src/internal/compiling/compile-directory/updateMeta.js +154 -154
  110. package/src/internal/compiling/compile-directory/validateCache.js +265 -265
  111. package/src/internal/compiling/compileFile.js +233 -224
  112. package/src/internal/compiling/compileHtml.js +550 -550
  113. package/src/internal/compiling/createCompiledFileService.js +291 -291
  114. package/src/internal/compiling/html_source_file_service.js +403 -404
  115. package/src/internal/compiling/js-compilation-service/jsenvTransform.js +272 -270
  116. package/src/internal/compiling/jsenvCompilerForHtml.js +374 -308
  117. package/src/internal/compiling/jsenvCompilerForJavaScript.js +2 -0
  118. package/src/internal/compiling/startCompileServer.js +1086 -1048
  119. package/src/internal/compiling/transformResultToCompilationResult.js +220 -220
  120. package/src/internal/executing/coverage/babel_plugin_instrument.js +90 -90
  121. package/src/internal/executing/coverage/reportToCoverage.js +193 -187
  122. package/src/internal/executing/executePlan.js +183 -183
  123. package/src/internal/executing/launchAndExecute.js +458 -458
  124. package/src/internal/generateGroupMap/featuresCompatMap.js +29 -0
  125. package/src/internal/generateGroupMap/jsenvBabelPluginCompatMap.js +1 -8
  126. package/src/internal/runtime/createBrowserRuntime/scanBrowserRuntimeFeatures.js +246 -246
  127. package/src/internal/runtime/createNodeRuntime/scanNodeRuntimeFeatures.js +112 -112
  128. package/src/internal/runtime/s.js +727 -727
  129. package/src/internal/toolbar/jsenv-logo.svg +144 -144
  130. package/src/internal/toolbar/toolbar.main.css +196 -196
  131. package/src/internal/toolbar/toolbar.main.js +227 -227
  132. package/src/internal/url_conversion.js +317 -317
  133. package/src/startExploring.js +309 -309
@@ -1,550 +1,550 @@
1
- /**
2
-
3
- An important concern here:
4
-
5
- All script type="module" will be converted to inline script.
6
- These inline script execution order is non predictible it depends
7
- which one is being done first
8
-
9
- */
10
-
11
- import { createHash } from "crypto"
12
- import { require } from "../require.js"
13
- import { renderNamePattern } from "../renderNamePattern.js"
14
-
15
- // https://github.com/inikulin/parse5/blob/master/packages/parse5/lib/tree-adapters/default.js
16
- // eslint-disable-next-line import/no-unresolved
17
- // const treeAdapter = require("parse5/lib/tree-adapters/default.js")
18
-
19
- export const parseHtmlString = (htmlString) => {
20
- const parse5 = require("parse5")
21
- const htmlAst = parse5.parse(htmlString, { sourceCodeLocationInfo: true })
22
- return htmlAst
23
- }
24
-
25
- export const parseSvgString = (svgString) => {
26
- const parse5 = require("parse5")
27
- const svgAst = parse5.parseFragment(svgString, {
28
- sourceCodeLocationInfo: true,
29
- })
30
- return svgAst
31
- }
32
-
33
- export const stringifyHtmlAst = (htmlAst) => {
34
- const parse5 = require("parse5")
35
- const htmlString = parse5.serialize(htmlAst)
36
- return htmlString
37
- }
38
-
39
- export const findNode = (htmlStringOrAst, predicate) => {
40
- const htmlAst =
41
- typeof htmlStringOrAst === "string"
42
- ? parseHtmlString(htmlStringOrAst)
43
- : htmlStringOrAst
44
- let nodeMatching = null
45
- visitHtmlAst(htmlAst, (node) => {
46
- if (predicate(node)) {
47
- nodeMatching = node
48
- return "stop"
49
- }
50
- return null
51
- })
52
- return nodeMatching
53
- }
54
-
55
- export const findNodes = (htmlString, predicate) => {
56
- const htmlAst = parseHtmlString(htmlString)
57
- const nodes = []
58
- visitHtmlAst(htmlAst, (node) => {
59
- if (predicate(node)) {
60
- nodes.push(node)
61
- }
62
- return null
63
- })
64
- return nodes
65
- }
66
-
67
- export const findNodeByTagName = (htmlString, tagName) =>
68
- findNode(htmlString, (node) => node.nodeName === tagName)
69
-
70
- export const findHtmlNodeById = (htmlString, id) => {
71
- return findNode(htmlString, (node) => {
72
- const idAttribute = getHtmlNodeAttributeByName(node, "id")
73
- return idAttribute && idAttribute.value === id
74
- })
75
- }
76
-
77
- export const findAllNodeByTagName = (htmlString, tagName) =>
78
- findNodes(htmlString, (node) => node.nodeName === tagName)
79
-
80
- export const findFirstImportMapNode = (htmlStringOrAst) =>
81
- findNode(htmlStringOrAst, htmlNodeIsScriptImportmap)
82
-
83
- export const getHtmlNodeAttributeByName = (htmlNode, attributeName) => {
84
- const attrs = htmlNode.attrs
85
- return attrs && attrs.find((attr) => attr.name === attributeName)
86
- }
87
-
88
- export const removeHtmlNodeAttribute = (htmlNode, attributeToRemove) => {
89
- const attrIndex = htmlNode.attrs.indexOf(attributeToRemove)
90
- if (attrIndex === -1) {
91
- return false
92
- }
93
- htmlNode.attrs.splice(attrIndex, 1)
94
- return true
95
- }
96
-
97
- export const addHtmlNodeAttribute = (htmlNode, attributeToSet) => {
98
- if (typeof attributeToSet !== "object") {
99
- throw new TypeError(
100
- `addHtmlNodeAttribute attribute must be an object {name, value}`,
101
- )
102
- }
103
-
104
- const existingAttributeIndex = htmlNode.attrs.findIndex(
105
- (attr) => attr.name === attributeToSet.name,
106
- )
107
- if (existingAttributeIndex === -1) {
108
- htmlNode.attrs.push(attributeToSet)
109
- } else {
110
- htmlNode.attrs[existingAttributeIndex] = attributeToSet
111
- }
112
- }
113
-
114
- export const getHtmlNodeTextNode = (htmlNode) => {
115
- const firstChild = htmlNode.childNodes[0]
116
- return firstChild && firstChild.nodeName === "#text" ? firstChild : null
117
- }
118
-
119
- export const setHtmlNodeText = (htmlNode, textContent) => {
120
- const textNode = getHtmlNodeTextNode(htmlNode)
121
- if (textNode) {
122
- textNode.value = textContent
123
- } else {
124
- const newTextNode = {
125
- nodeName: "#text",
126
- value: textContent,
127
- parentNode: htmlNode,
128
- }
129
- htmlNode.childNodes.splice(0, 0, newTextNode)
130
- }
131
- }
132
-
133
- export const removeHtmlNodeText = (htmlNode) => {
134
- const textNode = getHtmlNodeTextNode(htmlNode)
135
- if (textNode) {
136
- htmlNode.childNodes = []
137
- }
138
- }
139
-
140
- export const removeHtmlNode = (htmlNode) => {
141
- const { childNodes } = htmlNode.parentNode
142
- childNodes.splice(childNodes.indexOf(htmlNode), 1)
143
- }
144
-
145
- export const getHtmlNodeLocation = (htmlNode, htmlAttributeName) => {
146
- const { sourceCodeLocation } = htmlNode
147
- if (!sourceCodeLocation) {
148
- return null
149
- }
150
-
151
- if (!htmlAttributeName) {
152
- const { startLine, startCol } = sourceCodeLocation
153
- return {
154
- line: startLine,
155
- column: startCol,
156
- }
157
- }
158
-
159
- const attributeSourceCodeLocation =
160
- sourceCodeLocation.attrs[htmlAttributeName]
161
- if (!attributeSourceCodeLocation) {
162
- return null
163
- }
164
- const { startLine, startCol } = attributeSourceCodeLocation
165
- return {
166
- line: startLine,
167
- column: startCol,
168
- }
169
- }
170
-
171
- export const findHtmlNode = (htmlAst, predicate) => {
172
- let nodeFound = null
173
- visitHtmlAst(htmlAst, (node) => {
174
- if (predicate(node)) {
175
- nodeFound = node
176
- return "stop"
177
- }
178
- return null
179
- })
180
- return nodeFound
181
- }
182
-
183
- export const htmlNodeIsScriptModule = (htmlNode) => {
184
- if (htmlNode.nodeName !== "script") {
185
- return false
186
- }
187
- const typeAttribute = getHtmlNodeAttributeByName(htmlNode, "type")
188
- if (!typeAttribute) {
189
- return false
190
- }
191
- return typeAttribute.value === "module"
192
- }
193
-
194
- export const htmlNodeIsScriptImportmap = (htmlNode) => {
195
- if (htmlNode.nodeName !== "script") {
196
- return false
197
- }
198
- const typeAttribute = getHtmlNodeAttributeByName(htmlNode, "type")
199
- if (!typeAttribute) {
200
- return false
201
- }
202
- return typeAttribute.value === "importmap"
203
- }
204
-
205
- export const parseSrcset = (srcsetString) => {
206
- const srcsetParts = []
207
- srcsetString.split(",").forEach((set) => {
208
- const [specifier, descriptor] = set.trim().split(" ")
209
- srcsetParts.push({
210
- specifier,
211
- descriptor,
212
- })
213
- })
214
- return srcsetParts
215
- }
216
-
217
- export const stringifySrcset = (srcsetParts) => {
218
- const srcsetString = srcsetParts
219
- .map(({ specifier, descriptor }) => `${specifier} ${descriptor}`)
220
- .join(", ")
221
- return srcsetString
222
- }
223
-
224
- // <img>, <link for favicon>, <link for css>, <styles>
225
- // <audio> <video> <picture> supports comes for free by detecting
226
- // <source src> attribute
227
- // ideally relative iframe should recursively fetch (not needed so lets ignore)
228
- export const parseHtmlAstRessources = (htmlAst) => {
229
- const links = []
230
- const styles = []
231
- const scripts = []
232
- const imgs = []
233
- const images = []
234
- const uses = []
235
- const sources = []
236
-
237
- visitHtmlAst(htmlAst, (node) => {
238
- if (node.nodeName === "link") {
239
- links.push(node)
240
- return
241
- }
242
-
243
- if (node.nodeName === "style") {
244
- styles.push(node)
245
- return
246
- }
247
-
248
- if (node.nodeName === "script") {
249
- scripts.push(node)
250
- return
251
- }
252
-
253
- if (node.nodeName === "img") {
254
- imgs.push(node)
255
- return
256
- }
257
-
258
- if (node.nodeName === "image") {
259
- images.push(node)
260
- return
261
- }
262
-
263
- if (node.nodeName === "use") {
264
- uses.push(node)
265
- return
266
- }
267
-
268
- if (node.nodeName === "source") {
269
- sources.push(node)
270
- return
271
- }
272
- })
273
-
274
- return {
275
- links,
276
- styles,
277
- scripts,
278
- imgs,
279
- images,
280
- uses,
281
- sources,
282
- }
283
- }
284
-
285
- export const collectHtmlDependenciesFromAst = (htmlAst) => {
286
- const { links, scripts, imgs, images, uses, sources } =
287
- parseHtmlAstRessources(htmlAst)
288
-
289
- const dependencies = []
290
- const visitSrcAttribute = (htmlNode) => {
291
- const srcAttribute = getHtmlNodeAttributeByName(htmlNode, "src")
292
- const src = srcAttribute ? srcAttribute.value : undefined
293
- if (src) {
294
- dependencies.push({
295
- htmlNode,
296
- attribute: srcAttribute,
297
- specifier: src,
298
- })
299
- }
300
- }
301
- const visitHrefAttribute = (htmlNode) => {
302
- const hrefAttribute = getHtmlNodeAttributeByName(htmlNode, "href")
303
- const href = hrefAttribute ? hrefAttribute.value : undefined
304
- if (href && href[0] !== "#") {
305
- dependencies.push({
306
- htmlNode,
307
- attribute: hrefAttribute,
308
- specifier: href,
309
- })
310
- }
311
- }
312
- const visitSrcSetAttribute = (htmlNode) => {
313
- const srcsetAttribute = getHtmlNodeAttributeByName(htmlNode, "srcset")
314
- if (srcsetAttribute) {
315
- const srcsetParts = parseSrcset(srcsetAttribute.value)
316
- srcsetParts.forEeach(({ specifier }) => {
317
- dependencies.push({
318
- htmlNode,
319
- attribute: srcsetAttribute,
320
- specifier,
321
- })
322
- })
323
- }
324
- }
325
-
326
- links.forEach((link) => {
327
- visitHrefAttribute(link)
328
- })
329
- scripts.forEach((script) => {
330
- visitSrcAttribute(script)
331
- })
332
- imgs.forEach((img) => {
333
- visitSrcAttribute(img)
334
- visitSrcSetAttribute(img)
335
- })
336
- sources.forEach((source) => {
337
- visitSrcAttribute(source)
338
- visitSrcSetAttribute(source)
339
- })
340
- // svg <image> tag
341
- images.forEach((image) => {
342
- visitHrefAttribute(image)
343
- })
344
- uses.forEach((use) => {
345
- visitHrefAttribute(use)
346
- })
347
-
348
- return dependencies
349
- }
350
-
351
- export const replaceHtmlNode = (
352
- node,
353
- replacement,
354
- { attributesInherit = true, attributesToIgnore = [] } = {},
355
- ) => {
356
- let newNode
357
- if (typeof replacement === "string") {
358
- newNode = parseHtmlAsSingleElement(replacement)
359
- } else {
360
- newNode = replacement
361
- }
362
-
363
- if (attributesInherit) {
364
- const attributeMap = {}
365
- // inherit attributes except thoos listed in attributesToIgnore
366
- node.attrs.forEach((attribute) => {
367
- if (attributesToIgnore.includes(attribute.name)) {
368
- return
369
- }
370
- attributeMap[attribute.name] = attribute
371
- })
372
- newNode.attrs.forEach((newAttribute) => {
373
- attributeMap[newAttribute.name] = newAttribute
374
- })
375
- const attributes = []
376
- Object.keys(attributeMap).forEach((attributeName) => {
377
- attributes.push(attributeMap[attributeName])
378
- })
379
- newNode.attrs = attributes
380
- }
381
-
382
- replaceNode(node, newNode)
383
- }
384
-
385
- export const manipulateHtmlAst = (htmlAst, { scriptInjections = [] }) => {
386
- if (scriptInjections.length === 0) {
387
- return
388
- }
389
-
390
- const htmlNode = htmlAst.childNodes.find((node) => node.nodeName === "html")
391
- const headNode = htmlNode.childNodes[0]
392
- const bodyNode = htmlNode.childNodes[1]
393
-
394
- const scriptsToPreprendInHead = []
395
- scriptInjections.forEach((script) => {
396
- const scriptExistingInHead = findExistingScript(headNode, script)
397
- if (scriptExistingInHead) {
398
- replaceNode(scriptExistingInHead, scriptToNode(script))
399
- return
400
- }
401
- const scriptExistingInBody = findExistingScript(bodyNode, script)
402
- if (scriptExistingInBody) {
403
- replaceNode(scriptExistingInBody, scriptToNode(script))
404
- return
405
- }
406
- scriptsToPreprendInHead.push(script)
407
- })
408
- const headScriptsFragment = scriptsToFragment(scriptsToPreprendInHead)
409
- insertFragmentBefore(
410
- headNode,
411
- headScriptsFragment,
412
- findChild(headNode, (node) => node.nodeName === "script"),
413
- )
414
- }
415
-
416
- const insertFragmentBefore = (node, fragment, childNode) => {
417
- const { childNodes = [] } = node
418
-
419
- if (childNode) {
420
- const childNodeIndex = childNodes.indexOf(childNode)
421
- node.childNodes = [
422
- ...childNodes.slice(0, childNodeIndex),
423
- ...fragment.childNodes.map((child) => {
424
- return { ...child, parentNode: node }
425
- }),
426
- ...childNodes.slice(childNodeIndex),
427
- ]
428
- } else {
429
- node.childNodes = [
430
- ...childNodes,
431
- ...fragment.childNodes.map((child) => {
432
- return { ...child, parentNode: node }
433
- }),
434
- ]
435
- }
436
- }
437
-
438
- const scriptToNode = (script) => {
439
- return scriptsToFragment([script]).childNodes[0]
440
- }
441
-
442
- const scriptsToFragment = (scripts) => {
443
- const html = scripts.reduce((previous, script) => {
444
- const { text = "", ...attributes } = script
445
- const scriptAttributes = objectToHtmlAttributes(attributes)
446
- return `${previous}<script ${scriptAttributes}>${text}</script>
447
- `
448
- }, "")
449
- const parse5 = require("parse5")
450
- const fragment = parse5.parseFragment(html)
451
- return fragment
452
- }
453
-
454
- const findExistingScript = (node, script) =>
455
- findChild(node, (childNode) => {
456
- return childNode.nodeName === "script" && sameScript(childNode, script)
457
- })
458
-
459
- const findChild = ({ childNodes = [] }, predicate) => childNodes.find(predicate)
460
-
461
- const sameScript = (node, { type = "text/javascript", src }) => {
462
- const typeAttribute = getHtmlNodeAttributeByName(node, "type")
463
- const leftScriptType = typeAttribute ? typeAttribute.value : "text/javascript"
464
- if (leftScriptType !== type) {
465
- return false
466
- }
467
-
468
- const srcAttribute = getHtmlNodeAttributeByName(node, "src")
469
- if (!srcAttribute && src) {
470
- return false
471
- }
472
- if (srcAttribute && srcAttribute.value !== src) {
473
- return false
474
- }
475
-
476
- return true
477
- }
478
-
479
- const objectToHtmlAttributes = (object) => {
480
- return Object.keys(object)
481
- .map((key) => `${key}=${valueToHtmlAttributeValue(object[key])}`)
482
- .join(" ")
483
- }
484
-
485
- const valueToHtmlAttributeValue = (value) => {
486
- if (typeof value === "string") {
487
- return JSON.stringify(value)
488
- }
489
- return `"${JSON.stringify(value)}"`
490
- }
491
-
492
- export const createInlineScriptHash = (script) => {
493
- const hash = createHash("sha256")
494
- hash.update(getHtmlNodeTextNode(script).value)
495
- return hash.digest("hex").slice(0, 8)
496
- }
497
-
498
- export const getUniqueNameForInlineHtmlNode = (node, nodes, pattern) => {
499
- return renderNamePattern(pattern, {
500
- id: () => {
501
- const idAttribute = getHtmlNodeAttributeByName(node, "id")
502
- if (idAttribute) {
503
- return idAttribute.value
504
- }
505
-
506
- const { line, column } = getHtmlNodeLocation(node) || {}
507
- const lineTaken = nodes.some((nodeCandidate) => {
508
- if (nodeCandidate === node) return false
509
- const htmlNodeLocation = getHtmlNodeLocation(nodeCandidate)
510
- if (!htmlNodeLocation) return false
511
- return htmlNodeLocation.line === line
512
- })
513
- if (lineTaken) {
514
- return `${line}.${column}`
515
- }
516
-
517
- return line
518
- },
519
- })
520
- }
521
-
522
- const parseHtmlAsSingleElement = (html) => {
523
- const parse5 = require("parse5")
524
- const fragment = parse5.parseFragment(html)
525
- return fragment.childNodes[0]
526
- }
527
-
528
- const replaceNode = (node, newNode) => {
529
- const { parentNode } = node
530
- const parentNodeChildNodes = parentNode.childNodes
531
- const nodeIndex = parentNodeChildNodes.indexOf(node)
532
- parentNodeChildNodes[nodeIndex] = newNode
533
- }
534
-
535
- export const visitHtmlAst = (htmlAst, callback) => {
536
- const visitNode = (node) => {
537
- const callbackReturnValue = callback(node)
538
- if (callbackReturnValue === "stop") {
539
- return
540
- }
541
- const { childNodes } = node
542
- if (childNodes) {
543
- let i = 0
544
- while (i < childNodes.length) {
545
- visitNode(childNodes[i++])
546
- }
547
- }
548
- }
549
- visitNode(htmlAst)
550
- }
1
+ /**
2
+
3
+ An important concern here:
4
+
5
+ All script type="module" will be converted to inline script.
6
+ These inline script execution order is non predictible it depends
7
+ which one is being done first
8
+
9
+ */
10
+
11
+ import { createHash } from "crypto"
12
+ import { require } from "../require.js"
13
+ import { renderNamePattern } from "../renderNamePattern.js"
14
+
15
+ // https://github.com/inikulin/parse5/blob/master/packages/parse5/lib/tree-adapters/default.js
16
+ // eslint-disable-next-line import/no-unresolved
17
+ // const treeAdapter = require("parse5/lib/tree-adapters/default.js")
18
+
19
+ export const parseHtmlString = (htmlString) => {
20
+ const parse5 = require("parse5")
21
+ const htmlAst = parse5.parse(htmlString, { sourceCodeLocationInfo: true })
22
+ return htmlAst
23
+ }
24
+
25
+ export const parseSvgString = (svgString) => {
26
+ const parse5 = require("parse5")
27
+ const svgAst = parse5.parseFragment(svgString, {
28
+ sourceCodeLocationInfo: true,
29
+ })
30
+ return svgAst
31
+ }
32
+
33
+ export const stringifyHtmlAst = (htmlAst) => {
34
+ const parse5 = require("parse5")
35
+ const htmlString = parse5.serialize(htmlAst)
36
+ return htmlString
37
+ }
38
+
39
+ export const findNode = (htmlStringOrAst, predicate) => {
40
+ const htmlAst =
41
+ typeof htmlStringOrAst === "string"
42
+ ? parseHtmlString(htmlStringOrAst)
43
+ : htmlStringOrAst
44
+ let nodeMatching = null
45
+ visitHtmlAst(htmlAst, (node) => {
46
+ if (predicate(node)) {
47
+ nodeMatching = node
48
+ return "stop"
49
+ }
50
+ return null
51
+ })
52
+ return nodeMatching
53
+ }
54
+
55
+ export const findNodes = (htmlString, predicate) => {
56
+ const htmlAst = parseHtmlString(htmlString)
57
+ const nodes = []
58
+ visitHtmlAst(htmlAst, (node) => {
59
+ if (predicate(node)) {
60
+ nodes.push(node)
61
+ }
62
+ return null
63
+ })
64
+ return nodes
65
+ }
66
+
67
+ export const findNodeByTagName = (htmlString, tagName) =>
68
+ findNode(htmlString, (node) => node.nodeName === tagName)
69
+
70
+ export const findHtmlNodeById = (htmlString, id) => {
71
+ return findNode(htmlString, (node) => {
72
+ const idAttribute = getHtmlNodeAttributeByName(node, "id")
73
+ return idAttribute && idAttribute.value === id
74
+ })
75
+ }
76
+
77
+ export const findAllNodeByTagName = (htmlString, tagName) =>
78
+ findNodes(htmlString, (node) => node.nodeName === tagName)
79
+
80
+ export const findFirstImportMapNode = (htmlStringOrAst) =>
81
+ findNode(htmlStringOrAst, htmlNodeIsScriptImportmap)
82
+
83
+ export const getHtmlNodeAttributeByName = (htmlNode, attributeName) => {
84
+ const attrs = htmlNode.attrs
85
+ return attrs && attrs.find((attr) => attr.name === attributeName)
86
+ }
87
+
88
+ export const removeHtmlNodeAttribute = (htmlNode, attributeToRemove) => {
89
+ const attrIndex = htmlNode.attrs.indexOf(attributeToRemove)
90
+ if (attrIndex === -1) {
91
+ return false
92
+ }
93
+ htmlNode.attrs.splice(attrIndex, 1)
94
+ return true
95
+ }
96
+
97
+ export const addHtmlNodeAttribute = (htmlNode, attributeToSet) => {
98
+ if (typeof attributeToSet !== "object") {
99
+ throw new TypeError(
100
+ `addHtmlNodeAttribute attribute must be an object {name, value}`,
101
+ )
102
+ }
103
+
104
+ const existingAttributeIndex = htmlNode.attrs.findIndex(
105
+ (attr) => attr.name === attributeToSet.name,
106
+ )
107
+ if (existingAttributeIndex === -1) {
108
+ htmlNode.attrs.push(attributeToSet)
109
+ } else {
110
+ htmlNode.attrs[existingAttributeIndex] = attributeToSet
111
+ }
112
+ }
113
+
114
+ export const getHtmlNodeTextNode = (htmlNode) => {
115
+ const firstChild = htmlNode.childNodes[0]
116
+ return firstChild && firstChild.nodeName === "#text" ? firstChild : null
117
+ }
118
+
119
+ export const setHtmlNodeText = (htmlNode, textContent) => {
120
+ const textNode = getHtmlNodeTextNode(htmlNode)
121
+ if (textNode) {
122
+ textNode.value = textContent
123
+ } else {
124
+ const newTextNode = {
125
+ nodeName: "#text",
126
+ value: textContent,
127
+ parentNode: htmlNode,
128
+ }
129
+ htmlNode.childNodes.splice(0, 0, newTextNode)
130
+ }
131
+ }
132
+
133
+ export const removeHtmlNodeText = (htmlNode) => {
134
+ const textNode = getHtmlNodeTextNode(htmlNode)
135
+ if (textNode) {
136
+ htmlNode.childNodes = []
137
+ }
138
+ }
139
+
140
+ export const removeHtmlNode = (htmlNode) => {
141
+ const { childNodes } = htmlNode.parentNode
142
+ childNodes.splice(childNodes.indexOf(htmlNode), 1)
143
+ }
144
+
145
+ export const getHtmlNodeLocation = (htmlNode, htmlAttributeName) => {
146
+ const { sourceCodeLocation } = htmlNode
147
+ if (!sourceCodeLocation) {
148
+ return null
149
+ }
150
+
151
+ if (!htmlAttributeName) {
152
+ const { startLine, startCol } = sourceCodeLocation
153
+ return {
154
+ line: startLine,
155
+ column: startCol,
156
+ }
157
+ }
158
+
159
+ const attributeSourceCodeLocation =
160
+ sourceCodeLocation.attrs[htmlAttributeName]
161
+ if (!attributeSourceCodeLocation) {
162
+ return null
163
+ }
164
+ const { startLine, startCol } = attributeSourceCodeLocation
165
+ return {
166
+ line: startLine,
167
+ column: startCol,
168
+ }
169
+ }
170
+
171
+ export const findHtmlNode = (htmlAst, predicate) => {
172
+ let nodeFound = null
173
+ visitHtmlAst(htmlAst, (node) => {
174
+ if (predicate(node)) {
175
+ nodeFound = node
176
+ return "stop"
177
+ }
178
+ return null
179
+ })
180
+ return nodeFound
181
+ }
182
+
183
+ export const htmlNodeIsScriptModule = (htmlNode) => {
184
+ if (htmlNode.nodeName !== "script") {
185
+ return false
186
+ }
187
+ const typeAttribute = getHtmlNodeAttributeByName(htmlNode, "type")
188
+ if (!typeAttribute) {
189
+ return false
190
+ }
191
+ return typeAttribute.value === "module"
192
+ }
193
+
194
+ export const htmlNodeIsScriptImportmap = (htmlNode) => {
195
+ if (htmlNode.nodeName !== "script") {
196
+ return false
197
+ }
198
+ const typeAttribute = getHtmlNodeAttributeByName(htmlNode, "type")
199
+ if (!typeAttribute) {
200
+ return false
201
+ }
202
+ return typeAttribute.value === "importmap"
203
+ }
204
+
205
+ export const parseSrcset = (srcsetString) => {
206
+ const srcsetParts = []
207
+ srcsetString.split(",").forEach((set) => {
208
+ const [specifier, descriptor] = set.trim().split(" ")
209
+ srcsetParts.push({
210
+ specifier,
211
+ descriptor,
212
+ })
213
+ })
214
+ return srcsetParts
215
+ }
216
+
217
+ export const stringifySrcset = (srcsetParts) => {
218
+ const srcsetString = srcsetParts
219
+ .map(({ specifier, descriptor }) => `${specifier} ${descriptor}`)
220
+ .join(", ")
221
+ return srcsetString
222
+ }
223
+
224
+ // <img>, <link for favicon>, <link for css>, <styles>
225
+ // <audio> <video> <picture> supports comes for free by detecting
226
+ // <source src> attribute
227
+ // ideally relative iframe should recursively fetch (not needed so lets ignore)
228
+ export const parseHtmlAstRessources = (htmlAst) => {
229
+ const links = []
230
+ const styles = []
231
+ const scripts = []
232
+ const imgs = []
233
+ const images = []
234
+ const uses = []
235
+ const sources = []
236
+
237
+ visitHtmlAst(htmlAst, (node) => {
238
+ if (node.nodeName === "link") {
239
+ links.push(node)
240
+ return
241
+ }
242
+
243
+ if (node.nodeName === "style") {
244
+ styles.push(node)
245
+ return
246
+ }
247
+
248
+ if (node.nodeName === "script") {
249
+ scripts.push(node)
250
+ return
251
+ }
252
+
253
+ if (node.nodeName === "img") {
254
+ imgs.push(node)
255
+ return
256
+ }
257
+
258
+ if (node.nodeName === "image") {
259
+ images.push(node)
260
+ return
261
+ }
262
+
263
+ if (node.nodeName === "use") {
264
+ uses.push(node)
265
+ return
266
+ }
267
+
268
+ if (node.nodeName === "source") {
269
+ sources.push(node)
270
+ return
271
+ }
272
+ })
273
+
274
+ return {
275
+ links,
276
+ styles,
277
+ scripts,
278
+ imgs,
279
+ images,
280
+ uses,
281
+ sources,
282
+ }
283
+ }
284
+
285
+ export const collectHtmlDependenciesFromAst = (htmlAst) => {
286
+ const { links, scripts, imgs, images, uses, sources } =
287
+ parseHtmlAstRessources(htmlAst)
288
+
289
+ const dependencies = []
290
+ const visitSrcAttribute = (htmlNode) => {
291
+ const srcAttribute = getHtmlNodeAttributeByName(htmlNode, "src")
292
+ const src = srcAttribute ? srcAttribute.value : undefined
293
+ if (src) {
294
+ dependencies.push({
295
+ htmlNode,
296
+ attribute: srcAttribute,
297
+ specifier: src,
298
+ })
299
+ }
300
+ }
301
+ const visitHrefAttribute = (htmlNode) => {
302
+ const hrefAttribute = getHtmlNodeAttributeByName(htmlNode, "href")
303
+ const href = hrefAttribute ? hrefAttribute.value : undefined
304
+ if (href && href[0] !== "#") {
305
+ dependencies.push({
306
+ htmlNode,
307
+ attribute: hrefAttribute,
308
+ specifier: href,
309
+ })
310
+ }
311
+ }
312
+ const visitSrcSetAttribute = (htmlNode) => {
313
+ const srcsetAttribute = getHtmlNodeAttributeByName(htmlNode, "srcset")
314
+ if (srcsetAttribute) {
315
+ const srcsetParts = parseSrcset(srcsetAttribute.value)
316
+ srcsetParts.forEeach(({ specifier }) => {
317
+ dependencies.push({
318
+ htmlNode,
319
+ attribute: srcsetAttribute,
320
+ specifier,
321
+ })
322
+ })
323
+ }
324
+ }
325
+
326
+ links.forEach((link) => {
327
+ visitHrefAttribute(link)
328
+ })
329
+ scripts.forEach((script) => {
330
+ visitSrcAttribute(script)
331
+ })
332
+ imgs.forEach((img) => {
333
+ visitSrcAttribute(img)
334
+ visitSrcSetAttribute(img)
335
+ })
336
+ sources.forEach((source) => {
337
+ visitSrcAttribute(source)
338
+ visitSrcSetAttribute(source)
339
+ })
340
+ // svg <image> tag
341
+ images.forEach((image) => {
342
+ visitHrefAttribute(image)
343
+ })
344
+ uses.forEach((use) => {
345
+ visitHrefAttribute(use)
346
+ })
347
+
348
+ return dependencies
349
+ }
350
+
351
+ export const replaceHtmlNode = (
352
+ node,
353
+ replacement,
354
+ { attributesInherit = true, attributesToIgnore = [] } = {},
355
+ ) => {
356
+ let newNode
357
+ if (typeof replacement === "string") {
358
+ newNode = parseHtmlAsSingleElement(replacement)
359
+ } else {
360
+ newNode = replacement
361
+ }
362
+
363
+ if (attributesInherit) {
364
+ const attributeMap = {}
365
+ // inherit attributes except thoos listed in attributesToIgnore
366
+ node.attrs.forEach((attribute) => {
367
+ if (attributesToIgnore.includes(attribute.name)) {
368
+ return
369
+ }
370
+ attributeMap[attribute.name] = attribute
371
+ })
372
+ newNode.attrs.forEach((newAttribute) => {
373
+ attributeMap[newAttribute.name] = newAttribute
374
+ })
375
+ const attributes = []
376
+ Object.keys(attributeMap).forEach((attributeName) => {
377
+ attributes.push(attributeMap[attributeName])
378
+ })
379
+ newNode.attrs = attributes
380
+ }
381
+
382
+ replaceNode(node, newNode)
383
+ }
384
+
385
+ export const manipulateHtmlAst = (htmlAst, { scriptInjections = [] }) => {
386
+ if (scriptInjections.length === 0) {
387
+ return
388
+ }
389
+
390
+ const htmlNode = htmlAst.childNodes.find((node) => node.nodeName === "html")
391
+ const headNode = htmlNode.childNodes[0]
392
+ const bodyNode = htmlNode.childNodes[1]
393
+
394
+ const scriptsToPreprendInHead = []
395
+ scriptInjections.forEach((script) => {
396
+ const scriptExistingInHead = findExistingScript(headNode, script)
397
+ if (scriptExistingInHead) {
398
+ replaceNode(scriptExistingInHead, scriptToNode(script))
399
+ return
400
+ }
401
+ const scriptExistingInBody = findExistingScript(bodyNode, script)
402
+ if (scriptExistingInBody) {
403
+ replaceNode(scriptExistingInBody, scriptToNode(script))
404
+ return
405
+ }
406
+ scriptsToPreprendInHead.push(script)
407
+ })
408
+ const headScriptsFragment = scriptsToFragment(scriptsToPreprendInHead)
409
+ insertFragmentBefore(
410
+ headNode,
411
+ headScriptsFragment,
412
+ findChild(headNode, (node) => node.nodeName === "script"),
413
+ )
414
+ }
415
+
416
+ const insertFragmentBefore = (node, fragment, childNode) => {
417
+ const { childNodes = [] } = node
418
+
419
+ if (childNode) {
420
+ const childNodeIndex = childNodes.indexOf(childNode)
421
+ node.childNodes = [
422
+ ...childNodes.slice(0, childNodeIndex),
423
+ ...fragment.childNodes.map((child) => {
424
+ return { ...child, parentNode: node }
425
+ }),
426
+ ...childNodes.slice(childNodeIndex),
427
+ ]
428
+ } else {
429
+ node.childNodes = [
430
+ ...childNodes,
431
+ ...fragment.childNodes.map((child) => {
432
+ return { ...child, parentNode: node }
433
+ }),
434
+ ]
435
+ }
436
+ }
437
+
438
+ const scriptToNode = (script) => {
439
+ return scriptsToFragment([script]).childNodes[0]
440
+ }
441
+
442
+ const scriptsToFragment = (scripts) => {
443
+ const html = scripts.reduce((previous, script) => {
444
+ const { text = "", ...attributes } = script
445
+ const scriptAttributes = objectToHtmlAttributes(attributes)
446
+ return `${previous}<script ${scriptAttributes}>${text}</script>
447
+ `
448
+ }, "")
449
+ const parse5 = require("parse5")
450
+ const fragment = parse5.parseFragment(html)
451
+ return fragment
452
+ }
453
+
454
+ const findExistingScript = (node, script) =>
455
+ findChild(node, (childNode) => {
456
+ return childNode.nodeName === "script" && sameScript(childNode, script)
457
+ })
458
+
459
+ const findChild = ({ childNodes = [] }, predicate) => childNodes.find(predicate)
460
+
461
+ const sameScript = (node, { type = "text/javascript", src }) => {
462
+ const typeAttribute = getHtmlNodeAttributeByName(node, "type")
463
+ const leftScriptType = typeAttribute ? typeAttribute.value : "text/javascript"
464
+ if (leftScriptType !== type) {
465
+ return false
466
+ }
467
+
468
+ const srcAttribute = getHtmlNodeAttributeByName(node, "src")
469
+ if (!srcAttribute && src) {
470
+ return false
471
+ }
472
+ if (srcAttribute && srcAttribute.value !== src) {
473
+ return false
474
+ }
475
+
476
+ return true
477
+ }
478
+
479
+ const objectToHtmlAttributes = (object) => {
480
+ return Object.keys(object)
481
+ .map((key) => `${key}=${valueToHtmlAttributeValue(object[key])}`)
482
+ .join(" ")
483
+ }
484
+
485
+ const valueToHtmlAttributeValue = (value) => {
486
+ if (typeof value === "string") {
487
+ return JSON.stringify(value)
488
+ }
489
+ return `"${JSON.stringify(value)}"`
490
+ }
491
+
492
+ export const createInlineScriptHash = (script) => {
493
+ const hash = createHash("sha256")
494
+ hash.update(getHtmlNodeTextNode(script).value)
495
+ return hash.digest("hex").slice(0, 8)
496
+ }
497
+
498
+ export const getUniqueNameForInlineHtmlNode = (node, nodes, pattern) => {
499
+ return renderNamePattern(pattern, {
500
+ id: () => {
501
+ const idAttribute = getHtmlNodeAttributeByName(node, "id")
502
+ if (idAttribute) {
503
+ return idAttribute.value
504
+ }
505
+
506
+ const { line, column } = getHtmlNodeLocation(node) || {}
507
+ const lineTaken = nodes.some((nodeCandidate) => {
508
+ if (nodeCandidate === node) return false
509
+ const htmlNodeLocation = getHtmlNodeLocation(nodeCandidate)
510
+ if (!htmlNodeLocation) return false
511
+ return htmlNodeLocation.line === line
512
+ })
513
+ if (lineTaken) {
514
+ return `${line}.${column}`
515
+ }
516
+
517
+ return line
518
+ },
519
+ })
520
+ }
521
+
522
+ const parseHtmlAsSingleElement = (html) => {
523
+ const parse5 = require("parse5")
524
+ const fragment = parse5.parseFragment(html)
525
+ return fragment.childNodes[0]
526
+ }
527
+
528
+ const replaceNode = (node, newNode) => {
529
+ const { parentNode } = node
530
+ const parentNodeChildNodes = parentNode.childNodes
531
+ const nodeIndex = parentNodeChildNodes.indexOf(node)
532
+ parentNodeChildNodes[nodeIndex] = newNode
533
+ }
534
+
535
+ export const visitHtmlAst = (htmlAst, callback) => {
536
+ const visitNode = (node) => {
537
+ const callbackReturnValue = callback(node)
538
+ if (callbackReturnValue === "stop") {
539
+ return
540
+ }
541
+ const { childNodes } = node
542
+ if (childNodes) {
543
+ let i = 0
544
+ while (i < childNodes.length) {
545
+ visitNode(childNodes[i++])
546
+ }
547
+ }
548
+ }
549
+ visitNode(htmlAst)
550
+ }