oxphobia 0.0.1 → 0.0.2
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/README.md +103 -0
- package/cli.js +138 -67
- package/package.json +5 -5
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# 📦 Oxphobia
|
|
2
|
+
|
|
3
|
+
**Oxphobia** is an ultra-fast JavaScript bundle size calculator powered by [Oxc (The Oxide JavaScript Tools)](https://github.com/oxc-project/oxc).
|
|
4
|
+
|
|
5
|
+
By leveraging the Rust-based `oxc-parser` and `oxc-minify`, it performs high-speed dependency analysis and minification to instantly measure the "Minified + Gzipped" size of npm packages or local projects.
|
|
6
|
+
|
|
7
|
+
## ✨ Features
|
|
8
|
+
|
|
9
|
+
* 🚀 **Blazing Fast**: Powered by the Rust-based Oxc toolchain for incredibly quick parsing and compression.
|
|
10
|
+
* ☁️ **npm Package Support**: Recursively fetches and calculates the size of specified packages via [jsDelivr](https://www.jsdelivr.com/) .
|
|
11
|
+
* 📂 **Local Project Support**: Run it at your project root to automatically detect entry points and estimate the total size including dependencies.
|
|
12
|
+
* 🌳 **Simple Tree-Shaking**: Analyzes conditional branches based on `process.env.NODE_ENV` to exclude unnecessary code from the bundle.
|
|
13
|
+
|
|
14
|
+
## 📦 Installation
|
|
15
|
+
|
|
16
|
+
You can run it directly via `npx` or install it globally.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Run directly
|
|
20
|
+
npx oxphobia [package-name]
|
|
21
|
+
pnpm dlx oxphobia [package-name]
|
|
22
|
+
yarn dlx oxphobia [package-name]
|
|
23
|
+
# or
|
|
24
|
+
dx npm:oxphobia [package-name]
|
|
25
|
+
# or
|
|
26
|
+
bunx oxphobia [package-name]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or install globally:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install -g oxphobia
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 🚀 Usage
|
|
36
|
+
|
|
37
|
+
### 1. Measure npm package size
|
|
38
|
+
|
|
39
|
+
Specify a package name to fetch sources from the CDN (jsDelivr) and calculate its size.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx oxphobia react
|
|
43
|
+
npx oxphobia lodash-es
|
|
44
|
+
npx oxphobia three
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Measure local project size
|
|
48
|
+
|
|
49
|
+
Run without arguments to read the `package.json` in the current directory. It will identify the entry point from the `main`, `module`, or `exports` fields for analysis.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cd my-awesome-project
|
|
53
|
+
npx oxphobia
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Example Output
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
📦 Analyzing Package: react
|
|
60
|
+
|
|
61
|
+
📥 Downloaded: https://cdn.jsdelivr.net/npm/react@18.2.0/index.js
|
|
62
|
+
🔎 Dependencies: ./cjs/react.production.min.js
|
|
63
|
+
📥 Downloaded: https://cdn.jsdelivr.net/npm/react@18.2.0/cjs/react.production.min.js
|
|
64
|
+
...
|
|
65
|
+
|
|
66
|
+
✅ Dependency resolution complete.
|
|
67
|
+
⏳ Minifying with Oxc Minify...
|
|
68
|
+
|
|
69
|
+
========================================
|
|
70
|
+
📊 Result for "react"
|
|
71
|
+
========================================
|
|
72
|
+
Files count : 2
|
|
73
|
+
Minified size : 6.42 KB (6,572 bytes)
|
|
74
|
+
Gzipped size : 2.75 KB (2,814 bytes)
|
|
75
|
+
========================================
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 🛠️ How It Works
|
|
79
|
+
|
|
80
|
+
1. **Parsing**: Uses `oxc-parser` to build an AST (Abstract Syntax Tree) and extracts dependencies from `import`, `require`, and `export` statements.
|
|
81
|
+
2. **Resolution**:
|
|
82
|
+
* Local files are read from the file system.
|
|
83
|
+
* External packages are resolved and downloaded via the jsDelivr API.
|
|
84
|
+
3. **Bundling**: Combines dependencies in-memory.
|
|
85
|
+
4. **Minification**: Compresses the code (Mangle & Compress) using `oxc-minify`.
|
|
86
|
+
5. **Measuring**: Compresses the minified code using Node.js `zlib` (Gzip) and calculates the final byte count.
|
|
87
|
+
|
|
88
|
+
## ⚠️ Limitations
|
|
89
|
+
|
|
90
|
+
* Non-JS assets such as CSS and images are ignored.
|
|
91
|
+
* Advanced bundler configurations (e.g., Webpack/Rollup/Vite plugins) are not supported. It only tracks pure JS/ESM dependencies.
|
|
92
|
+
|
|
93
|
+
## 💻 Requirements
|
|
94
|
+
|
|
95
|
+
* Node.js >= 18.12.0
|
|
96
|
+
|
|
97
|
+
## 🤝 Contributing
|
|
98
|
+
|
|
99
|
+
Pull requests are welcome!
|
|
100
|
+
|
|
101
|
+
## 📄 License
|
|
102
|
+
|
|
103
|
+
MIT
|
package/cli.js
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
3
|
+
import { parseSync } from 'oxc-parser';
|
|
4
|
+
import { minifySync } from 'oxc-minify';
|
|
5
5
|
import { gzipSync } from 'node:zlib';
|
|
6
6
|
import { Buffer } from 'node:buffer';
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
7
9
|
|
|
8
|
-
//
|
|
9
|
-
const args = process.argv.slice(2);
|
|
10
|
-
const pkg = args[0];
|
|
11
|
-
|
|
12
|
-
if (!pkg) {
|
|
13
|
-
console.error('\x1b[31mError: パッケージ名を指定してください。\x1b[0m');
|
|
14
|
-
console.log('Usage: node cli.js <package-name>');
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
|
|
10
|
+
// --- Logger ---
|
|
18
11
|
const log = {
|
|
19
12
|
info: (msg) => console.log(`\x1b[90m${msg}\x1b[0m`),
|
|
20
13
|
success: (msg) => console.log(`\x1b[32m${msg}\x1b[0m`),
|
|
@@ -24,11 +17,48 @@ const log = {
|
|
|
24
17
|
warn: (msg) => console.log(`\x1b[33m${msg}\x1b[0m`),
|
|
25
18
|
};
|
|
26
19
|
|
|
27
|
-
// ---
|
|
20
|
+
// --- Command Line Arguments & Mode Detection ---
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
let targetPkg = args[0];
|
|
23
|
+
let isLocalMode = false;
|
|
24
|
+
let localProjectRoot = process.cwd();
|
|
25
|
+
|
|
26
|
+
// 引数がない場合はローカルモードとして動作
|
|
27
|
+
if (!targetPkg) {
|
|
28
|
+
const localPkgJsonPath = path.join(localProjectRoot, 'package.json');
|
|
29
|
+
|
|
30
|
+
if (fs.existsSync(localPkgJsonPath)) {
|
|
31
|
+
try {
|
|
32
|
+
const pkgData = JSON.parse(fs.readFileSync(localPkgJsonPath, 'utf-8'));
|
|
33
|
+
targetPkg = pkgData.name || 'local-project';
|
|
34
|
+
isLocalMode = true;
|
|
35
|
+
|
|
36
|
+
// エントリーポイントの特定
|
|
37
|
+
let entry = pkgData.main || pkgData.module || pkgData.exports?.['.'] || 'index.js';
|
|
38
|
+
if (typeof entry === 'object') entry = entry.import || entry.default || 'index.js';
|
|
39
|
+
|
|
40
|
+
// ローカルのエントリーファイルを絶対パスとしてセット
|
|
41
|
+
targetPkg = path.resolve(localProjectRoot, entry);
|
|
42
|
+
|
|
43
|
+
log.info(`📂 Local project detected: ${pkgData.name || 'unnamed'}`);
|
|
44
|
+
log.info(`🚀 Entry point: ${path.relative(process.cwd(), targetPkg)}`);
|
|
45
|
+
} catch (e) {
|
|
46
|
+
log.error('❌ Failed to read package.json');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
console.error('\x1b[31mError: パッケージ名を指定するか、npmプロジェクトのルートで実行してください。\x1b[0m');
|
|
51
|
+
console.log('Usage: npx oxphobia <package-name>');
|
|
52
|
+
console.log(' npx oxphobia (inside a project)');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// --- AST Utilities (Oxc Walker) ---
|
|
28
58
|
|
|
29
59
|
function isProcessEnvNodeEnv(node) {
|
|
30
60
|
if (!node) return false;
|
|
31
|
-
// process.env.NODE_ENV
|
|
61
|
+
// process.env.NODE_ENV
|
|
32
62
|
if (node.type === 'MemberExpression') {
|
|
33
63
|
const propName = node.property && (node.property.name || node.property.value);
|
|
34
64
|
if (propName === 'NODE_ENV') {
|
|
@@ -49,7 +79,7 @@ function evaluateEnvCondition(node) {
|
|
|
49
79
|
if (!node || node.type !== 'BinaryExpression') return null;
|
|
50
80
|
|
|
51
81
|
const getString = (n) => {
|
|
52
|
-
if (n.type === 'Literal' && typeof n.value === 'string') return n.value;
|
|
82
|
+
if ((n.type === 'Literal' || n.type === 'StringLiteral') && typeof n.value === 'string') return n.value;
|
|
53
83
|
return null;
|
|
54
84
|
};
|
|
55
85
|
|
|
@@ -69,7 +99,7 @@ function evaluateEnvCondition(node) {
|
|
|
69
99
|
}
|
|
70
100
|
|
|
71
101
|
/**
|
|
72
|
-
*
|
|
102
|
+
* Oxc ASTを探索し、依存関係を抽出する
|
|
73
103
|
*/
|
|
74
104
|
function findDependencies(node, deps) {
|
|
75
105
|
if (!node || typeof node !== 'object') return;
|
|
@@ -79,7 +109,7 @@ function findDependencies(node, deps) {
|
|
|
79
109
|
return;
|
|
80
110
|
}
|
|
81
111
|
|
|
82
|
-
//
|
|
112
|
+
// process.env.NODE_ENV ハンドリング
|
|
83
113
|
if (node.type === 'IfStatement') {
|
|
84
114
|
const isProd = evaluateEnvCondition(node.test);
|
|
85
115
|
if (isProd === true) {
|
|
@@ -91,7 +121,7 @@ function findDependencies(node, deps) {
|
|
|
91
121
|
}
|
|
92
122
|
}
|
|
93
123
|
|
|
94
|
-
if (node.type === 'ConditionalExpression') {
|
|
124
|
+
if (node.type === 'ConditionalExpression') {
|
|
95
125
|
const isProd = evaluateEnvCondition(node.test);
|
|
96
126
|
if (isProd === true) {
|
|
97
127
|
findDependencies(node.consequent, deps);
|
|
@@ -102,10 +132,10 @@ function findDependencies(node, deps) {
|
|
|
102
132
|
}
|
|
103
133
|
}
|
|
104
134
|
|
|
105
|
-
// --- 依存関係の抽出 ---
|
|
106
135
|
const extractString = (n) => {
|
|
107
136
|
if (!n) return null;
|
|
108
|
-
|
|
137
|
+
// Oxc Parser uses 'StringLiteral' often, but also supports ESTree 'Literal'
|
|
138
|
+
if ((n.type === 'Literal' || n.type === 'StringLiteral') && typeof n.value === 'string') return n.value;
|
|
109
139
|
return null;
|
|
110
140
|
};
|
|
111
141
|
|
|
@@ -117,7 +147,7 @@ function findDependencies(node, deps) {
|
|
|
117
147
|
}
|
|
118
148
|
}
|
|
119
149
|
|
|
120
|
-
// Dynamic Import
|
|
150
|
+
// Dynamic Import: import('...')
|
|
121
151
|
if (node.type === 'ImportExpression') {
|
|
122
152
|
const val = extractString(node.source);
|
|
123
153
|
if (val) deps.add(val);
|
|
@@ -126,22 +156,22 @@ function findDependencies(node, deps) {
|
|
|
126
156
|
// CommonJS require()
|
|
127
157
|
if (node.type === 'CallExpression') {
|
|
128
158
|
const callee = node.callee;
|
|
159
|
+
// Oxc AST might represent callee differently if not standard ESTree, but usually Identifier works
|
|
129
160
|
const isRequire = callee && callee.type === 'Identifier' && callee.name === 'require';
|
|
130
161
|
|
|
131
162
|
if (isRequire && node.arguments && node.arguments.length > 0) {
|
|
163
|
+
// Oxc puts arguments in `arguments` vector
|
|
132
164
|
const val = extractString(node.arguments[0]);
|
|
133
165
|
if (val) deps.add(val);
|
|
134
166
|
}
|
|
135
167
|
}
|
|
136
168
|
|
|
137
|
-
//
|
|
169
|
+
// 再帰探索キー (Oxc AST構造に対応)
|
|
138
170
|
const keysToVisit = [
|
|
139
171
|
'body', 'declarations', 'init', 'expression', 'callee', 'arguments',
|
|
140
172
|
'consequent', 'alternate', 'test', 'left', 'right', 'source', 'specifiers',
|
|
141
|
-
'exported', 'local', 'imported', 'program',
|
|
142
|
-
'elements',
|
|
143
|
-
'properties', 'value', // オブジェクト内 { a: require('a') }
|
|
144
|
-
'block', 'handler', 'finalizer' // try-catch
|
|
173
|
+
'exported', 'local', 'imported', 'program', 'statements',
|
|
174
|
+
'elements', 'properties', 'value', 'block', 'handler', 'finalizer'
|
|
145
175
|
];
|
|
146
176
|
|
|
147
177
|
for (const key of keysToVisit) {
|
|
@@ -149,11 +179,10 @@ function findDependencies(node, deps) {
|
|
|
149
179
|
}
|
|
150
180
|
}
|
|
151
181
|
|
|
152
|
-
|
|
153
182
|
// --- Package Resolution ---
|
|
154
183
|
|
|
155
184
|
const JSDELIVR_BASE = "https://cdn.jsdelivr.net/npm/";
|
|
156
|
-
const NODE_BUILTINS = new Set(['fs', 'path', 'os', 'crypto', 'stream', 'http', 'https', 'zlib', 'url', 'util', 'buffer', 'events', 'assert', 'child_process', 'process', 'net', 'tls', 'dgram', 'dns', 'perf_hooks', 'worker_threads']);
|
|
185
|
+
const NODE_BUILTINS = new Set(['fs', 'path', 'os', 'crypto', 'stream', 'http', 'https', 'zlib', 'url', 'util', 'buffer', 'events', 'assert', 'child_process', 'process', 'net', 'tls', 'dgram', 'dns', 'perf_hooks', 'worker_threads', 'node:fs', 'node:path', 'node:process']);
|
|
157
186
|
|
|
158
187
|
function parseBareSpecifier(specifier) {
|
|
159
188
|
let pkgName = specifier, subpath = "";
|
|
@@ -189,7 +218,6 @@ async function resolvePackageUrl(specifier) {
|
|
|
189
218
|
function resolveExports(exp) {
|
|
190
219
|
if (typeof exp === 'string') return exp;
|
|
191
220
|
if (typeof exp === 'object' && exp !== null) {
|
|
192
|
-
// 優先順位: production -> node -> require -> default -> import -> browser
|
|
193
221
|
const conditions = ['production', 'node', 'require', 'default', 'import', 'browser'];
|
|
194
222
|
for (const cond of conditions) {
|
|
195
223
|
if (cond in exp) return resolveExports(exp[cond]);
|
|
@@ -236,7 +264,28 @@ let activeTasks = 0;
|
|
|
236
264
|
let onQueueEmpty = null;
|
|
237
265
|
let hasError = false;
|
|
238
266
|
|
|
267
|
+
// ローカルファイルかどうかを判定
|
|
268
|
+
const isLocalFile = (url) => !url.startsWith('http');
|
|
269
|
+
|
|
239
270
|
async function fetchFile(urls) {
|
|
271
|
+
// ローカルファイルの場合 (1つのパスしか来ない想定)
|
|
272
|
+
if (isLocalFile(urls[0])) {
|
|
273
|
+
const filePath = urls[0];
|
|
274
|
+
const extensions = ['', '.js', '.mjs', '.cjs', '.ts', '/index.js'];
|
|
275
|
+
|
|
276
|
+
for (const ext of extensions) {
|
|
277
|
+
const tryPath = filePath + ext;
|
|
278
|
+
if (fs.existsSync(tryPath) && fs.statSync(tryPath).isFile()) {
|
|
279
|
+
try {
|
|
280
|
+
const code = fs.readFileSync(tryPath, 'utf-8');
|
|
281
|
+
return { code, finalUrl: tryPath, isLocal: true };
|
|
282
|
+
} catch(e) { return null; }
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
throw new Error(`Local file not found: ${filePath}`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// リモート (jsDelivr) の場合
|
|
240
289
|
const tryFetch = async (u) => {
|
|
241
290
|
try {
|
|
242
291
|
const r = await fetch(u);
|
|
@@ -249,19 +298,33 @@ async function fetchFile(urls) {
|
|
|
249
298
|
if (!res && !url.match(/\.(js|mjs|cjs|ts)$/)) {
|
|
250
299
|
res = await tryFetch(url + '.js') || await tryFetch(url + '.mjs') || await tryFetch(url + '/index.js');
|
|
251
300
|
}
|
|
252
|
-
if (res) return { code: await res.text(), finalUrl: res.url };
|
|
301
|
+
if (res) return { code: await res.text(), finalUrl: res.url, isLocal: false };
|
|
253
302
|
}
|
|
254
303
|
throw new Error(`Fetch failed: ${urls[0]}`);
|
|
255
304
|
}
|
|
256
305
|
|
|
257
306
|
async function resolveUrl(url, baseUrl) {
|
|
258
|
-
if (url.startsWith('http')) return [url];
|
|
259
307
|
if (NODE_BUILTINS.has(url) || url.startsWith('node:')) return null;
|
|
260
|
-
|
|
308
|
+
|
|
309
|
+
// 絶対URL (http)
|
|
310
|
+
if (url.startsWith('http')) return [url];
|
|
311
|
+
|
|
312
|
+
// 相対パス (. or /)
|
|
261
313
|
if (url.startsWith('.') || url.startsWith('/')) {
|
|
262
|
-
if (!baseUrl) return [url];
|
|
314
|
+
if (!baseUrl) return [url]; // エントリーポイント等
|
|
315
|
+
|
|
316
|
+
// Baseがローカルファイルの場合 -> ファイルシステム上で解決
|
|
317
|
+
if (isLocalFile(baseUrl)) {
|
|
318
|
+
const dir = path.dirname(baseUrl);
|
|
319
|
+
return [path.resolve(dir, url)];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// BaseがURLの場合 -> URL結合
|
|
263
323
|
return [new URL(url, baseUrl).href];
|
|
264
324
|
}
|
|
325
|
+
|
|
326
|
+
// Bare Specifier (e.g. "react")
|
|
327
|
+
// ローカルモードでもリモートモードでも、外部パッケージは jsDelivr から引く仕様
|
|
265
328
|
return await resolvePackageUrl(url);
|
|
266
329
|
}
|
|
267
330
|
|
|
@@ -284,38 +347,42 @@ function enqueueFile(url, baseUrl) {
|
|
|
284
347
|
return;
|
|
285
348
|
}
|
|
286
349
|
|
|
287
|
-
const { code, finalUrl } = fetchResult;
|
|
350
|
+
const { code, finalUrl, isLocal } = fetchResult;
|
|
288
351
|
|
|
289
352
|
if (parsedUrls.has(finalUrl)) return;
|
|
290
353
|
parsedUrls.add(finalUrl);
|
|
291
354
|
if (primaryUrl !== finalUrl) parsedUrls.add(primaryUrl);
|
|
292
355
|
|
|
293
|
-
|
|
356
|
+
if (isLocal) {
|
|
357
|
+
log.fetch(` 📁 Read Local: ${path.relative(process.cwd(), finalUrl)}`);
|
|
358
|
+
} else {
|
|
359
|
+
log.fetch(` 📥 Downloaded: ${finalUrl}`);
|
|
360
|
+
}
|
|
361
|
+
|
|
294
362
|
bundleParts.push(code);
|
|
295
363
|
|
|
296
|
-
// --- パース (
|
|
297
|
-
let
|
|
364
|
+
// --- パース (Oxc Parserを使用) ---
|
|
365
|
+
let program;
|
|
298
366
|
try {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
367
|
+
// Oxc parseSync returns { program, errors }
|
|
368
|
+
const ret = parseSync(finalUrl, code, {
|
|
369
|
+
sourceType: 'module', // ESMを基本とする
|
|
370
|
+
sourceFilename: finalUrl
|
|
302
371
|
});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
ecmaVersion: 2022,
|
|
308
|
-
sourceType: 'script'
|
|
309
|
-
});
|
|
310
|
-
} catch (e2) {
|
|
311
|
-
log.warn(` ⚠️ Parse failed for ${finalUrl.split('/').pop()}: ${e2.message}`);
|
|
312
|
-
return; // パース失敗時は依存関係探索をスキップ
|
|
372
|
+
|
|
373
|
+
if (ret.errors.length > 0) {
|
|
374
|
+
// エラーがあってもASTが返る場合があるが、警告を出す
|
|
375
|
+
// log.warn(` ⚠️ Oxc Parse warnings for ${path.basename(finalUrl)}`);
|
|
313
376
|
}
|
|
377
|
+
program = ret.program;
|
|
378
|
+
} catch (e) {
|
|
379
|
+
log.warn(` ⚠️ Parse failed for ${path.basename(finalUrl)}: ${e.message}`);
|
|
380
|
+
return;
|
|
314
381
|
}
|
|
315
382
|
|
|
316
|
-
if (
|
|
383
|
+
if (program) {
|
|
317
384
|
const deps = new Set();
|
|
318
|
-
findDependencies(
|
|
385
|
+
findDependencies(program, deps);
|
|
319
386
|
|
|
320
387
|
if (deps.size > 0) {
|
|
321
388
|
log.analyze(` 🔎 Dependencies: ${Array.from(deps).join(', ')}`);
|
|
@@ -340,9 +407,10 @@ function enqueueFile(url, baseUrl) {
|
|
|
340
407
|
// --- Main Execution ---
|
|
341
408
|
|
|
342
409
|
async function run() {
|
|
343
|
-
|
|
410
|
+
const displayTarget = isLocalMode ? path.relative(process.cwd(), targetPkg) : targetPkg;
|
|
411
|
+
log.success(`\n📦 Analyzing Package: ${displayTarget}\n`);
|
|
344
412
|
|
|
345
|
-
enqueueFile(
|
|
413
|
+
enqueueFile(targetPkg, null);
|
|
346
414
|
|
|
347
415
|
await new Promise(resolve => {
|
|
348
416
|
if (activeTasks === 0) resolve();
|
|
@@ -350,29 +418,32 @@ async function run() {
|
|
|
350
418
|
});
|
|
351
419
|
|
|
352
420
|
if (bundleParts.length === 0) {
|
|
353
|
-
log.error("\n❌ No files were successfully
|
|
421
|
+
log.error("\n❌ No files were successfully processed.");
|
|
354
422
|
process.exit(1);
|
|
355
423
|
}
|
|
356
424
|
|
|
357
425
|
log.success(`\n✅ Dependency resolution complete.`);
|
|
358
|
-
log.analyze(`⏳ Minifying with
|
|
426
|
+
log.analyze(`⏳ Minifying with Oxc Minify...`);
|
|
359
427
|
|
|
360
428
|
let minifiedCode = "";
|
|
361
429
|
try {
|
|
362
430
|
const combinedCode = bundleParts.join('\n');
|
|
363
431
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
global_defs: { 'process.env.NODE_ENV': 'production' },
|
|
367
|
-
dead_code: true,
|
|
368
|
-
toplevel: false,
|
|
369
|
-
passes: 2
|
|
370
|
-
},
|
|
432
|
+
// Oxc Minify Execution
|
|
433
|
+
const result = minifySync("bundle.js", combinedCode, {
|
|
371
434
|
mangle: true,
|
|
372
|
-
|
|
435
|
+
compress: {
|
|
436
|
+
dead_code: true, // Oxc may have different option keys, usually defaults are good
|
|
437
|
+
drop_console: false
|
|
438
|
+
},
|
|
439
|
+
sourceMap: false
|
|
373
440
|
});
|
|
374
441
|
|
|
375
|
-
minifiedCode =
|
|
442
|
+
minifiedCode = result.code;
|
|
443
|
+
|
|
444
|
+
// もしMinify結果が空の場合(エラー時など)、生コードを使用
|
|
445
|
+
if (!minifiedCode) throw new Error("Empty output");
|
|
446
|
+
|
|
376
447
|
} catch(e) {
|
|
377
448
|
log.error(`⚠️ Minification failed: ${e.message}. Using raw size.`);
|
|
378
449
|
minifiedCode = bundleParts.join('\n');
|
|
@@ -382,7 +453,7 @@ async function run() {
|
|
|
382
453
|
const gzipBytes = gzipSync(Buffer.from(minifiedCode)).length;
|
|
383
454
|
|
|
384
455
|
console.log('\n========================================');
|
|
385
|
-
console.log(` 📊 \x1b[1mResult for "${
|
|
456
|
+
console.log(` 📊 \x1b[1mResult for "${isLocalMode ? 'Local Project' : targetPkg}"\x1b[0m`);
|
|
386
457
|
console.log('========================================');
|
|
387
458
|
console.log(` Files count : \x1b[32m${bundleParts.length}\x1b[0m`);
|
|
388
459
|
console.log(` Minified size : \x1b[33m${(minifiedBytes / 1024).toFixed(2)}\x1b[0m KB (${minifiedBytes.toLocaleString()} bytes)`);
|
|
@@ -390,4 +461,4 @@ async function run() {
|
|
|
390
461
|
console.log('========================================\n');
|
|
391
462
|
}
|
|
392
463
|
|
|
393
|
-
run();
|
|
464
|
+
run();
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oxphobia",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Calculate minified & gzipped bundle size via JSDelivr + Oxc
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Calculate minified & gzipped bundle size via JSDelivr + Oxc (Parser/Minifier)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"oxphobia": "./cli.js"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
9
|
"engines": {
|
|
10
|
-
"node": ">=18.
|
|
10
|
+
"node": ">=18.12.0"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"oxc-
|
|
14
|
-
"
|
|
13
|
+
"oxc-minify": "^0.114.0",
|
|
14
|
+
"oxc-parser": "^0.114.0"
|
|
15
15
|
}
|
|
16
16
|
}
|