@jsenv/core 24.6.5 → 25.0.0-alpha.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/browser_runtime/asset-manifest.json +2 -1
- package/dist/browser_runtime/{browser_runtime-bb0e3aa4.js → browser_runtime_a8097085.js} +1 -1
- package/dist/browser_runtime/{browser_runtime-bb0e3aa4.js.map → browser_runtime_a8097085.js.map} +1 -1
- package/dist/build_manifest.js +6 -6
- package/dist/compile_proxy/asset-manifest.json +2 -1
- package/dist/compile_proxy/{compile_proxy-6eb67db4.html → compile_proxy_e16d7de8.html} +2 -2
- package/dist/compile_proxy/{compile_proxy.html__inline__20-9e168143.js.map → compile_proxy_e3b0c442_9e168143.js.map} +0 -0
- package/dist/event_source_client/asset-manifest.json +2 -1
- package/dist/event_source_client/{event_source_client-9f14c8b9.js → event_source_client_620fbc2c.js} +1 -1
- package/dist/event_source_client/{event_source_client-9f14c8b9.js.map → event_source_client_620fbc2c.js.map} +1 -1
- package/dist/redirector/asset-manifest.json +2 -1
- package/dist/redirector/{redirector-b6ad84bf.html → redirector_2e0c8abe.html} +2 -2
- package/dist/redirector/{redirector.html__inline__15-3a34a156.js.map → redirector_e3b0c442_3a34a156.js.map} +0 -0
- package/dist/toolbar/asset-manifest.json +11 -10
- package/dist/toolbar/assets/{compilation.css-209d68b4.map → compilation.css_e37c747b.map} +1 -1
- package/dist/toolbar/assets/{eventsource.css-38cd0a36.map → eventsource.css_c0c71e7b.map} +1 -1
- package/dist/toolbar/assets/{execution.css-0ebe522f.map → execution.css_f3377c10.map} +1 -1
- package/dist/toolbar/assets/{focus.css-3f9c156d.map → focus.css_896f3e45.map} +1 -1
- package/dist/toolbar/assets/{light-theme.css-78b19a80.map → light-theme.css_72a60fa3.map} +1 -1
- package/dist/toolbar/assets/{overflow-menu.css-d9688a1c.map → overflow-menu.css_2859d519.map} +1 -1
- package/dist/toolbar/assets/{settings.css-2b81b245.map → settings.css_61548139.map} +1 -1
- package/dist/toolbar/assets/{toolbar.main.css-846920e9.map → toolbar.main.css_269d7ce2.map} +9 -9
- package/dist/toolbar/assets/{tooltip.css-03395ee6.map → tooltip.css_a94a8bdd.map} +1 -1
- package/dist/toolbar/{toolbar.main-a5ef2c60.js.map → toolbar.main_a5ef2c60.js.map} +0 -0
- package/dist/toolbar/{toolbar-1fbf8dcb.html → toolbar_412abb83.html} +3 -5
- package/dist/toolbar_injector/asset-manifest.json +3 -2
- package/dist/toolbar_injector/assets/{jsenv-logo-188b9ca6.svg → jsenv-logo_188b9ca6.svg} +0 -0
- package/dist/toolbar_injector/{toolbar_injector-997dbaa0.js → toolbar_injector_4f9c19e5.js} +3 -3
- package/dist/toolbar_injector/{toolbar_injector-997dbaa0.js.map → toolbar_injector_4f9c19e5.js.map} +2 -2
- package/package.json +1 -1
- package/readme.md +2 -2
- package/src/buildProject.js +18 -16
- package/src/internal/building/buildUsingRollup.js +9 -7
- package/src/internal/building/build_logs.js +2 -2
- package/src/internal/building/build_stats.js +11 -1
- package/src/internal/building/html/parseHtmlRessource.js +2 -26
- package/src/internal/building/js/parseJsRessource.js +3 -2
- package/src/internal/building/json_module.js +11 -0
- package/src/internal/building/parseRessource.js +1 -1
- package/src/internal/building/ressource_builder.js +216 -215
- package/src/internal/building/rollup_plugin_jsenv.js +462 -306
- package/src/internal/building/url_loader.js +8 -145
- package/src/internal/building/url_versioning.js +226 -0
- package/src/internal/compiling/compileHtml.js +8 -1
- package/src/jsenvServiceWorkerFinalizer.js +7 -8
- package/src/internal/building/asset_url_versioning.js +0 -50
- package/src/internal/building/rollup_build_sourcemap.js +0 -54
- package/src/internal/building/url-versioning.js +0 -96
- package/src/internal/renderNamePattern.js +0 -6
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { transformJs } from "@jsenv/core/src/internal/compiling/js-compilation-service/transformJs.js"
|
|
4
|
-
import { convertCssTextToJavascriptModule } from "@jsenv/core/src/internal/building/css_module.js"
|
|
1
|
+
import { convertJsonTextToJavascriptModule } from "@jsenv/core/src/internal/building/json_module.js"
|
|
5
2
|
import { getJavaScriptSourceMappingUrl } from "@jsenv/core/src/internal/sourceMappingURLUtils.js"
|
|
3
|
+
|
|
6
4
|
import { loadSourcemap } from "./sourcemap_loader.js"
|
|
7
5
|
|
|
8
6
|
export const createUrlLoader = ({
|
|
9
|
-
|
|
10
|
-
buildDirectoryUrl,
|
|
11
|
-
babelPluginMap,
|
|
7
|
+
urlCustomLoaders,
|
|
12
8
|
allowJson,
|
|
13
9
|
urlImporterMap,
|
|
14
|
-
inlineModuleScripts,
|
|
15
10
|
|
|
16
11
|
asServerUrl,
|
|
17
12
|
asProjectUrl,
|
|
@@ -21,115 +16,13 @@ export const createUrlLoader = ({
|
|
|
21
16
|
}) => {
|
|
22
17
|
const urlResponseBodyMap = {}
|
|
23
18
|
|
|
24
|
-
const loadUrl = async (rollupUrl, { signal, logger
|
|
19
|
+
const loadUrl = async (rollupUrl, { signal, logger }) => {
|
|
25
20
|
let url = asServerUrl(rollupUrl)
|
|
26
|
-
const { importType, urlWithoutImportType } = extractImportTypeFromUrl(url)
|
|
27
|
-
// importing CSS from JS with import assertions
|
|
28
|
-
if (importType === "css") {
|
|
29
|
-
const importer = urlImporterMap[url]
|
|
30
|
-
const cssReference =
|
|
31
|
-
await ressourceBuilder.createReferenceFoundInJsModule({
|
|
32
|
-
referenceLabel: "css import assertion",
|
|
33
|
-
// If all references to a ressource are only import assertions
|
|
34
|
-
// the file referenced do not need to be written on filesystem
|
|
35
|
-
// as it was converted to a js file
|
|
36
|
-
// We pass "isImportAssertion: true" for this purpose
|
|
37
|
-
isImportAssertion: true,
|
|
38
|
-
jsUrl: importer.url,
|
|
39
|
-
jsLine: importer.line,
|
|
40
|
-
jsColumn: importer.column,
|
|
41
|
-
ressourceSpecifier: urlWithoutImportType,
|
|
42
|
-
contentTypeExpected: "text/css",
|
|
43
|
-
})
|
|
44
|
-
await cssReference.ressource.getReadyPromise()
|
|
45
|
-
const cssBuildUrl = resolveUrl(
|
|
46
|
-
cssReference.ressource.buildRelativeUrl,
|
|
47
|
-
buildDirectoryUrl,
|
|
48
|
-
)
|
|
49
|
-
const jsBuildUrl = resolveUrl(
|
|
50
|
-
urlToFilename(importer.url),
|
|
51
|
-
buildDirectoryUrl,
|
|
52
|
-
)
|
|
53
|
-
let code = String(cssReference.ressource.bufferAfterBuild)
|
|
54
|
-
let map
|
|
55
|
-
const sourcemapReference = cssReference.ressource.dependencies.find(
|
|
56
|
-
(dependency) => {
|
|
57
|
-
return dependency.ressource.isSourcemap
|
|
58
|
-
},
|
|
59
|
-
)
|
|
60
|
-
if (sourcemapReference) {
|
|
61
|
-
// because css is ready, it's sourcemap is also ready
|
|
62
|
-
// we can read directly sourcemapReference.ressource.bufferAfterBuild
|
|
63
|
-
map = JSON.parse(sourcemapReference.ressource.bufferAfterBuild)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const jsModuleConversionResult = await convertCssTextToJavascriptModule({
|
|
67
|
-
cssUrl: cssBuildUrl,
|
|
68
|
-
jsUrl: jsBuildUrl,
|
|
69
|
-
code,
|
|
70
|
-
map,
|
|
71
|
-
})
|
|
72
|
-
code = jsModuleConversionResult.code
|
|
73
|
-
map = jsModuleConversionResult.map
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
url,
|
|
77
|
-
code,
|
|
78
|
-
map,
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
// importing json from JS with import assertion
|
|
82
|
-
if (importType === "json") {
|
|
83
|
-
const importer = urlImporterMap[url]
|
|
84
|
-
const jsonReference =
|
|
85
|
-
await ressourceBuilder.createReferenceFoundInJsModule({
|
|
86
|
-
referenceLabel: "json import assertion",
|
|
87
|
-
// If all references to a ressource are only import assertions
|
|
88
|
-
// the file referenced do not need to be written on filesystem
|
|
89
|
-
// as it was converted to a js file
|
|
90
|
-
// We pass "isImportAssertion: true" for this purpose
|
|
91
|
-
isImportAssertion: true,
|
|
92
|
-
jsUrl: importer.url,
|
|
93
|
-
jsLine: importer.line,
|
|
94
|
-
jsColumn: importer.column,
|
|
95
|
-
ressourceSpecifier: asServerUrl(urlWithoutImportType),
|
|
96
|
-
contentTypeExpected: "application/json",
|
|
97
|
-
})
|
|
98
|
-
await jsonReference.ressource.getReadyPromise()
|
|
99
|
-
let code = String(jsonReference.ressource.bufferAfterBuild)
|
|
100
|
-
let map
|
|
101
|
-
|
|
102
|
-
const jsModuleConversionResult = await convertJsonTextToJavascriptModule({
|
|
103
|
-
code,
|
|
104
|
-
map,
|
|
105
|
-
})
|
|
106
|
-
code = jsModuleConversionResult.code
|
|
107
|
-
map = jsModuleConversionResult.map
|
|
108
21
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (url in inlineModuleScripts) {
|
|
116
|
-
const transformResult = await transformJs({
|
|
117
|
-
code: String(inlineModuleScripts[url].bufferBeforeBuild),
|
|
118
|
-
url: asOriginalUrl(url), // transformJs expect a file:// url
|
|
119
|
-
projectDirectoryUrl,
|
|
120
|
-
babelPluginMap,
|
|
121
|
-
// moduleOutFormat: format // we are compiling for rollup output must be "esmodule"
|
|
122
|
-
// we compile for rollup, let top level await untouched
|
|
123
|
-
// it will be converted, if needed, during "renderChunk" hook
|
|
124
|
-
topLevelAwait: "ignore",
|
|
125
|
-
})
|
|
126
|
-
let code = transformResult.code
|
|
127
|
-
let map = transformResult.map
|
|
128
|
-
return {
|
|
129
|
-
url,
|
|
130
|
-
code,
|
|
131
|
-
map,
|
|
132
|
-
}
|
|
22
|
+
const customLoader = urlCustomLoaders[url]
|
|
23
|
+
if (customLoader) {
|
|
24
|
+
const result = await customLoader()
|
|
25
|
+
return result
|
|
133
26
|
}
|
|
134
27
|
|
|
135
28
|
const response = await urlFetcher.fetchUrl(url, {
|
|
@@ -236,33 +129,3 @@ export const createUrlLoader = ({
|
|
|
236
129
|
getUrlResponseBodyMap: () => urlResponseBodyMap,
|
|
237
130
|
}
|
|
238
131
|
}
|
|
239
|
-
|
|
240
|
-
const extractImportTypeFromUrl = (url) => {
|
|
241
|
-
const urlObject = new URL(url)
|
|
242
|
-
const { search } = urlObject
|
|
243
|
-
const searchParams = new URLSearchParams(search)
|
|
244
|
-
|
|
245
|
-
const importType = searchParams.get("import_type")
|
|
246
|
-
if (!importType) {
|
|
247
|
-
return {}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
searchParams.delete("import_type")
|
|
251
|
-
urlObject.search = String(searchParams)
|
|
252
|
-
return {
|
|
253
|
-
importType,
|
|
254
|
-
urlWithoutImportType: urlObject.href,
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const convertJsonTextToJavascriptModule = ({ code }) => {
|
|
259
|
-
// here we could do the following
|
|
260
|
-
// return export default jsonText
|
|
261
|
-
// This would return valid js, that would be minified later
|
|
262
|
-
// however we will prefer using JSON.parse because it's faster
|
|
263
|
-
// for js engine to parse JSON than JS
|
|
264
|
-
|
|
265
|
-
return {
|
|
266
|
-
code: `export default JSON.parse(${JSON.stringify(code.trim())})`,
|
|
267
|
-
}
|
|
268
|
-
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { createHash } from "crypto"
|
|
2
|
+
import {
|
|
3
|
+
urlToParentUrl,
|
|
4
|
+
urlToBasename,
|
|
5
|
+
urlToExtension,
|
|
6
|
+
} from "@jsenv/filesystem"
|
|
7
|
+
|
|
8
|
+
export const createUrlVersioner = ({
|
|
9
|
+
entryPointUrls,
|
|
10
|
+
workerUrls,
|
|
11
|
+
classicWorkerUrls,
|
|
12
|
+
serviceWorkerUrls,
|
|
13
|
+
classicServiceWorkerUrls,
|
|
14
|
+
asOriginalUrl,
|
|
15
|
+
lineBreakNormalization,
|
|
16
|
+
}) => {
|
|
17
|
+
const computeBuildRelativeUrl = (ressource) => {
|
|
18
|
+
const pattern = getFilenamePattern({
|
|
19
|
+
ressource,
|
|
20
|
+
entryPointUrls,
|
|
21
|
+
workerUrls,
|
|
22
|
+
classicWorkerUrls,
|
|
23
|
+
serviceWorkerUrls,
|
|
24
|
+
classicServiceWorkerUrls,
|
|
25
|
+
asOriginalUrl,
|
|
26
|
+
precomputeBuildRelativeUrl,
|
|
27
|
+
})
|
|
28
|
+
const buildRelativeUrl = generateBuildRelativeUrl(
|
|
29
|
+
ressource.url,
|
|
30
|
+
ressource.bufferAfterBuild,
|
|
31
|
+
{
|
|
32
|
+
pattern,
|
|
33
|
+
contentType: ressource.contentType,
|
|
34
|
+
lineBreakNormalization,
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
return buildRelativeUrl
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const precomputeBuildRelativeUrl = (ressource, bufferAfterBuild = "") => {
|
|
41
|
+
if (ressource.buildRelativeUrl) {
|
|
42
|
+
return ressource.buildRelativeUrl
|
|
43
|
+
}
|
|
44
|
+
if (ressource.precomputedBuildRelativeUrl) {
|
|
45
|
+
return ressource.precomputedBuildRelativeUrl
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
ressource.bufferAfterBuild = bufferAfterBuild
|
|
49
|
+
const precomputedBuildRelativeUrl = computeBuildRelativeUrl(ressource)
|
|
50
|
+
ressource.bufferAfterBuild = undefined
|
|
51
|
+
ressource.precomputedBuildRelativeUrl = precomputedBuildRelativeUrl
|
|
52
|
+
return precomputedBuildRelativeUrl
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
computeBuildRelativeUrl,
|
|
57
|
+
precomputeBuildRelativeUrl,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const getFilenamePattern = ({
|
|
62
|
+
ressource,
|
|
63
|
+
entryPointUrls,
|
|
64
|
+
workerUrls,
|
|
65
|
+
classicWorkerUrls,
|
|
66
|
+
serviceWorkerUrls,
|
|
67
|
+
classicServiceWorkerUrls,
|
|
68
|
+
asOriginalUrl,
|
|
69
|
+
precomputeBuildRelativeUrl,
|
|
70
|
+
}) => {
|
|
71
|
+
if (ressource.isEntryPoint) {
|
|
72
|
+
const originalUrl = asOriginalUrl(ressource.url)
|
|
73
|
+
const entryPointBuildRelativeUrl = entryPointUrls[originalUrl]
|
|
74
|
+
return entryPointBuildRelativeUrl
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// service worker:
|
|
78
|
+
// - MUST be at the root (same level than the HTML file)
|
|
79
|
+
// otherwise it might be registered for the scope "/assets/" instead of "/"
|
|
80
|
+
// - MUST not be versioned
|
|
81
|
+
if (ressource.isServiceWorker) {
|
|
82
|
+
const originalUrl = asOriginalUrl(ressource.url)
|
|
83
|
+
const serviceWorkerBuildRelativeUrl = ressource.isJsModule
|
|
84
|
+
? serviceWorkerUrls[originalUrl]
|
|
85
|
+
: classicServiceWorkerUrls[originalUrl]
|
|
86
|
+
// we could/should log a warning when service worker is not at the root
|
|
87
|
+
// we should also disable versioning for this file
|
|
88
|
+
// of the build directory
|
|
89
|
+
return serviceWorkerBuildRelativeUrl
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// it's a module worker
|
|
93
|
+
if (ressource.isWorker) {
|
|
94
|
+
const originalUrl = asOriginalUrl(ressource.url)
|
|
95
|
+
const workerBuildRelativeUrl = ressource.isJsModule
|
|
96
|
+
? workerUrls[originalUrl]
|
|
97
|
+
: classicWorkerUrls[originalUrl]
|
|
98
|
+
return workerBuildRelativeUrl
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// inline ressource inherits location
|
|
102
|
+
if (ressource.isInline) {
|
|
103
|
+
// inherit parent directory location because it's an inline file
|
|
104
|
+
const importerBuildRelativeUrl = precomputeBuildRelativeUrl(
|
|
105
|
+
ressource.importer,
|
|
106
|
+
)
|
|
107
|
+
const name = urlToBasename(`file://${importerBuildRelativeUrl}`)
|
|
108
|
+
return `${name}_[hash][extname]`
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// importmap.
|
|
112
|
+
// we want to force the fileName for the importmap
|
|
113
|
+
// so that we don't have to rewrite its content
|
|
114
|
+
// the goal is to put the importmap at the same relative path
|
|
115
|
+
// than in the project
|
|
116
|
+
if (ressource.contentType === "application/importmap+json") {
|
|
117
|
+
const importmapRessourceUrl = ressource.url
|
|
118
|
+
const name = urlToBasename(importmapRessourceUrl)
|
|
119
|
+
return `${name}_[hash][extname]`
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (ressource.isJsModule) {
|
|
123
|
+
// it's a js module
|
|
124
|
+
return "[name]_[hash][extname]"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return ressource.urlVersioningDisabled
|
|
128
|
+
? "assets/[name][extname]"
|
|
129
|
+
: "assets/[name]_[hash][extname]"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const generateBuildRelativeUrl = (
|
|
133
|
+
fileUrl,
|
|
134
|
+
fileContent,
|
|
135
|
+
{
|
|
136
|
+
name = urlToBasename(fileUrl),
|
|
137
|
+
pattern = "[name]-[hash][extname]",
|
|
138
|
+
contentType = "application/octet-stream",
|
|
139
|
+
lineBreakNormalization = false,
|
|
140
|
+
} = {},
|
|
141
|
+
) => {
|
|
142
|
+
pattern = typeof pattern === "function" ? pattern() : pattern
|
|
143
|
+
if (pattern.startsWith("./")) pattern = pattern.slice(2)
|
|
144
|
+
const buildRelativeUrl = renderFileNamePattern(pattern, {
|
|
145
|
+
dirname: () => urlToParentUrl(fileUrl),
|
|
146
|
+
name: () => name,
|
|
147
|
+
hash: () =>
|
|
148
|
+
generateContentHash(fileContent, {
|
|
149
|
+
contentType,
|
|
150
|
+
lineBreakNormalization,
|
|
151
|
+
}),
|
|
152
|
+
extname: () => urlToExtension(fileUrl),
|
|
153
|
+
})
|
|
154
|
+
return buildRelativeUrl
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const renderFileNamePattern = (pattern, replacements) => {
|
|
158
|
+
return pattern.replace(/\[(\w+)\]/g, (_match, type) => {
|
|
159
|
+
const replacement = replacements[type]()
|
|
160
|
+
return replacement
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// https://github.com/rollup/rollup/blob/19e50af3099c2f627451a45a84e2fa90d20246d5/src/utils/FileEmitter.ts#L47
|
|
165
|
+
export const generateContentHash = (
|
|
166
|
+
stringOrBuffer,
|
|
167
|
+
{
|
|
168
|
+
contentType = "application/octet-stream",
|
|
169
|
+
lineBreakNormalization = false,
|
|
170
|
+
} = {},
|
|
171
|
+
) => {
|
|
172
|
+
const hash = createHash("sha256")
|
|
173
|
+
hash.update(
|
|
174
|
+
lineBreakNormalization && contentTypeIsTextual(contentType)
|
|
175
|
+
? normalizeLineBreaks(stringOrBuffer)
|
|
176
|
+
: stringOrBuffer,
|
|
177
|
+
)
|
|
178
|
+
return hash.digest("hex").slice(0, 8)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const contentTypeIsTextual = (contentType) => {
|
|
182
|
+
if (contentType.startsWith("text/")) {
|
|
183
|
+
return true
|
|
184
|
+
}
|
|
185
|
+
// catch things like application/manifest+json, application/importmap+json
|
|
186
|
+
if (/^application\/\w+\+json$/.test(contentType)) {
|
|
187
|
+
return true
|
|
188
|
+
}
|
|
189
|
+
if (CONTENT_TYPE_AS_TEXT.includes(contentType)) {
|
|
190
|
+
return true
|
|
191
|
+
}
|
|
192
|
+
return false
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const CONTENT_TYPE_AS_TEXT = [
|
|
196
|
+
"application/javascript",
|
|
197
|
+
"application/json",
|
|
198
|
+
"image/svg+xml",
|
|
199
|
+
]
|
|
200
|
+
|
|
201
|
+
const normalizeLineBreaks = (stringOrBuffer) => {
|
|
202
|
+
if (typeof stringOrBuffer === "string") {
|
|
203
|
+
const stringWithLinuxBreaks = stringOrBuffer.replace(/\r\n/g, "\n")
|
|
204
|
+
return stringWithLinuxBreaks
|
|
205
|
+
}
|
|
206
|
+
return normalizeLineBreaksForBuffer(stringOrBuffer)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// https://github.com/nodejs/help/issues/1738#issuecomment-458460503
|
|
210
|
+
const normalizeLineBreaksForBuffer = (buffer) => {
|
|
211
|
+
const int32Array = new Int32Array(buffer, 0, buffer.length)
|
|
212
|
+
const int32ArrayWithLineBreaksNormalized = int32Array.filter(
|
|
213
|
+
(element, index, typedArray) => {
|
|
214
|
+
if (element === 0x0d) {
|
|
215
|
+
if (typedArray[index + 1] === 0x0a) {
|
|
216
|
+
// Windows -> Unix
|
|
217
|
+
return false
|
|
218
|
+
}
|
|
219
|
+
// Mac OS -> Unix
|
|
220
|
+
typedArray[index] = 0x0a
|
|
221
|
+
}
|
|
222
|
+
return true
|
|
223
|
+
},
|
|
224
|
+
)
|
|
225
|
+
return Buffer.from(int32ArrayWithLineBreaksNormalized)
|
|
226
|
+
}
|
|
@@ -9,8 +9,8 @@ which one is being done first
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { createHash } from "crypto"
|
|
12
|
+
|
|
12
13
|
import { require } from "../require.js"
|
|
13
|
-
import { renderNamePattern } from "../renderNamePattern.js"
|
|
14
14
|
|
|
15
15
|
// https://github.com/inikulin/parse5/blob/master/packages/parse5/lib/tree-adapters/default.js
|
|
16
16
|
// eslint-disable-next-line import/no-unresolved
|
|
@@ -519,6 +519,13 @@ export const getUniqueNameForInlineHtmlNode = (node, nodes, pattern) => {
|
|
|
519
519
|
})
|
|
520
520
|
}
|
|
521
521
|
|
|
522
|
+
const renderNamePattern = (pattern, replacements) => {
|
|
523
|
+
return pattern.replace(/\[(\w+)\]/g, (_match, type) => {
|
|
524
|
+
const replacement = replacements[type]()
|
|
525
|
+
return replacement
|
|
526
|
+
})
|
|
527
|
+
}
|
|
528
|
+
|
|
522
529
|
const parseHtmlAsSingleElement = (html) => {
|
|
523
530
|
const parse5 = require("parse5")
|
|
524
531
|
const fragment = parse5.parseFragment(html)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { resolveUrl, urlToRelativeUrl } from "@jsenv/filesystem"
|
|
2
2
|
|
|
3
|
-
import { generateContentHash } from "./internal/building/
|
|
3
|
+
import { generateContentHash } from "./internal/building/url_versioning.js"
|
|
4
4
|
|
|
5
5
|
export const jsenvServiceWorkerFinalizer = (
|
|
6
6
|
code,
|
|
7
7
|
{
|
|
8
8
|
serviceWorkerBuildRelativeUrl,
|
|
9
9
|
buildManifest,
|
|
10
|
-
|
|
10
|
+
buildFileContents,
|
|
11
11
|
lineBreakNormalization,
|
|
12
12
|
},
|
|
13
13
|
) => {
|
|
@@ -32,7 +32,7 @@ export const jsenvServiceWorkerFinalizer = (
|
|
|
32
32
|
return
|
|
33
33
|
}
|
|
34
34
|
const versioned = fileNameContainsHash(buildRelativeUrl)
|
|
35
|
-
const
|
|
35
|
+
const buildFileContent = buildFileContents[buildRelativeUrl]
|
|
36
36
|
|
|
37
37
|
generatedUrlsConfig[urlRelativeToServiceWorker] = {
|
|
38
38
|
versioned,
|
|
@@ -42,10 +42,9 @@ export const jsenvServiceWorkerFinalizer = (
|
|
|
42
42
|
// when url is not versioned we compute a "version" for that url anyway
|
|
43
43
|
// so that service worker source still changes and navigator
|
|
44
44
|
// detect there is a change
|
|
45
|
-
version: generateContentHash(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
),
|
|
45
|
+
version: generateContentHash(buildFileContent, {
|
|
46
|
+
lineBreakNormalization,
|
|
47
|
+
}),
|
|
49
48
|
}),
|
|
50
49
|
}
|
|
51
50
|
})
|
|
@@ -57,4 +56,4 @@ ${code}
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
const fileNameContainsHash = (fileName) =>
|
|
60
|
-
|
|
59
|
+
/_[a-z0-9]{8,}(\..*?)?$/.test(fileName)
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { computeBuildRelativeUrl } from "./url-versioning.js"
|
|
2
|
-
|
|
3
|
-
export const computeBuildRelativeUrlForRessource = (
|
|
4
|
-
ressource,
|
|
5
|
-
{ lineBreakNormalization },
|
|
6
|
-
) => {
|
|
7
|
-
return computeBuildRelativeUrl(ressource.url, ressource.bufferAfterBuild, {
|
|
8
|
-
pattern: fileNamePatternFromRessource(ressource),
|
|
9
|
-
contentType: ressource.contentType,
|
|
10
|
-
lineBreakNormalization,
|
|
11
|
-
})
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const assetFileNamePattern = "assets/[name]-[hash][extname]"
|
|
15
|
-
const assetFileNamePatternWithoutHash = "assets/[name][extname]"
|
|
16
|
-
|
|
17
|
-
const fileNamePatternFromRessource = (ressource) => {
|
|
18
|
-
if (ressource.fileNamePattern) {
|
|
19
|
-
return ressource.fileNamePattern
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (ressource.urlVersioningDisabled) {
|
|
23
|
-
if (canMoveToAssetsDirectory(ressource)) {
|
|
24
|
-
return assetFileNamePatternWithoutHash
|
|
25
|
-
}
|
|
26
|
-
return `[name][extname]`
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (canMoveToAssetsDirectory(ressource)) {
|
|
30
|
-
return assetFileNamePattern
|
|
31
|
-
}
|
|
32
|
-
return `[name]-[hash][extname]`
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const canMoveToAssetsDirectory = (ressource) => {
|
|
36
|
-
if (ressource.isEntryPoint) {
|
|
37
|
-
return false
|
|
38
|
-
}
|
|
39
|
-
// in theory js module can be moved to assets directory
|
|
40
|
-
// but that needs to be tested
|
|
41
|
-
if (ressource.isJsModule) {
|
|
42
|
-
return false
|
|
43
|
-
}
|
|
44
|
-
// service worker MUST be at the root (same level than the HTML file)
|
|
45
|
-
// otherwise it might be registered for the scope "/assets/" instead of "/"
|
|
46
|
-
if (ressource.isServiceWorker) {
|
|
47
|
-
return false
|
|
48
|
-
}
|
|
49
|
-
return true
|
|
50
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { resolveUrl, urlToRelativeUrl } from "@jsenv/filesystem"
|
|
2
|
-
|
|
3
|
-
import { setJavaScriptSourceMappingUrl } from "@jsenv/core/src/internal/sourceMappingURLUtils.js"
|
|
4
|
-
|
|
5
|
-
export const injectSourcemapInRollupBuild = (
|
|
6
|
-
rollupBuild,
|
|
7
|
-
{ buildDirectoryUrl },
|
|
8
|
-
) => {
|
|
9
|
-
const rollupBuildWithSourcemap = {}
|
|
10
|
-
|
|
11
|
-
Object.keys(rollupBuild).forEach((buildRelativeUrl) => {
|
|
12
|
-
const rollupFileInfo = rollupBuild[buildRelativeUrl]
|
|
13
|
-
const { type, code, map } = rollupFileInfo
|
|
14
|
-
|
|
15
|
-
if (type === "asset" || !map) {
|
|
16
|
-
rollupBuildWithSourcemap[buildRelativeUrl] = rollupFileInfo
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const sourcemapBuildRelativeUrl = `${buildRelativeUrl}.map`
|
|
21
|
-
const sourcemapRollupFileInfo = rollupBuild[sourcemapBuildRelativeUrl]
|
|
22
|
-
if (sourcemapRollupFileInfo) {
|
|
23
|
-
rollupBuildWithSourcemap[buildRelativeUrl] = rollupFileInfo
|
|
24
|
-
return
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const fileBuildUrl = resolveUrl(buildRelativeUrl, buildDirectoryUrl)
|
|
28
|
-
const sourcemapBuildUrl = resolveUrl(
|
|
29
|
-
sourcemapBuildRelativeUrl,
|
|
30
|
-
buildDirectoryUrl,
|
|
31
|
-
)
|
|
32
|
-
const fileSourcemapString = JSON.stringify(map, null, " ")
|
|
33
|
-
const sourcemapBuildUrlRelativeToFileBuildUrl = urlToRelativeUrl(
|
|
34
|
-
sourcemapBuildUrl,
|
|
35
|
-
fileBuildUrl,
|
|
36
|
-
)
|
|
37
|
-
const codeWithSourcemapComment = setJavaScriptSourceMappingUrl(
|
|
38
|
-
code,
|
|
39
|
-
sourcemapBuildUrlRelativeToFileBuildUrl,
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
rollupBuildWithSourcemap[sourcemapBuildRelativeUrl] = {
|
|
43
|
-
type: "asset",
|
|
44
|
-
fileName: sourcemapBuildRelativeUrl,
|
|
45
|
-
source: fileSourcemapString,
|
|
46
|
-
}
|
|
47
|
-
rollupBuildWithSourcemap[buildRelativeUrl] = {
|
|
48
|
-
...rollupFileInfo,
|
|
49
|
-
code: codeWithSourcemapComment,
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
return rollupBuildWithSourcemap
|
|
54
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { createHash } from "crypto"
|
|
2
|
-
import {
|
|
3
|
-
urlToParentUrl,
|
|
4
|
-
urlToBasename,
|
|
5
|
-
urlToExtension,
|
|
6
|
-
} from "@jsenv/filesystem"
|
|
7
|
-
import { renderNamePattern } from "../renderNamePattern.js"
|
|
8
|
-
|
|
9
|
-
export const computeBuildRelativeUrl = (
|
|
10
|
-
fileUrl,
|
|
11
|
-
fileContent,
|
|
12
|
-
{
|
|
13
|
-
pattern = "[name]-[hash][extname]",
|
|
14
|
-
contentType = "application/octet-stream",
|
|
15
|
-
lineBreakNormalization = false,
|
|
16
|
-
} = {},
|
|
17
|
-
) => {
|
|
18
|
-
const buildRelativeUrl = renderNamePattern(
|
|
19
|
-
typeof pattern === "function" ? pattern() : pattern,
|
|
20
|
-
{
|
|
21
|
-
dirname: () => urlToParentUrl(fileUrl),
|
|
22
|
-
name: () => urlToBasename(fileUrl),
|
|
23
|
-
hash: () =>
|
|
24
|
-
generateContentHash(fileContent, {
|
|
25
|
-
contentType,
|
|
26
|
-
lineBreakNormalization,
|
|
27
|
-
}),
|
|
28
|
-
extname: () => urlToExtension(fileUrl),
|
|
29
|
-
},
|
|
30
|
-
)
|
|
31
|
-
return buildRelativeUrl
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// https://github.com/rollup/rollup/blob/19e50af3099c2f627451a45a84e2fa90d20246d5/src/utils/FileEmitter.ts#L47
|
|
35
|
-
export const generateContentHash = (
|
|
36
|
-
stringOrBuffer,
|
|
37
|
-
{
|
|
38
|
-
contentType = "application/octet-stream",
|
|
39
|
-
lineBreakNormalization = false,
|
|
40
|
-
} = {},
|
|
41
|
-
) => {
|
|
42
|
-
const hash = createHash("sha256")
|
|
43
|
-
hash.update(
|
|
44
|
-
lineBreakNormalization && contentTypeIsTextual(contentType)
|
|
45
|
-
? normalizeLineBreaks(stringOrBuffer)
|
|
46
|
-
: stringOrBuffer,
|
|
47
|
-
)
|
|
48
|
-
return hash.digest("hex").slice(0, 8)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const contentTypeIsTextual = (contentType) => {
|
|
52
|
-
if (contentType.startsWith("text/")) {
|
|
53
|
-
return true
|
|
54
|
-
}
|
|
55
|
-
// catch things like application/manifest+json, application/importmap+json
|
|
56
|
-
if (/^application\/\w+\+json$/.test(contentType)) {
|
|
57
|
-
return true
|
|
58
|
-
}
|
|
59
|
-
if (CONTENT_TYPE_AS_TEXT.includes(contentType)) {
|
|
60
|
-
return true
|
|
61
|
-
}
|
|
62
|
-
return false
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const CONTENT_TYPE_AS_TEXT = [
|
|
66
|
-
"application/javascript",
|
|
67
|
-
"application/json",
|
|
68
|
-
"image/svg+xml",
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
const normalizeLineBreaks = (stringOrBuffer) => {
|
|
72
|
-
if (typeof stringOrBuffer === "string") {
|
|
73
|
-
const stringWithLinuxBreaks = stringOrBuffer.replace(/\r\n/g, "\n")
|
|
74
|
-
return stringWithLinuxBreaks
|
|
75
|
-
}
|
|
76
|
-
return normalizeLineBreaksForBuffer(stringOrBuffer)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// https://github.com/nodejs/help/issues/1738#issuecomment-458460503
|
|
80
|
-
const normalizeLineBreaksForBuffer = (buffer) => {
|
|
81
|
-
const int32Array = new Int32Array(buffer, 0, buffer.length)
|
|
82
|
-
const int32ArrayWithLineBreaksNormalized = int32Array.filter(
|
|
83
|
-
(element, index, typedArray) => {
|
|
84
|
-
if (element === 0x0d) {
|
|
85
|
-
if (typedArray[index + 1] === 0x0a) {
|
|
86
|
-
// Windows -> Unix
|
|
87
|
-
return false
|
|
88
|
-
}
|
|
89
|
-
// Mac OS -> Unix
|
|
90
|
-
typedArray[index] = 0x0a
|
|
91
|
-
}
|
|
92
|
-
return true
|
|
93
|
-
},
|
|
94
|
-
)
|
|
95
|
-
return Buffer.from(int32ArrayWithLineBreaksNormalized)
|
|
96
|
-
}
|