@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.
- package/dist/jsenv_browser_system.js +46 -39
- package/dist/jsenv_browser_system.js.map +14 -14
- package/dist/jsenv_compile_proxy.js.map +6 -6
- package/dist/jsenv_exploring_index.js.map +5 -5
- package/dist/jsenv_exploring_redirector.js.map +12 -12
- package/dist/jsenv_toolbar.js.map +7 -7
- package/dist/jsenv_toolbar_injector.js.map +5 -5
- package/helpers/babel/.eslintrc.cjs +24 -24
- package/helpers/babel/AsyncGenerator/AsyncGenerator.js +81 -81
- package/helpers/babel/AwaitValue/AwaitValue.js +3 -3
- package/helpers/babel/applyDecoratorDescriptor/applyDecoratorDescriptor.js +33 -33
- package/helpers/babel/arrayLikeToArray/arrayLikeToArray.js +7 -7
- package/helpers/babel/arrayWithHoles/arrayWithHoles.js +4 -4
- package/helpers/babel/arrayWithoutHoles/arrayWithoutHoles.js +6 -6
- package/helpers/babel/assertThisInitialized/assertThisInitialized.js +7 -7
- package/helpers/babel/asyncGeneratorDelegate/asyncGeneratorDelegate.js +40 -40
- package/helpers/babel/asyncIterator/asyncIterator.js +12 -12
- package/helpers/babel/asyncToGenerator/asyncToGenerator.js +34 -34
- package/helpers/babel/awaitAsyncGenerator/awaitAsyncGenerator.js +5 -5
- package/helpers/babel/classApplyDescriptorDestructureSet/classApplyDescriptorDestructureSet.js +20 -20
- package/helpers/babel/classApplyDescriptorGet/classApplyDescriptorGet.js +6 -6
- package/helpers/babel/classApplyDescriptorSet/classApplyDescriptorSet.js +13 -13
- package/helpers/babel/classCallCheck/classCallCheck.js +5 -5
- package/helpers/babel/classCheckPrivateStaticAccess/classCheckPrivateStaticAccess.js +5 -5
- package/helpers/babel/classCheckPrivateStaticFieldDescriptor/classCheckPrivateStaticFieldDescriptor.js +6 -6
- package/helpers/babel/classExtractFieldDescriptor/classExtractFieldDescriptor.js +7 -7
- package/helpers/babel/classNameTDZError/classNameTDZError.js +4 -4
- package/helpers/babel/classPrivateFieldDestructureSet/classPrivateFieldDestructureSet.js +7 -7
- package/helpers/babel/classPrivateFieldGet/classPrivateFieldGet.js +7 -7
- package/helpers/babel/classPrivateFieldLooseBase/classPrivateFieldLooseBase.js +6 -6
- package/helpers/babel/classPrivateFieldLooseKey/classPrivateFieldLooseKey.js +5 -5
- package/helpers/babel/classPrivateFieldSet/classPrivateFieldSet.js +8 -8
- package/helpers/babel/classPrivateMethodGet/classPrivateMethodGet.js +6 -6
- package/helpers/babel/classPrivateMethodSet/classPrivateMethodSet.js +3 -3
- package/helpers/babel/classStaticPrivateFieldSpecGet/classStaticPrivateFieldSpecGet.js +9 -9
- package/helpers/babel/classStaticPrivateFieldSpecSet/classStaticPrivateFieldSpecSet.js +15 -15
- package/helpers/babel/classStaticPrivateMethodGet/classStaticPrivateMethodGet.js +6 -6
- package/helpers/babel/classStaticPrivateMethodSet/classStaticPrivateMethodSet.js +3 -3
- package/helpers/babel/construct/construct.js +16 -16
- package/helpers/babel/createClass/createClass.js +15 -15
- package/helpers/babel/createForOfIteratorHelper/createForOfIteratorHelper.js +60 -60
- package/helpers/babel/createForOfIteratorHelperLoose/createForOfIteratorHelperLoose.js +23 -23
- package/helpers/babel/createRawReactElement/createRawReactElement.js +50 -50
- package/helpers/babel/createSuper/createSuper.js +22 -22
- package/helpers/babel/decorate/decorate.js +403 -403
- package/helpers/babel/defaults/defaults.js +11 -11
- package/helpers/babel/defineEnumerableProperties/defineEnumerableProperties.js +23 -23
- package/helpers/babel/defineProperty/defineProperty.js +18 -18
- package/helpers/babel/extends/extends.js +14 -14
- package/helpers/babel/get/get.js +13 -13
- package/helpers/babel/getPrototypeOf/getPrototypeOf.js +4 -4
- package/helpers/babel/inherits/inherits.js +15 -15
- package/helpers/babel/inheritsLoose/inheritsLoose.js +7 -7
- package/helpers/babel/initializerDefineProperty/initializerDefineProperty.js +10 -10
- package/helpers/babel/initializerWarningHelper/initializerWarningHelper.js +6 -6
- package/helpers/babel/instanceof/instanceof.js +6 -6
- package/helpers/babel/interopRequireDefault/interopRequireDefault.js +3 -3
- package/helpers/babel/interopRequireWildcard/interopRequireWildcard.js +37 -37
- package/helpers/babel/isNativeFunction/isNativeFunction.js +4 -4
- package/helpers/babel/isNativeReflectConstruct/isNativeReflectConstruct.js +21 -21
- package/helpers/babel/iterableToArray/iterableToArray.js +7 -7
- package/helpers/babel/iterableToArrayLimit/iterableToArrayLimit.js +36 -36
- package/helpers/babel/iterableToArrayLimitLoose/iterableToArrayLimitLoose.js +10 -10
- package/helpers/babel/jsx/jsx.js +45 -45
- package/helpers/babel/maybeArrayLike/maybeArrayLike.js +10 -10
- package/helpers/babel/newArrowCheck/newArrowCheck.js +5 -5
- package/helpers/babel/nonIterableRest/nonIterableRest.js +5 -5
- package/helpers/babel/nonIterableSpread/nonIterableSpread.js +5 -5
- package/helpers/babel/objectDestructuringEmpty/objectDestructuringEmpty.js +3 -3
- package/helpers/babel/objectSpread/objectSpread.js +23 -23
- package/helpers/babel/objectSpread2/objectSpread2.js +33 -33
- package/helpers/babel/objectWithoutProperties/objectWithoutProperties.js +19 -19
- package/helpers/babel/objectWithoutPropertiesLoose/objectWithoutPropertiesLoose.js +13 -13
- package/helpers/babel/possibleConstructorReturn/possibleConstructorReturn.js +10 -10
- package/helpers/babel/readOnlyError/readOnlyError.js +4 -4
- package/helpers/babel/readme.md +9 -9
- package/helpers/babel/set/set.js +44 -44
- package/helpers/babel/setPrototypeOf/setPrototypeOf.js +6 -6
- package/helpers/babel/skipFirstGeneratorNext/skipFirstGeneratorNext.js +8 -8
- package/helpers/babel/slicedToArray/slicedToArray.js +10 -10
- package/helpers/babel/slicedToArrayLoose/slicedToArrayLoose.js +13 -13
- package/helpers/babel/superPropBase/superPropBase.js +10 -10
- package/helpers/babel/taggedTemplateLiteral/taggedTemplateLiteral.js +10 -10
- package/helpers/babel/taggedTemplateLiteralLoose/taggedTemplateLiteralLoose.js +7 -7
- package/helpers/babel/tdz/tdz.js +4 -4
- package/helpers/babel/temporalRef/temporalRef.js +6 -6
- package/helpers/babel/temporalUndefined/temporalUndefined.js +3 -3
- package/helpers/babel/toArray/toArray.js +10 -10
- package/helpers/babel/toConsumableArray/toConsumableArray.js +10 -10
- package/helpers/babel/toPrimitive/toPrimitive.js +10 -10
- package/helpers/babel/toPropertyKey/toPropertyKey.js +6 -6
- package/helpers/babel/typeof/typeof.js +14 -14
- package/helpers/babel/unsupportedIterableToArray/unsupportedIterableToArray.js +12 -12
- package/helpers/babel/wrapAsyncGenerator/wrapAsyncGenerator.js +8 -8
- package/helpers/babel/wrapNativeSuper/wrapNativeSuper.js +30 -30
- package/helpers/babel/wrapRegExp/wrapRegExp.js +63 -63
- package/helpers/babel/writeOnlyError/writeOnlyError.js +4 -4
- package/helpers/regenerator-runtime/regenerator-runtime.js +748 -748
- package/{LICENSE → license} +21 -21
- package/package.json +2 -2
- package/src/buildProject.js +300 -300
- package/src/execute.js +184 -184
- package/src/internal/browser-launcher/jsenv-browser-system.js +203 -199
- package/src/internal/building/buildUsingRollup.js +2 -10
- package/src/internal/compiling/babel_plugin_import_assertions.js +121 -121
- package/src/internal/compiling/babel_plugin_import_metadata.js +22 -22
- package/src/internal/compiling/babel_plugin_import_visitor.js +84 -84
- package/src/internal/compiling/compile-directory/getOrGenerateCompiledFile.js +268 -268
- package/src/internal/compiling/compile-directory/updateMeta.js +154 -154
- package/src/internal/compiling/compile-directory/validateCache.js +265 -265
- package/src/internal/compiling/compileFile.js +233 -224
- package/src/internal/compiling/compileHtml.js +550 -550
- package/src/internal/compiling/createCompiledFileService.js +291 -291
- package/src/internal/compiling/html_source_file_service.js +403 -404
- package/src/internal/compiling/js-compilation-service/jsenvTransform.js +272 -270
- package/src/internal/compiling/jsenvCompilerForHtml.js +374 -308
- package/src/internal/compiling/jsenvCompilerForJavaScript.js +2 -0
- package/src/internal/compiling/startCompileServer.js +1086 -1048
- package/src/internal/compiling/transformResultToCompilationResult.js +220 -220
- package/src/internal/executing/coverage/babel_plugin_instrument.js +90 -90
- package/src/internal/executing/coverage/reportToCoverage.js +193 -187
- package/src/internal/executing/executePlan.js +183 -183
- package/src/internal/executing/launchAndExecute.js +458 -458
- package/src/internal/generateGroupMap/featuresCompatMap.js +29 -0
- package/src/internal/generateGroupMap/jsenvBabelPluginCompatMap.js +1 -8
- package/src/internal/runtime/createBrowserRuntime/scanBrowserRuntimeFeatures.js +246 -246
- package/src/internal/runtime/createNodeRuntime/scanNodeRuntimeFeatures.js +112 -112
- package/src/internal/runtime/s.js +727 -727
- package/src/internal/toolbar/jsenv-logo.svg +144 -144
- package/src/internal/toolbar/toolbar.main.css +196 -196
- package/src/internal/toolbar/toolbar.main.js +227 -227
- package/src/internal/url_conversion.js +317 -317
- package/src/startExploring.js +309 -309
|
@@ -1,404 +1,403 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Send a modified version of the html files instead of serving
|
|
3
|
-
* the source html files
|
|
4
|
-
* - force inlining of importmap
|
|
5
|
-
* - inject a script into html head to have window.__jsenv__
|
|
6
|
-
* - <script type="module" src="file.js">
|
|
7
|
-
* into
|
|
8
|
-
* <script type="module">
|
|
9
|
-
* window.__jsenv__.executeFileUsingDynamicImport('file.js')
|
|
10
|
-
* </script>
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
resolveUrl,
|
|
15
|
-
urlToRelativeUrl,
|
|
16
|
-
urlToExtension,
|
|
17
|
-
readFile,
|
|
18
|
-
urlToFilename,
|
|
19
|
-
urlIsInsideOf,
|
|
20
|
-
} from "@jsenv/filesystem"
|
|
21
|
-
import { moveImportMap } from "@jsenv/importmap"
|
|
22
|
-
import { createDetailedMessage } from "@jsenv/logger"
|
|
23
|
-
|
|
24
|
-
import { fetchUrl } from "@jsenv/core/src/internal/fetchUrl.js"
|
|
25
|
-
import {
|
|
26
|
-
jsenvToolbarHtmlFileInfo,
|
|
27
|
-
jsenvBrowserSystemFileInfo,
|
|
28
|
-
jsenvToolbarInjectorFileInfo,
|
|
29
|
-
} from "@jsenv/core/src/internal/jsenvInternalFiles.js"
|
|
30
|
-
import { stringifyDataUrl } from "@jsenv/core/src/internal/dataUrl.utils.js"
|
|
31
|
-
import {
|
|
32
|
-
parseHtmlString,
|
|
33
|
-
parseHtmlAstRessources,
|
|
34
|
-
collectHtmlDependenciesFromAst,
|
|
35
|
-
getHtmlNodeAttributeByName,
|
|
36
|
-
replaceHtmlNode,
|
|
37
|
-
stringifyHtmlAst,
|
|
38
|
-
manipulateHtmlAst,
|
|
39
|
-
removeHtmlNodeAttribute,
|
|
40
|
-
getHtmlNodeTextNode,
|
|
41
|
-
setHtmlNodeText,
|
|
42
|
-
getUniqueNameForInlineHtmlNode,
|
|
43
|
-
} from "./compileHtml.js"
|
|
44
|
-
|
|
45
|
-
export const createTransformHtmlSourceFileService = ({
|
|
46
|
-
logger,
|
|
47
|
-
projectDirectoryUrl,
|
|
48
|
-
inlineImportMapIntoHTML,
|
|
49
|
-
jsenvScriptInjection,
|
|
50
|
-
jsenvToolbarInjection,
|
|
51
|
-
}) => {
|
|
52
|
-
/**
|
|
53
|
-
* htmlInlineScriptMap is composed as below
|
|
54
|
-
* "file:///project_directory/index.html.10.js": {
|
|
55
|
-
* "htmlFileUrl": "file:///project_directory/index.html",
|
|
56
|
-
* "scriptContent": "console.log(`Hello world`)"
|
|
57
|
-
* }
|
|
58
|
-
* It is used to serve the inline script as if they where inside a file
|
|
59
|
-
* Every time the html file is retransformed, the list of inline script inside it
|
|
60
|
-
* are deleted so that when html file and page is reloaded, the inline script are updated
|
|
61
|
-
*/
|
|
62
|
-
const htmlInlineScriptMap = new Map()
|
|
63
|
-
|
|
64
|
-
return async (request, { pushResponse }) => {
|
|
65
|
-
const { ressource } = request
|
|
66
|
-
const relativeUrl = ressource.slice(1)
|
|
67
|
-
const fileUrl = resolveUrl(relativeUrl, projectDirectoryUrl)
|
|
68
|
-
|
|
69
|
-
const inlineScript = htmlInlineScriptMap.get(fileUrl)
|
|
70
|
-
if (inlineScript) {
|
|
71
|
-
return {
|
|
72
|
-
status: 200,
|
|
73
|
-
headers: {
|
|
74
|
-
"content-type": "application/javascript",
|
|
75
|
-
"content-length": Buffer.byteLength(inlineScript.scriptContent),
|
|
76
|
-
},
|
|
77
|
-
body: inlineScript.scriptContent,
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (urlToExtension(fileUrl) !== ".html") {
|
|
82
|
-
return null
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let fileContent
|
|
86
|
-
try {
|
|
87
|
-
fileContent = await readFile(fileUrl, { as: "string" })
|
|
88
|
-
} catch (e) {
|
|
89
|
-
if (e.code === "ENOENT") {
|
|
90
|
-
return {
|
|
91
|
-
status: 404,
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
throw e
|
|
95
|
-
}
|
|
96
|
-
htmlInlineScriptMap.forEach((inlineScript, inlineScriptUrl) => {
|
|
97
|
-
if (inlineScript.htmlFileUrl === fileUrl) {
|
|
98
|
-
htmlInlineScriptMap.delete(inlineScriptUrl)
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
const htmlTransformed = await transformHTMLSourceFile({
|
|
102
|
-
logger,
|
|
103
|
-
projectDirectoryUrl,
|
|
104
|
-
fileUrl,
|
|
105
|
-
fileContent,
|
|
106
|
-
request,
|
|
107
|
-
pushResponse,
|
|
108
|
-
inlineImportMapIntoHTML,
|
|
109
|
-
jsenvScriptInjection,
|
|
110
|
-
jsenvToolbarInjection,
|
|
111
|
-
onInlineModuleScript: ({ scriptContent, scriptIdentifier }) => {
|
|
112
|
-
const inlineScriptUrl = resolveUrl(scriptIdentifier, fileUrl)
|
|
113
|
-
htmlInlineScriptMap.set(inlineScriptUrl, {
|
|
114
|
-
htmlFileUrl: fileUrl,
|
|
115
|
-
scriptContent,
|
|
116
|
-
})
|
|
117
|
-
},
|
|
118
|
-
})
|
|
119
|
-
return {
|
|
120
|
-
status: 200,
|
|
121
|
-
headers: {
|
|
122
|
-
"content-type": "text/html",
|
|
123
|
-
"content-length": Buffer.byteLength(htmlTransformed),
|
|
124
|
-
"cache-control": "no-cache",
|
|
125
|
-
},
|
|
126
|
-
body: htmlTransformed,
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const transformHTMLSourceFile = async ({
|
|
132
|
-
logger,
|
|
133
|
-
projectDirectoryUrl,
|
|
134
|
-
fileUrl,
|
|
135
|
-
fileContent,
|
|
136
|
-
request,
|
|
137
|
-
pushResponse,
|
|
138
|
-
inlineImportMapIntoHTML,
|
|
139
|
-
jsenvScriptInjection,
|
|
140
|
-
jsenvToolbarInjection,
|
|
141
|
-
onInlineModuleScript = () => {},
|
|
142
|
-
}) => {
|
|
143
|
-
const htmlAst = parseHtmlString(fileContent)
|
|
144
|
-
if (inlineImportMapIntoHTML) {
|
|
145
|
-
await inlineImportmapScripts({
|
|
146
|
-
logger,
|
|
147
|
-
htmlAst,
|
|
148
|
-
htmlFileUrl: fileUrl,
|
|
149
|
-
projectDirectoryUrl,
|
|
150
|
-
})
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const jsenvBrowserBuildUrlRelativeToProject = urlToRelativeUrl(
|
|
154
|
-
jsenvBrowserSystemFileInfo.jsenvBuildUrl,
|
|
155
|
-
projectDirectoryUrl,
|
|
156
|
-
)
|
|
157
|
-
const jsenvToolbarInjectorBuildRelativeUrlForProject = urlToRelativeUrl(
|
|
158
|
-
jsenvToolbarInjectorFileInfo.jsenvBuildUrl,
|
|
159
|
-
projectDirectoryUrl,
|
|
160
|
-
)
|
|
161
|
-
manipulateHtmlAst(htmlAst, {
|
|
162
|
-
scriptInjections: [
|
|
163
|
-
...(fileUrl !== jsenvToolbarHtmlFileInfo.url && jsenvScriptInjection
|
|
164
|
-
? [
|
|
165
|
-
{
|
|
166
|
-
src: `/${jsenvBrowserBuildUrlRelativeToProject}`,
|
|
167
|
-
},
|
|
168
|
-
]
|
|
169
|
-
: []),
|
|
170
|
-
...(fileUrl !== jsenvToolbarHtmlFileInfo.url && jsenvToolbarInjection
|
|
171
|
-
? [
|
|
172
|
-
{
|
|
173
|
-
src: `/${jsenvToolbarInjectorBuildRelativeUrlForProject}`,
|
|
174
|
-
},
|
|
175
|
-
]
|
|
176
|
-
: []),
|
|
177
|
-
],
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
if (request.http2) {
|
|
181
|
-
const htmlDependencies = collectHtmlDependenciesFromAst(htmlAst)
|
|
182
|
-
htmlDependencies.forEach(({ specifier }) => {
|
|
183
|
-
const requestUrl = resolveUrl(request.ressource, request.origin)
|
|
184
|
-
const dependencyUrl = resolveUrl(specifier, requestUrl)
|
|
185
|
-
if (!urlIsInsideOf(dependencyUrl, request.origin)) {
|
|
186
|
-
// ignore external urls
|
|
187
|
-
return
|
|
188
|
-
}
|
|
189
|
-
if (dependencyUrl.startsWith("data:")) {
|
|
190
|
-
return
|
|
191
|
-
}
|
|
192
|
-
const dependencyRelativeUrl = urlToRelativeUrl(
|
|
193
|
-
dependencyUrl,
|
|
194
|
-
request.origin,
|
|
195
|
-
)
|
|
196
|
-
pushResponse({ path: `/${dependencyRelativeUrl}` })
|
|
197
|
-
})
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (jsenvScriptInjection) {
|
|
201
|
-
const { scripts } = parseHtmlAstRessources(htmlAst)
|
|
202
|
-
scripts.forEach((script) => {
|
|
203
|
-
const typeAttribute = getHtmlNodeAttributeByName(script, "type")
|
|
204
|
-
const srcAttribute = getHtmlNodeAttributeByName(script, "src")
|
|
205
|
-
|
|
206
|
-
// remote
|
|
207
|
-
if (typeAttribute && typeAttribute.value === "module" && srcAttribute) {
|
|
208
|
-
removeHtmlNodeAttribute(script, srcAttribute)
|
|
209
|
-
setHtmlNodeText(
|
|
210
|
-
script,
|
|
211
|
-
`window.__jsenv__.executeFileUsingDynamicImport(${JSON.stringify(
|
|
212
|
-
srcAttribute.value,
|
|
213
|
-
)})`,
|
|
214
|
-
)
|
|
215
|
-
return
|
|
216
|
-
}
|
|
217
|
-
// inline
|
|
218
|
-
const textNode = getHtmlNodeTextNode(script)
|
|
219
|
-
if (typeAttribute && typeAttribute.value === "module" && textNode) {
|
|
220
|
-
const scriptIdentifier = getUniqueNameForInlineHtmlNode(
|
|
221
|
-
script,
|
|
222
|
-
scripts,
|
|
223
|
-
`${urlToFilename(fileUrl)}__inline__[id].js`,
|
|
224
|
-
)
|
|
225
|
-
onInlineModuleScript({
|
|
226
|
-
scriptContent: textNode.value,
|
|
227
|
-
scriptIdentifier,
|
|
228
|
-
})
|
|
229
|
-
setHtmlNodeText(
|
|
230
|
-
script,
|
|
231
|
-
`window.__jsenv__.executeFileUsingDynamicImport(${JSON.stringify(
|
|
232
|
-
`./${scriptIdentifier}`,
|
|
233
|
-
)})`,
|
|
234
|
-
)
|
|
235
|
-
return
|
|
236
|
-
}
|
|
237
|
-
})
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
await forceInlineRessources({
|
|
241
|
-
logger,
|
|
242
|
-
htmlAst,
|
|
243
|
-
htmlFileUrl: fileUrl,
|
|
244
|
-
projectDirectoryUrl,
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
return stringifyHtmlAst(htmlAst)
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const inlineImportmapScripts = async ({ logger, htmlAst, htmlFileUrl }) => {
|
|
251
|
-
const { scripts } = parseHtmlAstRessources(htmlAst)
|
|
252
|
-
const remoteImportmapScripts = scripts.filter((script) => {
|
|
253
|
-
const typeAttribute = getHtmlNodeAttributeByName(script, "type")
|
|
254
|
-
const srcAttribute = getHtmlNodeAttributeByName(script, "src")
|
|
255
|
-
if (typeAttribute && typeAttribute.value === "importmap" && srcAttribute) {
|
|
256
|
-
return true
|
|
257
|
-
}
|
|
258
|
-
return false
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
await Promise.all(
|
|
262
|
-
remoteImportmapScripts.map(async (remoteImportmapScript) => {
|
|
263
|
-
const srcAttribute = getHtmlNodeAttributeByName(
|
|
264
|
-
remoteImportmapScript,
|
|
265
|
-
"src",
|
|
266
|
-
)
|
|
267
|
-
const importMapUrl = resolveUrl(srcAttribute.value, htmlFileUrl)
|
|
268
|
-
const importMapResponse = await fetchUrl(importMapUrl)
|
|
269
|
-
if (importMapResponse.status !== 200) {
|
|
270
|
-
logger.warn(
|
|
271
|
-
createDetailedMessage(
|
|
272
|
-
importMapResponse.status === 404
|
|
273
|
-
? `Cannot inline importmap script because file cannot be found.`
|
|
274
|
-
: `Cannot inline importmap script due to unexpected response status (${importMapResponse.status}).`,
|
|
275
|
-
{
|
|
276
|
-
"importmap script src": srcAttribute.value,
|
|
277
|
-
"importmap url": importMapUrl,
|
|
278
|
-
"html url": htmlFileUrl,
|
|
279
|
-
},
|
|
280
|
-
),
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
})
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Send a modified version of the html files instead of serving
|
|
3
|
+
* the source html files
|
|
4
|
+
* - force inlining of importmap
|
|
5
|
+
* - inject a script into html head to have window.__jsenv__
|
|
6
|
+
* - <script type="module" src="file.js">
|
|
7
|
+
* into
|
|
8
|
+
* <script type="module">
|
|
9
|
+
* window.__jsenv__.executeFileUsingDynamicImport('file.js')
|
|
10
|
+
* </script>
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
resolveUrl,
|
|
15
|
+
urlToRelativeUrl,
|
|
16
|
+
urlToExtension,
|
|
17
|
+
readFile,
|
|
18
|
+
urlToFilename,
|
|
19
|
+
urlIsInsideOf,
|
|
20
|
+
} from "@jsenv/filesystem"
|
|
21
|
+
import { moveImportMap } from "@jsenv/importmap"
|
|
22
|
+
import { createDetailedMessage } from "@jsenv/logger"
|
|
23
|
+
|
|
24
|
+
import { fetchUrl } from "@jsenv/core/src/internal/fetchUrl.js"
|
|
25
|
+
import {
|
|
26
|
+
jsenvToolbarHtmlFileInfo,
|
|
27
|
+
jsenvBrowserSystemFileInfo,
|
|
28
|
+
jsenvToolbarInjectorFileInfo,
|
|
29
|
+
} from "@jsenv/core/src/internal/jsenvInternalFiles.js"
|
|
30
|
+
import { stringifyDataUrl } from "@jsenv/core/src/internal/dataUrl.utils.js"
|
|
31
|
+
import {
|
|
32
|
+
parseHtmlString,
|
|
33
|
+
parseHtmlAstRessources,
|
|
34
|
+
collectHtmlDependenciesFromAst,
|
|
35
|
+
getHtmlNodeAttributeByName,
|
|
36
|
+
replaceHtmlNode,
|
|
37
|
+
stringifyHtmlAst,
|
|
38
|
+
manipulateHtmlAst,
|
|
39
|
+
removeHtmlNodeAttribute,
|
|
40
|
+
getHtmlNodeTextNode,
|
|
41
|
+
setHtmlNodeText,
|
|
42
|
+
getUniqueNameForInlineHtmlNode,
|
|
43
|
+
} from "./compileHtml.js"
|
|
44
|
+
|
|
45
|
+
export const createTransformHtmlSourceFileService = ({
|
|
46
|
+
logger,
|
|
47
|
+
projectDirectoryUrl,
|
|
48
|
+
inlineImportMapIntoHTML,
|
|
49
|
+
jsenvScriptInjection,
|
|
50
|
+
jsenvToolbarInjection,
|
|
51
|
+
}) => {
|
|
52
|
+
/**
|
|
53
|
+
* htmlInlineScriptMap is composed as below
|
|
54
|
+
* "file:///project_directory/index.html.10.js": {
|
|
55
|
+
* "htmlFileUrl": "file:///project_directory/index.html",
|
|
56
|
+
* "scriptContent": "console.log(`Hello world`)"
|
|
57
|
+
* }
|
|
58
|
+
* It is used to serve the inline script as if they where inside a file
|
|
59
|
+
* Every time the html file is retransformed, the list of inline script inside it
|
|
60
|
+
* are deleted so that when html file and page is reloaded, the inline script are updated
|
|
61
|
+
*/
|
|
62
|
+
const htmlInlineScriptMap = new Map()
|
|
63
|
+
|
|
64
|
+
return async (request, { pushResponse }) => {
|
|
65
|
+
const { ressource } = request
|
|
66
|
+
const relativeUrl = ressource.slice(1)
|
|
67
|
+
const fileUrl = resolveUrl(relativeUrl, projectDirectoryUrl)
|
|
68
|
+
|
|
69
|
+
const inlineScript = htmlInlineScriptMap.get(fileUrl)
|
|
70
|
+
if (inlineScript) {
|
|
71
|
+
return {
|
|
72
|
+
status: 200,
|
|
73
|
+
headers: {
|
|
74
|
+
"content-type": "application/javascript",
|
|
75
|
+
"content-length": Buffer.byteLength(inlineScript.scriptContent),
|
|
76
|
+
},
|
|
77
|
+
body: inlineScript.scriptContent,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (urlToExtension(fileUrl) !== ".html") {
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let fileContent
|
|
86
|
+
try {
|
|
87
|
+
fileContent = await readFile(fileUrl, { as: "string" })
|
|
88
|
+
} catch (e) {
|
|
89
|
+
if (e.code === "ENOENT") {
|
|
90
|
+
return {
|
|
91
|
+
status: 404,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
throw e
|
|
95
|
+
}
|
|
96
|
+
htmlInlineScriptMap.forEach((inlineScript, inlineScriptUrl) => {
|
|
97
|
+
if (inlineScript.htmlFileUrl === fileUrl) {
|
|
98
|
+
htmlInlineScriptMap.delete(inlineScriptUrl)
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
const htmlTransformed = await transformHTMLSourceFile({
|
|
102
|
+
logger,
|
|
103
|
+
projectDirectoryUrl,
|
|
104
|
+
fileUrl,
|
|
105
|
+
fileContent,
|
|
106
|
+
request,
|
|
107
|
+
pushResponse,
|
|
108
|
+
inlineImportMapIntoHTML,
|
|
109
|
+
jsenvScriptInjection,
|
|
110
|
+
jsenvToolbarInjection,
|
|
111
|
+
onInlineModuleScript: ({ scriptContent, scriptIdentifier }) => {
|
|
112
|
+
const inlineScriptUrl = resolveUrl(scriptIdentifier, fileUrl)
|
|
113
|
+
htmlInlineScriptMap.set(inlineScriptUrl, {
|
|
114
|
+
htmlFileUrl: fileUrl,
|
|
115
|
+
scriptContent,
|
|
116
|
+
})
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
return {
|
|
120
|
+
status: 200,
|
|
121
|
+
headers: {
|
|
122
|
+
"content-type": "text/html",
|
|
123
|
+
"content-length": Buffer.byteLength(htmlTransformed),
|
|
124
|
+
"cache-control": "no-cache",
|
|
125
|
+
},
|
|
126
|
+
body: htmlTransformed,
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const transformHTMLSourceFile = async ({
|
|
132
|
+
logger,
|
|
133
|
+
projectDirectoryUrl,
|
|
134
|
+
fileUrl,
|
|
135
|
+
fileContent,
|
|
136
|
+
request,
|
|
137
|
+
pushResponse,
|
|
138
|
+
inlineImportMapIntoHTML,
|
|
139
|
+
jsenvScriptInjection,
|
|
140
|
+
jsenvToolbarInjection,
|
|
141
|
+
onInlineModuleScript = () => {},
|
|
142
|
+
}) => {
|
|
143
|
+
const htmlAst = parseHtmlString(fileContent)
|
|
144
|
+
if (inlineImportMapIntoHTML) {
|
|
145
|
+
await inlineImportmapScripts({
|
|
146
|
+
logger,
|
|
147
|
+
htmlAst,
|
|
148
|
+
htmlFileUrl: fileUrl,
|
|
149
|
+
projectDirectoryUrl,
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const jsenvBrowserBuildUrlRelativeToProject = urlToRelativeUrl(
|
|
154
|
+
jsenvBrowserSystemFileInfo.jsenvBuildUrl,
|
|
155
|
+
projectDirectoryUrl,
|
|
156
|
+
)
|
|
157
|
+
const jsenvToolbarInjectorBuildRelativeUrlForProject = urlToRelativeUrl(
|
|
158
|
+
jsenvToolbarInjectorFileInfo.jsenvBuildUrl,
|
|
159
|
+
projectDirectoryUrl,
|
|
160
|
+
)
|
|
161
|
+
manipulateHtmlAst(htmlAst, {
|
|
162
|
+
scriptInjections: [
|
|
163
|
+
...(fileUrl !== jsenvToolbarHtmlFileInfo.url && jsenvScriptInjection
|
|
164
|
+
? [
|
|
165
|
+
{
|
|
166
|
+
src: `/${jsenvBrowserBuildUrlRelativeToProject}`,
|
|
167
|
+
},
|
|
168
|
+
]
|
|
169
|
+
: []),
|
|
170
|
+
...(fileUrl !== jsenvToolbarHtmlFileInfo.url && jsenvToolbarInjection
|
|
171
|
+
? [
|
|
172
|
+
{
|
|
173
|
+
src: `/${jsenvToolbarInjectorBuildRelativeUrlForProject}`,
|
|
174
|
+
},
|
|
175
|
+
]
|
|
176
|
+
: []),
|
|
177
|
+
],
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
if (request.http2) {
|
|
181
|
+
const htmlDependencies = collectHtmlDependenciesFromAst(htmlAst)
|
|
182
|
+
htmlDependencies.forEach(({ specifier }) => {
|
|
183
|
+
const requestUrl = resolveUrl(request.ressource, request.origin)
|
|
184
|
+
const dependencyUrl = resolveUrl(specifier, requestUrl)
|
|
185
|
+
if (!urlIsInsideOf(dependencyUrl, request.origin)) {
|
|
186
|
+
// ignore external urls
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
if (dependencyUrl.startsWith("data:")) {
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
const dependencyRelativeUrl = urlToRelativeUrl(
|
|
193
|
+
dependencyUrl,
|
|
194
|
+
request.origin,
|
|
195
|
+
)
|
|
196
|
+
pushResponse({ path: `/${dependencyRelativeUrl}` })
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (jsenvScriptInjection) {
|
|
201
|
+
const { scripts } = parseHtmlAstRessources(htmlAst)
|
|
202
|
+
scripts.forEach((script) => {
|
|
203
|
+
const typeAttribute = getHtmlNodeAttributeByName(script, "type")
|
|
204
|
+
const srcAttribute = getHtmlNodeAttributeByName(script, "src")
|
|
205
|
+
|
|
206
|
+
// remote
|
|
207
|
+
if (typeAttribute && typeAttribute.value === "module" && srcAttribute) {
|
|
208
|
+
removeHtmlNodeAttribute(script, srcAttribute)
|
|
209
|
+
setHtmlNodeText(
|
|
210
|
+
script,
|
|
211
|
+
`window.__jsenv__.executeFileUsingDynamicImport(${JSON.stringify(
|
|
212
|
+
srcAttribute.value,
|
|
213
|
+
)})`,
|
|
214
|
+
)
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
// inline
|
|
218
|
+
const textNode = getHtmlNodeTextNode(script)
|
|
219
|
+
if (typeAttribute && typeAttribute.value === "module" && textNode) {
|
|
220
|
+
const scriptIdentifier = getUniqueNameForInlineHtmlNode(
|
|
221
|
+
script,
|
|
222
|
+
scripts,
|
|
223
|
+
`${urlToFilename(fileUrl)}__inline__[id].js`,
|
|
224
|
+
)
|
|
225
|
+
onInlineModuleScript({
|
|
226
|
+
scriptContent: textNode.value,
|
|
227
|
+
scriptIdentifier,
|
|
228
|
+
})
|
|
229
|
+
setHtmlNodeText(
|
|
230
|
+
script,
|
|
231
|
+
`window.__jsenv__.executeFileUsingDynamicImport(${JSON.stringify(
|
|
232
|
+
`./${scriptIdentifier}`,
|
|
233
|
+
)})`,
|
|
234
|
+
)
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
await forceInlineRessources({
|
|
241
|
+
logger,
|
|
242
|
+
htmlAst,
|
|
243
|
+
htmlFileUrl: fileUrl,
|
|
244
|
+
projectDirectoryUrl,
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
return stringifyHtmlAst(htmlAst)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const inlineImportmapScripts = async ({ logger, htmlAst, htmlFileUrl }) => {
|
|
251
|
+
const { scripts } = parseHtmlAstRessources(htmlAst)
|
|
252
|
+
const remoteImportmapScripts = scripts.filter((script) => {
|
|
253
|
+
const typeAttribute = getHtmlNodeAttributeByName(script, "type")
|
|
254
|
+
const srcAttribute = getHtmlNodeAttributeByName(script, "src")
|
|
255
|
+
if (typeAttribute && typeAttribute.value === "importmap" && srcAttribute) {
|
|
256
|
+
return true
|
|
257
|
+
}
|
|
258
|
+
return false
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
await Promise.all(
|
|
262
|
+
remoteImportmapScripts.map(async (remoteImportmapScript) => {
|
|
263
|
+
const srcAttribute = getHtmlNodeAttributeByName(
|
|
264
|
+
remoteImportmapScript,
|
|
265
|
+
"src",
|
|
266
|
+
)
|
|
267
|
+
const importMapUrl = resolveUrl(srcAttribute.value, htmlFileUrl)
|
|
268
|
+
const importMapResponse = await fetchUrl(importMapUrl)
|
|
269
|
+
if (importMapResponse.status !== 200) {
|
|
270
|
+
logger.warn(
|
|
271
|
+
createDetailedMessage(
|
|
272
|
+
importMapResponse.status === 404
|
|
273
|
+
? `Cannot inline importmap script because file cannot be found.`
|
|
274
|
+
: `Cannot inline importmap script due to unexpected response status (${importMapResponse.status}).`,
|
|
275
|
+
{
|
|
276
|
+
"importmap script src": srcAttribute.value,
|
|
277
|
+
"importmap url": importMapUrl,
|
|
278
|
+
"html url": htmlFileUrl,
|
|
279
|
+
},
|
|
280
|
+
),
|
|
281
|
+
)
|
|
282
|
+
return
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const importMapContent = await importMapResponse.json()
|
|
286
|
+
const importMapInlined = moveImportMap(
|
|
287
|
+
importMapContent,
|
|
288
|
+
importMapUrl,
|
|
289
|
+
htmlFileUrl,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
replaceHtmlNode(
|
|
293
|
+
remoteImportmapScript,
|
|
294
|
+
`<script type="importmap">${JSON.stringify(
|
|
295
|
+
importMapInlined,
|
|
296
|
+
null,
|
|
297
|
+
" ",
|
|
298
|
+
)}</script>`,
|
|
299
|
+
{
|
|
300
|
+
attributesToIgnore: ["src"],
|
|
301
|
+
},
|
|
302
|
+
)
|
|
303
|
+
}),
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const forceInlineRessources = async ({
|
|
308
|
+
htmlAst,
|
|
309
|
+
htmlFileUrl,
|
|
310
|
+
projectDirectoryUrl,
|
|
311
|
+
}) => {
|
|
312
|
+
const { scripts, links, imgs } = parseHtmlAstRessources(htmlAst)
|
|
313
|
+
|
|
314
|
+
const inlineOperations = []
|
|
315
|
+
scripts.forEach((script) => {
|
|
316
|
+
const forceInlineAttribute = getJsenvForceInlineAttribute(script)
|
|
317
|
+
if (!forceInlineAttribute) {
|
|
318
|
+
return
|
|
319
|
+
}
|
|
320
|
+
const srcAttribute = getHtmlNodeAttributeByName(script, "src")
|
|
321
|
+
const src = srcAttribute ? srcAttribute.value : ""
|
|
322
|
+
if (!src) {
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
inlineOperations.push({
|
|
326
|
+
specifier: src,
|
|
327
|
+
mutateHtml: async (response) => {
|
|
328
|
+
replaceHtmlNode(script, `<script>${await response.text()}</script>`, {
|
|
329
|
+
attributesToIgnore: ["src"],
|
|
330
|
+
})
|
|
331
|
+
},
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
links.forEach((link) => {
|
|
335
|
+
const forceInlineAttribute = getJsenvForceInlineAttribute(link)
|
|
336
|
+
if (!forceInlineAttribute) {
|
|
337
|
+
return
|
|
338
|
+
}
|
|
339
|
+
const relAttribute = getHtmlNodeAttributeByName(link, "rel")
|
|
340
|
+
const rel = relAttribute ? relAttribute.value : ""
|
|
341
|
+
if (rel !== "stylesheet") {
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
const hrefAttribute = getHtmlNodeAttributeByName(link, "href")
|
|
345
|
+
const href = hrefAttribute ? hrefAttribute.value : ""
|
|
346
|
+
if (!href) {
|
|
347
|
+
return
|
|
348
|
+
}
|
|
349
|
+
inlineOperations.push({
|
|
350
|
+
specifier: href,
|
|
351
|
+
mutateHtml: async (response) => {
|
|
352
|
+
replaceHtmlNode(link, `<style>${await response.text()}</style>`, {
|
|
353
|
+
attributesToIgnore: ["rel", "href"],
|
|
354
|
+
})
|
|
355
|
+
},
|
|
356
|
+
})
|
|
357
|
+
})
|
|
358
|
+
imgs.forEach((img) => {
|
|
359
|
+
const forceInlineAttribute = getJsenvForceInlineAttribute(img)
|
|
360
|
+
if (!forceInlineAttribute) {
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
const srcAttribute = getHtmlNodeAttributeByName(img, "src")
|
|
364
|
+
const src = srcAttribute ? srcAttribute.value : ""
|
|
365
|
+
if (!src) {
|
|
366
|
+
return
|
|
367
|
+
}
|
|
368
|
+
inlineOperations.push({
|
|
369
|
+
specifier: src,
|
|
370
|
+
mutateHtml: async (response) => {
|
|
371
|
+
const responseArrayBuffer = await response.arrayBuffer()
|
|
372
|
+
const responseAsBase64 = stringifyDataUrl({
|
|
373
|
+
data: responseArrayBuffer,
|
|
374
|
+
base64Flag: true,
|
|
375
|
+
mediaType: response.headers["content-type"],
|
|
376
|
+
})
|
|
377
|
+
replaceHtmlNode(img, `<img src=${responseAsBase64} />`)
|
|
378
|
+
},
|
|
379
|
+
})
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
await Promise.all(
|
|
383
|
+
inlineOperations.map(async (inlineOperation) => {
|
|
384
|
+
const url = resolveUrl(inlineOperation.specifier, htmlFileUrl)
|
|
385
|
+
if (!urlIsInsideOf(url, projectDirectoryUrl)) {
|
|
386
|
+
return
|
|
387
|
+
}
|
|
388
|
+
const response = await fetchUrl(url)
|
|
389
|
+
if (response.status !== 200) {
|
|
390
|
+
return
|
|
391
|
+
}
|
|
392
|
+
await inlineOperation.mutateHtml(response)
|
|
393
|
+
}),
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const getJsenvForceInlineAttribute = (htmlNode) => {
|
|
398
|
+
const jsenvForceInlineAttribute = getHtmlNodeAttributeByName(
|
|
399
|
+
htmlNode,
|
|
400
|
+
"data-jsenv-force-inline",
|
|
401
|
+
)
|
|
402
|
+
return jsenvForceInlineAttribute
|
|
403
|
+
}
|