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.
@@ -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 };
@@ -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.1",
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
- "import": "./src/rebundle.ts"
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.2",
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, save their content
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
- try {
146
- const build = await rolldown({ ...chunkOptions.input, input: chunkPath })
147
- await build.write({ sourcemap: !!this.config.build.sourcemap, ...chunkOptions.output, file: chunkPath })
148
- } catch (error) {
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