vite-plugin-rebundle 1.2.16 → 1.3.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 +5 -7
- package/readme.md +8 -11
- package/src/index.ts +9 -0
- package/src/rebundle.ts +236 -0
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -6
- package/dist/rebundle.d.ts +0 -34
- package/dist/rebundle.js +0 -213
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-rebundle",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "imkost",
|
|
@@ -10,24 +10,22 @@
|
|
|
10
10
|
"vite-plugin"
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
|
-
"dev": "rm -rf dist && tsc --watch",
|
|
14
|
-
"build": "rm -rf dist && tsc",
|
|
15
13
|
"lint": "tsc --noEmit",
|
|
16
|
-
"release": "sh -c 'npm version ${1:-patch} && npm
|
|
14
|
+
"release": "sh -c 'npm version ${1:-patch} && npm publish' --"
|
|
17
15
|
},
|
|
18
16
|
"exports": {
|
|
19
|
-
"import": "./
|
|
17
|
+
"import": "./src/index.ts"
|
|
20
18
|
},
|
|
21
19
|
"files": [
|
|
22
|
-
"
|
|
20
|
+
"src"
|
|
23
21
|
],
|
|
24
22
|
"dependencies": {
|
|
25
23
|
"@eposlabs/utils": "^1.0.13",
|
|
26
24
|
"@types/ws": "^8.18.1",
|
|
27
25
|
"chalk": "^5.6.2",
|
|
28
|
-
"esbuild": "^0.25.9",
|
|
29
26
|
"filesize": "^11.0.2",
|
|
30
27
|
"portfinder": "^1.0.38",
|
|
28
|
+
"rolldown": "^1.0.0-beta.40",
|
|
31
29
|
"rollup": "^4.50.1",
|
|
32
30
|
"vite": "^7.1.5",
|
|
33
31
|
"ws": "^8.18.3"
|
package/readme.md
CHANGED
|
@@ -4,7 +4,7 @@ A Vite plugin that guarantees **one standalone file per entry point**. Each entr
|
|
|
4
4
|
|
|
5
5
|
## Why?
|
|
6
6
|
|
|
7
|
-
Sometimes you need bundles without dynamic imports. Vite/Rollup don’t provide this option when building with multiple entries. This plugin solves it by rebundling Vite’s output with
|
|
7
|
+
Sometimes you need bundles without dynamic imports. Vite/Rollup don’t provide this option when building with multiple entries. This plugin solves it by rebundling Vite’s output with rolldown to enforce single-file output.
|
|
8
8
|
|
|
9
9
|
> ⚠️ This plugin runs **only during** `vite build`. It does not affect the Vite dev server.
|
|
10
10
|
|
|
@@ -38,21 +38,18 @@ export default defineConfig({
|
|
|
38
38
|
|
|
39
39
|
## Options
|
|
40
40
|
|
|
41
|
-
You can provide **
|
|
41
|
+
You can provide **rolldown options per entry point**. This is useful, for example, to inject custom define variables into specific bundles:
|
|
42
42
|
|
|
43
43
|
```javascript
|
|
44
44
|
export default defineConfig({
|
|
45
45
|
plugins: [
|
|
46
46
|
rebundle({
|
|
47
47
|
app: {
|
|
48
|
-
define: {
|
|
49
|
-
BUNDLE_NAME: JSON.stringify('app'),
|
|
50
|
-
},
|
|
48
|
+
output: { define: { BUNDLE_NAME: JSON.stringify('app') } },
|
|
51
49
|
},
|
|
52
50
|
libs: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
51
|
+
input: { keepNames: true },
|
|
52
|
+
output: { define: { BUNDLE_NAME: JSON.stringify('libs') } },
|
|
56
53
|
},
|
|
57
54
|
}),
|
|
58
55
|
],
|
|
@@ -73,10 +70,10 @@ export default defineConfig({
|
|
|
73
70
|
## How it works
|
|
74
71
|
|
|
75
72
|
When you run `vite build`, Rollup normally outputs multiple chunks per entry if code-splitting is needed.
|
|
76
|
-
`vite-plugin-rebundle` hooks into the build and **rebundles each entry’s output with
|
|
73
|
+
`vite-plugin-rebundle` hooks into the build and **rebundles each entry’s output with rolldown**, forcing a single self-contained file per entry.
|
|
77
74
|
|
|
78
|
-
-
|
|
79
|
-
- Afterward, each entry is passed through
|
|
75
|
+
- Vite still handles the initial build (tree-shaking, asset pipeline, etc.).
|
|
76
|
+
- Afterward, each entry is passed through rolldown.
|
|
80
77
|
- The final result is one .js file per entry with no dynamic imports or shared chunks.
|
|
81
78
|
- If sourcemaps are enabled, they are preserved — the rebundled files include correct mappings.
|
|
82
79
|
|
package/src/index.ts
ADDED
package/src/rebundle.ts
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { safe, Unit } from '@eposlabs/utils'
|
|
2
|
+
import chalk from 'chalk'
|
|
3
|
+
import { filesize } from 'filesize'
|
|
4
|
+
import { readdir, readFile, rmdir, stat, unlink, writeFile } from 'node:fs/promises'
|
|
5
|
+
import { dirname, extname, join } from 'node:path'
|
|
6
|
+
import { getPort } from 'portfinder'
|
|
7
|
+
import { rolldown, type InputOptions, type OutputOptions } from 'rolldown'
|
|
8
|
+
import type { NormalizedOutputOptions, OutputBundle, OutputChunk } from 'rollup'
|
|
9
|
+
import type { Plugin, ResolvedConfig } from 'vite'
|
|
10
|
+
import { WebSocketServer } from 'ws'
|
|
11
|
+
|
|
12
|
+
export const _code_ = Symbol('rebundle:code')
|
|
13
|
+
export const _sourcemap_ = Symbol('rebundle:sourcemap')
|
|
14
|
+
|
|
15
|
+
export type Options = {
|
|
16
|
+
[bundleName: string]: {
|
|
17
|
+
input: InputOptions
|
|
18
|
+
output: OutputOptions
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class Rebundle extends Unit {
|
|
23
|
+
private options: Options
|
|
24
|
+
private config: ResolvedConfig | null = null
|
|
25
|
+
private originalFiles: Record<string, string> = {}
|
|
26
|
+
private rebundledFiles: Record<string, string> = {}
|
|
27
|
+
private hasError = false
|
|
28
|
+
private port: number | null = null
|
|
29
|
+
private ws: WebSocketServer | null = null
|
|
30
|
+
|
|
31
|
+
constructor(options: Options) {
|
|
32
|
+
super()
|
|
33
|
+
this.options = options
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get vite(): Plugin {
|
|
37
|
+
return {
|
|
38
|
+
name: 'vite-plugin-rebundle',
|
|
39
|
+
apply: 'build',
|
|
40
|
+
enforce: 'post',
|
|
41
|
+
config: this.onConfig,
|
|
42
|
+
configResolved: this.onConfigResolved,
|
|
43
|
+
buildEnd: this.onBuildEnd,
|
|
44
|
+
writeBundle: this.onWriteBundle,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// VITE HOOKS
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
private onConfig = async () => {
|
|
53
|
+
this.port = await getPort({ port: 3100 })
|
|
54
|
+
return {
|
|
55
|
+
define: {
|
|
56
|
+
'import.meta.env.REBUNDLE_PORT': JSON.stringify(this.port),
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private onConfigResolved = async (config: ResolvedConfig) => {
|
|
62
|
+
// Save resolved config
|
|
63
|
+
this.config = config
|
|
64
|
+
|
|
65
|
+
// Hide js files from output logs (rollup only, not supported in rolldown)
|
|
66
|
+
const info = this.config.logger.info
|
|
67
|
+
this.config.logger.info = (message, options) => {
|
|
68
|
+
const path = message.split(/\s+/)[0]
|
|
69
|
+
if (extname(path) === '.js') return
|
|
70
|
+
info(message, options)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private onBuildEnd = (error?: Error) => {
|
|
75
|
+
this.hasError = !!error
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private onWriteBundle = async (_output: NormalizedOutputOptions, bundle: OutputBundle) => {
|
|
79
|
+
if (this.hasError) return
|
|
80
|
+
if (!this.config) throw this.never
|
|
81
|
+
|
|
82
|
+
const chunks = Object.values(bundle).filter(chunkOrAsset => chunkOrAsset.type === 'chunk')
|
|
83
|
+
const entryChunks = chunks.filter(chunk => chunk.isEntry)
|
|
84
|
+
const nonEntryChunks = chunks.filter(chunk => !chunk.isEntry)
|
|
85
|
+
|
|
86
|
+
// Rebundle entry chunks
|
|
87
|
+
const modifiedChunkNames: string[] = []
|
|
88
|
+
await Promise.all(
|
|
89
|
+
entryChunks.map(async chunk => {
|
|
90
|
+
const modified = await this.rebundleChunk(chunk, bundle)
|
|
91
|
+
if (modified) modifiedChunkNames.push(chunk.name)
|
|
92
|
+
}),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
// Remove non-entry chunks
|
|
96
|
+
for (const chunk of nonEntryChunks) {
|
|
97
|
+
await this.removeChunk(chunk, bundle)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Notify about modified chunks
|
|
101
|
+
if (this.config.build.watch && modifiedChunkNames.length > 0) {
|
|
102
|
+
const ws = await this.ensureWs()
|
|
103
|
+
ws.clients.forEach(client => client.send(JSON.stringify(modifiedChunkNames)))
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// CHUNK METHODS
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
async rebundleChunk(chunk: OutputChunk, bundle: OutputBundle) {
|
|
112
|
+
if (!this.config) throw this.never
|
|
113
|
+
|
|
114
|
+
// Delete chunk from bundle to hide log for rolldown-vite. Call for rollup for consistency.
|
|
115
|
+
delete bundle[chunk.fileName]
|
|
116
|
+
|
|
117
|
+
const chunkPath = join(this.dist, chunk.fileName)
|
|
118
|
+
const chunkOptions = this.options[chunk.name] ?? {}
|
|
119
|
+
|
|
120
|
+
// Read chunk files, save their content
|
|
121
|
+
const chunkFiles = await this.readChunkFiles(chunk)
|
|
122
|
+
|
|
123
|
+
// Check if chunk was modified
|
|
124
|
+
const chunkFilePaths = Object.keys(chunkFiles)
|
|
125
|
+
const chunkModified = chunkFilePaths.some(path => chunkFiles[path] !== this.originalFiles[path])
|
|
126
|
+
|
|
127
|
+
// Chunk was not modified? -> Use previous content
|
|
128
|
+
if (!chunkModified) {
|
|
129
|
+
// Overwrite vite's output with previously rebundled code
|
|
130
|
+
const code = this.rebundledFiles[chunk.fileName]
|
|
131
|
+
await this.writeToDist(chunk.fileName, code)
|
|
132
|
+
|
|
133
|
+
// Overwrite vite's sourcemap
|
|
134
|
+
if (chunk.sourcemapFileName) {
|
|
135
|
+
const sourcemap = this.rebundledFiles[chunk.sourcemapFileName]
|
|
136
|
+
if (sourcemap) await this.writeToDist(chunk.sourcemapFileName, sourcemap)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Build with rolldown
|
|
143
|
+
try {
|
|
144
|
+
const build = await rolldown({ ...chunkOptions.input, input: chunkPath })
|
|
145
|
+
await build.write({ sourcemap: !!this.config.build.sourcemap, ...chunkOptions.output, file: chunkPath })
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(error)
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Log successful build
|
|
152
|
+
const { size } = await stat(chunkPath)
|
|
153
|
+
const _dist_ = chalk.dim(`${this.dist}/`)
|
|
154
|
+
const _fileName_ = chalk.cyan(chunk.fileName)
|
|
155
|
+
const _rebundle_ = chalk.dim.cyan('[rebundle]')
|
|
156
|
+
const _size_ = chalk.bold.dim(`${filesize(size)}`)
|
|
157
|
+
console.log(`${_dist_}${_fileName_} ${_rebundle_} ${_size_}`)
|
|
158
|
+
|
|
159
|
+
// Save code
|
|
160
|
+
const code = await this.readFromDist(chunk.fileName)
|
|
161
|
+
if (!code) throw this.never
|
|
162
|
+
this.rebundledFiles[chunk.fileName] = code
|
|
163
|
+
|
|
164
|
+
// Save sourcemap
|
|
165
|
+
if (chunk.sourcemapFileName) {
|
|
166
|
+
const sourcemap = await this.readFromDist(chunk.sourcemapFileName)
|
|
167
|
+
if (sourcemap) this.rebundledFiles[chunk.sourcemapFileName] = sourcemap
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return true
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private async removeChunk(chunk: OutputChunk, bundle: OutputBundle) {
|
|
174
|
+
await this.removeFromDist(chunk.fileName)
|
|
175
|
+
delete bundle[chunk.fileName]
|
|
176
|
+
|
|
177
|
+
if (chunk.sourcemapFileName) {
|
|
178
|
+
await this.removeFromDist(chunk.sourcemapFileName)
|
|
179
|
+
delete bundle[chunk.sourcemapFileName]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Recursively remove containing directory if empty
|
|
183
|
+
const dir = dirname(join(this.dist, chunk.fileName))
|
|
184
|
+
await this.removeDirectoryIfEmpty(dir)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private async readChunkFiles(chunk: OutputChunk) {
|
|
188
|
+
const files: Record<string, string> = {}
|
|
189
|
+
const usedPaths = [chunk.fileName, ...chunk.imports]
|
|
190
|
+
|
|
191
|
+
await Promise.all(
|
|
192
|
+
usedPaths.map(async path => {
|
|
193
|
+
const content = await this.readFromDist(path)
|
|
194
|
+
files[path] = content ?? ''
|
|
195
|
+
}),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
return files
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// HELPERS
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
|
|
205
|
+
private get dist() {
|
|
206
|
+
if (!this.config) throw this.never
|
|
207
|
+
return this.config.build.outDir
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private async readFromDist(path: string) {
|
|
211
|
+
const [content] = await safe(readFile(join(this.dist, path), 'utf-8'))
|
|
212
|
+
return content
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private async writeToDist(path: string, content: string) {
|
|
216
|
+
await writeFile(join(this.dist, path), content, 'utf-8')
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private async removeFromDist(path: string) {
|
|
220
|
+
await safe(unlink(join(this.dist, path)))
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private async ensureWs() {
|
|
224
|
+
if (this.ws) return this.ws
|
|
225
|
+
if (!this.port) throw this.never
|
|
226
|
+
this.ws = new WebSocketServer({ port: this.port })
|
|
227
|
+
return this.ws
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private async removeDirectoryIfEmpty(dir: string) {
|
|
231
|
+
const files = await readdir(dir)
|
|
232
|
+
if (files.length > 0) return
|
|
233
|
+
await rmdir(dir)
|
|
234
|
+
await this.removeDirectoryIfEmpty(dirname(dir))
|
|
235
|
+
}
|
|
236
|
+
}
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
package/dist/rebundle.d.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import * as $utils from '@eposlabs/utils';
|
|
2
|
-
import type { BuildOptions } from 'esbuild';
|
|
3
|
-
import type { OutputBundle, OutputChunk } from 'rollup';
|
|
4
|
-
import type { Plugin } from 'vite';
|
|
5
|
-
export declare const _code_: unique symbol;
|
|
6
|
-
export declare const _sourcemap_: unique symbol;
|
|
7
|
-
export type Options = {
|
|
8
|
-
[chunkName: string]: BuildOptions;
|
|
9
|
-
};
|
|
10
|
-
export declare class Rebundle extends $utils.Unit {
|
|
11
|
-
private options;
|
|
12
|
-
private config;
|
|
13
|
-
private chunkFiles;
|
|
14
|
-
private rebundledContent;
|
|
15
|
-
private hasError;
|
|
16
|
-
private port;
|
|
17
|
-
private ws;
|
|
18
|
-
constructor(options: Options);
|
|
19
|
-
get vite(): Plugin;
|
|
20
|
-
private onConfig;
|
|
21
|
-
private onConfigResolved;
|
|
22
|
-
private onBuildEnd;
|
|
23
|
-
private onWriteBundle;
|
|
24
|
-
rebundleChunk(chunk: OutputChunk, bundle: OutputBundle): Promise<boolean | undefined>;
|
|
25
|
-
private removeChunk;
|
|
26
|
-
private readChunkFiles;
|
|
27
|
-
private get dist();
|
|
28
|
-
private resolve;
|
|
29
|
-
private read;
|
|
30
|
-
private write;
|
|
31
|
-
private remove;
|
|
32
|
-
private ensureWs;
|
|
33
|
-
private removeDirectoryIfEmpty;
|
|
34
|
-
}
|
package/dist/rebundle.js
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import * as $utils from '@eposlabs/utils';
|
|
2
|
-
import $chalk from 'chalk';
|
|
3
|
-
import * as $esbuild from 'esbuild';
|
|
4
|
-
import { filesize } from 'filesize';
|
|
5
|
-
import * as $fs from 'node:fs/promises';
|
|
6
|
-
import * as $path from 'node:path';
|
|
7
|
-
import $portfinder from 'portfinder';
|
|
8
|
-
import * as $ws from 'ws';
|
|
9
|
-
export const _code_ = Symbol('rebundle:code');
|
|
10
|
-
export const _sourcemap_ = Symbol('rebundle:sourcemap');
|
|
11
|
-
export class Rebundle extends $utils.Unit {
|
|
12
|
-
options;
|
|
13
|
-
config = null;
|
|
14
|
-
chunkFiles = {};
|
|
15
|
-
rebundledContent = {};
|
|
16
|
-
hasError = false;
|
|
17
|
-
port = null;
|
|
18
|
-
ws = null;
|
|
19
|
-
constructor(options) {
|
|
20
|
-
super();
|
|
21
|
-
this.options = options;
|
|
22
|
-
}
|
|
23
|
-
get vite() {
|
|
24
|
-
return {
|
|
25
|
-
name: 'vite-plugin-rebundle',
|
|
26
|
-
apply: 'build',
|
|
27
|
-
enforce: 'post',
|
|
28
|
-
config: this.onConfig,
|
|
29
|
-
configResolved: this.onConfigResolved,
|
|
30
|
-
buildEnd: this.onBuildEnd,
|
|
31
|
-
writeBundle: this.onWriteBundle,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
// VITE HOOKS
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
onConfig = async () => {
|
|
38
|
-
this.port = await $portfinder.getPort({ port: 3100 });
|
|
39
|
-
return {
|
|
40
|
-
define: {
|
|
41
|
-
'import.meta.env.REBUNDLE_PORT': JSON.stringify(this.port),
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
onConfigResolved = async (config) => {
|
|
46
|
-
// Save resolved config
|
|
47
|
-
this.config = config;
|
|
48
|
-
// Hide js files from output logs (rollup only, not supported in rolldown)
|
|
49
|
-
const info = this.config.logger.info;
|
|
50
|
-
this.config.logger.info = (message, options) => {
|
|
51
|
-
const path = message.split(/\s+/)[0];
|
|
52
|
-
if ($path.extname(path) === '.js')
|
|
53
|
-
return;
|
|
54
|
-
info(message, options);
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
onBuildEnd = (error) => {
|
|
58
|
-
this.hasError = !!error;
|
|
59
|
-
};
|
|
60
|
-
onWriteBundle = async (_output, bundle) => {
|
|
61
|
-
if (this.hasError)
|
|
62
|
-
return;
|
|
63
|
-
if (!this.config)
|
|
64
|
-
throw this.never;
|
|
65
|
-
const chunks = Object.values(bundle).filter(chunkOrAsset => chunkOrAsset.type === 'chunk');
|
|
66
|
-
const entryChunks = chunks.filter(chunk => chunk.isEntry);
|
|
67
|
-
const nonEntryChunks = chunks.filter(chunk => !chunk.isEntry);
|
|
68
|
-
// Rebundle entry chunks
|
|
69
|
-
const modifiedChunkNames = [];
|
|
70
|
-
await Promise.all(entryChunks.map(async (chunk) => {
|
|
71
|
-
const modified = await this.rebundleChunk(chunk, bundle);
|
|
72
|
-
if (modified)
|
|
73
|
-
modifiedChunkNames.push(chunk.name);
|
|
74
|
-
}));
|
|
75
|
-
// Remove non-entry chunks
|
|
76
|
-
for (const chunk of nonEntryChunks) {
|
|
77
|
-
await this.removeChunk(chunk, bundle);
|
|
78
|
-
}
|
|
79
|
-
// Notify about modified chunks
|
|
80
|
-
if (this.config.build.watch && modifiedChunkNames.length > 0) {
|
|
81
|
-
const ws = await this.ensureWs();
|
|
82
|
-
ws.clients.forEach(client => client.send(JSON.stringify(modifiedChunkNames)));
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
// ---------------------------------------------------------------------------
|
|
86
|
-
// CHUNK METHODS
|
|
87
|
-
// ---------------------------------------------------------------------------
|
|
88
|
-
async rebundleChunk(chunk, bundle) {
|
|
89
|
-
if (!this.config)
|
|
90
|
-
throw this.never;
|
|
91
|
-
// Delete chunk from bundle to hide log for vite-rolldown.
|
|
92
|
-
// Call for rollup as well for consistency.
|
|
93
|
-
delete bundle[chunk.fileName];
|
|
94
|
-
const chunkPath = this.resolve(chunk.fileName);
|
|
95
|
-
const chunkBuildOptions = this.options[chunk.name] ?? {};
|
|
96
|
-
const chunkFiles = await this.readChunkFiles(chunk);
|
|
97
|
-
const chunkFilePaths = Object.keys(chunkFiles);
|
|
98
|
-
const chunkChanged = chunkFilePaths.some(path => chunkFiles[path] !== this.chunkFiles[path]);
|
|
99
|
-
// Update files cache
|
|
100
|
-
Object.assign(this.chunkFiles, chunkFiles);
|
|
101
|
-
// Not modified? -> Use pervious content
|
|
102
|
-
if (!chunkChanged) {
|
|
103
|
-
// Overwrite vite's code
|
|
104
|
-
const code = this.rebundledContent[chunk.fileName];
|
|
105
|
-
await this.write(chunk.fileName, code);
|
|
106
|
-
// Overwrite vite's sourcemap
|
|
107
|
-
if (chunk.sourcemapFileName) {
|
|
108
|
-
const sourcemap = this.rebundledContent[chunk.sourcemapFileName];
|
|
109
|
-
if (sourcemap)
|
|
110
|
-
await this.write(chunk.sourcemapFileName, sourcemap);
|
|
111
|
-
}
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
// Build with esbuild
|
|
115
|
-
let result;
|
|
116
|
-
try {
|
|
117
|
-
result = await $esbuild.build({
|
|
118
|
-
sourcemap: Boolean(this.config.build.sourcemap),
|
|
119
|
-
format: 'esm',
|
|
120
|
-
...chunkBuildOptions,
|
|
121
|
-
banner: { ...chunkBuildOptions.banner, js: ';(async () => {' + (chunkBuildOptions.banner?.js ?? '') },
|
|
122
|
-
footer: { ...chunkBuildOptions.footer, js: (chunkBuildOptions.footer?.js ?? '') + '})();' },
|
|
123
|
-
outfile: chunkPath,
|
|
124
|
-
entryPoints: [chunkPath],
|
|
125
|
-
bundle: true,
|
|
126
|
-
allowOverwrite: true,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
catch (err) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
// Errors? -> Ignore, esbuild will show errors in console
|
|
133
|
-
if (result.errors.length > 0)
|
|
134
|
-
return;
|
|
135
|
-
// Log successful build
|
|
136
|
-
const { size } = await $fs.stat(chunkPath);
|
|
137
|
-
const _dist_ = $chalk.dim(`${this.dist}/`);
|
|
138
|
-
const _fileName_ = $chalk.cyan(chunk.fileName);
|
|
139
|
-
const _rebundle_ = $chalk.dim.cyan('[rebundle]');
|
|
140
|
-
const _size_ = $chalk.bold.dim(`${filesize(size)}`);
|
|
141
|
-
console.log(`${_dist_}${_fileName_} ${_rebundle_} ${_size_}`);
|
|
142
|
-
// Save code
|
|
143
|
-
const code = await this.read(chunk.fileName);
|
|
144
|
-
if (!code)
|
|
145
|
-
throw this.never;
|
|
146
|
-
this.rebundledContent[chunk.fileName] = code;
|
|
147
|
-
// Save sourcemap
|
|
148
|
-
if (chunk.sourcemapFileName) {
|
|
149
|
-
const sourcemap = await this.read(chunk.sourcemapFileName);
|
|
150
|
-
if (sourcemap)
|
|
151
|
-
this.rebundledContent[chunk.sourcemapFileName] = sourcemap;
|
|
152
|
-
}
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
async removeChunk(chunk, bundle) {
|
|
156
|
-
await this.remove(chunk.fileName);
|
|
157
|
-
delete bundle[chunk.fileName];
|
|
158
|
-
if (chunk.sourcemapFileName) {
|
|
159
|
-
await this.remove(chunk.sourcemapFileName);
|
|
160
|
-
delete bundle[chunk.sourcemapFileName];
|
|
161
|
-
}
|
|
162
|
-
// Recursively remove containing directory if empty
|
|
163
|
-
const dir = $path.dirname(this.resolve(chunk.fileName));
|
|
164
|
-
await this.removeDirectoryIfEmpty(dir);
|
|
165
|
-
}
|
|
166
|
-
async readChunkFiles(chunk) {
|
|
167
|
-
const files = {};
|
|
168
|
-
const usedPaths = [chunk.fileName, ...chunk.imports];
|
|
169
|
-
await Promise.all(usedPaths.map(async (path) => {
|
|
170
|
-
const content = await this.read(path);
|
|
171
|
-
if (!content)
|
|
172
|
-
return;
|
|
173
|
-
files[path] = content;
|
|
174
|
-
}));
|
|
175
|
-
return files;
|
|
176
|
-
}
|
|
177
|
-
// ---------------------------------------------------------------------------
|
|
178
|
-
// HELPERS
|
|
179
|
-
// ---------------------------------------------------------------------------
|
|
180
|
-
get dist() {
|
|
181
|
-
if (!this.config)
|
|
182
|
-
throw this.never;
|
|
183
|
-
return this.config.build.outDir;
|
|
184
|
-
}
|
|
185
|
-
resolve(path) {
|
|
186
|
-
return $path.join(this.dist, path);
|
|
187
|
-
}
|
|
188
|
-
async read(path) {
|
|
189
|
-
const [content] = await $utils.safe($fs.readFile(this.resolve(path), 'utf-8'));
|
|
190
|
-
return content;
|
|
191
|
-
}
|
|
192
|
-
async write(path, content) {
|
|
193
|
-
await $fs.writeFile(this.resolve(path), content, 'utf-8');
|
|
194
|
-
}
|
|
195
|
-
async remove(path) {
|
|
196
|
-
await $utils.safe($fs.unlink(this.resolve(path)));
|
|
197
|
-
}
|
|
198
|
-
async ensureWs() {
|
|
199
|
-
if (this.ws)
|
|
200
|
-
return this.ws;
|
|
201
|
-
if (!this.port)
|
|
202
|
-
throw this.never;
|
|
203
|
-
this.ws = new $ws.WebSocketServer({ port: this.port });
|
|
204
|
-
return this.ws;
|
|
205
|
-
}
|
|
206
|
-
async removeDirectoryIfEmpty(dir) {
|
|
207
|
-
const files = await $fs.readdir(dir);
|
|
208
|
-
if (files.length > 0)
|
|
209
|
-
return;
|
|
210
|
-
await $fs.rmdir(dir);
|
|
211
|
-
await this.removeDirectoryIfEmpty($path.dirname(dir));
|
|
212
|
-
}
|
|
213
|
-
}
|