@jsenv/core 25.0.0-alpha.3 → 25.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build_manifest.js +2 -2
- package/dist/toolbar/asset-manifest.json +2 -2
- package/dist/toolbar/{toolbar.main2_6c1b3d82.js.map → toolbar.main_6c1b3d82.js.map} +0 -0
- package/dist/toolbar/{toolbar_04ba410c.html → toolbar_f7b8a263.html} +1 -1
- package/dist/toolbar_injector/asset-manifest.json +2 -2
- package/dist/toolbar_injector/{toolbar_injector_4a48bc53.js → toolbar_injector_49e4756e.js} +2 -2
- package/dist/toolbar_injector/{toolbar_injector_4a48bc53.js.map → toolbar_injector_49e4756e.js.map} +2 -2
- package/package.json +2 -2
- package/readme.md +16 -6
- package/src/internal/building/js/parseJsRessource.js +1 -6
- package/src/internal/building/ressource_builder.js +8 -2
- package/src/internal/building/rollup_plugin_jsenv.js +3 -0
- package/src/internal/building/url_versioning.js +34 -16
- package/src/internal/compiling/jsenvCompilerForHtml.js +445 -207
- package/src/internal/compiling/startCompileServer.js +5 -2
- package/src/internal/jsenvCoreDirectoryUrl.js +2 -0
package/dist/build_manifest.js
CHANGED
|
@@ -7,6 +7,6 @@ export const COMPILE_PROXY_BUILD_URL = new URL("compile_proxy/compile_proxy_7ad5
|
|
|
7
7
|
|
|
8
8
|
export const EVENT_SOURCE_CLIENT_BUILD_URL = new URL("event_source_client/event_source_client_80644aee.js", import.meta.url).href
|
|
9
9
|
|
|
10
|
-
export const TOOLBAR_BUILD_URL = new URL("toolbar/
|
|
10
|
+
export const TOOLBAR_BUILD_URL = new URL("toolbar/toolbar_f7b8a263.html", import.meta.url).href
|
|
11
11
|
|
|
12
|
-
export const TOOLBAR_INJECTOR_BUILD_URL = new URL("toolbar_injector/
|
|
12
|
+
export const TOOLBAR_INJECTOR_BUILD_URL = new URL("toolbar_injector/toolbar_injector_49e4756e.js", import.meta.url).href
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"assets/settings.css.map": "assets/settings.css_61548139.map",
|
|
9
9
|
"assets/toolbar.main.css.map": "assets/toolbar.main.css_269d7ce2.map",
|
|
10
10
|
"assets/tooltip.css.map": "assets/tooltip.css_a94a8bdd.map",
|
|
11
|
-
"toolbar.html": "
|
|
12
|
-
"toolbar.
|
|
11
|
+
"toolbar.html": "toolbar_f7b8a263.html",
|
|
12
|
+
"toolbar.main.js.map": "toolbar.main_6c1b3d82.js.map"
|
|
13
13
|
}
|
|
File without changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"assets/jsenv-logo.svg": "assets/jsenv-logo_188b9ca6.svg",
|
|
3
|
-
"toolbar_injector.js": "
|
|
4
|
-
"toolbar_injector.js.map": "
|
|
3
|
+
"toolbar_injector.js": "toolbar_injector_49e4756e.js",
|
|
4
|
+
"toolbar_injector.js.map": "toolbar_injector_49e4756e.js.map"
|
|
5
5
|
}
|
|
@@ -746,7 +746,7 @@
|
|
|
746
746
|
return then ? value.then(then) : value;
|
|
747
747
|
}
|
|
748
748
|
|
|
749
|
-
var TOOLBAR_BUILD_RELATIVE_URL = "dist/toolbar/
|
|
749
|
+
var TOOLBAR_BUILD_RELATIVE_URL = "dist/toolbar/toolbar_f7b8a263.html";
|
|
750
750
|
|
|
751
751
|
function _call(body, then, direct) {
|
|
752
752
|
if (direct) {
|
|
@@ -970,4 +970,4 @@
|
|
|
970
970
|
|
|
971
971
|
})();
|
|
972
972
|
|
|
973
|
-
//# sourceMappingURL=
|
|
973
|
+
//# sourceMappingURL=toolbar_injector_49e4756e.js.map
|
package/dist/toolbar_injector/{toolbar_injector_4a48bc53.js.map → toolbar_injector_49e4756e.js.map}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"file": "
|
|
3
|
+
"file": "toolbar_injector_49e4756e.js",
|
|
4
4
|
"sources": [
|
|
5
5
|
"../../helpers/babel/typeof/typeof.js",
|
|
6
6
|
"../../helpers/babel/defineProperty/defineProperty.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"import { fetchUrl } from \"./fetch-browser.js\"\n\nexport const fetchJson = async (url, options = {}) => {\n const response = await fetchUrl(url, options)\n const object = await response.json()\n return object\n}\n",
|
|
26
26
|
"import { fetchJson } from \"../../browser_utils/fetchJson.js\"\n\nexport const fetchExploringJson = async ({ signal } = {}) => {\n try {\n const exploringInfo = await fetchJson(\"/.jsenv/exploring.json\", {\n signal,\n })\n return exploringInfo\n } catch (e) {\n if (signal && signal.aborted && e.name === \"AbortError\") {\n throw e\n }\n throw new Error(\n `Cannot communicate with exploring server due to a network error\n--- error stack ---\n${e.stack}`,\n )\n }\n}\n",
|
|
27
27
|
"export const updateIframeOverflowOnParentWindow = () => {\n if (!window.parent) {\n // can happen while parent iframe reloads\n return\n }\n\n const aTooltipIsOpened =\n document.querySelector(\"[data-tooltip-visible]\") ||\n document.querySelector(\"[data-tooltip-auto-visible]\")\n const settingsAreOpened = document.querySelector(\"#settings[data-active]\")\n\n if (aTooltipIsOpened || settingsAreOpened) {\n enableIframeOverflowOnParentWindow()\n } else {\n disableIframeOverflowOnParentWindow()\n }\n}\n\nlet iframeOverflowEnabled = false\nconst enableIframeOverflowOnParentWindow = () => {\n if (iframeOverflowEnabled) return\n iframeOverflowEnabled = true\n\n const iframe = getToolbarIframe()\n const transitionDuration = iframe.style.transitionDuration\n setStyles(iframe, { \"height\": \"100%\", \"transition-duration\": \"0ms\" })\n if (transitionDuration) {\n setTimeout(() => {\n setStyles(iframe, { \"transition-duration\": transitionDuration })\n })\n }\n}\n\nconst disableIframeOverflowOnParentWindow = () => {\n if (!iframeOverflowEnabled) return\n iframeOverflowEnabled = false\n\n const iframe = getToolbarIframe()\n const transitionDuration = iframe.style.transitionDuration\n setStyles(iframe, { \"height\": \"40px\", \"transition-duration\": \"0ms\" })\n if (transitionDuration) {\n setTimeout(() => {\n setStyles(iframe, { \"transition-duration\": transitionDuration })\n })\n }\n}\n\nexport const getToolbarIframe = () => {\n const iframes = Array.from(window.parent.document.querySelectorAll(\"iframe\"))\n return iframes.find((iframe) => iframe.contentWindow === window)\n}\n\nexport const forceHideElement = (element) => {\n element.setAttribute(\"data-force-hide\", \"\")\n}\n\nexport const removeForceHideElement = (element) => {\n element.removeAttribute(\"data-force-hide\")\n}\n\nexport const setStyles = (element, styles) => {\n const elementStyle = element.style\n const restoreStyles = Object.keys(styles).map((styleName) => {\n let restore\n if (styleName in elementStyle) {\n const currentStyle = elementStyle[styleName]\n restore = () => {\n elementStyle[styleName] = currentStyle\n }\n } else {\n restore = () => {\n delete elementStyle[styleName]\n }\n }\n\n elementStyle[styleName] = styles[styleName]\n\n return restore\n })\n return () => {\n restoreStyles.forEach((restore) => restore())\n }\n}\n\nexport const setAttributes = (element, attributes) => {\n Object.keys(attributes).forEach((name) => {\n element.setAttribute(name, attributes[name])\n })\n}\n\nexport const getDocumentScroll = () => {\n return {\n x: document.documentElement.scrollLeft,\n y: document.documentElement.scrollTop,\n }\n}\n\nexport const toolbarSectionIsActive = (element) => {\n return element.hasAttribute(\"data-active\")\n}\n\nexport const activateToolbarSection = (element) => {\n element.setAttribute(\"data-active\", \"\")\n}\n\nexport const deactivateToolbarSection = (element) => {\n element.removeAttribute(\"data-active\")\n}\n",
|
|
28
|
-
"import { fetchExploringJson } from \"@jsenv/core/src/internal/dev_server/exploring/fetchExploringJson.js\"\nimport { setAttributes, setStyles } from \"./util/dom.js\"\n\n// eslint-disable-next-line no-undef\nconst TOOLBAR_BUILD_RELATIVE_URL = \"dist/toolbar/
|
|
28
|
+
"import { fetchExploringJson } from \"@jsenv/core/src/internal/dev_server/exploring/fetchExploringJson.js\"\nimport { setAttributes, setStyles } from \"./util/dom.js\"\n\n// eslint-disable-next-line no-undef\nconst TOOLBAR_BUILD_RELATIVE_URL = \"dist/toolbar/toolbar_f7b8a263.html\"\nconst jsenvLogoSvgUrl = new URL(\"./jsenv-logo.svg\", import.meta.url)\n\nconst injectToolbar = async () => {\n await new Promise((resolve) => {\n if (window.requestIdleCallback) {\n window.requestIdleCallback(resolve)\n } else {\n window.requestAnimationFrame(resolve)\n }\n })\n\n const { jsenvDirectoryRelativeUrl } = await fetchExploringJson()\n const jsenvDirectoryServerUrl = resolveUrl(\n jsenvDirectoryRelativeUrl,\n document.location.origin,\n )\n\n const placeholder = getToolbarPlaceholder()\n\n const iframe = document.createElement(\"iframe\")\n setAttributes(iframe, {\n tabindex: -1,\n // sandbox: \"allow-forms allow-modals allow-pointer-lock allow-popups allow-presentation allow-same-origin allow-scripts allow-top-navigation-by-user-activation\",\n // allow: \"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; vr\",\n allowtransparency: true,\n })\n setStyles(iframe, {\n \"position\": \"fixed\",\n \"zIndex\": 1000,\n \"bottom\": 0,\n \"left\": 0,\n \"width\": \"100%\",\n \"height\": 0,\n /* ensure toolbar children are not focusable when hidden */\n \"visibility\": \"hidden\",\n \"transition-duration\": \"300ms\",\n \"transition-property\": \"height, visibility\",\n \"border\": \"none\",\n })\n const iframeLoadedPromise = iframeToLoadedPromise(iframe)\n const jsenvToolbarHtmlServerUrl = resolveUrl(\n TOOLBAR_BUILD_RELATIVE_URL,\n jsenvDirectoryServerUrl,\n )\n // set iframe src BEFORE putting it into the DOM (prevent firefox adding an history entry)\n iframe.setAttribute(\"src\", jsenvToolbarHtmlServerUrl)\n placeholder.parentNode.replaceChild(iframe, placeholder)\n\n await iframeLoadedPromise\n iframe.removeAttribute(\"tabindex\")\n\n const div = document.createElement(\"div\")\n div.innerHTML = `\n<div id=\"jsenv-toolbar-trigger\">\n <svg id=\"jsenv-toolbar-trigger-icon\">\n <use xlink:href=\"${jsenvLogoSvgUrl}#jsenv-logo\"></use>\n </svg>\n <style>\n #jsenv-toolbar-trigger {\n display: block;\n overflow: hidden;\n position: fixed;\n z-index: 1000;\n bottom: -32px;\n right: 20px;\n height: 40px;\n width: 40px;\n padding: 0;\n margin: 0;\n border-radius: 5px 5px 0 0;\n border: 1px solid rgba(0, 0, 0, 0.33);\n border-bottom: none;\n box-shadow: 0px 0px 6px 2px rgba(0, 0, 0, 0.46);\n background: transparent;\n text-align: center;\n transition: 600ms;\n }\n\n #jsenv-toolbar-trigger:hover {\n cursor: pointer;\n }\n\n #jsenv-toolbar-trigger[data-expanded] {\n bottom: 0;\n }\n\n #jsenv-toolbar-trigger-icon {\n width: 35px;\n height: 35px;\n opacity: 0;\n transition: 600ms;\n }\n\n #jsenv-toolbar-trigger[data-expanded] #jsenv-toolbar-trigger-icon {\n opacity: 1;\n }\n </style>\n</div>`\n const toolbarTrigger = div.firstElementChild\n iframe.parentNode.appendChild(toolbarTrigger)\n\n let timer\n toolbarTrigger.onmouseenter = () => {\n toolbarTrigger.setAttribute(\"data-animate\", \"\")\n timer = setTimeout(expandToolbarTrigger, 500)\n }\n toolbarTrigger.onmouseleave = () => {\n clearTimeout(timer)\n collapseToolbarTrigger()\n }\n toolbarTrigger.onfocus = () => {\n toolbarTrigger.removeAttribute(\"data-animate\")\n expandToolbarTrigger()\n }\n toolbarTrigger.onblur = () => {\n toolbarTrigger.removeAttribute(\"data-animate\")\n clearTimeout(timer)\n collapseToolbarTrigger()\n }\n toolbarTrigger.onclick = () => {\n sendCommandToToolbar(iframe, \"showToolbar\")\n }\n\n const showToolbarTrigger = () => {\n toolbarTrigger.style.display = \"block\"\n }\n\n const hideToolbarTrigger = () => {\n toolbarTrigger.style.display = \"none\"\n }\n\n const expandToolbarTrigger = () => {\n toolbarTrigger.setAttribute(\"data-expanded\", \"\")\n }\n\n const collapseToolbarTrigger = () => {\n toolbarTrigger.removeAttribute(\"data-expanded\", \"\")\n }\n\n hideToolbarTrigger()\n addToolbarEventCallback(iframe, \"toolbar-visibility-change\", (visible) => {\n if (visible) {\n hideToolbarTrigger()\n } else {\n showToolbarTrigger()\n }\n })\n addToolbarEventCallback(iframe, \"toolbar_ready\", () => {\n sendCommandToToolbar(iframe, \"renderToolbar\")\n })\n\n return iframe\n}\n\nconst addToolbarEventCallback = (iframe, eventName, callback) => {\n const messageEventCallback = (messageEvent) => {\n const { data } = messageEvent\n if (typeof data !== \"object\") {\n return\n }\n const { __jsenv__ } = data\n if (!__jsenv__) {\n return\n }\n if (__jsenv__.event !== eventName) {\n return\n }\n callback(__jsenv__.data)\n }\n\n window.addEventListener(\"message\", messageEventCallback, false)\n return () => {\n window.removeEventListener(\"message\", messageEventCallback, false)\n }\n}\n\nconst sendCommandToToolbar = (iframe, command, ...args) => {\n iframe.contentWindow.postMessage(\n {\n __jsenv__: {\n command,\n args,\n },\n },\n window.origin,\n )\n}\n\nconst getToolbarPlaceholder = () => {\n const placeholder = queryPlaceholder()\n if (placeholder) {\n if (document.body.contains(placeholder)) {\n return placeholder\n }\n // otherwise iframe would not be visible because in <head>\n console.warn(\n \"element with [data-jsenv-toolbar-placeholder] must be inside document.body\",\n )\n return createTooolbarPlaceholder()\n }\n return createTooolbarPlaceholder()\n}\n\nconst queryPlaceholder = () => {\n return document.querySelector(\"[data-jsenv-toolbar-placeholder]\")\n}\n\nconst createTooolbarPlaceholder = () => {\n const placeholder = document.createElement(\"span\")\n document.body.appendChild(placeholder)\n return placeholder\n}\n\nconst iframeToLoadedPromise = (iframe) => {\n return new Promise((resolve) => {\n const onload = () => {\n iframe.removeEventListener(\"load\", onload, true)\n resolve()\n }\n iframe.addEventListener(\"load\", onload, true)\n })\n}\n\nconst resolveUrl = (url, baseUrl) => String(new URL(url, baseUrl))\n\nif (document.readyState === \"complete\") {\n injectToolbar()\n} else {\n window.addEventListener(\"load\", injectToolbar)\n}\n"
|
|
29
29
|
],
|
|
30
30
|
"names": [
|
|
31
31
|
"nativeTypeOf",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/core",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.1.0",
|
|
4
4
|
"description": "Tool to develop, test and build js projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"url": "https://github.com/jsenv/jsenv-core"
|
|
9
9
|
},
|
|
10
10
|
"engines": {
|
|
11
|
-
"node": ">=
|
|
11
|
+
"node": ">=16.13.0"
|
|
12
12
|
},
|
|
13
13
|
"publishConfig": {
|
|
14
14
|
"access": "public"
|
package/readme.md
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
# jsenv [](https://www.npmjs.com/package/@jsenv/core) [](https://github.com/jsenv/jsenv-core/actions?workflow=main) [](https://codecov.io/gh/jsenv/jsenv-core)
|
|
2
2
|
|
|
3
|
-
_@jsenv/core_ is a quick start pack to launch a js project.
|
|
3
|
+
_@jsenv/core_ is a quick start pack to launch a js project. It provides what you need from the beginning: develoment, testing and building all in one.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Jsenv **integrates naturally with standard** HTML, CSS and JS: you don't have to pick a JavaScript framework.
|
|
6
6
|
|
|
7
|
-
#
|
|
7
|
+
# Overview
|
|
8
|
+
|
|
9
|
+
This section demos 3 things jsenv provides:
|
|
10
|
+
|
|
11
|
+
1. A test runner
|
|
12
|
+
2. A dev server
|
|
13
|
+
3. A build script
|
|
14
|
+
|
|
15
|
+
Don't be fooled by the apparent simplicity of the following demos, jsenv can be used on more complex scenarios as well.
|
|
16
|
+
|
|
17
|
+
## Test runner overview
|
|
8
18
|
|
|
9
19
|
Let's assume you want to test `countDogs` exported by _animals.js_ file.
|
|
10
20
|
|
|
@@ -94,7 +104,7 @@ total duration: 1.2 seconds
|
|
|
94
104
|
|
|
95
105
|
To read more about testing in jsenv, check [jsenv test runner documentation](./docs/testing/readme.md#jsenv-test-runner).
|
|
96
106
|
|
|
97
|
-
|
|
107
|
+
## Dev server overview
|
|
98
108
|
|
|
99
109
|
You want to execute the following _main.html_ file in a browser.
|
|
100
110
|
|
|
@@ -155,7 +165,7 @@ Browser navigates to _main.html_ and execute the file. Hello world is displayed
|
|
|
155
165
|
|
|
156
166
|
To read more about jsenv dev server, check [jsenv dev server documentation](./docs/dev_server/readme.md#jsenv-dev-server).
|
|
157
167
|
|
|
158
|
-
|
|
168
|
+
## Build overview
|
|
159
169
|
|
|
160
170
|
Following the steps below turns a `main.html` into an optimized `dist/main.prod.html`.
|
|
161
171
|
Only the content of html files is shown below because the content of non-html files is trivial.
|
|
@@ -253,7 +263,7 @@ Jsenv was first created to write tests that could be executed in different runti
|
|
|
253
263
|
- A test runner to execute test files
|
|
254
264
|
- A build tool to optimize files for production
|
|
255
265
|
|
|
256
|
-
Jsenv relies on standard web features
|
|
266
|
+
Jsenv relies on **standard web features**. Each standard listed below is potentially supported natively by the browser. When browser supports all of them, jsenv will use source files without modification. Otherwise, the files are compiled to be executable in the browser.
|
|
257
267
|
|
|
258
268
|
- `<script type="module">`
|
|
259
269
|
- `<script type="importmap">`
|
|
@@ -14,8 +14,8 @@ export const parseJsRessource = async (
|
|
|
14
14
|
const jsUrl = jsRessource.url
|
|
15
15
|
const jsString = String(jsRessource.bufferBeforeBuild)
|
|
16
16
|
const jsSourcemapUrl = getJavaScriptSourceMappingUrl(jsString)
|
|
17
|
-
let sourcemapReference
|
|
18
17
|
|
|
18
|
+
let sourcemapReference
|
|
19
19
|
if (jsSourcemapUrl) {
|
|
20
20
|
sourcemapReference = notifyReferenceFound({
|
|
21
21
|
referenceLabel: "js sourcemapping comment",
|
|
@@ -76,13 +76,10 @@ export const parseJsRessource = async (
|
|
|
76
76
|
code = result.code
|
|
77
77
|
map = result.map
|
|
78
78
|
}
|
|
79
|
-
|
|
80
79
|
jsRessource.buildEnd(code)
|
|
81
|
-
|
|
82
80
|
if (!map) {
|
|
83
81
|
return
|
|
84
82
|
}
|
|
85
|
-
|
|
86
83
|
// In theory code should never be modified once buildEnd() is called
|
|
87
84
|
// because buildRelativeUrl might be versioned based on file content
|
|
88
85
|
// There is an exception for sourcemap because we want to update sourcemap.file
|
|
@@ -99,7 +96,6 @@ export const parseJsRessource = async (
|
|
|
99
96
|
`${urlToFilename(jsBuildUrl)}.map`,
|
|
100
97
|
jsBuildUrl,
|
|
101
98
|
)
|
|
102
|
-
|
|
103
99
|
map.file = urlToFilename(jsBuildUrl)
|
|
104
100
|
if (map.sources) {
|
|
105
101
|
map.sources = map.sources.map((source) => {
|
|
@@ -114,7 +110,6 @@ export const parseJsRessource = async (
|
|
|
114
110
|
}
|
|
115
111
|
const mapAsText = JSON.stringify(map, null, " ")
|
|
116
112
|
sourcemapRessource.buildEnd(mapAsText)
|
|
117
|
-
|
|
118
113
|
const sourcemapBuildUrl = resolveUrl(
|
|
119
114
|
sourcemapRessource.buildRelativeUrl,
|
|
120
115
|
buildDirectoryUrl,
|
|
@@ -948,8 +948,14 @@ export const createRessourceBuilder = (
|
|
|
948
948
|
}
|
|
949
949
|
return {
|
|
950
950
|
...htmlUrlSite,
|
|
951
|
-
line:
|
|
952
|
-
|
|
951
|
+
line:
|
|
952
|
+
typeof urlSite.line === "number"
|
|
953
|
+
? htmlUrlSite.line + urlSite.line
|
|
954
|
+
: htmlUrlSite.line,
|
|
955
|
+
column:
|
|
956
|
+
typeof urlSite.column === "number"
|
|
957
|
+
? htmlUrlSite.column + urlSite.column
|
|
958
|
+
: htmlUrlSite.column,
|
|
953
959
|
}
|
|
954
960
|
}
|
|
955
961
|
|
|
@@ -690,6 +690,9 @@ export const createRollupPlugins = async ({
|
|
|
690
690
|
// we compile for rollup, let top level await untouched
|
|
691
691
|
// it will be converted, if needed, during "renderChunk" hook
|
|
692
692
|
topLevelAwait: "ignore",
|
|
693
|
+
// if we put babel helpers, rollup might try to share them and a file
|
|
694
|
+
// might try to import from an inline script resulting in 404.
|
|
695
|
+
babelHelpersInjectionAsImport: false,
|
|
693
696
|
})
|
|
694
697
|
let code = transformResult.code
|
|
695
698
|
let map = transformResult.map
|
|
@@ -10,20 +10,7 @@ export const createUrlVersioner = ({
|
|
|
10
10
|
asOriginalUrl,
|
|
11
11
|
lineBreakNormalization,
|
|
12
12
|
}) => {
|
|
13
|
-
const
|
|
14
|
-
const getFreeName = (name) => {
|
|
15
|
-
let nameCandidate = name
|
|
16
|
-
let integer = 1
|
|
17
|
-
// eslint-disable-next-line no-constant-condition
|
|
18
|
-
while (true) {
|
|
19
|
-
if (!names.includes(nameCandidate)) {
|
|
20
|
-
names.push(nameCandidate)
|
|
21
|
-
return nameCandidate
|
|
22
|
-
}
|
|
23
|
-
integer++
|
|
24
|
-
nameCandidate = `${name}${integer}`
|
|
25
|
-
}
|
|
26
|
-
}
|
|
13
|
+
const availableNameGenerator = createAvailableNameGenerator()
|
|
27
14
|
|
|
28
15
|
const computeBuildRelativeUrl = (ressource, precomputation) => {
|
|
29
16
|
const pattern = getFilenamePattern({
|
|
@@ -40,7 +27,11 @@ export const createUrlVersioner = ({
|
|
|
40
27
|
pattern,
|
|
41
28
|
getName: precomputation
|
|
42
29
|
? () => urlToBasename(ressource.url)
|
|
43
|
-
: () =>
|
|
30
|
+
: () =>
|
|
31
|
+
availableNameGenerator.getAvailableNameForPattern(
|
|
32
|
+
urlToBasename(ressource.url),
|
|
33
|
+
pattern,
|
|
34
|
+
),
|
|
44
35
|
contentType: ressource.contentType,
|
|
45
36
|
lineBreakNormalization,
|
|
46
37
|
},
|
|
@@ -69,6 +60,33 @@ export const createUrlVersioner = ({
|
|
|
69
60
|
}
|
|
70
61
|
}
|
|
71
62
|
|
|
63
|
+
const createAvailableNameGenerator = () => {
|
|
64
|
+
const cache = {}
|
|
65
|
+
const getAvailableNameForPattern = (name, pattern) => {
|
|
66
|
+
let names = cache[pattern]
|
|
67
|
+
if (!names) {
|
|
68
|
+
names = []
|
|
69
|
+
cache[pattern] = names
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let nameCandidate = name
|
|
73
|
+
let integer = 1
|
|
74
|
+
// eslint-disable-next-line no-constant-condition
|
|
75
|
+
while (true) {
|
|
76
|
+
if (!names.includes(nameCandidate)) {
|
|
77
|
+
names.push(nameCandidate)
|
|
78
|
+
return nameCandidate
|
|
79
|
+
}
|
|
80
|
+
integer++
|
|
81
|
+
nameCandidate = `${name}${integer}`
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
getAvailableNameForPattern,
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
72
90
|
const getFilenamePattern = ({
|
|
73
91
|
ressource,
|
|
74
92
|
entryPointUrls,
|
|
@@ -115,7 +133,7 @@ const getFilenamePattern = ({
|
|
|
115
133
|
}
|
|
116
134
|
|
|
117
135
|
if (ressource.isJsModule) {
|
|
118
|
-
return "[name]_[hash]
|
|
136
|
+
return "[name]_[hash].js"
|
|
119
137
|
}
|
|
120
138
|
|
|
121
139
|
return ressource.urlVersioningDisabled
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
resolveUrl,
|
|
3
|
+
urlToFilename,
|
|
4
|
+
urlToBasename,
|
|
5
|
+
urlToRelativeUrl,
|
|
6
|
+
urlIsInsideOf,
|
|
7
|
+
} from "@jsenv/filesystem"
|
|
2
8
|
import { moveImportMap, composeTwoImportMaps } from "@jsenv/importmap"
|
|
3
9
|
import { createDetailedMessage } from "@jsenv/logger"
|
|
4
10
|
|
|
11
|
+
import { jsenvDistDirectoryUrl } from "@jsenv/core/src/internal/jsenvCoreDirectoryUrl.js"
|
|
5
12
|
import {
|
|
6
13
|
BROWSER_RUNTIME_BUILD_URL,
|
|
7
14
|
EVENT_SOURCE_CLIENT_BUILD_URL,
|
|
@@ -39,6 +46,7 @@ export const compileHtml = async ({
|
|
|
39
46
|
url,
|
|
40
47
|
compiledUrl,
|
|
41
48
|
projectDirectoryUrl,
|
|
49
|
+
compileServerOrigin,
|
|
42
50
|
outDirectoryRelativeUrl,
|
|
43
51
|
compileId,
|
|
44
52
|
|
|
@@ -104,248 +112,478 @@ export const compileHtml = async ({
|
|
|
104
112
|
],
|
|
105
113
|
})
|
|
106
114
|
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
const sources = []
|
|
116
|
+
const sourcesContent = []
|
|
117
|
+
const assets = []
|
|
118
|
+
const assetsContent = []
|
|
119
|
+
|
|
120
|
+
const addHtmlSourceFile = ({ url, content }) => {
|
|
121
|
+
sources.push(url)
|
|
122
|
+
sourcesContent.push(content)
|
|
123
|
+
}
|
|
124
|
+
addHtmlSourceFile({ url, content: code })
|
|
125
|
+
|
|
109
126
|
const { scripts } = parseHtmlAstRessources(htmlAst)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const importMapResponse = await fetchUrl(importmapInfo.url)
|
|
125
|
-
if (importMapResponse.status === 200) {
|
|
126
|
-
const importmapAsText = await importMapResponse.text()
|
|
127
|
-
let htmlImportmap = JSON.parse(importmapAsText)
|
|
128
|
-
htmlImportmap = moveImportMap(
|
|
129
|
-
htmlImportmap,
|
|
130
|
-
importmapInfo.url,
|
|
131
|
-
url,
|
|
132
|
-
)
|
|
133
|
-
sources.push(importmapInfo.url)
|
|
134
|
-
sourcesContent.push(importmapAsText)
|
|
135
|
-
return htmlImportmap
|
|
136
|
-
}
|
|
137
|
-
logger.warn(
|
|
138
|
-
createDetailedMessage(
|
|
139
|
-
importMapResponse.status === 404
|
|
140
|
-
? `importmap script file cannot be found.`
|
|
141
|
-
: `importmap script file unexpected response status (${importMapResponse.status}).`,
|
|
142
|
-
{
|
|
143
|
-
"importmap url": importmapInfo.url,
|
|
144
|
-
"html url": url,
|
|
145
|
-
},
|
|
146
|
-
),
|
|
147
|
-
)
|
|
148
|
-
return {}
|
|
149
|
-
},
|
|
150
|
-
}
|
|
151
|
-
} else {
|
|
152
|
-
importmapInfo = {
|
|
153
|
-
script,
|
|
154
|
-
url: compiledUrl,
|
|
155
|
-
load: () => {
|
|
156
|
-
const jsenvImportmap = getDefaultImportmap(compiledUrl, {
|
|
157
|
-
projectDirectoryUrl,
|
|
158
|
-
compileDirectoryUrl: `${projectDirectoryUrl}${compileId}/${outDirectoryRelativeUrl}`,
|
|
159
|
-
})
|
|
160
|
-
const htmlImportmap = JSON.parse(
|
|
161
|
-
getHtmlNodeTextNode(script).value,
|
|
162
|
-
)
|
|
163
|
-
const importmap = composeTwoImportMaps(
|
|
164
|
-
jsenvImportmap,
|
|
165
|
-
htmlImportmap,
|
|
166
|
-
)
|
|
167
|
-
return importmap
|
|
168
|
-
},
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
})
|
|
174
|
-
if (importmapInfo) {
|
|
175
|
-
const importmap = await importmapInfo.load()
|
|
176
|
-
const importmapAsText = JSON.stringify(importmap, null, " ")
|
|
177
|
-
replaceHtmlNode(
|
|
178
|
-
importmapInfo.script,
|
|
179
|
-
`<script type="${
|
|
180
|
-
moduleOutFormat === "systemjs" ? "systemjs-importmap" : "importmap"
|
|
181
|
-
}">${importmapAsText}</script>`,
|
|
182
|
-
{
|
|
183
|
-
attributesToIgnore: ["src"],
|
|
184
|
-
},
|
|
185
|
-
)
|
|
186
|
-
importmapInfo.inlinedFrom = importmapInfo.url
|
|
187
|
-
importmapInfo.text = importmapAsText
|
|
188
|
-
} else {
|
|
189
|
-
const defaultImportMap = getDefaultImportmap(compiledUrl, {
|
|
190
|
-
projectDirectoryUrl,
|
|
191
|
-
compileDirectoryUrl: `${projectDirectoryUrl}${outDirectoryRelativeUrl}${compileId}/`,
|
|
127
|
+
const htmlDependencies = collectHtmlDependenciesFromAst(htmlAst)
|
|
128
|
+
|
|
129
|
+
const htmlAssetGenerators = []
|
|
130
|
+
const htmlMutations = []
|
|
131
|
+
const addHtmlAssetGenerator = (htmlAssetGenerator) => {
|
|
132
|
+
htmlAssetGenerators.push(htmlAssetGenerator)
|
|
133
|
+
}
|
|
134
|
+
const addHtmlMutation = (htmlMutation) => {
|
|
135
|
+
htmlMutations.push(htmlMutation)
|
|
136
|
+
}
|
|
137
|
+
const addHtmlDependency = ({ htmlNode, specifier }) => {
|
|
138
|
+
htmlDependencies.push({
|
|
139
|
+
htmlNode,
|
|
140
|
+
specifier,
|
|
192
141
|
})
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const importmapInfo = await visitImportmapScript({
|
|
145
|
+
logger,
|
|
146
|
+
url,
|
|
147
|
+
compiledUrl,
|
|
148
|
+
projectDirectoryUrl,
|
|
149
|
+
compileId,
|
|
150
|
+
outDirectoryRelativeUrl,
|
|
151
|
+
scripts,
|
|
152
|
+
addHtmlSourceFile,
|
|
153
|
+
})
|
|
154
|
+
const importmap = (await importmapInfo.load()) || {}
|
|
155
|
+
const importmapAsText = JSON.stringify(importmap, null, " ")
|
|
156
|
+
importmapInfo.inlinedFrom = importmapInfo.url
|
|
157
|
+
importmapInfo.text = importmapAsText
|
|
158
|
+
addHtmlMutation(() => {
|
|
159
|
+
if (importmapInfo.needsInjection) {
|
|
160
|
+
manipulateHtmlAst(htmlAst, {
|
|
161
|
+
scriptInjections: [
|
|
162
|
+
{
|
|
163
|
+
type:
|
|
164
|
+
moduleOutFormat === "systemjs"
|
|
165
|
+
? "systemjs-importmap"
|
|
166
|
+
: "importmap",
|
|
167
|
+
// in case there is no importmap, force the presence
|
|
168
|
+
// so that '@jsenv/core/' are still remapped
|
|
169
|
+
text: importmapAsText,
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
})
|
|
173
|
+
} else {
|
|
174
|
+
replaceHtmlNode(
|
|
175
|
+
importmapInfo.script,
|
|
176
|
+
`<script type="${
|
|
177
|
+
moduleOutFormat === "systemjs" ? "systemjs-importmap" : "importmap"
|
|
178
|
+
}">${importmapAsText}</script>`,
|
|
196
179
|
{
|
|
197
|
-
|
|
198
|
-
moduleOutFormat === "systemjs" ? "systemjs-importmap" : "importmap",
|
|
199
|
-
// in case there is no importmap, force the presence
|
|
200
|
-
// so that '@jsenv/core/' are still remapped
|
|
201
|
-
text: importmapAsText,
|
|
180
|
+
attributesToIgnore: ["src"],
|
|
202
181
|
},
|
|
203
|
-
|
|
204
|
-
})
|
|
205
|
-
importmapInfo = {
|
|
206
|
-
url: compiledUrl,
|
|
207
|
-
text: importmapAsText,
|
|
182
|
+
)
|
|
208
183
|
}
|
|
209
|
-
}
|
|
184
|
+
})
|
|
210
185
|
onHtmlImportmapInfo({
|
|
211
186
|
htmlUrl: url,
|
|
212
187
|
importmapInfo,
|
|
213
188
|
})
|
|
214
189
|
|
|
215
|
-
|
|
216
|
-
|
|
190
|
+
await visitScripts({
|
|
191
|
+
logger,
|
|
192
|
+
projectDirectoryUrl,
|
|
193
|
+
compileServerOrigin,
|
|
194
|
+
url,
|
|
195
|
+
compiledUrl,
|
|
196
|
+
scripts,
|
|
197
|
+
addHtmlSourceFile,
|
|
198
|
+
addHtmlAssetGenerator,
|
|
199
|
+
addHtmlMutation,
|
|
200
|
+
addHtmlDependency,
|
|
201
|
+
|
|
202
|
+
babelPluginMap,
|
|
203
|
+
moduleOutFormat,
|
|
204
|
+
importMetaFormat,
|
|
205
|
+
topLevelAwait,
|
|
206
|
+
sourcemapMethod,
|
|
207
|
+
})
|
|
208
|
+
await Promise.all(
|
|
209
|
+
htmlAssetGenerators.map(async (htmlAssetGenerator) => {
|
|
210
|
+
const assetInfos = await htmlAssetGenerator()
|
|
211
|
+
assetInfos.forEach((assetInfo) => {
|
|
212
|
+
assets.push(assetInfo.url)
|
|
213
|
+
assetsContent.push(assetInfo.content)
|
|
214
|
+
})
|
|
215
|
+
}),
|
|
216
|
+
)
|
|
217
|
+
htmlAssetGenerators.length = 0
|
|
218
|
+
|
|
219
|
+
htmlMutations.forEach((htmlMutation) => {
|
|
220
|
+
htmlMutation()
|
|
221
|
+
})
|
|
222
|
+
htmlMutations.length = 0
|
|
223
|
+
const htmlAfterTransformation = stringifyHtmlAst(htmlAst)
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
contentType: "text/html",
|
|
227
|
+
compiledSource: htmlAfterTransformation,
|
|
228
|
+
sources,
|
|
229
|
+
sourcesContent,
|
|
230
|
+
assets,
|
|
231
|
+
assetsContent,
|
|
232
|
+
dependencies: htmlDependencies.map(({ specifier }) => {
|
|
233
|
+
return specifier
|
|
234
|
+
}),
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const visitImportmapScript = async ({
|
|
239
|
+
logger,
|
|
240
|
+
url,
|
|
241
|
+
compiledUrl,
|
|
242
|
+
projectDirectoryUrl,
|
|
243
|
+
compileId,
|
|
244
|
+
outDirectoryRelativeUrl,
|
|
245
|
+
scripts,
|
|
246
|
+
addHtmlSourceFile,
|
|
247
|
+
}) => {
|
|
248
|
+
const importmapScripts = scripts.filter((script) => {
|
|
249
|
+
const typeAttribute = getHtmlNodeAttributeByName(script, "type")
|
|
250
|
+
const type = typeAttribute ? typeAttribute.value : "application/javascript"
|
|
251
|
+
return type === "importmap"
|
|
252
|
+
})
|
|
253
|
+
if (importmapScripts.length === 0) {
|
|
254
|
+
return {
|
|
255
|
+
needsInjection: true,
|
|
256
|
+
url: compiledUrl,
|
|
257
|
+
load: () => {
|
|
258
|
+
const defaultImportMap = getDefaultImportmap(compiledUrl, {
|
|
259
|
+
projectDirectoryUrl,
|
|
260
|
+
compileDirectoryUrl: `${projectDirectoryUrl}${outDirectoryRelativeUrl}${compileId}/`,
|
|
261
|
+
})
|
|
262
|
+
return defaultImportMap
|
|
263
|
+
},
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (importmapScripts.length > 1) {
|
|
268
|
+
logger.error("HTML file must contain max 1 importmap")
|
|
269
|
+
}
|
|
270
|
+
const firstImportmapScript = importmapScripts[0]
|
|
271
|
+
const srcAttribute = getHtmlNodeAttributeByName(firstImportmapScript, "src")
|
|
272
|
+
const src = srcAttribute ? srcAttribute.value : ""
|
|
273
|
+
if (src) {
|
|
274
|
+
const importmapUrl = resolveUrl(src, url)
|
|
275
|
+
const importmapInfo = {
|
|
276
|
+
script: firstImportmapScript,
|
|
277
|
+
url: importmapUrl,
|
|
278
|
+
load: async () => {
|
|
279
|
+
const importMapResponse = await fetchUrl(importmapUrl)
|
|
280
|
+
if (importMapResponse.status === 200) {
|
|
281
|
+
const importmapAsText = await importMapResponse.text()
|
|
282
|
+
addHtmlSourceFile({
|
|
283
|
+
url: importmapUrl,
|
|
284
|
+
content: importmapAsText,
|
|
285
|
+
})
|
|
286
|
+
let htmlImportmap = JSON.parse(importmapAsText)
|
|
287
|
+
htmlImportmap = moveImportMap(htmlImportmap, importmapUrl, url)
|
|
288
|
+
return htmlImportmap
|
|
289
|
+
}
|
|
290
|
+
logger.warn(
|
|
291
|
+
createDetailedMessage(
|
|
292
|
+
importMapResponse.status === 404
|
|
293
|
+
? `importmap script file cannot be found.`
|
|
294
|
+
: `importmap script file unexpected response status (${importMapResponse.status}).`,
|
|
295
|
+
{
|
|
296
|
+
"importmap url": importmapInfo.url,
|
|
297
|
+
"html url": url,
|
|
298
|
+
},
|
|
299
|
+
),
|
|
300
|
+
)
|
|
301
|
+
return null
|
|
302
|
+
},
|
|
303
|
+
}
|
|
304
|
+
return importmapInfo
|
|
305
|
+
}
|
|
306
|
+
const importmapInfo = {
|
|
307
|
+
script: firstImportmapScript,
|
|
308
|
+
url: compiledUrl,
|
|
309
|
+
load: () => {
|
|
310
|
+
const jsenvImportmap = getDefaultImportmap(compiledUrl, {
|
|
311
|
+
projectDirectoryUrl,
|
|
312
|
+
compileDirectoryUrl: `${projectDirectoryUrl}${compileId}/${outDirectoryRelativeUrl}`,
|
|
313
|
+
})
|
|
314
|
+
const htmlImportmap = JSON.parse(
|
|
315
|
+
getHtmlNodeTextNode(firstImportmapScript).value,
|
|
316
|
+
)
|
|
317
|
+
const importmap = composeTwoImportMaps(jsenvImportmap, htmlImportmap)
|
|
318
|
+
return importmap
|
|
319
|
+
},
|
|
320
|
+
}
|
|
321
|
+
return importmapInfo
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const visitScripts = async ({
|
|
325
|
+
logger,
|
|
326
|
+
projectDirectoryUrl,
|
|
327
|
+
compileServerOrigin,
|
|
328
|
+
url,
|
|
329
|
+
compiledUrl,
|
|
330
|
+
scripts,
|
|
331
|
+
addHtmlSourceFile,
|
|
332
|
+
addHtmlAssetGenerator,
|
|
333
|
+
addHtmlMutation,
|
|
334
|
+
addHtmlDependency,
|
|
335
|
+
|
|
336
|
+
babelPluginMap,
|
|
337
|
+
moduleOutFormat,
|
|
338
|
+
importMetaFormat,
|
|
339
|
+
topLevelAwait,
|
|
340
|
+
sourcemapMethod,
|
|
341
|
+
}) => {
|
|
217
342
|
scripts.forEach((script) => {
|
|
218
343
|
const typeAttribute = getHtmlNodeAttributeByName(script, "type")
|
|
344
|
+
const type = typeAttribute ? typeAttribute.value : "application/javascript"
|
|
219
345
|
const srcAttribute = getHtmlNodeAttributeByName(script, "src")
|
|
220
346
|
const src = srcAttribute ? srcAttribute.value : ""
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
347
|
+
const textNode = getHtmlNodeTextNode(script)
|
|
348
|
+
|
|
349
|
+
if (type === "module") {
|
|
350
|
+
if (src) {
|
|
351
|
+
addHtmlMutation(() => {
|
|
352
|
+
if (moduleOutFormat === "systemjs") {
|
|
353
|
+
removeHtmlNodeAttribute(script, typeAttribute)
|
|
354
|
+
}
|
|
355
|
+
removeHtmlNodeAttribute(script, srcAttribute)
|
|
356
|
+
const jsenvMethod =
|
|
357
|
+
moduleOutFormat === "systemjs"
|
|
358
|
+
? "executeFileUsingSystemJs"
|
|
359
|
+
: "executeFileUsingDynamicImport"
|
|
360
|
+
setHtmlNodeText(
|
|
361
|
+
script,
|
|
362
|
+
`window.__jsenv__.${jsenvMethod}(${JSON.stringify(src)})`,
|
|
363
|
+
)
|
|
364
|
+
})
|
|
365
|
+
return
|
|
225
366
|
}
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
moduleOutFormat === "systemjs"
|
|
229
|
-
? "executeFileUsingSystemJs"
|
|
230
|
-
: "executeFileUsingDynamicImport"
|
|
231
|
-
setHtmlNodeText(
|
|
367
|
+
|
|
368
|
+
const inlineScriptName = getUniqueNameForInlineHtmlNode(
|
|
232
369
|
script,
|
|
233
|
-
|
|
370
|
+
scripts,
|
|
371
|
+
`[id].js`,
|
|
234
372
|
)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
// inline module script
|
|
239
|
-
const textNode = getHtmlNodeTextNode(script)
|
|
240
|
-
if (typeAttribute && typeAttribute.value === "module" && textNode) {
|
|
241
|
-
if (moduleOutFormat === "systemjs") {
|
|
242
|
-
removeHtmlNodeAttribute(script, typeAttribute)
|
|
243
|
-
}
|
|
244
|
-
removeHtmlNodeAttribute(script, srcAttribute)
|
|
245
|
-
const scriptAssetUrl = generateCompiledFileAssetUrl(
|
|
373
|
+
const scriptOriginalUrl = resolveUrl(inlineScriptName, url)
|
|
374
|
+
const scriptCompiledUrl = generateCompiledFileAssetUrl(
|
|
246
375
|
compiledUrl,
|
|
247
|
-
|
|
376
|
+
inlineScriptName,
|
|
248
377
|
)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
:
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
378
|
+
addHtmlAssetGenerator(async () => {
|
|
379
|
+
return transformHtmlScript({
|
|
380
|
+
projectDirectoryUrl,
|
|
381
|
+
url: scriptOriginalUrl,
|
|
382
|
+
compiledUrl: scriptCompiledUrl,
|
|
383
|
+
code: textNode.value,
|
|
384
|
+
|
|
385
|
+
type: "module",
|
|
386
|
+
babelPluginMap,
|
|
387
|
+
moduleOutFormat,
|
|
388
|
+
importMetaFormat,
|
|
389
|
+
topLevelAwait,
|
|
390
|
+
sourcemapMethod,
|
|
391
|
+
})
|
|
392
|
+
})
|
|
393
|
+
const specifier = `./${urlToRelativeUrl(scriptCompiledUrl, compiledUrl)}`
|
|
394
|
+
addHtmlMutation(() => {
|
|
395
|
+
if (moduleOutFormat === "systemjs") {
|
|
396
|
+
removeHtmlNodeAttribute(script, typeAttribute)
|
|
397
|
+
}
|
|
398
|
+
removeHtmlNodeAttribute(script, srcAttribute)
|
|
399
|
+
const jsenvMethod =
|
|
400
|
+
moduleOutFormat === "systemjs"
|
|
401
|
+
? "executeFileUsingSystemJs"
|
|
402
|
+
: "executeFileUsingDynamicImport"
|
|
403
|
+
setHtmlNodeText(
|
|
404
|
+
script,
|
|
405
|
+
`window.__jsenv__.${jsenvMethod}(${JSON.stringify(specifier)})`,
|
|
406
|
+
)
|
|
407
|
+
})
|
|
408
|
+
addHtmlDependency({
|
|
260
409
|
htmlNode: script,
|
|
261
410
|
specifier,
|
|
262
411
|
})
|
|
263
412
|
return
|
|
264
413
|
}
|
|
265
|
-
})
|
|
266
|
-
const htmlAfterTransformation = stringifyHtmlAst(htmlAst)
|
|
267
414
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const scriptBeforeCompilation = inlineScriptsContentMap[scriptSrc]
|
|
278
|
-
let scriptTransformResult
|
|
279
|
-
try {
|
|
280
|
-
scriptTransformResult = await transformJs({
|
|
281
|
-
code: scriptBeforeCompilation,
|
|
282
|
-
url: scriptOriginalFileUrl,
|
|
283
|
-
compiledUrl: scriptCompiledFileUrl,
|
|
415
|
+
if (type === "application/javascript" || type === "text/javascript") {
|
|
416
|
+
if (src) {
|
|
417
|
+
const htmlServerUrl = url.replace(
|
|
418
|
+
projectDirectoryUrl,
|
|
419
|
+
`${compileServerOrigin}/`,
|
|
420
|
+
)
|
|
421
|
+
const scriptOriginalServerUrl = resolveUrl(src, htmlServerUrl)
|
|
422
|
+
const scriptOriginalUrl = scriptOriginalServerUrl.replace(
|
|
423
|
+
`${compileServerOrigin}/`,
|
|
284
424
|
projectDirectoryUrl,
|
|
425
|
+
)
|
|
426
|
+
const fileIsInsideJsenvDistDirectory = urlIsInsideOf(
|
|
427
|
+
scriptOriginalUrl,
|
|
428
|
+
jsenvDistDirectoryUrl,
|
|
429
|
+
)
|
|
430
|
+
if (fileIsInsideJsenvDistDirectory) {
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const scriptCompiledUrl = generateCompiledFileAssetUrl(
|
|
435
|
+
compiledUrl,
|
|
436
|
+
urlToFilename(scriptOriginalUrl),
|
|
437
|
+
)
|
|
438
|
+
addHtmlAssetGenerator(async () => {
|
|
439
|
+
// we fetch scriptOriginalUrl on purpose because we do
|
|
440
|
+
// the transformation here and not in compile server
|
|
441
|
+
// (because compile server would think it's a module script
|
|
442
|
+
// and add things like systemjs)
|
|
443
|
+
const scriptResponse = await fetchUrl(scriptOriginalUrl)
|
|
444
|
+
if (scriptResponse.status !== 200) {
|
|
445
|
+
logger.warn(
|
|
446
|
+
createDetailedMessage(
|
|
447
|
+
scriptResponse.status === 404
|
|
448
|
+
? `script file cannot be found.`
|
|
449
|
+
: `script file unexpected response status (${scriptResponse.status}).`,
|
|
450
|
+
{
|
|
451
|
+
"script url": script.url,
|
|
452
|
+
"html url": url,
|
|
453
|
+
},
|
|
454
|
+
),
|
|
455
|
+
)
|
|
456
|
+
return []
|
|
457
|
+
}
|
|
458
|
+
const scriptAsText = await scriptResponse.text()
|
|
459
|
+
addHtmlSourceFile({
|
|
460
|
+
url: scriptOriginalUrl,
|
|
461
|
+
content: scriptAsText,
|
|
462
|
+
})
|
|
463
|
+
return transformHtmlScript({
|
|
464
|
+
projectDirectoryUrl,
|
|
465
|
+
url: scriptOriginalUrl,
|
|
466
|
+
compiledUrl: scriptCompiledUrl,
|
|
467
|
+
code: scriptAsText,
|
|
285
468
|
|
|
469
|
+
type: "classic",
|
|
470
|
+
babelPluginMap,
|
|
471
|
+
moduleOutFormat,
|
|
472
|
+
importMetaFormat,
|
|
473
|
+
topLevelAwait,
|
|
474
|
+
sourcemapMethod,
|
|
475
|
+
})
|
|
476
|
+
})
|
|
477
|
+
addHtmlMutation(() => {
|
|
478
|
+
srcAttribute.value = `./${urlToRelativeUrl(
|
|
479
|
+
scriptCompiledUrl,
|
|
480
|
+
compiledUrl,
|
|
481
|
+
)}`
|
|
482
|
+
})
|
|
483
|
+
return
|
|
484
|
+
}
|
|
485
|
+
const inlineScriptName = getUniqueNameForInlineHtmlNode(
|
|
486
|
+
script,
|
|
487
|
+
scripts,
|
|
488
|
+
`[id].js`,
|
|
489
|
+
)
|
|
490
|
+
const scriptOriginalUrl = resolveUrl(inlineScriptName, url)
|
|
491
|
+
const scriptCompiledUrl = generateCompiledFileAssetUrl(
|
|
492
|
+
compiledUrl,
|
|
493
|
+
inlineScriptName,
|
|
494
|
+
)
|
|
495
|
+
addHtmlAssetGenerator(async () => {
|
|
496
|
+
const htmlAssets = await transformHtmlScript({
|
|
497
|
+
projectDirectoryUrl,
|
|
498
|
+
url: scriptOriginalUrl,
|
|
499
|
+
compiledUrl: scriptCompiledUrl,
|
|
500
|
+
code: textNode.value,
|
|
501
|
+
|
|
502
|
+
type: "classic",
|
|
286
503
|
babelPluginMap,
|
|
287
504
|
moduleOutFormat,
|
|
288
505
|
importMetaFormat,
|
|
289
506
|
topLevelAwait,
|
|
507
|
+
sourcemapMethod,
|
|
290
508
|
})
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const code = scriptBeforeCompilation
|
|
301
|
-
assets = [...assets, scriptAssetUrl]
|
|
302
|
-
assetsContent = [...assetsContent, code]
|
|
303
|
-
return
|
|
304
|
-
}
|
|
305
|
-
throw e
|
|
306
|
-
}
|
|
307
|
-
const sourcemapFileUrl = resolveUrl(
|
|
308
|
-
`${scriptBasename}.map`,
|
|
309
|
-
scriptCompiledFileUrl,
|
|
310
|
-
)
|
|
509
|
+
addHtmlMutation(() => {
|
|
510
|
+
setHtmlNodeText(script, htmlAssets[0].content)
|
|
511
|
+
})
|
|
512
|
+
return htmlAssets
|
|
513
|
+
})
|
|
514
|
+
return
|
|
515
|
+
}
|
|
516
|
+
})
|
|
517
|
+
}
|
|
311
518
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
519
|
+
const transformHtmlScript = async ({
|
|
520
|
+
projectDirectoryUrl,
|
|
521
|
+
url,
|
|
522
|
+
compiledUrl,
|
|
523
|
+
code,
|
|
524
|
+
type,
|
|
317
525
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
]
|
|
332
|
-
}
|
|
333
|
-
}),
|
|
334
|
-
)
|
|
335
|
-
sources.push(url)
|
|
336
|
-
sourcesContent.push(code)
|
|
526
|
+
babelPluginMap,
|
|
527
|
+
moduleOutFormat,
|
|
528
|
+
importMetaFormat,
|
|
529
|
+
topLevelAwait,
|
|
530
|
+
sourcemapMethod,
|
|
531
|
+
}) => {
|
|
532
|
+
let transformResult
|
|
533
|
+
try {
|
|
534
|
+
transformResult = await transformJs({
|
|
535
|
+
code,
|
|
536
|
+
url,
|
|
537
|
+
compiledUrl,
|
|
538
|
+
projectDirectoryUrl,
|
|
337
539
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
540
|
+
babelPluginMap,
|
|
541
|
+
moduleOutFormat: type === "module" ? moduleOutFormat : "global",
|
|
542
|
+
importMetaFormat,
|
|
543
|
+
topLevelAwait: type === "module" ? topLevelAwait : false,
|
|
544
|
+
babelHelpersInjectionAsImport: type === "module" ? undefined : false,
|
|
545
|
+
})
|
|
546
|
+
} catch (e) {
|
|
547
|
+
// If there is a syntax error in inline script
|
|
548
|
+
// we put the raw script without transformation.
|
|
549
|
+
// when systemjs will try to instantiate to script it
|
|
550
|
+
// will re-throw this syntax error.
|
|
551
|
+
// Thanks to this we see the syntax error in the
|
|
552
|
+
// document and livereloading still works
|
|
553
|
+
// because we gracefully handle this error
|
|
554
|
+
if (e.code === "PARSE_ERROR") {
|
|
555
|
+
return [{ url, content: code }]
|
|
556
|
+
}
|
|
557
|
+
throw e
|
|
348
558
|
}
|
|
559
|
+
|
|
560
|
+
code = transformResult.code
|
|
561
|
+
let map = transformResult.map
|
|
562
|
+
const sourcemapUrl = resolveUrl(
|
|
563
|
+
`${urlToBasename(compiledUrl)}.map`,
|
|
564
|
+
compiledUrl,
|
|
565
|
+
)
|
|
566
|
+
if (sourcemapMethod === "inline") {
|
|
567
|
+
code = setJavaScriptSourceMappingUrl(code, sourcemapToBase64Url(map))
|
|
568
|
+
return [
|
|
569
|
+
{
|
|
570
|
+
url: compiledUrl,
|
|
571
|
+
content: code,
|
|
572
|
+
},
|
|
573
|
+
]
|
|
574
|
+
}
|
|
575
|
+
const sourcemapSpecifier = urlToRelativeUrl(sourcemapUrl, compiledUrl)
|
|
576
|
+
code = setJavaScriptSourceMappingUrl(code, sourcemapSpecifier)
|
|
577
|
+
return [
|
|
578
|
+
{
|
|
579
|
+
url: compiledUrl,
|
|
580
|
+
content: code,
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
url: sourcemapUrl,
|
|
584
|
+
content: JSON.stringify(map, null, " "),
|
|
585
|
+
},
|
|
586
|
+
]
|
|
349
587
|
}
|
|
350
588
|
|
|
351
589
|
// transform <link type="modulepreload"> into <link type="preload">
|
|
@@ -42,7 +42,10 @@ import {
|
|
|
42
42
|
sourcemapMainFileInfo,
|
|
43
43
|
sourcemapMappingFileInfo,
|
|
44
44
|
} from "../jsenvInternalFiles.js"
|
|
45
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
jsenvCoreDirectoryUrl,
|
|
47
|
+
jsenvDistDirectoryUrl,
|
|
48
|
+
} from "../jsenvCoreDirectoryUrl.js"
|
|
46
49
|
import { babelPluginReplaceExpressions } from "../babel_plugin_replace_expressions.js"
|
|
47
50
|
import { babelPluginGlobalThisAsJsenvImport } from "./babel_plugin_global_this_as_jsenv_import.js"
|
|
48
51
|
import { babelPluginNewStylesheetAsJsenvImport } from "./babel_plugin_new_stylesheet_as_jsenv_import.js"
|
|
@@ -945,7 +948,7 @@ const createSourceFileService = ({
|
|
|
945
948
|
.href
|
|
946
949
|
const fileIsInsideJsenvDistDirectory = urlIsInsideOf(
|
|
947
950
|
fileUrl,
|
|
948
|
-
|
|
951
|
+
jsenvDistDirectoryUrl,
|
|
949
952
|
)
|
|
950
953
|
|
|
951
954
|
const responsePromise = fetchFileSystem(fileUrl, {
|