@jsenv/plugin-bundling 1.0.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/package.json +42 -0
- package/src/css/bundle_css.js +140 -0
- package/src/css/sort_by_dependencies.js +28 -0
- package/src/js_classic_workers/bundle_js_classic_workers.js +13 -0
- package/src/js_module/bundle_js_modules.js +386 -0
- package/src/js_module/file_url_converter.js +21 -0
- package/src/jsenv_plugin_bundling.js +43 -0
- package/src/main.js +2 -0
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jsenv/plugin-bundling",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/jsenv/jsenv-core",
|
|
8
|
+
"directory": "packages/jsenv-plugin-bundling"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./src/main.js"
|
|
17
|
+
},
|
|
18
|
+
"./*": "./*"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"/src/"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "node --conditions=development ./scripts/test.mjs"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@jsenv/ast": "1.4.8",
|
|
28
|
+
"@jsenv/babel-plugins": "1.1.0",
|
|
29
|
+
"@jsenv/log": "3.3.2",
|
|
30
|
+
"@jsenv/url-meta": "7.0.0",
|
|
31
|
+
"@jsenv/urls": "1.2.8",
|
|
32
|
+
"@jsenv/sourcemap": "1.0.8",
|
|
33
|
+
"acorn-import-assertions": "1.8.0",
|
|
34
|
+
"rollup": "3.7.4"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@jsenv/core": "*"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@jsenv/core": "../../"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Each @import found in css is replaced by the file content
|
|
3
|
+
* - There is no need to worry about urls (such as background-image: url())
|
|
4
|
+
* because they are absolute (file://*) and will be made relative again by jsenv build
|
|
5
|
+
* - The sourcemap are not generated but ideally they should be
|
|
6
|
+
* It can be quite challenging, see "bundle_sourcemap.js"
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createMagicSource } from "@jsenv/sourcemap"
|
|
10
|
+
import { applyPostCss, postCssPluginUrlVisitor } from "@jsenv/ast"
|
|
11
|
+
|
|
12
|
+
import { sortByDependencies } from "./sort_by_dependencies.js"
|
|
13
|
+
|
|
14
|
+
// Do not use until https://github.com/parcel-bundler/parcel-css/issues/181
|
|
15
|
+
export const bundleCss = async ({ cssUrlInfos, context }) => {
|
|
16
|
+
const bundledCssUrlInfos = {}
|
|
17
|
+
const cssBundleInfos = await performCssBundling({
|
|
18
|
+
cssEntryUrlInfos: cssUrlInfos,
|
|
19
|
+
context,
|
|
20
|
+
})
|
|
21
|
+
cssUrlInfos.forEach((cssUrlInfo) => {
|
|
22
|
+
bundledCssUrlInfos[cssUrlInfo.url] = {
|
|
23
|
+
data: {
|
|
24
|
+
bundlerName: "parcel",
|
|
25
|
+
},
|
|
26
|
+
contentType: "text/css",
|
|
27
|
+
content: cssBundleInfos[cssUrlInfo.url].bundleContent,
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
return bundledCssUrlInfos
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const performCssBundling = async ({ cssEntryUrlInfos, context }) => {
|
|
34
|
+
const cssBundleInfos = await loadCssUrls({
|
|
35
|
+
cssEntryUrlInfos,
|
|
36
|
+
context,
|
|
37
|
+
})
|
|
38
|
+
const cssUrlsSorted = sortByDependencies(cssBundleInfos)
|
|
39
|
+
cssUrlsSorted.forEach((cssUrl) => {
|
|
40
|
+
const cssBundleInfo = cssBundleInfos[cssUrl]
|
|
41
|
+
const magicSource = createMagicSource(cssBundleInfo.content)
|
|
42
|
+
cssBundleInfo.cssUrls.forEach((cssUrl) => {
|
|
43
|
+
if (cssUrl.type === "@import") {
|
|
44
|
+
magicSource.replace({
|
|
45
|
+
start: cssUrl.atRuleStart,
|
|
46
|
+
end: cssUrl.atRuleEnd,
|
|
47
|
+
replacement: cssBundleInfos[cssUrl.url].bundleContent,
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
const { content } = magicSource.toContentAndSourcemap()
|
|
52
|
+
cssBundleInfo.bundleContent = content.trim()
|
|
53
|
+
})
|
|
54
|
+
return cssBundleInfos
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const parseCssUrls = async ({ css, url }) => {
|
|
58
|
+
const cssUrls = []
|
|
59
|
+
await applyPostCss({
|
|
60
|
+
sourcemaps: false,
|
|
61
|
+
plugins: [
|
|
62
|
+
postCssPluginUrlVisitor({
|
|
63
|
+
urlVisitor: ({
|
|
64
|
+
type,
|
|
65
|
+
specifier,
|
|
66
|
+
specifierStart,
|
|
67
|
+
specifierEnd,
|
|
68
|
+
atRuleStart,
|
|
69
|
+
atRuleEnd,
|
|
70
|
+
}) => {
|
|
71
|
+
cssUrls.push({
|
|
72
|
+
type,
|
|
73
|
+
url: new URL(specifier, url).href,
|
|
74
|
+
specifierStart,
|
|
75
|
+
specifierEnd,
|
|
76
|
+
atRuleStart,
|
|
77
|
+
atRuleEnd,
|
|
78
|
+
})
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
],
|
|
82
|
+
url,
|
|
83
|
+
content: css,
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
return cssUrls
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const loadCssUrls = async ({ cssEntryUrlInfos, context }) => {
|
|
90
|
+
const cssBundleInfos = {}
|
|
91
|
+
const promises = []
|
|
92
|
+
const promiseMap = new Map()
|
|
93
|
+
|
|
94
|
+
const load = (cssUrlInfo) => {
|
|
95
|
+
const promiseFromData = promiseMap.get(cssUrlInfo.url)
|
|
96
|
+
if (promiseFromData) return promiseFromData
|
|
97
|
+
const promise = _load(cssUrlInfo)
|
|
98
|
+
promises.push(promise)
|
|
99
|
+
promiseMap.set(cssUrlInfo.url, promise)
|
|
100
|
+
return promise
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const _load = async (cssUrlInfo) => {
|
|
104
|
+
const cssUrls = await parseCssUrls({
|
|
105
|
+
css: cssUrlInfo.content,
|
|
106
|
+
url: cssUrlInfo.url,
|
|
107
|
+
})
|
|
108
|
+
const cssBundleInfo = {
|
|
109
|
+
content: cssUrlInfo.content,
|
|
110
|
+
cssUrls,
|
|
111
|
+
dependencies: [],
|
|
112
|
+
}
|
|
113
|
+
cssBundleInfos[cssUrlInfo.url] = cssBundleInfo
|
|
114
|
+
cssUrls.forEach((cssUrl) => {
|
|
115
|
+
if (cssUrl.type === "@import") {
|
|
116
|
+
cssBundleInfo.dependencies.push(cssUrl.url)
|
|
117
|
+
const importedCssUrlInfo = context.urlGraph.getUrlInfo(cssUrl.url)
|
|
118
|
+
load(importedCssUrlInfo)
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
cssEntryUrlInfos.forEach((cssEntryUrlInfo) => {
|
|
124
|
+
load(cssEntryUrlInfo)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const waitAll = async () => {
|
|
128
|
+
if (promises.length === 0) {
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
const promisesToWait = promises.slice()
|
|
132
|
+
promises.length = 0
|
|
133
|
+
await Promise.all(promisesToWait)
|
|
134
|
+
await waitAll()
|
|
135
|
+
}
|
|
136
|
+
await waitAll()
|
|
137
|
+
promiseMap.clear()
|
|
138
|
+
|
|
139
|
+
return cssBundleInfos
|
|
140
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const sortByDependencies = (nodes) => {
|
|
2
|
+
const visited = []
|
|
3
|
+
const sorted = []
|
|
4
|
+
const circular = []
|
|
5
|
+
const visit = (url) => {
|
|
6
|
+
const isSorted = sorted.includes(url)
|
|
7
|
+
if (isSorted) {
|
|
8
|
+
return
|
|
9
|
+
}
|
|
10
|
+
const isVisited = visited.includes(url)
|
|
11
|
+
if (isVisited) {
|
|
12
|
+
if (!circular.includes(url)) {
|
|
13
|
+
circular.push(url)
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
visited.push(url)
|
|
17
|
+
nodes[url].dependencies.forEach((dependencyUrl) => {
|
|
18
|
+
visit(dependencyUrl, url)
|
|
19
|
+
})
|
|
20
|
+
sorted.push(url)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
Object.keys(nodes).forEach((url) => {
|
|
24
|
+
visit(url)
|
|
25
|
+
})
|
|
26
|
+
sorted.circular = circular
|
|
27
|
+
return sorted
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* TODO:
|
|
3
|
+
* for each js_classic where subtype is a worker
|
|
4
|
+
* take the url info and find importScripts calls
|
|
5
|
+
* and replace them with the corresponding url info file content
|
|
6
|
+
* we'll ikely need to save the importScripts node location to be able to do that
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// import { createMagicSource } from "@jsenv/utils/sourcemap/magic_source.js"
|
|
10
|
+
|
|
11
|
+
export const bundleJsClassicWorkers = () => {
|
|
12
|
+
return {}
|
|
13
|
+
}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url"
|
|
2
|
+
|
|
3
|
+
import { URL_META } from "@jsenv/url-meta"
|
|
4
|
+
import { isFileSystemPath } from "@jsenv/urls"
|
|
5
|
+
import { createDetailedMessage } from "@jsenv/log"
|
|
6
|
+
import { babelHelperNameFromUrl } from "@jsenv/babel-plugins"
|
|
7
|
+
import { sourcemapConverter } from "@jsenv/sourcemap"
|
|
8
|
+
|
|
9
|
+
import { fileUrlConverter } from "./file_url_converter.js"
|
|
10
|
+
|
|
11
|
+
export const bundleJsModules = async ({
|
|
12
|
+
jsModuleUrlInfos,
|
|
13
|
+
context,
|
|
14
|
+
options,
|
|
15
|
+
}) => {
|
|
16
|
+
const {
|
|
17
|
+
signal,
|
|
18
|
+
logger,
|
|
19
|
+
rootDirectoryUrl,
|
|
20
|
+
buildDirectoryUrl,
|
|
21
|
+
assetsDirectory,
|
|
22
|
+
urlGraph,
|
|
23
|
+
runtimeCompat,
|
|
24
|
+
sourcemaps,
|
|
25
|
+
} = context
|
|
26
|
+
const {
|
|
27
|
+
babelHelpersChunk = true,
|
|
28
|
+
include,
|
|
29
|
+
preserveDynamicImport = false,
|
|
30
|
+
strictExports = false,
|
|
31
|
+
rollup,
|
|
32
|
+
rollupInput = {},
|
|
33
|
+
rollupOutput = {},
|
|
34
|
+
rollupPlugins = [],
|
|
35
|
+
} = options
|
|
36
|
+
|
|
37
|
+
const resultRef = { current: null }
|
|
38
|
+
try {
|
|
39
|
+
await applyRollupPlugins({
|
|
40
|
+
rollup,
|
|
41
|
+
rollupPlugins: [
|
|
42
|
+
...rollupPlugins,
|
|
43
|
+
rollupPluginJsenv({
|
|
44
|
+
signal,
|
|
45
|
+
logger,
|
|
46
|
+
rootDirectoryUrl,
|
|
47
|
+
buildDirectoryUrl,
|
|
48
|
+
assetsDirectory,
|
|
49
|
+
urlGraph,
|
|
50
|
+
jsModuleUrlInfos,
|
|
51
|
+
|
|
52
|
+
runtimeCompat,
|
|
53
|
+
sourcemaps,
|
|
54
|
+
include,
|
|
55
|
+
babelHelpersChunk,
|
|
56
|
+
preserveDynamicImport,
|
|
57
|
+
strictExports,
|
|
58
|
+
resultRef,
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
rollupInput: {
|
|
62
|
+
input: [],
|
|
63
|
+
onwarn: (warning) => {
|
|
64
|
+
if (warning.code === "CIRCULAR_DEPENDENCY") {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
if (warning.code === "THIS_IS_UNDEFINED") {
|
|
68
|
+
const urlInfo = urlGraph.getUrlInfo(pathToFileURL(warning.id).href)
|
|
69
|
+
if (urlInfo && urlInfo.data.isBabelClientFile) {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (warning.code === "EVAL") {
|
|
74
|
+
// ideally we should disable only for jsenv files
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
logger.warn(String(warning))
|
|
78
|
+
},
|
|
79
|
+
...rollupInput,
|
|
80
|
+
},
|
|
81
|
+
rollupOutput: {
|
|
82
|
+
...rollupOutput,
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
return resultRef.current.jsModuleBundleUrlInfos
|
|
86
|
+
} catch (e) {
|
|
87
|
+
if (e.code === "MISSING_EXPORT") {
|
|
88
|
+
const detailedMessage = createDetailedMessage(e.message, {
|
|
89
|
+
frame: e.frame,
|
|
90
|
+
})
|
|
91
|
+
throw new Error(detailedMessage, { cause: e })
|
|
92
|
+
}
|
|
93
|
+
throw e
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const rollupPluginJsenv = ({
|
|
98
|
+
// logger,
|
|
99
|
+
rootDirectoryUrl,
|
|
100
|
+
buildDirectoryUrl,
|
|
101
|
+
assetsDirectory,
|
|
102
|
+
urlGraph,
|
|
103
|
+
jsModuleUrlInfos,
|
|
104
|
+
sourcemaps,
|
|
105
|
+
|
|
106
|
+
include,
|
|
107
|
+
babelHelpersChunk,
|
|
108
|
+
preserveDynamicImport,
|
|
109
|
+
strictExports,
|
|
110
|
+
|
|
111
|
+
resultRef,
|
|
112
|
+
}) => {
|
|
113
|
+
let _rollupEmitFile = () => {
|
|
114
|
+
throw new Error("not implemented")
|
|
115
|
+
}
|
|
116
|
+
const format = jsModuleUrlInfos.some((jsModuleUrlInfo) =>
|
|
117
|
+
jsModuleUrlInfo.filename.endsWith(".cjs"),
|
|
118
|
+
)
|
|
119
|
+
? "cjs"
|
|
120
|
+
: "esm"
|
|
121
|
+
const emitChunk = (chunk) => {
|
|
122
|
+
return _rollupEmitFile({
|
|
123
|
+
type: "chunk",
|
|
124
|
+
...chunk,
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
let importCanBeBundled = () => true
|
|
128
|
+
if (include) {
|
|
129
|
+
const associations = URL_META.resolveAssociations(
|
|
130
|
+
{ bundle: include },
|
|
131
|
+
rootDirectoryUrl,
|
|
132
|
+
)
|
|
133
|
+
importCanBeBundled = (url) => {
|
|
134
|
+
return URL_META.applyAssociations({ url, associations }).bundle
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const urlImporters = {}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
name: "jsenv",
|
|
141
|
+
async buildStart() {
|
|
142
|
+
_rollupEmitFile = (...args) => this.emitFile(...args)
|
|
143
|
+
let previousNonEntryPointModuleId
|
|
144
|
+
jsModuleUrlInfos.forEach((jsModuleUrlInfo) => {
|
|
145
|
+
const id = jsModuleUrlInfo.url
|
|
146
|
+
if (jsModuleUrlInfo.isEntryPoint) {
|
|
147
|
+
emitChunk({
|
|
148
|
+
id,
|
|
149
|
+
})
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
emitChunk({
|
|
153
|
+
id,
|
|
154
|
+
implicitlyLoadedAfterOneOf: previousNonEntryPointModuleId
|
|
155
|
+
? [previousNonEntryPointModuleId]
|
|
156
|
+
: null,
|
|
157
|
+
preserveSignature: strictExports
|
|
158
|
+
? "strict"
|
|
159
|
+
: jsModuleUrlInfo.dependents.size < 2
|
|
160
|
+
? "allow-extension"
|
|
161
|
+
: "strict",
|
|
162
|
+
})
|
|
163
|
+
previousNonEntryPointModuleId = id
|
|
164
|
+
})
|
|
165
|
+
},
|
|
166
|
+
async generateBundle(outputOptions, rollupResult) {
|
|
167
|
+
_rollupEmitFile = (...args) => this.emitFile(...args)
|
|
168
|
+
|
|
169
|
+
const jsModuleBundleUrlInfos = {}
|
|
170
|
+
Object.keys(rollupResult).forEach((fileName) => {
|
|
171
|
+
const rollupFileInfo = rollupResult[fileName]
|
|
172
|
+
// there is 3 types of file: "placeholder", "asset", "chunk"
|
|
173
|
+
if (rollupFileInfo.type === "chunk") {
|
|
174
|
+
const sourceUrls = Object.keys(rollupFileInfo.modules).map((id) =>
|
|
175
|
+
fileUrlConverter.asFileUrl(id),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
let url
|
|
179
|
+
let originalUrl
|
|
180
|
+
if (rollupFileInfo.facadeModuleId) {
|
|
181
|
+
url = fileUrlConverter.asFileUrl(rollupFileInfo.facadeModuleId)
|
|
182
|
+
originalUrl = url
|
|
183
|
+
} else {
|
|
184
|
+
url = new URL(rollupFileInfo.fileName, buildDirectoryUrl).href
|
|
185
|
+
if (rollupFileInfo.isDynamicEntry) {
|
|
186
|
+
originalUrl = sourceUrls[sourceUrls.length - 1]
|
|
187
|
+
} else {
|
|
188
|
+
originalUrl = url
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const jsModuleBundleUrlInfo = {
|
|
193
|
+
url,
|
|
194
|
+
originalUrl,
|
|
195
|
+
type: format === "esm" ? "js_module" : "common_js",
|
|
196
|
+
data: {
|
|
197
|
+
bundlerName: "rollup",
|
|
198
|
+
bundleRelativeUrl: rollupFileInfo.fileName,
|
|
199
|
+
usesImport:
|
|
200
|
+
rollupFileInfo.imports.length > 0 ||
|
|
201
|
+
rollupFileInfo.dynamicImports.length > 0,
|
|
202
|
+
isDynamicEntry: rollupFileInfo.isDynamicEntry,
|
|
203
|
+
},
|
|
204
|
+
sourceUrls,
|
|
205
|
+
contentType: "text/javascript",
|
|
206
|
+
content: rollupFileInfo.code,
|
|
207
|
+
sourcemap: rollupFileInfo.map,
|
|
208
|
+
}
|
|
209
|
+
jsModuleBundleUrlInfos[url] = jsModuleBundleUrlInfo
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
resultRef.current = {
|
|
213
|
+
jsModuleBundleUrlInfos,
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
outputOptions: (outputOptions) => {
|
|
217
|
+
// const sourcemapFile = buildDirectoryUrl
|
|
218
|
+
Object.assign(outputOptions, {
|
|
219
|
+
format,
|
|
220
|
+
dir: fileUrlConverter.asFilePath(buildDirectoryUrl),
|
|
221
|
+
sourcemap: sourcemaps === "file" || sourcemaps === "inline",
|
|
222
|
+
// sourcemapFile,
|
|
223
|
+
sourcemapPathTransform: (relativePath) => {
|
|
224
|
+
return new URL(relativePath, buildDirectoryUrl).href
|
|
225
|
+
},
|
|
226
|
+
entryFileNames: () => {
|
|
227
|
+
return `[name].js`
|
|
228
|
+
},
|
|
229
|
+
chunkFileNames: (chunkInfo) => {
|
|
230
|
+
const insideJs = willBeInsideJsDirectory({
|
|
231
|
+
chunkInfo,
|
|
232
|
+
fileUrlConverter,
|
|
233
|
+
jsModuleUrlInfos,
|
|
234
|
+
})
|
|
235
|
+
let nameFromUrlInfo
|
|
236
|
+
if (chunkInfo.facadeModuleId) {
|
|
237
|
+
const url = fileUrlConverter.asFileUrl(chunkInfo.facadeModuleId)
|
|
238
|
+
const urlInfo = jsModuleUrlInfos.find(
|
|
239
|
+
(jsModuleUrlInfo) => jsModuleUrlInfo.url === url,
|
|
240
|
+
)
|
|
241
|
+
if (urlInfo) {
|
|
242
|
+
nameFromUrlInfo = urlInfo.filename
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const name = nameFromUrlInfo || `${chunkInfo.name}.js`
|
|
246
|
+
return insideJs ? `${assetsDirectory}js/${name}` : `${name}`
|
|
247
|
+
},
|
|
248
|
+
manualChunks: (id) => {
|
|
249
|
+
if (babelHelpersChunk) {
|
|
250
|
+
const fileUrl = fileUrlConverter.asFileUrl(id)
|
|
251
|
+
if (
|
|
252
|
+
fileUrl.endsWith(
|
|
253
|
+
"babel-plugin-transform-async-to-promises/helpers.mjs",
|
|
254
|
+
)
|
|
255
|
+
) {
|
|
256
|
+
return "babel_helpers"
|
|
257
|
+
}
|
|
258
|
+
if (babelHelperNameFromUrl(fileUrl)) {
|
|
259
|
+
return "babel_helpers"
|
|
260
|
+
}
|
|
261
|
+
const urlInfo = urlGraph.getUrlInfo(fileUrl)
|
|
262
|
+
if (urlInfo && urlInfo.data.isBabelClientFile) {
|
|
263
|
+
return "babel_helpers"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return null
|
|
267
|
+
},
|
|
268
|
+
// https://rollupjs.org/guide/en/#outputpaths
|
|
269
|
+
// paths: (id) => {
|
|
270
|
+
// return id
|
|
271
|
+
// },
|
|
272
|
+
})
|
|
273
|
+
},
|
|
274
|
+
// https://rollupjs.org/guide/en/#resolvedynamicimport
|
|
275
|
+
resolveDynamicImport: (specifier, importer) => {
|
|
276
|
+
if (preserveDynamicImport) {
|
|
277
|
+
let urlObject
|
|
278
|
+
if (specifier[0] === "/") {
|
|
279
|
+
urlObject = new URL(specifier.slice(1), rootDirectoryUrl)
|
|
280
|
+
} else {
|
|
281
|
+
if (isFileSystemPath(importer)) {
|
|
282
|
+
importer = fileUrlConverter.asFileUrl(importer)
|
|
283
|
+
}
|
|
284
|
+
urlObject = new URL(specifier, importer)
|
|
285
|
+
}
|
|
286
|
+
urlObject.searchParams.set("as_js_classic_library", "")
|
|
287
|
+
return { external: true, id: urlObject.href }
|
|
288
|
+
}
|
|
289
|
+
return null
|
|
290
|
+
},
|
|
291
|
+
resolveId: (specifier, importer = rootDirectoryUrl) => {
|
|
292
|
+
if (isFileSystemPath(importer)) {
|
|
293
|
+
importer = fileUrlConverter.asFileUrl(importer)
|
|
294
|
+
}
|
|
295
|
+
let url
|
|
296
|
+
if (specifier[0] === "/") {
|
|
297
|
+
url = new URL(specifier.slice(1), rootDirectoryUrl).href
|
|
298
|
+
} else {
|
|
299
|
+
url = new URL(specifier, importer).href
|
|
300
|
+
}
|
|
301
|
+
const existingImporter = urlImporters[url]
|
|
302
|
+
if (!existingImporter) {
|
|
303
|
+
urlImporters[url] = importer
|
|
304
|
+
}
|
|
305
|
+
if (!url.startsWith("file:")) {
|
|
306
|
+
return { id: url, external: true }
|
|
307
|
+
}
|
|
308
|
+
if (!importCanBeBundled(url)) {
|
|
309
|
+
return { id: url, external: true }
|
|
310
|
+
}
|
|
311
|
+
const urlInfo = urlGraph.getUrlInfo(url)
|
|
312
|
+
if (!urlInfo) {
|
|
313
|
+
// happen when excluded by urlAnalysis.include
|
|
314
|
+
return { id: url, external: true }
|
|
315
|
+
}
|
|
316
|
+
if (!urlInfo.shouldHandle) {
|
|
317
|
+
return { id: url, external: true }
|
|
318
|
+
}
|
|
319
|
+
const filePath = fileUrlConverter.asFilePath(url)
|
|
320
|
+
return filePath
|
|
321
|
+
},
|
|
322
|
+
async load(rollupId) {
|
|
323
|
+
const fileUrl = fileUrlConverter.asFileUrl(rollupId)
|
|
324
|
+
const urlInfo = urlGraph.getUrlInfo(fileUrl)
|
|
325
|
+
return {
|
|
326
|
+
code: urlInfo.content,
|
|
327
|
+
map:
|
|
328
|
+
(sourcemaps === "file" || sourcemaps === "inline") &&
|
|
329
|
+
urlInfo.sourcemap
|
|
330
|
+
? sourcemapConverter.toFilePaths(urlInfo.sourcemap)
|
|
331
|
+
: null,
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const applyRollupPlugins = async ({
|
|
338
|
+
rollup,
|
|
339
|
+
rollupPlugins,
|
|
340
|
+
rollupInput,
|
|
341
|
+
rollupOutput,
|
|
342
|
+
}) => {
|
|
343
|
+
if (!rollup) {
|
|
344
|
+
const rollupModule = await import("rollup")
|
|
345
|
+
rollup = rollupModule.rollup
|
|
346
|
+
}
|
|
347
|
+
const { importAssertions } = await import("acorn-import-assertions")
|
|
348
|
+
const rollupReturnValue = await rollup({
|
|
349
|
+
...rollupInput,
|
|
350
|
+
plugins: rollupPlugins,
|
|
351
|
+
acornInjectPlugins: [
|
|
352
|
+
importAssertions,
|
|
353
|
+
...(rollupInput.acornInjectPlugins || []),
|
|
354
|
+
],
|
|
355
|
+
})
|
|
356
|
+
const rollupOutputArray = await rollupReturnValue.generate(rollupOutput)
|
|
357
|
+
return rollupOutputArray
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const willBeInsideJsDirectory = ({
|
|
361
|
+
chunkInfo,
|
|
362
|
+
fileUrlConverter,
|
|
363
|
+
jsModuleUrlInfos,
|
|
364
|
+
}) => {
|
|
365
|
+
// if the chunk is generated dynamically by rollup
|
|
366
|
+
// for an entry point jsenv will put that file inside js/ directory
|
|
367
|
+
// if it's generated dynamically for a file already in js/ directory
|
|
368
|
+
// both will be inside the js/ directory
|
|
369
|
+
if (!chunkInfo.facadeModuleId) {
|
|
370
|
+
// generated by rollup
|
|
371
|
+
return true
|
|
372
|
+
}
|
|
373
|
+
const url = fileUrlConverter.asFileUrl(chunkInfo.facadeModuleId)
|
|
374
|
+
const jsModuleUrlInfo = jsModuleUrlInfos.find(
|
|
375
|
+
(jsModuleUrlInfo) => jsModuleUrlInfo.url === url,
|
|
376
|
+
)
|
|
377
|
+
if (!jsModuleUrlInfo) {
|
|
378
|
+
// generated by rollup
|
|
379
|
+
return true
|
|
380
|
+
}
|
|
381
|
+
if (!jsModuleUrlInfo.isEntryPoint) {
|
|
382
|
+
// not an entry point, jsenv will put it inside js/ directory
|
|
383
|
+
return true
|
|
384
|
+
}
|
|
385
|
+
return false
|
|
386
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { fileSystemPathToUrl, urlToFileSystemPath } from "@jsenv/urls"
|
|
2
|
+
|
|
3
|
+
export const fileUrlConverter = {
|
|
4
|
+
asFilePath: (fileUrl) => {
|
|
5
|
+
const filePath = urlToFileSystemPath(fileUrl)
|
|
6
|
+
const urlObject = new URL(fileUrl)
|
|
7
|
+
const { searchParams } = urlObject
|
|
8
|
+
return `${filePath}${stringifyQuery(searchParams)}`
|
|
9
|
+
},
|
|
10
|
+
asFileUrl: (filePath) => {
|
|
11
|
+
return decodeURIComponent(fileSystemPathToUrl(filePath)).replace(
|
|
12
|
+
/[=](?=&|$)/g,
|
|
13
|
+
"",
|
|
14
|
+
)
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const stringifyQuery = (searchParams) => {
|
|
19
|
+
const search = searchParams.toString()
|
|
20
|
+
return search ? `?${search}` : ""
|
|
21
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { bundleCss } from "./css/bundle_css.js"
|
|
2
|
+
import { bundleJsClassicWorkers } from "./js_classic_workers/bundle_js_classic_workers.js"
|
|
3
|
+
import { bundleJsModules } from "./js_module/bundle_js_modules.js"
|
|
4
|
+
|
|
5
|
+
export const jsenvPluginBundling = ({
|
|
6
|
+
css = {},
|
|
7
|
+
js_classic = {},
|
|
8
|
+
js_module = {},
|
|
9
|
+
} = {}) => {
|
|
10
|
+
return {
|
|
11
|
+
name: "jsenv:bundling",
|
|
12
|
+
appliesDuring: "build",
|
|
13
|
+
bundle: {
|
|
14
|
+
css: css
|
|
15
|
+
? (cssUrlInfos, context) => {
|
|
16
|
+
return bundleCss({
|
|
17
|
+
cssUrlInfos,
|
|
18
|
+
context,
|
|
19
|
+
options: css === true ? {} : css,
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
: undefined,
|
|
23
|
+
js_classic: js_classic
|
|
24
|
+
? (jsClassicUrlInfos, context) => {
|
|
25
|
+
return bundleJsClassicWorkers({
|
|
26
|
+
jsClassicUrlInfos,
|
|
27
|
+
context,
|
|
28
|
+
options: js_classic === true ? {} : js_classic,
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
: undefined,
|
|
32
|
+
js_module: js_module
|
|
33
|
+
? (jsModuleUrlInfos, context) => {
|
|
34
|
+
return bundleJsModules({
|
|
35
|
+
jsModuleUrlInfos,
|
|
36
|
+
context,
|
|
37
|
+
options: js_module === true ? {} : js_module,
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
: undefined,
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/main.js
ADDED