vite-plugin-rebundle 1.3.1 → 1.3.3
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 +41 -0
- package/dist/rebundle.js +180 -0
- package/package.json +12 -5
- package/src/rebundle.ts +10 -8
|
@@ -0,0 +1,41 @@
|
|
|
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 onWriteBundle;
|
|
29
|
+
rebundleChunk(chunk: OutputChunk, bundle: OutputBundle): Promise<boolean | undefined>;
|
|
30
|
+
private removeChunk;
|
|
31
|
+
private readChunkFiles;
|
|
32
|
+
private get dist();
|
|
33
|
+
private readFromDist;
|
|
34
|
+
private writeToDist;
|
|
35
|
+
private removeFromDist;
|
|
36
|
+
private ensureWs;
|
|
37
|
+
private removeDirectoryIfEmpty;
|
|
38
|
+
}
|
|
39
|
+
declare function rebundle(options?: Options): Plugin<any>;
|
|
40
|
+
|
|
41
|
+
export { type Options, Rebundle, type RolldownOptions, _code_, _sourcemap_, rebundle as default, rebundle };
|
package/dist/rebundle.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
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, writeFile } 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
|
+
writeBundle: this.onWriteBundle
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// VITE HOOKS
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
onConfig = async () => {
|
|
39
|
+
this.port = await getPort({ port: 3100 });
|
|
40
|
+
return {
|
|
41
|
+
define: {
|
|
42
|
+
"import.meta.env.REBUNDLE_PORT": JSON.stringify(this.port)
|
|
43
|
+
}
|
|
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
|
+
onWriteBundle = async (_output, bundle) => {
|
|
59
|
+
if (this.hasError) return;
|
|
60
|
+
if (!this.config) throw this.never;
|
|
61
|
+
const chunks = Object.values(bundle).filter((chunkOrAsset) => chunkOrAsset.type === "chunk");
|
|
62
|
+
const entryChunks = chunks.filter((chunk) => chunk.isEntry);
|
|
63
|
+
const nonEntryChunks = chunks.filter((chunk) => !chunk.isEntry);
|
|
64
|
+
const modifiedChunkNames = [];
|
|
65
|
+
await Promise.all(
|
|
66
|
+
entryChunks.map(async (chunk) => {
|
|
67
|
+
const modified = await this.rebundleChunk(chunk, bundle);
|
|
68
|
+
if (modified) modifiedChunkNames.push(chunk.name);
|
|
69
|
+
})
|
|
70
|
+
);
|
|
71
|
+
for (const chunk of nonEntryChunks) {
|
|
72
|
+
await this.removeChunk(chunk, bundle);
|
|
73
|
+
}
|
|
74
|
+
if (this.config.build.watch && modifiedChunkNames.length > 0) {
|
|
75
|
+
const ws = await this.ensureWs();
|
|
76
|
+
ws.clients.forEach((client) => client.send(JSON.stringify(modifiedChunkNames)));
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// CHUNK METHODS
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
async rebundleChunk(chunk, bundle) {
|
|
83
|
+
if (!this.config) throw this.never;
|
|
84
|
+
delete bundle[chunk.fileName];
|
|
85
|
+
const chunkPath = join(this.dist, chunk.fileName);
|
|
86
|
+
const chunkOptions = this.options[chunk.name] ?? {};
|
|
87
|
+
const chunkFiles = await this.readChunkFiles(chunk);
|
|
88
|
+
const chunkFilePaths = Object.keys(chunkFiles);
|
|
89
|
+
const chunkModified = chunkFilePaths.some((path) => chunkFiles[path] !== this.originalFiles[path]);
|
|
90
|
+
Object.assign(this.originalFiles, chunkFiles);
|
|
91
|
+
if (!chunkModified) {
|
|
92
|
+
const code2 = this.rebundledFiles[chunk.fileName];
|
|
93
|
+
await this.writeToDist(chunk.fileName, code2);
|
|
94
|
+
if (chunk.sourcemapFileName) {
|
|
95
|
+
const sourcemap = this.rebundledFiles[chunk.sourcemapFileName];
|
|
96
|
+
if (sourcemap) await this.writeToDist(chunk.sourcemapFileName, sourcemap);
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const [build] = await safe(rolldown({ ...chunkOptions.input, input: chunkPath }));
|
|
101
|
+
if (!build) return;
|
|
102
|
+
const [_, error] = await safe(build.write({ ...chunkOptions.output, file: chunkPath }));
|
|
103
|
+
if (error) return;
|
|
104
|
+
const { size } = await stat(chunkPath);
|
|
105
|
+
const _dist_ = chalk.dim(`${this.dist}/`);
|
|
106
|
+
const _fileName_ = chalk.cyan(chunk.fileName);
|
|
107
|
+
const _rebundle_ = chalk.dim.cyan("[rebundle]");
|
|
108
|
+
const _size_ = chalk.bold.dim(`${filesize(size)}`);
|
|
109
|
+
console.log(`${_dist_}${_fileName_} ${_rebundle_} ${_size_}`);
|
|
110
|
+
const code = await this.readFromDist(chunk.fileName);
|
|
111
|
+
if (!code) throw this.never;
|
|
112
|
+
this.rebundledFiles[chunk.fileName] = code;
|
|
113
|
+
if (chunk.sourcemapFileName) {
|
|
114
|
+
const sourcemap = await this.readFromDist(chunk.sourcemapFileName);
|
|
115
|
+
if (sourcemap) this.rebundledFiles[chunk.sourcemapFileName] = sourcemap;
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
async removeChunk(chunk, bundle) {
|
|
120
|
+
await this.removeFromDist(chunk.fileName);
|
|
121
|
+
delete bundle[chunk.fileName];
|
|
122
|
+
if (chunk.sourcemapFileName) {
|
|
123
|
+
await this.removeFromDist(chunk.sourcemapFileName);
|
|
124
|
+
delete bundle[chunk.sourcemapFileName];
|
|
125
|
+
}
|
|
126
|
+
const dir = dirname(join(this.dist, chunk.fileName));
|
|
127
|
+
await this.removeDirectoryIfEmpty(dir);
|
|
128
|
+
}
|
|
129
|
+
async readChunkFiles(chunk) {
|
|
130
|
+
const files = {};
|
|
131
|
+
const usedPaths = [chunk.fileName, ...chunk.imports];
|
|
132
|
+
await Promise.all(
|
|
133
|
+
usedPaths.map(async (path) => {
|
|
134
|
+
const content = await this.readFromDist(path);
|
|
135
|
+
files[path] = content ?? "";
|
|
136
|
+
})
|
|
137
|
+
);
|
|
138
|
+
return files;
|
|
139
|
+
}
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// HELPERS
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
get dist() {
|
|
144
|
+
if (!this.config) throw this.never;
|
|
145
|
+
return this.config.build.outDir;
|
|
146
|
+
}
|
|
147
|
+
async readFromDist(path) {
|
|
148
|
+
const [content] = await safe(readFile(join(this.dist, path), "utf-8"));
|
|
149
|
+
return content;
|
|
150
|
+
}
|
|
151
|
+
async writeToDist(path, content) {
|
|
152
|
+
await writeFile(join(this.dist, path), content, "utf-8");
|
|
153
|
+
}
|
|
154
|
+
async removeFromDist(path) {
|
|
155
|
+
await safe(unlink(join(this.dist, path)));
|
|
156
|
+
}
|
|
157
|
+
async ensureWs() {
|
|
158
|
+
if (this.ws) return this.ws;
|
|
159
|
+
if (!this.port) throw this.never;
|
|
160
|
+
this.ws = new WebSocketServer({ port: this.port });
|
|
161
|
+
return this.ws;
|
|
162
|
+
}
|
|
163
|
+
async removeDirectoryIfEmpty(dir) {
|
|
164
|
+
const files = await readdir(dir);
|
|
165
|
+
if (files.length > 0) return;
|
|
166
|
+
await rmdir(dir);
|
|
167
|
+
await this.removeDirectoryIfEmpty(dirname(dir));
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
function rebundle(options = {}) {
|
|
171
|
+
return new Rebundle(options).vite;
|
|
172
|
+
}
|
|
173
|
+
var rebundle_default = rebundle;
|
|
174
|
+
export {
|
|
175
|
+
Rebundle,
|
|
176
|
+
_code_,
|
|
177
|
+
_sourcemap_,
|
|
178
|
+
rebundle_default as default,
|
|
179
|
+
rebundle
|
|
180
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vite-plugin-rebundle",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "imkost",
|
|
@@ -10,14 +10,21 @@
|
|
|
10
10
|
"vite-plugin"
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
|
+
"build": "tsup src --format esm --dts --clean",
|
|
13
14
|
"lint": "tsc --noEmit",
|
|
14
|
-
"release": "sh -c 'npm version ${1:-patch} && npm publish' --"
|
|
15
|
+
"release": "sh -c 'npm version ${1:-patch} && npm run build && npm publish' --"
|
|
15
16
|
},
|
|
16
17
|
"exports": {
|
|
17
|
-
"
|
|
18
|
+
".": {
|
|
19
|
+
"import": "./dist/rebundle.js"
|
|
20
|
+
},
|
|
21
|
+
"./ts": {
|
|
22
|
+
"import": "./src/rebundle.ts"
|
|
23
|
+
}
|
|
18
24
|
},
|
|
19
25
|
"files": [
|
|
20
|
-
"src"
|
|
26
|
+
"src",
|
|
27
|
+
"dist"
|
|
21
28
|
],
|
|
22
29
|
"dependencies": {
|
|
23
30
|
"@eposlabs/utils": "^1.1.1",
|
|
@@ -26,7 +33,7 @@
|
|
|
26
33
|
"filesize": "^11.0.13",
|
|
27
34
|
"portfinder": "^1.0.38",
|
|
28
35
|
"rolldown": "^1.0.0-beta.40",
|
|
29
|
-
"rollup": "^4.52.
|
|
36
|
+
"rollup": "^4.52.3",
|
|
30
37
|
"vite": "^7.1.7",
|
|
31
38
|
"ws": "^8.18.3"
|
|
32
39
|
}
|
package/src/rebundle.ts
CHANGED
|
@@ -119,13 +119,16 @@ export class Rebundle extends Unit {
|
|
|
119
119
|
const chunkPath = join(this.dist, chunk.fileName)
|
|
120
120
|
const chunkOptions = this.options[chunk.name] ?? {}
|
|
121
121
|
|
|
122
|
-
// Read chunk files
|
|
122
|
+
// Read chunk files
|
|
123
123
|
const chunkFiles = await this.readChunkFiles(chunk)
|
|
124
124
|
|
|
125
125
|
// Check if chunk was modified
|
|
126
126
|
const chunkFilePaths = Object.keys(chunkFiles)
|
|
127
127
|
const chunkModified = chunkFilePaths.some(path => chunkFiles[path] !== this.originalFiles[path])
|
|
128
128
|
|
|
129
|
+
// Update original files content
|
|
130
|
+
Object.assign(this.originalFiles, chunkFiles)
|
|
131
|
+
|
|
129
132
|
// Chunk was not modified? -> Use previous content
|
|
130
133
|
if (!chunkModified) {
|
|
131
134
|
// Overwrite vite's output with previously rebundled code
|
|
@@ -138,17 +141,15 @@ export class Rebundle extends Unit {
|
|
|
138
141
|
if (sourcemap) await this.writeToDist(chunk.sourcemapFileName, sourcemap)
|
|
139
142
|
}
|
|
140
143
|
|
|
144
|
+
// Return not modified status
|
|
141
145
|
return false
|
|
142
146
|
}
|
|
143
147
|
|
|
144
148
|
// Build with rolldown
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
console.error(error)
|
|
150
|
-
return
|
|
151
|
-
}
|
|
149
|
+
const [build] = await safe(rolldown({ ...chunkOptions.input, input: chunkPath }))
|
|
150
|
+
if (!build) return
|
|
151
|
+
const [_, error] = await safe(build.write({ ...chunkOptions.output, file: chunkPath }))
|
|
152
|
+
if (error) return
|
|
152
153
|
|
|
153
154
|
// Log successful build
|
|
154
155
|
const { size } = await stat(chunkPath)
|
|
@@ -169,6 +170,7 @@ export class Rebundle extends Unit {
|
|
|
169
170
|
if (sourcemap) this.rebundledFiles[chunk.sourcemapFileName] = sourcemap
|
|
170
171
|
}
|
|
171
172
|
|
|
173
|
+
// Return modified status
|
|
172
174
|
return true
|
|
173
175
|
}
|
|
174
176
|
|