vite-plugin-rebundle 1.1.0 → 1.1.1

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/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { type Options } from './rebundle.js';
2
- export default function rebundle(options?: Options): import("vite").Plugin<any>;
1
+ import { type OptionsInput } from './rebundle.js';
2
+ export default function rebundle(options?: OptionsInput): import("vite").Plugin<any>;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { Rebundle } from './rebundle.js';
2
2
  export default function rebundle(options = {}) {
3
- return new Rebundle(options).plugin;
3
+ return new Rebundle(options).vite;
4
4
  }
@@ -1,20 +1,24 @@
1
+ import * as $utils from '@eposlabs/utils';
1
2
  import type { BuildOptions } from 'esbuild';
2
3
  import type { Plugin } from 'vite';
3
4
  export type Options = {
4
5
  [chunkName: string]: BuildOptions;
5
6
  };
6
- export type ContentMap = {
7
- [path: string]: string;
8
- };
9
- export declare class Rebundle {
7
+ export type OptionsInput = Options | (() => Options | Promise<Options>);
8
+ export declare class Rebundle extends $utils.Unit {
10
9
  private options;
11
10
  private config;
12
- private content;
13
- constructor(options: Options);
14
- get plugin(): Plugin;
11
+ private chunkFiles;
12
+ private rebundledContent;
13
+ constructor(options: OptionsInput);
14
+ get vite(): Plugin;
15
15
  private onConfigResolved;
16
16
  private onWriteBundle;
17
17
  private get outDir();
18
- private getChunkContentMap;
19
- private get never();
18
+ private outPath;
19
+ private outRead;
20
+ private outWrite;
21
+ private getOptions;
22
+ private readChunkFiles;
23
+ private removeDirectoryIfEmpty;
20
24
  }
package/dist/rebundle.js CHANGED
@@ -1,15 +1,18 @@
1
- import $chalk from 'chalk';
2
1
  import * as $esbuild from 'esbuild';
3
2
  import * as $fs from 'node:fs/promises';
4
3
  import * as $path from 'node:path';
5
- export class Rebundle {
4
+ import * as $utils from '@eposlabs/utils';
5
+ import $chalk from 'chalk';
6
+ export class Rebundle extends $utils.Unit {
6
7
  options;
7
8
  config = null;
8
- content = {};
9
+ chunkFiles = {};
10
+ rebundledContent = {};
9
11
  constructor(options) {
12
+ super();
10
13
  this.options = options;
11
14
  }
12
- get plugin() {
15
+ get vite() {
13
16
  return {
14
17
  name: 'vite-plugin-rebundle',
15
18
  apply: 'build',
@@ -34,83 +37,86 @@ export class Rebundle {
34
37
  };
35
38
  };
36
39
  onWriteBundle = async (output, bundle) => {
40
+ const options = await this.getOptions();
37
41
  // Get entry js chunks
38
42
  const entryJsChunks = Object.values(bundle)
39
43
  .filter(chunkOrAsset => chunkOrAsset.type === 'chunk')
40
44
  .filter(chunk => chunk.isEntry && $path.extname(chunk.fileName) === '.js');
41
- // Get non-entry chunks (.js, .js.map)
42
- const nonEntryChunks = Object.values(bundle)
43
- .filter(chunkOrAsset => chunkOrAsset.type === 'chunk')
44
- .filter(chunk => !chunk.isEntry);
45
45
  // Rebundle entry js chunks with esbuild
46
46
  await Promise.all(entryJsChunks.map(async (chunk) => {
47
47
  if (!this.config)
48
48
  throw this.never;
49
- // Check if chunk has been modified
50
- const content = await this.getChunkContentMap(chunk);
51
- const usedPaths = Object.keys(content);
52
- const changed = usedPaths.some(path => content[path] !== this.content[path]);
53
- // Update content map
54
- Object.assign(this.content, content);
55
- // Not modified? -> Skip
56
- if (!changed)
57
- return;
58
- // Prepare chunk path and build options
59
- const chunkPath = $path.join(this.outDir, chunk.fileName);
60
- const options = this.options[chunk.name] ?? {};
61
- // Build with esbuild
62
- await $esbuild.build({
63
- minify: Boolean(this.config.build.minify),
64
- sourcemap: Boolean(this.config.build.sourcemap),
65
- ...options,
66
- outfile: chunkPath,
67
- entryPoints: [chunkPath],
68
- bundle: true,
69
- allowOverwrite: true,
70
- plugins: [
71
- ...(options.plugins ?? []),
72
- {
73
- name: 'logger',
74
- setup: build => {
75
- build.onEnd(result => {
76
- if (result.errors.length > 0)
77
- return;
78
- const dir = $chalk.dim(`${this.outDir}/`);
79
- const name = $chalk.cyan(chunk.fileName);
80
- const tag = $chalk.dim.cyan('rebundle');
81
- console.log(`${dir}${name} ${tag}`);
82
- });
49
+ const chunkPath = this.outPath(chunk.fileName);
50
+ const chunkBuildOptions = options[chunk.name] ?? {};
51
+ const chunkFiles = await this.readChunkFiles(chunk);
52
+ const chunkFilePaths = Object.keys(chunkFiles);
53
+ const chunkChanged = chunkFilePaths.some(path => chunkFiles[path] !== this.chunkFiles[path]);
54
+ // Update files cache
55
+ Object.assign(this.chunkFiles, chunkFiles);
56
+ // Modified? -> Rebundle
57
+ if (chunkChanged) {
58
+ // Build with esbuild
59
+ await $esbuild.build({
60
+ sourcemap: Boolean(this.config.build.sourcemap),
61
+ ...chunkBuildOptions,
62
+ outfile: chunkPath,
63
+ entryPoints: [chunkPath],
64
+ bundle: true,
65
+ minify: false,
66
+ allowOverwrite: true,
67
+ plugins: [
68
+ ...(chunkBuildOptions.plugins ?? []),
69
+ {
70
+ name: 'logger',
71
+ setup: build => {
72
+ build.onEnd(result => {
73
+ if (result.errors.length > 0)
74
+ return;
75
+ const outDir = $chalk.dim(`${this.outDir}/`);
76
+ const fileName = $chalk.cyan(chunk.fileName);
77
+ const rebundleTag = $chalk.dim.cyan('rebundle');
78
+ console.log(`${outDir}${fileName} ${rebundleTag}`);
79
+ });
80
+ },
83
81
  },
84
- },
85
- ],
86
- });
87
- // Update chunk and corresponding sourcemap chunk
88
- chunk.code = await $fs.readFile(chunkPath, 'utf-8');
82
+ ],
83
+ });
84
+ // Save chunk content
85
+ this.rebundledContent[chunk.fileName] = await this.outRead(chunk.fileName);
86
+ // Save sourcemap content
87
+ if (chunk.sourcemapFileName) {
88
+ this.rebundledContent[chunk.sourcemapFileName] = await this.outRead(chunk.sourcemapFileName);
89
+ }
90
+ }
91
+ // Overwrite chunk
92
+ await this.outWrite(chunk.fileName, this.rebundledContent[chunk.fileName]);
93
+ chunk.code = this.rebundledContent[chunk.fileName];
94
+ // Overwrite sourcemap
89
95
  if (chunk.sourcemapFileName) {
90
- const sourcemapChunk = bundle[chunk.sourcemapFileName];
91
- if (sourcemapChunk.type !== 'asset')
96
+ const sourcemapAsset = bundle[chunk.sourcemapFileName];
97
+ if (sourcemapAsset.type !== 'asset')
92
98
  throw this.never;
93
- const sourcemapChunkPath = $path.join(this.outDir, chunk.sourcemapFileName);
94
- sourcemapChunk.source = await $fs.readFile(sourcemapChunkPath, 'utf-8');
99
+ await this.outWrite(chunk.sourcemapFileName, this.rebundledContent[chunk.sourcemapFileName]);
100
+ sourcemapAsset.source = this.rebundledContent[chunk.sourcemapFileName];
95
101
  }
96
102
  }));
103
+ // Get non-entry chunks (.js, .js.map)
104
+ const nonEntryChunks = Object.values(bundle)
105
+ .filter(chunkOrAsset => chunkOrAsset.type === 'chunk')
106
+ .filter(chunk => !chunk.isEntry);
97
107
  // Remove all non-entry chunks
98
108
  for (const chunk of nonEntryChunks) {
99
- // Remove file itself
100
- const path = $path.resolve(this.outDir, chunk.fileName);
101
- await $fs.unlink(path);
109
+ // Remove chunk
110
+ await $fs.unlink(this.outPath(chunk.fileName));
102
111
  delete bundle[chunk.fileName];
103
- // Remove sourcemap if any
112
+ // Remove sourcemap
104
113
  if (chunk.sourcemapFileName) {
105
- const sourcemapPath = $path.resolve(this.outDir, chunk.sourcemapFileName);
106
- await $fs.unlink(sourcemapPath);
114
+ await $fs.unlink(this.outPath(chunk.sourcemapFileName));
107
115
  delete bundle[chunk.sourcemapFileName];
108
116
  }
109
- // Remove containing directory if empty
110
- const dir = $path.dirname(path);
111
- const files = await $fs.readdir(dir);
112
- if (files.length === 0)
113
- await $fs.rmdir(dir);
117
+ // Remove containing directory if empty (recursively)
118
+ const dir = $path.dirname(this.outPath(chunk.fileName));
119
+ await this.removeDirectoryIfEmpty(dir);
114
120
  }
115
121
  };
116
122
  // ---------------------------------------------------------------------------
@@ -121,16 +127,32 @@ export class Rebundle {
121
127
  throw this.never;
122
128
  return this.config.build.outDir;
123
129
  }
124
- async getChunkContentMap(chunk) {
125
- const content = {};
126
- const usedPaths = [chunk.fileName, ...chunk.imports];
127
- await Promise.all(usedPaths.map(async (path) => {
128
- const fullPath = $path.join(this.outDir, path);
129
- content[path] = await $fs.readFile(fullPath, 'utf-8');
130
+ outPath(path) {
131
+ return $path.join(this.outDir, path);
132
+ }
133
+ async outRead(path) {
134
+ return await $fs.readFile(this.outPath(path), 'utf-8');
135
+ }
136
+ async outWrite(path, content) {
137
+ await $fs.writeFile(this.outPath(path), content, 'utf-8');
138
+ }
139
+ async getOptions() {
140
+ if (typeof this.options !== 'function')
141
+ return this.options;
142
+ return await this.options();
143
+ }
144
+ async readChunkFiles(chunk) {
145
+ const files = {};
146
+ await Promise.all([chunk.fileName, ...chunk.imports].map(async (path) => {
147
+ files[path] = await this.outRead(path);
130
148
  }));
131
- return content;
149
+ return files;
132
150
  }
133
- get never() {
134
- throw new Error('never');
151
+ async removeDirectoryIfEmpty(dir) {
152
+ const files = await $fs.readdir(dir);
153
+ if (files.length > 0)
154
+ return;
155
+ await $fs.rmdir(dir);
156
+ await this.removeDirectoryIfEmpty($path.dirname(dir));
135
157
  }
136
158
  }
package/package.json CHANGED
@@ -1,17 +1,21 @@
1
1
  {
2
2
  "name": "vite-plugin-rebundle",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "imkost",
7
7
  "description": "Vite plugin that forces single-file output per entry. Ensures each entry point is bundled into a standalone file without code-splitting.",
8
+ "keywords": [
9
+ "vite",
10
+ "vite-plugin"
11
+ ],
8
12
  "scripts": {
9
13
  "dev": "rm -rf dist && tsc --watch",
10
14
  "build": "rm -rf dist && tsc",
11
15
  "lint": "eslint src",
12
- "release": "npm run build && npm publish",
13
- "release:patch": "npm version patch && npm run release",
14
- "release:minor": "npm version minor && npm run release"
16
+ "release": "npm version patch && npm run release:raw",
17
+ "release:raw": "npm run build && npm publish",
18
+ "release:minor": "npm version minor && npm run release:raw"
15
19
  },
16
20
  "exports": {
17
21
  "import": "./dist/index.js"
@@ -19,11 +23,8 @@
19
23
  "files": [
20
24
  "dist"
21
25
  ],
22
- "keywords": [
23
- "vite",
24
- "vite-plugin"
25
- ],
26
26
  "dependencies": {
27
- "chalk": "^5.6.0"
27
+ "chalk": "^5.6.0",
28
+ "esbuild": "^0.25.9"
28
29
  }
29
30
  }