@path-ioc/pack 0.1.2 → 0.1.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hanlin Lian
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -15,12 +15,16 @@
15
15
  </p>
16
16
  </div>
17
17
 
18
+ > 💡 **Architectural Positioning & Usage Boundary**
19
+ > `@path-ioc/pack` is a dedicated Vite build plugin designed to bundle an isolated Mesh module directory into a **distributable standalone npm package / component library**.
20
+ > **Warning**: This plugin automatically intercepts Vite configuration and forces Library Mode (`build.lib`). **Do NOT unconditionally embed it in standard web application builds (e.g. standard Vite SPA/SSR configs).** It is recommended to use a dedicated packaging config (e.g. `vite.config.pack.ts`) or trigger it via separate packaging scripts.
21
+
18
22
  ---
19
23
 
20
- ## Features
24
+ ## Core Capabilities
21
25
 
22
26
  - **Physical Entrypoint Generation**:
23
- During Vite builds, automatically scans `src/modules` for physical directories containing `index.ts/tsx` and synthesizes clean on-disk entrypoints (default `.modular-plugin-entry.ts`).
27
+ During Vite builds, automatically scans `src/modules` for physical directories containing `index.ts/tsx` and synthesizes clean on-disk entrypoints (default `node_modules/.path-ioc/.modular-plugin-entry.ts`);
24
28
  - **Full Mesh Registry & Type Exports**:
25
29
  Generates unified module import aliases and exports the canonical runtime module registry array `modules`:
26
30
  ```typescript
@@ -30,9 +34,11 @@
30
34
  ];
31
35
  ```
32
36
  - **Static Type Mapping Extraction (`sharedMappings`)**:
33
- Fills `sharedMappings` and `sharedContainerMappings` to provide accurate type descriptions for cross-package SDKs or microfrontend mesh topologies.
34
- - **Physical Isolation & Bundler Obfuscation Compatibility**:
35
- Generated physical entrypoint files integrate seamlessly into standard npm packaging pipelines, fully compatible with downstream AST transformers and code protection tools.
37
+ Populates `sharedMappings` and `sharedContainerMappings` arrays to provide accurate type descriptions for cross-package SDKs or microfrontend mesh topologies;
38
+ - **Built-in Obfuscation Protection**:
39
+ During the `closeBundle` lifecycle, automatically performs deep code obfuscation using `javascript-obfuscator` by default. Can be disabled via environment variable `MODULAR_OBFUSCATE=false`;
40
+ - **Automated Packaging**:
41
+ Automatically synthesizes package-ready metadata (`package.json`, `index.d.ts`) and triggers `npm pack` in `outDir` to produce distribution `.tgz` tarballs.
36
42
 
37
43
  ---
38
44
 
@@ -48,7 +54,7 @@ npm install -D @path-ioc/pack
48
54
 
49
55
  ## Quick Start
50
56
 
51
- ### Vite Configuration (`vite.config.ts`)
57
+ ### Dedicated Packaging Config (`vite.config.pack.ts`)
52
58
 
53
59
  ```typescript
54
60
  import { defineConfig } from "vite";
@@ -61,7 +67,8 @@ export default defineConfig({
61
67
  plugins: [
62
68
  modularPackPlugin({
63
69
  modulesPath: "src/modules",
64
- // Optional: Custom on-disk entrypoint path (default: node_modules/.path-ioc/.modular-plugin-entry.ts)
70
+ outDir: "dist-plugin",
71
+ // Optional: Custom on-disk entrypoint path
65
72
  entryFile: "node_modules/.path-ioc/.modular-plugin-entry.ts",
66
73
  sharedMappings,
67
74
  sharedContainerMappings,
@@ -70,6 +77,11 @@ export default defineConfig({
70
77
  });
71
78
  ```
72
79
 
80
+ To build and package your mesh bundle:
81
+ ```bash
82
+ vite build --config vite.config.pack.ts
83
+ ```
84
+
73
85
  ---
74
86
 
75
87
  ## Configuration Options (`PackPluginOptions`)
@@ -78,11 +90,18 @@ export default defineConfig({
78
90
  | :--- | :--- | :--- | :--- |
79
91
  | **`modulesPath`** | `string` | `'src/modules'` | Root directory scanned for modular IoC entrypoints. |
80
92
  | **`entryFile`** | `string` | `'node_modules/.path-ioc/.modular-plugin-entry.ts'` | Target physical path for the generated entrypoint file. |
93
+ | **`outDir`** | `string` | `'dist-plugin'` | Output directory for the packaged library bundle and npm tarball. |
81
94
  | **`sharedMappings`** | `string[]` | `[]` *(optional)* | Reference to an array that receives generated type mapping strings. |
82
95
  | **`sharedContainerMappings`** | `string[]` | `[]` *(optional)* | Reference to an array that receives generated container type mapping strings. |
83
96
 
84
97
  ---
85
98
 
99
+ ## Environment Variables
100
+
101
+ - **`MODULAR_OBFUSCATE`**: Set to `false` (e.g. `MODULAR_OBFUSCATE=false vite build`) to disable the default `javascript-obfuscator` pass for debugging or open-source distribution.
102
+
103
+ ---
104
+
86
105
  ## Exported API
87
106
 
88
107
  ```typescript
package/README.zh-CN.md CHANGED
@@ -15,12 +15,16 @@
15
15
  </p>
16
16
  </div>
17
17
 
18
+ > 💡 **架构定位与使用边界警告**
19
+ > `@path-ioc/pack` 是专用于将 Mesh 模块目录打包为**独立发布的 npm 插件包/组件库**的专用 Vite 构建插件。
20
+ > **警告**:该插件会在构建期自动接管改写 Vite 的 Library 模式(`build.lib`)。**严禁无条件直接写入通用 Web 前端应用(如 SPA/SSR)的主构建配置中。** 推荐通过独立的打包配置(例如 `vite.config.pack.ts`)或专属构建脚本按需触发。
21
+
18
22
  ---
19
23
 
20
24
  ## 核心特性 (Features)
21
25
 
22
26
  - **物理入口自动生成 (Physical Entrypoint Generation)**:
23
- 在 Vite 构建流程中,自动深度扫描 `src/modules` 下所有包含 `index.ts/tsx` 的物理子目录,并在磁盘上自动生成/更新干净的入口文件(默认 `.modular-plugin-entry.ts`)。
27
+ 在 Vite 构建流程中,自动深度扫描 `src/modules` 下所有包含 `index.ts/tsx` 的物理子目录,并在磁盘上自动生成/更新入口文件(默认 `node_modules/.path-ioc/.modular-plugin-entry.ts`);
24
28
  - **Mesh 注册表与类型全导出**:
25
29
  自动生成统一的模块导入别名与导出,并导出标准运行期注册表数组 `modules`:
26
30
  ```typescript
@@ -30,9 +34,11 @@
30
34
  ];
31
35
  ```
32
36
  - **静态类型映射提取 (`sharedMappings`)**:
33
- 自动填充 `sharedMappings` 与 `sharedContainerMappings` 集合,为构建跨包 SDK 或微前端网格映射提供准确的类型描述。
34
- - **物理隔离与代码混淆兼容**:
35
- 物理生成的入口文件可直接用于 npm 打包流程,完美兼容各类 AST 物理打散与防篡改混淆工具。
37
+ 自动填充 `sharedMappings` 与 `sharedContainerMappings` 集合,为构建跨包 SDK 或微前端网格映射提供准确的类型描述;
38
+ - **内置源码深度混淆保护**:
39
+ 构建结束时(`closeBundle` 生命周期)默认集成 `javascript-obfuscator` 对产物代码进行深度混淆保护,可通过环境变量 `MODULAR_OBFUSCATE=false` 关闭;
40
+ - **交付包全自动打包**:
41
+ 自动生成交付标准的 `package.json` 与 `index.d.ts` 类型声明,并自动在 `outDir` 目录调用 `npm pack` 产出可分发的 `.tgz` 压缩包。
36
42
 
37
43
  ---
38
44
 
@@ -48,7 +54,7 @@ npm install -D @path-ioc/pack
48
54
 
49
55
  ## 快速上手 (Quick Start)
50
56
 
51
- ### Vite 配置 (`vite.config.ts`)
57
+ ### 专属 Vite 打包配置 (`vite.config.pack.ts`)
52
58
 
53
59
  ```typescript
54
60
  import { defineConfig } from "vite";
@@ -61,7 +67,8 @@ export default defineConfig({
61
67
  plugins: [
62
68
  modularPackPlugin({
63
69
  modulesPath: "src/modules",
64
- // 可选:自定义生成的入口文件路径(默认存放在 node_modules/.path-ioc/.modular-plugin-entry.ts)
70
+ outDir: "dist-plugin",
71
+ // 可选:自定义生成的入口文件路径
65
72
  entryFile: "node_modules/.path-ioc/.modular-plugin-entry.ts",
66
73
  sharedMappings,
67
74
  sharedContainerMappings,
@@ -70,6 +77,11 @@ export default defineConfig({
70
77
  });
71
78
  ```
72
79
 
80
+ 执行打包命令:
81
+ ```bash
82
+ vite build --config vite.config.pack.ts
83
+ ```
84
+
73
85
  ---
74
86
 
75
87
  ## 配置选项 (PackPluginOptions)
@@ -78,11 +90,18 @@ export default defineConfig({
78
90
  | :--- | :--- | :--- | :--- |
79
91
  | **`modulesPath`** | `string` | `'src/modules'` | 模块扫描的物理根目录路径。 |
80
92
  | **`entryFile`** | `string` | `'node_modules/.path-ioc/.modular-plugin-entry.ts'` | 物理生成的入口文件路径。 |
93
+ | **`outDir`** | `string` | `'dist-plugin'` | 构建产物输出与 npm 打包目录。 |
81
94
  | **`sharedMappings`** | `string[]` | `[]` *(可选)* | 接收生成的类型映射字符串数组引用。 |
82
95
  | **`sharedContainerMappings`** | `string[]` | `[]` *(可选)* | 接收生成的容器类型映射字符串数组引用。 |
83
96
 
84
97
  ---
85
98
 
99
+ ## 环境变量 (Environment Variables)
100
+
101
+ - **`MODULAR_OBFUSCATE`**:设置为 `false`(例如 `MODULAR_OBFUSCATE=false vite build --config vite.config.pack.ts`)可关闭默认的 `javascript-obfuscator` 混淆步骤,便于本地调试或开源包发布。
102
+
103
+ ---
104
+
86
105
  ## 导出的 API
87
106
 
88
107
  ```typescript
package/dist/index.cjs CHANGED
@@ -35,21 +35,31 @@ __export(index_exports, {
35
35
  module.exports = __toCommonJS(index_exports);
36
36
  var import_node_path = __toESM(require("path"), 1);
37
37
  var import_promises = __toESM(require("fs/promises"), 1);
38
+ var import_node_fs = __toESM(require("fs"), 1);
39
+ var import_node_child_process = require("child_process");
40
+ var import_node_util = require("util");
38
41
  var import_lodash_es = require("lodash-es");
42
+ var import_javascript_obfuscator = __toESM(require("javascript-obfuscator"), 1);
43
+ var execAsync = (0, import_node_util.promisify)(import_node_child_process.exec);
39
44
  function modularPackPlugin({
40
45
  modulesPath = "src/modules",
41
46
  entryFile = "node_modules/.path-ioc/.modular-plugin-entry.ts",
47
+ outDir = "dist-plugin",
42
48
  sharedMappings = [],
43
49
  sharedContainerMappings = []
44
50
  } = {}) {
45
51
  let projectRoot;
52
+ let entryFilePath;
53
+ let outputFullPath;
54
+ let savedEntryLines = [];
46
55
  return {
47
56
  name: "vite-plugin-modular-pack",
48
57
  enforce: "pre",
49
58
  async config(config) {
50
59
  projectRoot = import_node_path.default.resolve(config.root || process.cwd());
60
+ outputFullPath = import_node_path.default.resolve(projectRoot, outDir);
51
61
  const modulesRoot = import_node_path.default.resolve(projectRoot, modulesPath);
52
- const entryFilePath = import_node_path.default.resolve(projectRoot, entryFile);
62
+ entryFilePath = import_node_path.default.resolve(projectRoot, entryFile);
53
63
  const entryFileDir = import_node_path.default.dirname(entryFilePath);
54
64
  await import_promises.default.mkdir(entryFileDir, { recursive: true });
55
65
  const absoluteFolders = await searchIndexTsFiles(modulesRoot);
@@ -108,8 +118,156 @@ function modularPackPlugin({
108
118
  entryLines.push(runtimeModulesArray.join(",\n"));
109
119
  entryLines.push(`];
110
120
  `);
121
+ savedEntryLines = [...entryLines];
111
122
  await import_promises.default.writeFile(entryFilePath, entryLines.join("\n"), "utf-8");
112
123
  console.log(`\x1B[32m[ModularPack] Generated physical entry file: ${entryFile}\x1B[0m`);
124
+ if (!config.build) config.build = {};
125
+ config.build.outDir = outDir;
126
+ config.build.emptyOutDir = true;
127
+ config.build.lib = {
128
+ entry: entryFilePath,
129
+ formats: ["es"],
130
+ fileName: () => "index.js"
131
+ };
132
+ if (!config.build.rollupOptions) config.build.rollupOptions = {};
133
+ if (config.build.rollupOptions.input) {
134
+ delete config.build.rollupOptions.input;
135
+ }
136
+ let pkg = {};
137
+ try {
138
+ const pkgPath = import_node_path.default.resolve(projectRoot, "package.json");
139
+ pkg = JSON.parse(import_node_fs.default.readFileSync(pkgPath, "utf-8"));
140
+ } catch {
141
+ }
142
+ const externalDeps = [
143
+ ...Object.keys(pkg.dependencies || {}),
144
+ ...Object.keys(pkg.peerDependencies || {})
145
+ ];
146
+ const existingExternal = config.build.rollupOptions.external;
147
+ config.build.rollupOptions.external = (source, importer, isResolved) => {
148
+ if (source.includes("?worker")) return false;
149
+ const isExternalDep = externalDeps.includes(source) || externalDeps.some((dep) => source.startsWith(`${dep}/`));
150
+ if (isExternalDep) return true;
151
+ if (existingExternal) {
152
+ if (Array.isArray(existingExternal)) {
153
+ if (existingExternal.includes(source)) return true;
154
+ } else if (typeof existingExternal === "function") {
155
+ if (existingExternal(source, importer, isResolved)) return true;
156
+ } else if (existingExternal instanceof RegExp) {
157
+ if (existingExternal.test(source)) return true;
158
+ }
159
+ }
160
+ return false;
161
+ };
162
+ if (!config.build.rollupOptions.output) {
163
+ config.build.rollupOptions.output = {};
164
+ }
165
+ if (Array.isArray(config.build.rollupOptions.output)) {
166
+ config.build.rollupOptions.output.forEach((out) => {
167
+ out.entryFileNames = "index.js";
168
+ });
169
+ } else {
170
+ config.build.rollupOptions.output.entryFileNames = "index.js";
171
+ }
172
+ },
173
+ async closeBundle() {
174
+ try {
175
+ await import_promises.default.access(outputFullPath);
176
+ } catch {
177
+ return;
178
+ }
179
+ try {
180
+ const dtsPath = import_node_path.default.resolve(outputFullPath, "index.d.ts");
181
+ const dtsContent = [
182
+ `/// <reference types="@path-ioc/unplugin/virtual" />`,
183
+ ...savedEntryLines.filter((l) => !l.startsWith("//")),
184
+ `declare global {`,
185
+ ` interface ModuleMap {`,
186
+ ...sharedMappings,
187
+ ` }`,
188
+ ``,
189
+ ` interface ModularContainer {`,
190
+ ...sharedContainerMappings,
191
+ ` }`,
192
+ `}`,
193
+ ``,
194
+ `export {};`,
195
+ ``
196
+ ].join("\n");
197
+ await import_promises.default.writeFile(dtsPath, dtsContent, "utf-8");
198
+ console.log(`\x1B[32m[ModularPack] Generated declarations: ${import_node_path.default.relative(projectRoot, dtsPath)}\x1B[0m`);
199
+ } catch (err) {
200
+ console.warn("[ModularPack] Failed to generate index.d.ts:", err);
201
+ }
202
+ if (process.env.MODULAR_OBFUSCATE !== "false") {
203
+ try {
204
+ const jsFiles = await searchJsFiles(outputFullPath);
205
+ for (const file of jsFiles) {
206
+ let content = await import_promises.default.readFile(file, "utf-8");
207
+ content = content.replace(/process\.env\.NODE_ENV/g, "GLOBAL_VITE_PROCESS_ENV_NODE_ENV");
208
+ content = content.replace(/import\.meta\.env\.MODE/g, "GLOBAL_VITE_IMPORT_META_ENV_MODE");
209
+ const obfuscationResult = import_javascript_obfuscator.default.obfuscate(content, {
210
+ compact: true,
211
+ controlFlowFlattening: false,
212
+ deadCodeInjection: false,
213
+ identifierNamesGenerator: "hexadecimal",
214
+ renameGlobals: false,
215
+ selfDefending: false,
216
+ stringArray: true,
217
+ stringArrayEncoding: ["base64"],
218
+ transformObjectKeys: true,
219
+ unicodeEscapeSequence: false,
220
+ reservedStrings: ["\\.\\./assets/.*\\.js$", "\\./assets/.*\\.js$"]
221
+ });
222
+ let obfuscatedCode = obfuscationResult.getObfuscatedCode();
223
+ obfuscatedCode = obfuscatedCode.replace(/GLOBAL_VITE_PROCESS_ENV_NODE_ENV/g, "process.env.NODE_ENV");
224
+ obfuscatedCode = obfuscatedCode.replace(/GLOBAL_VITE_IMPORT_META_ENV_MODE/g, "import.meta.env.MODE");
225
+ await import_promises.default.writeFile(file, obfuscatedCode, "utf-8");
226
+ }
227
+ if (jsFiles.length > 0) {
228
+ console.log(`\x1B[32m[ModularPack] Obfuscated output JS files successfully!\x1B[0m`);
229
+ }
230
+ } catch (err) {
231
+ console.error("[ModularPack] Error during JS obfuscation:", err);
232
+ }
233
+ }
234
+ let pkg = {};
235
+ try {
236
+ const pkgPath = import_node_path.default.resolve(projectRoot, "package.json");
237
+ pkg = JSON.parse(await import_promises.default.readFile(pkgPath, "utf-8"));
238
+ } catch {
239
+ }
240
+ const mergedPeerDeps = {
241
+ ...pkg.dependencies || {},
242
+ ...pkg.peerDependencies || {}
243
+ };
244
+ const deliveryPkg = {
245
+ name: pkg.name || "modular-plugin",
246
+ version: pkg.version || "0.0.1",
247
+ type: "module",
248
+ main: "./index.js",
249
+ module: "./index.js",
250
+ types: "./index.d.ts",
251
+ files: ["index.js", "index.d.ts", "assets", "src"],
252
+ peerDependencies: mergedPeerDeps
253
+ };
254
+ try {
255
+ await import_promises.default.writeFile(
256
+ import_node_path.default.resolve(outputFullPath, "package.json"),
257
+ JSON.stringify(deliveryPkg, null, 2),
258
+ "utf-8"
259
+ );
260
+ } catch (err) {
261
+ console.warn("[ModularPack] Failed to generate delivery package.json:", err);
262
+ }
263
+ try {
264
+ await execAsync("npm pack", { cwd: outputFullPath });
265
+ console.log(`
266
+ \x1B[32m[ModularPack] Successfully built and packed vendor package! (Version: ${deliveryPkg.version})\x1B[0m
267
+ `);
268
+ } catch (err) {
269
+ console.warn(`[ModularPack] Failed to execute 'npm pack' in "${outDir}":`, err);
270
+ }
113
271
  }
114
272
  };
115
273
  }
@@ -141,6 +299,22 @@ var searchIndexTsFiles = async (rootPath) => {
141
299
  }
142
300
  return result;
143
301
  };
302
+ var searchJsFiles = async (dirPath) => {
303
+ const result = [];
304
+ try {
305
+ const entries = await import_promises.default.readdir(dirPath, { withFileTypes: true });
306
+ for (const entry of entries) {
307
+ const fullPath = import_node_path.default.join(dirPath, entry.name);
308
+ if (entry.isDirectory()) {
309
+ result.push(...await searchJsFiles(fullPath));
310
+ } else if (/\.(js|cjs|mjs)$/.test(entry.name) && !entry.name.includes("worker")) {
311
+ result.push(fullPath);
312
+ }
313
+ }
314
+ } catch {
315
+ }
316
+ return result;
317
+ };
144
318
  // Annotate the CommonJS export names for ESM import in node:
145
319
  0 && (module.exports = {
146
320
  modularPackPlugin
package/dist/index.d.cts CHANGED
@@ -1,12 +1,30 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
3
  interface PackPluginOptions {
4
+ /**
5
+ * Root directory to scan for modules containing index.ts or index.tsx.
6
+ * @default "src/modules"
7
+ */
4
8
  modulesPath?: string;
9
+ /**
10
+ * Destination path for the physical entry file.
11
+ * @default "node_modules/.path-ioc/.modular-plugin-entry.ts"
12
+ */
5
13
  entryFile?: string;
14
+ /**
15
+ * Output directory for the packaged library bundle.
16
+ * @default "dist-plugin"
17
+ */
18
+ outDir?: string;
19
+ /**
20
+ * Shared type mappings array reference (optional).
21
+ */
6
22
  sharedMappings?: string[];
23
+ /**
24
+ * Shared container type mappings array reference (optional).
25
+ */
7
26
  sharedContainerMappings?: string[];
8
27
  }
9
- type ModularPackPlugin = Pick<Plugin, "name" | "enforce" | "config">;
10
- declare function modularPackPlugin({ modulesPath, entryFile, sharedMappings, sharedContainerMappings, }?: PackPluginOptions): ModularPackPlugin;
28
+ declare function modularPackPlugin({ modulesPath, entryFile, outDir, sharedMappings, sharedContainerMappings, }?: PackPluginOptions): Plugin;
11
29
 
12
- export { type ModularPackPlugin, type PackPluginOptions, modularPackPlugin };
30
+ export { type PackPluginOptions, modularPackPlugin };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,30 @@
1
1
  import { Plugin } from 'vite';
2
2
 
3
3
  interface PackPluginOptions {
4
+ /**
5
+ * Root directory to scan for modules containing index.ts or index.tsx.
6
+ * @default "src/modules"
7
+ */
4
8
  modulesPath?: string;
9
+ /**
10
+ * Destination path for the physical entry file.
11
+ * @default "node_modules/.path-ioc/.modular-plugin-entry.ts"
12
+ */
5
13
  entryFile?: string;
14
+ /**
15
+ * Output directory for the packaged library bundle.
16
+ * @default "dist-plugin"
17
+ */
18
+ outDir?: string;
19
+ /**
20
+ * Shared type mappings array reference (optional).
21
+ */
6
22
  sharedMappings?: string[];
23
+ /**
24
+ * Shared container type mappings array reference (optional).
25
+ */
7
26
  sharedContainerMappings?: string[];
8
27
  }
9
- type ModularPackPlugin = Pick<Plugin, "name" | "enforce" | "config">;
10
- declare function modularPackPlugin({ modulesPath, entryFile, sharedMappings, sharedContainerMappings, }?: PackPluginOptions): ModularPackPlugin;
28
+ declare function modularPackPlugin({ modulesPath, entryFile, outDir, sharedMappings, sharedContainerMappings, }?: PackPluginOptions): Plugin;
11
29
 
12
- export { type ModularPackPlugin, type PackPluginOptions, modularPackPlugin };
30
+ export { type PackPluginOptions, modularPackPlugin };
package/dist/index.js CHANGED
@@ -1,21 +1,31 @@
1
1
  // src/index.ts
2
2
  import path from "path";
3
3
  import fs from "fs/promises";
4
+ import fsSync from "fs";
5
+ import { exec } from "child_process";
6
+ import { promisify } from "util";
4
7
  import { camelCase } from "lodash-es";
8
+ import JavaScriptObfuscator from "javascript-obfuscator";
9
+ var execAsync = promisify(exec);
5
10
  function modularPackPlugin({
6
11
  modulesPath = "src/modules",
7
12
  entryFile = "node_modules/.path-ioc/.modular-plugin-entry.ts",
13
+ outDir = "dist-plugin",
8
14
  sharedMappings = [],
9
15
  sharedContainerMappings = []
10
16
  } = {}) {
11
17
  let projectRoot;
18
+ let entryFilePath;
19
+ let outputFullPath;
20
+ let savedEntryLines = [];
12
21
  return {
13
22
  name: "vite-plugin-modular-pack",
14
23
  enforce: "pre",
15
24
  async config(config) {
16
25
  projectRoot = path.resolve(config.root || process.cwd());
26
+ outputFullPath = path.resolve(projectRoot, outDir);
17
27
  const modulesRoot = path.resolve(projectRoot, modulesPath);
18
- const entryFilePath = path.resolve(projectRoot, entryFile);
28
+ entryFilePath = path.resolve(projectRoot, entryFile);
19
29
  const entryFileDir = path.dirname(entryFilePath);
20
30
  await fs.mkdir(entryFileDir, { recursive: true });
21
31
  const absoluteFolders = await searchIndexTsFiles(modulesRoot);
@@ -74,8 +84,156 @@ function modularPackPlugin({
74
84
  entryLines.push(runtimeModulesArray.join(",\n"));
75
85
  entryLines.push(`];
76
86
  `);
87
+ savedEntryLines = [...entryLines];
77
88
  await fs.writeFile(entryFilePath, entryLines.join("\n"), "utf-8");
78
89
  console.log(`\x1B[32m[ModularPack] Generated physical entry file: ${entryFile}\x1B[0m`);
90
+ if (!config.build) config.build = {};
91
+ config.build.outDir = outDir;
92
+ config.build.emptyOutDir = true;
93
+ config.build.lib = {
94
+ entry: entryFilePath,
95
+ formats: ["es"],
96
+ fileName: () => "index.js"
97
+ };
98
+ if (!config.build.rollupOptions) config.build.rollupOptions = {};
99
+ if (config.build.rollupOptions.input) {
100
+ delete config.build.rollupOptions.input;
101
+ }
102
+ let pkg = {};
103
+ try {
104
+ const pkgPath = path.resolve(projectRoot, "package.json");
105
+ pkg = JSON.parse(fsSync.readFileSync(pkgPath, "utf-8"));
106
+ } catch {
107
+ }
108
+ const externalDeps = [
109
+ ...Object.keys(pkg.dependencies || {}),
110
+ ...Object.keys(pkg.peerDependencies || {})
111
+ ];
112
+ const existingExternal = config.build.rollupOptions.external;
113
+ config.build.rollupOptions.external = (source, importer, isResolved) => {
114
+ if (source.includes("?worker")) return false;
115
+ const isExternalDep = externalDeps.includes(source) || externalDeps.some((dep) => source.startsWith(`${dep}/`));
116
+ if (isExternalDep) return true;
117
+ if (existingExternal) {
118
+ if (Array.isArray(existingExternal)) {
119
+ if (existingExternal.includes(source)) return true;
120
+ } else if (typeof existingExternal === "function") {
121
+ if (existingExternal(source, importer, isResolved)) return true;
122
+ } else if (existingExternal instanceof RegExp) {
123
+ if (existingExternal.test(source)) return true;
124
+ }
125
+ }
126
+ return false;
127
+ };
128
+ if (!config.build.rollupOptions.output) {
129
+ config.build.rollupOptions.output = {};
130
+ }
131
+ if (Array.isArray(config.build.rollupOptions.output)) {
132
+ config.build.rollupOptions.output.forEach((out) => {
133
+ out.entryFileNames = "index.js";
134
+ });
135
+ } else {
136
+ config.build.rollupOptions.output.entryFileNames = "index.js";
137
+ }
138
+ },
139
+ async closeBundle() {
140
+ try {
141
+ await fs.access(outputFullPath);
142
+ } catch {
143
+ return;
144
+ }
145
+ try {
146
+ const dtsPath = path.resolve(outputFullPath, "index.d.ts");
147
+ const dtsContent = [
148
+ `/// <reference types="@path-ioc/unplugin/virtual" />`,
149
+ ...savedEntryLines.filter((l) => !l.startsWith("//")),
150
+ `declare global {`,
151
+ ` interface ModuleMap {`,
152
+ ...sharedMappings,
153
+ ` }`,
154
+ ``,
155
+ ` interface ModularContainer {`,
156
+ ...sharedContainerMappings,
157
+ ` }`,
158
+ `}`,
159
+ ``,
160
+ `export {};`,
161
+ ``
162
+ ].join("\n");
163
+ await fs.writeFile(dtsPath, dtsContent, "utf-8");
164
+ console.log(`\x1B[32m[ModularPack] Generated declarations: ${path.relative(projectRoot, dtsPath)}\x1B[0m`);
165
+ } catch (err) {
166
+ console.warn("[ModularPack] Failed to generate index.d.ts:", err);
167
+ }
168
+ if (process.env.MODULAR_OBFUSCATE !== "false") {
169
+ try {
170
+ const jsFiles = await searchJsFiles(outputFullPath);
171
+ for (const file of jsFiles) {
172
+ let content = await fs.readFile(file, "utf-8");
173
+ content = content.replace(/process\.env\.NODE_ENV/g, "GLOBAL_VITE_PROCESS_ENV_NODE_ENV");
174
+ content = content.replace(/import\.meta\.env\.MODE/g, "GLOBAL_VITE_IMPORT_META_ENV_MODE");
175
+ const obfuscationResult = JavaScriptObfuscator.obfuscate(content, {
176
+ compact: true,
177
+ controlFlowFlattening: false,
178
+ deadCodeInjection: false,
179
+ identifierNamesGenerator: "hexadecimal",
180
+ renameGlobals: false,
181
+ selfDefending: false,
182
+ stringArray: true,
183
+ stringArrayEncoding: ["base64"],
184
+ transformObjectKeys: true,
185
+ unicodeEscapeSequence: false,
186
+ reservedStrings: ["\\.\\./assets/.*\\.js$", "\\./assets/.*\\.js$"]
187
+ });
188
+ let obfuscatedCode = obfuscationResult.getObfuscatedCode();
189
+ obfuscatedCode = obfuscatedCode.replace(/GLOBAL_VITE_PROCESS_ENV_NODE_ENV/g, "process.env.NODE_ENV");
190
+ obfuscatedCode = obfuscatedCode.replace(/GLOBAL_VITE_IMPORT_META_ENV_MODE/g, "import.meta.env.MODE");
191
+ await fs.writeFile(file, obfuscatedCode, "utf-8");
192
+ }
193
+ if (jsFiles.length > 0) {
194
+ console.log(`\x1B[32m[ModularPack] Obfuscated output JS files successfully!\x1B[0m`);
195
+ }
196
+ } catch (err) {
197
+ console.error("[ModularPack] Error during JS obfuscation:", err);
198
+ }
199
+ }
200
+ let pkg = {};
201
+ try {
202
+ const pkgPath = path.resolve(projectRoot, "package.json");
203
+ pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
204
+ } catch {
205
+ }
206
+ const mergedPeerDeps = {
207
+ ...pkg.dependencies || {},
208
+ ...pkg.peerDependencies || {}
209
+ };
210
+ const deliveryPkg = {
211
+ name: pkg.name || "modular-plugin",
212
+ version: pkg.version || "0.0.1",
213
+ type: "module",
214
+ main: "./index.js",
215
+ module: "./index.js",
216
+ types: "./index.d.ts",
217
+ files: ["index.js", "index.d.ts", "assets", "src"],
218
+ peerDependencies: mergedPeerDeps
219
+ };
220
+ try {
221
+ await fs.writeFile(
222
+ path.resolve(outputFullPath, "package.json"),
223
+ JSON.stringify(deliveryPkg, null, 2),
224
+ "utf-8"
225
+ );
226
+ } catch (err) {
227
+ console.warn("[ModularPack] Failed to generate delivery package.json:", err);
228
+ }
229
+ try {
230
+ await execAsync("npm pack", { cwd: outputFullPath });
231
+ console.log(`
232
+ \x1B[32m[ModularPack] Successfully built and packed vendor package! (Version: ${deliveryPkg.version})\x1B[0m
233
+ `);
234
+ } catch (err) {
235
+ console.warn(`[ModularPack] Failed to execute 'npm pack' in "${outDir}":`, err);
236
+ }
79
237
  }
80
238
  };
81
239
  }
@@ -107,6 +265,22 @@ var searchIndexTsFiles = async (rootPath) => {
107
265
  }
108
266
  return result;
109
267
  };
268
+ var searchJsFiles = async (dirPath) => {
269
+ const result = [];
270
+ try {
271
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
272
+ for (const entry of entries) {
273
+ const fullPath = path.join(dirPath, entry.name);
274
+ if (entry.isDirectory()) {
275
+ result.push(...await searchJsFiles(fullPath));
276
+ } else if (/\.(js|cjs|mjs)$/.test(entry.name) && !entry.name.includes("worker")) {
277
+ result.push(fullPath);
278
+ }
279
+ }
280
+ } catch {
281
+ }
282
+ return result;
283
+ };
110
284
  export {
111
285
  modularPackPlugin
112
286
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@path-ioc/pack",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Mesh registry distribution bundler & obfuscator for Path-IoC",
5
5
  "keywords": [
6
6
  "ioc",
@@ -50,11 +50,11 @@
50
50
  }
51
51
  },
52
52
  "devDependencies": {
53
+ "@path-ioc/unplugin": "0.1.4",
53
54
  "@types/node": "^20.0.0",
54
- "vite": "^8.2.2",
55
55
  "tsup": "^8.0.0",
56
56
  "typescript": "^5.4.5",
57
- "@path-ioc/unplugin": "0.1.2"
57
+ "vite": "^8.2.2"
58
58
  },
59
59
  "scripts": {
60
60
  "build": "tsup src/index.ts --format cjs,esm --dts",