vite-plugin-rebundle 1.7.0 → 1.8.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/rebundle.d.ts +43 -0
- package/dist/rebundle.js +181 -0
- package/package.json +12 -5
- package/readme.md +0 -82
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Unit } from '@eposlabs/utils';
|
|
2
|
+
import { InputOptions, OutputOptions } from 'rolldown';
|
|
3
|
+
import { OutputChunk, OutputBundle } from 'rollup';
|
|
4
|
+
import { Plugin } from 'vite';
|
|
5
|
+
|
|
6
|
+
declare const _code_: unique symbol;
|
|
7
|
+
declare const _sourcemap_: unique symbol;
|
|
8
|
+
type RolldownOptions = {
|
|
9
|
+
input?: InputOptions;
|
|
10
|
+
output?: OutputOptions;
|
|
11
|
+
};
|
|
12
|
+
type Options = {
|
|
13
|
+
[bundleName: string]: RolldownOptions;
|
|
14
|
+
};
|
|
15
|
+
declare class Rebundle extends Unit {
|
|
16
|
+
private options;
|
|
17
|
+
private config;
|
|
18
|
+
private originalFiles;
|
|
19
|
+
private rebundledFiles;
|
|
20
|
+
private hasError;
|
|
21
|
+
private port;
|
|
22
|
+
private ws;
|
|
23
|
+
constructor(options: Options);
|
|
24
|
+
get vite(): Plugin;
|
|
25
|
+
private onConfig;
|
|
26
|
+
private onConfigResolved;
|
|
27
|
+
private onBuildEnd;
|
|
28
|
+
private onGenerateBundle;
|
|
29
|
+
private onWriteBundle;
|
|
30
|
+
rebundleChunk(chunk: OutputChunk, bundle: OutputBundle): Promise<boolean | undefined>;
|
|
31
|
+
private readChunkFiles;
|
|
32
|
+
private getChunks;
|
|
33
|
+
private get dist();
|
|
34
|
+
private prefixed;
|
|
35
|
+
private unprefixed;
|
|
36
|
+
private readFromDist;
|
|
37
|
+
private removeFromDist;
|
|
38
|
+
private ensureWs;
|
|
39
|
+
private removeDirectoryIfEmpty;
|
|
40
|
+
}
|
|
41
|
+
declare function rebundle(options?: Options): Plugin<any>;
|
|
42
|
+
|
|
43
|
+
export { type Options, Rebundle, type RolldownOptions, _code_, _sourcemap_, rebundle as default, rebundle };
|
package/dist/rebundle.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// src/rebundle.ts
|
|
2
|
+
import { safe, Unit } from "@eposlabs/utils";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { filesize } from "filesize";
|
|
5
|
+
import { readdir, readFile, rmdir, stat, unlink } from "fs/promises";
|
|
6
|
+
import { dirname, extname, join } from "path";
|
|
7
|
+
import { getPort } from "portfinder";
|
|
8
|
+
import { rolldown } from "rolldown";
|
|
9
|
+
import { WebSocketServer } from "ws";
|
|
10
|
+
var _code_ = Symbol("rebundle:code");
|
|
11
|
+
var _sourcemap_ = Symbol("rebundle:sourcemap");
|
|
12
|
+
var Rebundle = class extends Unit {
|
|
13
|
+
options;
|
|
14
|
+
config = null;
|
|
15
|
+
originalFiles = {};
|
|
16
|
+
rebundledFiles = {};
|
|
17
|
+
hasError = false;
|
|
18
|
+
port = null;
|
|
19
|
+
ws = null;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
super();
|
|
22
|
+
this.options = options;
|
|
23
|
+
}
|
|
24
|
+
get vite() {
|
|
25
|
+
return {
|
|
26
|
+
name: "vite-plugin-rebundle",
|
|
27
|
+
apply: "build",
|
|
28
|
+
enforce: "post",
|
|
29
|
+
config: this.onConfig,
|
|
30
|
+
configResolved: this.onConfigResolved,
|
|
31
|
+
buildEnd: this.onBuildEnd,
|
|
32
|
+
generateBundle: this.onGenerateBundle,
|
|
33
|
+
writeBundle: this.onWriteBundle
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// VITE HOOKS
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
onConfig = async () => {
|
|
40
|
+
this.port = await getPort({ port: 3100 });
|
|
41
|
+
return {
|
|
42
|
+
define: { "import.meta.env.REBUNDLE_PORT": JSON.stringify(this.port) },
|
|
43
|
+
build: { sourcemap: false }
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
onConfigResolved = async (config) => {
|
|
47
|
+
this.config = config;
|
|
48
|
+
const info = this.config.logger.info;
|
|
49
|
+
this.config.logger.info = (message, options) => {
|
|
50
|
+
const path = message.split(/\s+/)[0];
|
|
51
|
+
if (extname(path) === ".js") return;
|
|
52
|
+
info(message, options);
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
onBuildEnd = (error) => {
|
|
56
|
+
this.hasError = !!error;
|
|
57
|
+
};
|
|
58
|
+
onGenerateBundle = async (_options, bundle) => {
|
|
59
|
+
const chunks = this.getChunks(bundle);
|
|
60
|
+
for (const chunk of chunks) {
|
|
61
|
+
if (!chunk.isEntry) continue;
|
|
62
|
+
chunk.fileName = this.prefixed(chunk.fileName);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
onWriteBundle = async (_output, bundle) => {
|
|
66
|
+
if (this.hasError) return;
|
|
67
|
+
if (!this.config) throw this.never;
|
|
68
|
+
const chunks = this.getChunks(bundle);
|
|
69
|
+
const modifiedChunkNames = [];
|
|
70
|
+
await Promise.all(
|
|
71
|
+
chunks.map(async (chunk) => {
|
|
72
|
+
if (!chunk.isEntry) return;
|
|
73
|
+
const modified = await this.rebundleChunk(chunk, bundle);
|
|
74
|
+
if (modified) modifiedChunkNames.push(chunk.name);
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
for (const chunk of chunks) {
|
|
78
|
+
if (chunk.isEntry) continue;
|
|
79
|
+
await this.removeFromDist(chunk.fileName);
|
|
80
|
+
delete bundle[chunk.fileName];
|
|
81
|
+
const dir = dirname(join(this.dist, chunk.fileName));
|
|
82
|
+
await this.removeDirectoryIfEmpty(dir);
|
|
83
|
+
}
|
|
84
|
+
if (this.config.build.watch && modifiedChunkNames.length > 0) {
|
|
85
|
+
const ws = await this.ensureWs();
|
|
86
|
+
ws.clients.forEach((client) => client.send(JSON.stringify(modifiedChunkNames)));
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// CHUNK METHODS
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
async rebundleChunk(chunk, bundle) {
|
|
93
|
+
if (!this.config) throw this.never;
|
|
94
|
+
delete bundle[chunk.fileName];
|
|
95
|
+
const chunkFiles = await this.readChunkFiles(chunk);
|
|
96
|
+
const chunkFilePaths = Object.keys(chunkFiles);
|
|
97
|
+
const chunkModified = chunkFilePaths.some((path) => chunkFiles[path] !== this.originalFiles[path]);
|
|
98
|
+
Object.assign(this.originalFiles, chunkFiles);
|
|
99
|
+
if (!chunkModified) {
|
|
100
|
+
await this.removeFromDist(chunk.fileName);
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const options = this.options[chunk.name] ?? {};
|
|
104
|
+
const [result] = await safe(async () => {
|
|
105
|
+
const inputPath = join(this.dist, chunk.fileName);
|
|
106
|
+
const outputPath = join(this.dist, this.unprefixed(chunk.fileName));
|
|
107
|
+
const build = await rolldown({ ...options.input, input: inputPath });
|
|
108
|
+
const result2 = await build.write({ ...options.output, sourcemap: false, file: outputPath });
|
|
109
|
+
return result2;
|
|
110
|
+
});
|
|
111
|
+
if (!result) return;
|
|
112
|
+
const { size } = await stat(join(this.dist, chunk.fileName));
|
|
113
|
+
const _dist_ = chalk.dim(`${this.dist}/`);
|
|
114
|
+
const _fileName_ = chalk.cyan(this.unprefixed(chunk.fileName));
|
|
115
|
+
const _rebundle_ = chalk.dim.cyan("[rebundle]");
|
|
116
|
+
const _size_ = chalk.bold.dim(`${filesize(size)}`);
|
|
117
|
+
console.log(`${_dist_}${_fileName_} ${_rebundle_} ${_size_}`);
|
|
118
|
+
const code = result.output[0].code;
|
|
119
|
+
if (!code) throw this.never;
|
|
120
|
+
this.rebundledFiles[chunk.fileName] = code;
|
|
121
|
+
await this.removeFromDist(chunk.fileName);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
async readChunkFiles(chunk) {
|
|
125
|
+
const files = {};
|
|
126
|
+
const usedPaths = [chunk.fileName, ...chunk.imports];
|
|
127
|
+
await Promise.all(
|
|
128
|
+
usedPaths.map(async (path) => {
|
|
129
|
+
const content = await this.readFromDist(path);
|
|
130
|
+
files[path] = content ?? "";
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
return files;
|
|
134
|
+
}
|
|
135
|
+
getChunks(bundle) {
|
|
136
|
+
return Object.values(bundle).filter((chunkOrAsset) => chunkOrAsset.type === "chunk");
|
|
137
|
+
}
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// HELPERS
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
get dist() {
|
|
142
|
+
if (!this.config) throw this.never;
|
|
143
|
+
return this.config.build.outDir;
|
|
144
|
+
}
|
|
145
|
+
prefixed(fileName) {
|
|
146
|
+
return `rebundle-original-${fileName}`;
|
|
147
|
+
}
|
|
148
|
+
unprefixed(fileName) {
|
|
149
|
+
return fileName.replace("rebundle-original-", "");
|
|
150
|
+
}
|
|
151
|
+
async readFromDist(path) {
|
|
152
|
+
const [content] = await safe(readFile(join(this.dist, path), "utf-8"));
|
|
153
|
+
return content;
|
|
154
|
+
}
|
|
155
|
+
async removeFromDist(path) {
|
|
156
|
+
await safe(unlink(join(this.dist, path)));
|
|
157
|
+
}
|
|
158
|
+
async ensureWs() {
|
|
159
|
+
if (this.ws) return this.ws;
|
|
160
|
+
if (!this.port) throw this.never;
|
|
161
|
+
this.ws = new WebSocketServer({ port: this.port });
|
|
162
|
+
return this.ws;
|
|
163
|
+
}
|
|
164
|
+
async removeDirectoryIfEmpty(dir) {
|
|
165
|
+
const files = await readdir(dir);
|
|
166
|
+
if (files.length > 0) return;
|
|
167
|
+
await rmdir(dir);
|
|
168
|
+
await this.removeDirectoryIfEmpty(dirname(dir));
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
function rebundle(options = {}) {
|
|
172
|
+
return new Rebundle(options).vite;
|
|
173
|
+
}
|
|
174
|
+
var rebundle_default = rebundle;
|
|
175
|
+
export {
|
|
176
|
+
Rebundle,
|
|
177
|
+
_code_,
|
|
178
|
+
_sourcemap_,
|
|
179
|
+
rebundle_default as default,
|
|
180
|
+
rebundle
|
|
181
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-rebundle",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "imkost",
|
|
@@ -10,17 +10,24 @@
|
|
|
10
10
|
"vite-plugin"
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
|
+
"dev": "tsup --config ../tsup.config.ts --watch",
|
|
14
|
+
"build": "tsup --config ../tsup.config.ts",
|
|
13
15
|
"lint": "tsc --noEmit",
|
|
14
|
-
"release": "sh -c 'npm version ${1:-minor} && npm publish' --"
|
|
16
|
+
"release": "sh -c 'npm version ${1:-minor} && npm run build && npm publish' --"
|
|
15
17
|
},
|
|
16
18
|
"exports": {
|
|
17
|
-
".":
|
|
19
|
+
".": {
|
|
20
|
+
"source": "./src/rebundle.ts",
|
|
21
|
+
"import": "./dist/rebundle.js",
|
|
22
|
+
"types": "./dist/rebundle.d.ts"
|
|
23
|
+
}
|
|
18
24
|
},
|
|
19
25
|
"files": [
|
|
20
|
-
"src"
|
|
26
|
+
"src",
|
|
27
|
+
"dist"
|
|
21
28
|
],
|
|
22
29
|
"dependencies": {
|
|
23
|
-
"@eposlabs/utils": "^1.
|
|
30
|
+
"@eposlabs/utils": "^1.8.0",
|
|
24
31
|
"@types/ws": "^8.18.1",
|
|
25
32
|
"chalk": "^5.6.2",
|
|
26
33
|
"filesize": "^11.0.13",
|
package/readme.md
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
# vite-plugin-rebundle
|
|
2
|
-
|
|
3
|
-
A Vite plugin that guarantees **one standalone file per entry point**. Each entry is bundled into a single file with no code-splitting or dynamic imports.
|
|
4
|
-
|
|
5
|
-
## Why?
|
|
6
|
-
|
|
7
|
-
Sometimes you need bundles without dynamic imports. Vite/Rollup don’t provide this option when building with multiple entries. `vite-plugin-rebundle` solves this issue by rebundling Vite’s output with rolldown to enforce single-file output. This plugin runs only during `vite build`, and it does not affect the Vite dev server.
|
|
8
|
-
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm install -D vite-plugin-rebundle
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Usage
|
|
16
|
-
|
|
17
|
-
```javascript
|
|
18
|
-
import { defineConfig } from 'vite'
|
|
19
|
-
import { rebundle } from 'vite-plugin-rebundle'
|
|
20
|
-
|
|
21
|
-
export default defineConfig({
|
|
22
|
-
plugins: [rebundle()],
|
|
23
|
-
build: {
|
|
24
|
-
rollupOptions: {
|
|
25
|
-
input: {
|
|
26
|
-
app: 'src/app.js',
|
|
27
|
-
libs: 'src/libs.js',
|
|
28
|
-
},
|
|
29
|
-
output: {
|
|
30
|
-
entryFileNames: '[name].js',
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
})
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Options
|
|
38
|
-
|
|
39
|
-
You can provide rolldown options per entry point. This is useful, for example, to inject custom define variables into specific bundles:
|
|
40
|
-
|
|
41
|
-
```javascript
|
|
42
|
-
export default defineConfig({
|
|
43
|
-
plugins: [
|
|
44
|
-
rebundle({
|
|
45
|
-
app: {
|
|
46
|
-
output: {
|
|
47
|
-
define: { BUNDLE_NAME: JSON.stringify('app') },
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
libs: {
|
|
51
|
-
input: {
|
|
52
|
-
keepNames: true,
|
|
53
|
-
},
|
|
54
|
-
output: {
|
|
55
|
-
define: { BUNDLE_NAME: JSON.stringify('libs') },
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
}),
|
|
59
|
-
],
|
|
60
|
-
build: {
|
|
61
|
-
rollupOptions: {
|
|
62
|
-
input: {
|
|
63
|
-
app: 'src/app.js',
|
|
64
|
-
libs: 'src/libs.js',
|
|
65
|
-
},
|
|
66
|
-
output: {
|
|
67
|
-
entryFileNames: '[name].js',
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
})
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## How it works
|
|
75
|
-
|
|
76
|
-
When you run `vite build`, Rollup normally outputs multiple chunks per entry if code-splitting is needed.
|
|
77
|
-
`vite-plugin-rebundle` hooks into the build and **rebundles each entry’s output with rolldown**, forcing a single self-contained file per entry.
|
|
78
|
-
|
|
79
|
-
- Vite still handles the initial build (tree-shaking, asset pipeline, etc.).
|
|
80
|
-
- Afterward, each entry is passed through rolldown.
|
|
81
|
-
- The final result is one .js file per entry with no dynamic imports or shared chunks.
|
|
82
|
-
- Sourcemaps are ignored, as they would be inaccurate after rebundling.
|