1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | import { readFileSync } from "fs"
|
29 | import { dirname, resolve } from "path"
|
30 | import { pathnameIsInside, pathnameToRelativePath, hrefToScheme } from "@jsenv/href"
|
31 | import {
|
32 | pathnameToOperatingSystemPath,
|
33 | operatingSystemPathToPathname,
|
34 | } from "@jsenv/operating-system-path"
|
35 | import { writeOrUpdateSourceMappingURL } from "./helpers/source-mapping-url.js"
|
36 |
|
37 | export const bundleToCompilationResult = (
|
38 | { rollupBundle, arrayOfAbstractHref },
|
39 | { projectPathname, entryRelativePath, sourcemapAssetPath, sourcemapPath = sourcemapAssetPath },
|
40 | ) => {
|
41 | const sources = []
|
42 | const sourcesContent = []
|
43 | const assets = []
|
44 | const assetsContent = []
|
45 |
|
46 | const output = rollupBundle.output
|
47 | const main = output[0]
|
48 | const mainRollupSourcemap = main.map
|
49 |
|
50 | const mainDependencyMap = rollupSourcemapToDependencyMap({
|
51 | rollupSourcemap: mainRollupSourcemap,
|
52 | projectPathname,
|
53 | entryRelativePath,
|
54 | arrayOfAbstractHref,
|
55 | })
|
56 |
|
57 | const addDependencyMap = (dependencyMap) => {
|
58 | Object.keys(dependencyMap).forEach((relativePath) => {
|
59 | if (arrayOfAbstractHref.includes(`file://${projectPathname}${relativePath}`)) return
|
60 | if (sources.includes(relativePath)) return
|
61 |
|
62 | sources.push(relativePath)
|
63 | sourcesContent.push(dependencyMap[relativePath])
|
64 | })
|
65 | }
|
66 |
|
67 |
|
68 | const mainSourceContent = writeOrUpdateSourceMappingURL(main.code, sourcemapPath)
|
69 |
|
70 | assets.push(sourcemapAssetPath.slice(2))
|
71 | const mainSourcemap = {
|
72 | ...mainRollupSourcemap,
|
73 | ...dependencyMapToSourcemapSubset(mainDependencyMap),
|
74 | }
|
75 | assetsContent.push(JSON.stringify(mainSourcemap, null, " "))
|
76 |
|
77 | addDependencyMap(mainDependencyMap)
|
78 |
|
79 | output.slice(1).forEach((chunk) => {
|
80 | const chunkRollupSourcemap = chunk.map
|
81 | const chunkDependencyMap = rollupSourcemapToDependencyMap({
|
82 | rollupSourcemap: chunkRollupSourcemap,
|
83 | projectPathname,
|
84 | entryRelativePath,
|
85 | arrayOfAbstractHref,
|
86 | })
|
87 |
|
88 |
|
89 | assets.push(chunk.fileName)
|
90 | const chunkSourceContent = writeOrUpdateSourceMappingURL(chunk.code, `./${chunk.fileName}.map`)
|
91 | assetsContent.push(chunkSourceContent)
|
92 |
|
93 | assets.push(`${chunk.fileName}.map`)
|
94 | const chunkSourcemap = {
|
95 | ...chunkRollupSourcemap,
|
96 | ...dependencyMapToSourcemapSubset(chunkDependencyMap),
|
97 | }
|
98 | assetsContent.push(JSON.stringify(chunkSourcemap, null, " "))
|
99 |
|
100 | addDependencyMap(chunkDependencyMap)
|
101 | })
|
102 |
|
103 | return {
|
104 | contentType: "application/javascript",
|
105 | compiledSource: mainSourceContent,
|
106 | sources,
|
107 | sourcesContent,
|
108 | assets,
|
109 | assetsContent,
|
110 | }
|
111 | }
|
112 |
|
113 | const dependencyMapToSourcemapSubset = (dependencyMap) => {
|
114 | const sources = Object.keys(dependencyMap)
|
115 | const sourcesContent = sources.map((source) => {
|
116 |
|
117 |
|
118 | if (source.endsWith(".json")) return `export default ${dependencyMap[source]}`
|
119 | return dependencyMap[source]
|
120 | })
|
121 | return {
|
122 | sources,
|
123 | sourcesContent,
|
124 | }
|
125 | }
|
126 |
|
127 | const rollupSourcemapToDependencyMap = ({
|
128 | rollupSourcemap,
|
129 | projectPathname,
|
130 | entryRelativePath,
|
131 | arrayOfAbstractHref,
|
132 | }) => {
|
133 | const dependencyMap = {}
|
134 |
|
135 | rollupSourcemap.sources.forEach((source, index) => {
|
136 | if (hrefToScheme(source)) {
|
137 |
|
138 | return
|
139 | }
|
140 |
|
141 |
|
142 | const sourcePath = resolve(
|
143 | dirname(pathnameToOperatingSystemPath(`${projectPathname}${entryRelativePath}`)),
|
144 | source,
|
145 | )
|
146 | const sourcePathname = operatingSystemPathToPathname(sourcePath)
|
147 |
|
148 | if (!pathnameIsInside(sourcePathname, projectPathname)) {
|
149 | throw createSourceOutsideProjectError({
|
150 | sourcePath,
|
151 | projectPathname,
|
152 | source,
|
153 | file: entryRelativePath,
|
154 | })
|
155 | }
|
156 |
|
157 | const sourceRelativePath = pathnameToRelativePath(sourcePathname, projectPathname)
|
158 | const isAbstract = arrayOfAbstractHref.includes(
|
159 | `file://${projectPathname}${sourceRelativePath}`,
|
160 | )
|
161 |
|
162 | let dependencyContent
|
163 | const sourcesContent = rollupSourcemap.sourcesContent || []
|
164 | if (index in sourcesContent) {
|
165 |
|
166 |
|
167 | if (isAbstract) {
|
168 | dependencyContent = sourcesContent[index]
|
169 | }
|
170 |
|
171 |
|
172 |
|
173 |
|
174 | else if (sourceRelativePath.endsWith(".json")) {
|
175 | dependencyContent = readSourceContent({
|
176 | projectPathname,
|
177 | sourceRelativePath,
|
178 | entryRelativePath,
|
179 | })
|
180 | } else {
|
181 | dependencyContent = sourcesContent[index]
|
182 | }
|
183 | } else {
|
184 | if (isAbstract) {
|
185 | throw createAbstractSourceMissingError({ sourceRelativePath, entryRelativePath })
|
186 | }
|
187 | dependencyContent = readSourceContent({
|
188 | projectPathname,
|
189 | sourceRelativePath,
|
190 | entryRelativePath,
|
191 | })
|
192 | }
|
193 |
|
194 | dependencyMap[sourceRelativePath] = dependencyContent
|
195 | })
|
196 |
|
197 | return dependencyMap
|
198 | }
|
199 |
|
200 | const readSourceContent = ({ projectPathname, sourceRelativePath, entryRelativePath }) => {
|
201 | const sourcePath = pathnameToOperatingSystemPath(`${projectPathname}${sourceRelativePath}`)
|
202 |
|
203 |
|
204 |
|
205 | try {
|
206 | const buffer = readFileSync(sourcePath)
|
207 | return String(buffer)
|
208 | } catch (e) {
|
209 | if (e && e.code === "ENOENT") {
|
210 | throw createSourceNotFoundError({
|
211 | sourcePath,
|
212 | projectPathname,
|
213 | source: sourceRelativePath,
|
214 | file: entryRelativePath,
|
215 | })
|
216 | }
|
217 | throw e
|
218 | }
|
219 | }
|
220 |
|
221 | const createAbstractSourceMissingError = ({ source, file }) =>
|
222 | new Error(`an abstract file source content is missing.
|
223 | source: ${source}
|
224 | file: ${file}`)
|
225 |
|
226 | const createSourceNotFoundError = ({ sourcePath, projectPathname, source, file }) =>
|
227 | new Error(`a file source cannot be found.
|
228 | source path: ${sourcePath}
|
229 | project path: ${pathnameToOperatingSystemPath(projectPathname)}
|
230 | source: ${source}
|
231 | file: ${file}`)
|
232 |
|
233 | const createSourceOutsideProjectError = ({ sourcePath, projectPathname, source, file }) =>
|
234 | new Error(`a file source is not inside project.
|
235 | source path: ${sourcePath}
|
236 | project path: ${pathnameToOperatingSystemPath(projectPathname)}
|
237 | source: ${source}
|
238 | file: ${file}`)
|