@craftpipe/contextpack 1.0.0
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/.contextpackrc.example.json +167 -0
- package/.env.example +5 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +15 -0
- package/.github/pull_request_template.md +9 -0
- package/CODE_OF_CONDUCT.md +40 -0
- package/CONTRIBUTING.md +59 -0
- package/LICENSE +21 -0
- package/README.md +100 -0
- package/SECURITY.md +21 -0
- package/index.js +428 -0
- package/lib/analyzer.js +547 -0
- package/lib/bundler.js +477 -0
- package/lib/config.js +269 -0
- package/lib/license.js +180 -0
- package/lib/premium/config-file.js +917 -0
- package/lib/premium/gate.js +13 -0
- package/lib/premium/html-report.js +1094 -0
- package/lib/premium/index.js +57 -0
- package/lib/premium/watch-mode.js +627 -0
- package/lib/scanner.js +480 -0
- package/lib/tokenizer.js +291 -0
- package/lib/validator.js +561 -0
- package/package.json +12 -0
- package/tests/analyzer.test.mjs +128 -0
- package/tests/bundler.test.mjs +126 -0
- package/tests/config.test.mjs +103 -0
- package/tests/gate.test.mjs +118 -0
- package/tests/index.test.mjs +103 -0
- package/tests/license.test.mjs +97 -0
- package/tests/scanner.test.mjs +110 -0
- package/tests/tokenizer.test.mjs +103 -0
- package/tests/validator.test.mjs +111 -0
- package/vitest.config.mjs +13 -0
package/lib/bundler.js
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { analyzeProject, buildDependencyGraph } = require('./analyzer');
|
|
5
|
+
const { scanProject } = require('./scanner');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates a compact context bundle from scanner and analyzer output
|
|
9
|
+
* @param {string} projectDir - Root directory of the project to bundle
|
|
10
|
+
* @param {object} options - Optional configuration options
|
|
11
|
+
* @param {string[]} [options.include] - Glob patterns to include
|
|
12
|
+
* @param {string[]} [options.exclude] - Glob patterns to exclude
|
|
13
|
+
* @param {boolean} [options.includeDependencyMap] - Whether to include dependency map
|
|
14
|
+
* @param {boolean} [options.includeSymbolIndex] - Whether to include symbol index
|
|
15
|
+
* @param {number} [options.maxFileSummaryLength] - Max characters per file summary
|
|
16
|
+
* @returns {object} Bundle object with fileSummaries, symbolIndex, dependencyMap, metadata
|
|
17
|
+
*/
|
|
18
|
+
function createBundle(projectDir, options) {
|
|
19
|
+
const opts = options || {};
|
|
20
|
+
const {
|
|
21
|
+
include,
|
|
22
|
+
exclude,
|
|
23
|
+
includeDependencyMap = true,
|
|
24
|
+
includeSymbolIndex = true,
|
|
25
|
+
maxFileSummaryLength = 500,
|
|
26
|
+
} = opts;
|
|
27
|
+
|
|
28
|
+
if (!projectDir || typeof projectDir !== 'string') {
|
|
29
|
+
return {
|
|
30
|
+
metadata: {
|
|
31
|
+
error: 'Invalid projectDir provided',
|
|
32
|
+
createdAt: new Date().toISOString(),
|
|
33
|
+
projectDir: projectDir || null,
|
|
34
|
+
fileCount: 0,
|
|
35
|
+
totalSize: 0,
|
|
36
|
+
},
|
|
37
|
+
fileSummaries: [],
|
|
38
|
+
symbolIndex: {},
|
|
39
|
+
dependencyMap: {},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let scanResult;
|
|
44
|
+
try {
|
|
45
|
+
scanResult = scanProject(projectDir, { include, exclude });
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return {
|
|
48
|
+
metadata: {
|
|
49
|
+
error: `Scan failed: ${err.message}`,
|
|
50
|
+
createdAt: new Date().toISOString(),
|
|
51
|
+
projectDir,
|
|
52
|
+
fileCount: 0,
|
|
53
|
+
totalSize: 0,
|
|
54
|
+
},
|
|
55
|
+
fileSummaries: [],
|
|
56
|
+
symbolIndex: {},
|
|
57
|
+
dependencyMap: {},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const files = (scanResult && Array.isArray(scanResult.files)) ? scanResult.files : [];
|
|
62
|
+
|
|
63
|
+
let analysisResult;
|
|
64
|
+
try {
|
|
65
|
+
analysisResult = analyzeProject(projectDir, { include, exclude });
|
|
66
|
+
} catch (err) {
|
|
67
|
+
analysisResult = { symbolMap: {}, dependencies: {} };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const symbolMap = (analysisResult && analysisResult.symbolMap) ? analysisResult.symbolMap : {};
|
|
71
|
+
const dependencies = (analysisResult && analysisResult.dependencies) ? analysisResult.dependencies : {};
|
|
72
|
+
|
|
73
|
+
let dependencyGraph = {};
|
|
74
|
+
if (includeDependencyMap) {
|
|
75
|
+
try {
|
|
76
|
+
dependencyGraph = buildDependencyGraph(projectDir, { include, exclude }) || {};
|
|
77
|
+
} catch (err) {
|
|
78
|
+
dependencyGraph = dependencies || {};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const fileSummaries = buildFileSummaries(files, symbolMap, projectDir, maxFileSummaryLength);
|
|
83
|
+
const symbolIndex = includeSymbolIndex ? buildSymbolIndex(symbolMap, projectDir) : {};
|
|
84
|
+
const dependencyMap = includeDependencyMap ? buildDependencyMap(dependencyGraph, projectDir) : {};
|
|
85
|
+
|
|
86
|
+
const totalSize = files.reduce((sum, f) => sum + ((f && f.size) ? f.size : 0), 0);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
metadata: {
|
|
90
|
+
createdAt: new Date().toISOString(),
|
|
91
|
+
projectDir,
|
|
92
|
+
fileCount: files.length,
|
|
93
|
+
totalSize,
|
|
94
|
+
version: '1.0.0',
|
|
95
|
+
},
|
|
96
|
+
fileSummaries,
|
|
97
|
+
symbolIndex,
|
|
98
|
+
dependencyMap,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Build file summaries from scan results and symbol map
|
|
104
|
+
* @param {object[]} files - Array of file objects from scanner
|
|
105
|
+
* @param {object} symbolMap - Symbol map from analyzer
|
|
106
|
+
* @param {string} projectDir - Root project directory
|
|
107
|
+
* @param {number} maxLength - Max summary length
|
|
108
|
+
* @returns {object[]} Array of file summary objects
|
|
109
|
+
*/
|
|
110
|
+
function buildFileSummaries(files, symbolMap, projectDir, maxLength) {
|
|
111
|
+
if (!Array.isArray(files)) return [];
|
|
112
|
+
|
|
113
|
+
return files.map(function (file) {
|
|
114
|
+
if (!file) return null;
|
|
115
|
+
|
|
116
|
+
const filePath = file.path || file.absolutePath || '';
|
|
117
|
+
const relPath = projectDir ? path.relative(projectDir, filePath).replace(/\\/g, '/') : filePath;
|
|
118
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
119
|
+
|
|
120
|
+
const fileSymbols = symbolMap[filePath] || symbolMap[relPath] || null;
|
|
121
|
+
|
|
122
|
+
const symbols = fileSymbols ? extractSymbolNames(fileSymbols) : [];
|
|
123
|
+
const imports = fileSymbols && fileSymbols.imports ? fileSymbols.imports : [];
|
|
124
|
+
const exports = fileSymbols && fileSymbols.exports ? fileSymbols.exports : [];
|
|
125
|
+
|
|
126
|
+
let summary = buildSummaryText(relPath, ext, symbols, exports, imports);
|
|
127
|
+
if (summary.length > maxLength) {
|
|
128
|
+
summary = summary.slice(0, maxLength - 3) + '...';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
path: relPath,
|
|
133
|
+
absolutePath: filePath,
|
|
134
|
+
size: file.size || 0,
|
|
135
|
+
extension: ext,
|
|
136
|
+
summary,
|
|
137
|
+
symbols,
|
|
138
|
+
exports,
|
|
139
|
+
imports,
|
|
140
|
+
};
|
|
141
|
+
}).filter(Boolean);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Extract symbol names from a file's symbol data
|
|
146
|
+
* @param {object} fileSymbols - Symbol data for a file
|
|
147
|
+
* @returns {string[]} Array of symbol names
|
|
148
|
+
*/
|
|
149
|
+
function extractSymbolNames(fileSymbols) {
|
|
150
|
+
if (!fileSymbols) return [];
|
|
151
|
+
|
|
152
|
+
const names = new Set();
|
|
153
|
+
|
|
154
|
+
if (Array.isArray(fileSymbols.functions)) {
|
|
155
|
+
fileSymbols.functions.forEach(function (fn) {
|
|
156
|
+
if (fn && fn.name) names.add(fn.name);
|
|
157
|
+
else if (typeof fn === 'string') names.add(fn);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (Array.isArray(fileSymbols.classes)) {
|
|
162
|
+
fileSymbols.classes.forEach(function (cls) {
|
|
163
|
+
if (cls && cls.name) names.add(cls.name);
|
|
164
|
+
else if (typeof cls === 'string') names.add(cls);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (Array.isArray(fileSymbols.symbols)) {
|
|
169
|
+
fileSymbols.symbols.forEach(function (sym) {
|
|
170
|
+
if (sym && sym.name) names.add(sym.name);
|
|
171
|
+
else if (typeof sym === 'string') names.add(sym);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return Array.from(names);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Build a human-readable summary text for a file
|
|
180
|
+
* @param {string} relPath - Relative file path
|
|
181
|
+
* @param {string} ext - File extension
|
|
182
|
+
* @param {string[]} symbols - Symbol names
|
|
183
|
+
* @param {string[]} exports - Export names
|
|
184
|
+
* @param {string[]} imports - Import sources
|
|
185
|
+
* @returns {string} Summary text
|
|
186
|
+
*/
|
|
187
|
+
function buildSummaryText(relPath, ext, symbols, exports, imports) {
|
|
188
|
+
const parts = [];
|
|
189
|
+
|
|
190
|
+
parts.push('File: ' + relPath);
|
|
191
|
+
|
|
192
|
+
if (ext) {
|
|
193
|
+
parts.push('Type: ' + ext.replace('.', '').toUpperCase());
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (symbols && symbols.length > 0) {
|
|
197
|
+
parts.push('Symbols: ' + symbols.slice(0, 10).join(', ') + (symbols.length > 10 ? ' ...' : ''));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (exports && exports.length > 0) {
|
|
201
|
+
const exportNames = exports.map(function (e) {
|
|
202
|
+
return (e && typeof e === 'object') ? (e.name || e.value || JSON.stringify(e)) : String(e);
|
|
203
|
+
});
|
|
204
|
+
parts.push('Exports: ' + exportNames.slice(0, 10).join(', ') + (exportNames.length > 10 ? ' ...' : ''));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (imports && imports.length > 0) {
|
|
208
|
+
const importSources = imports.map(function (imp) {
|
|
209
|
+
return (imp && typeof imp === 'object') ? (imp.source || imp.from || imp.module || JSON.stringify(imp)) : String(imp);
|
|
210
|
+
});
|
|
211
|
+
parts.push('Imports: ' + importSources.slice(0, 5).join(', ') + (importSources.length > 5 ? ' ...' : ''));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return parts.join(' | ');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Build a symbol index mapping symbol names to file paths
|
|
219
|
+
* @param {object} symbolMap - Symbol map from analyzer
|
|
220
|
+
* @param {string} projectDir - Root project directory
|
|
221
|
+
* @returns {object} Symbol index object
|
|
222
|
+
*/
|
|
223
|
+
function buildSymbolIndex(symbolMap, projectDir) {
|
|
224
|
+
if (!symbolMap || typeof symbolMap !== 'object') return {};
|
|
225
|
+
|
|
226
|
+
const index = {};
|
|
227
|
+
|
|
228
|
+
Object.keys(symbolMap).forEach(function (filePath) {
|
|
229
|
+
const fileSymbols = symbolMap[filePath];
|
|
230
|
+
if (!fileSymbols) return;
|
|
231
|
+
|
|
232
|
+
const relPath = projectDir
|
|
233
|
+
? path.relative(projectDir, filePath).replace(/\\/g, '/')
|
|
234
|
+
: filePath;
|
|
235
|
+
|
|
236
|
+
const symbolNames = extractSymbolNames(fileSymbols);
|
|
237
|
+
|
|
238
|
+
symbolNames.forEach(function (name) {
|
|
239
|
+
if (!name) return;
|
|
240
|
+
if (!index[name]) {
|
|
241
|
+
index[name] = [];
|
|
242
|
+
}
|
|
243
|
+
if (!index[name].includes(relPath)) {
|
|
244
|
+
index[name].push(relPath);
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
if (Array.isArray(fileSymbols.exports)) {
|
|
249
|
+
fileSymbols.exports.forEach(function (exp) {
|
|
250
|
+
const expName = (exp && typeof exp === 'object') ? (exp.name || exp.value) : exp;
|
|
251
|
+
if (!expName || typeof expName !== 'string') return;
|
|
252
|
+
if (!index[expName]) {
|
|
253
|
+
index[expName] = [];
|
|
254
|
+
}
|
|
255
|
+
if (!index[expName].includes(relPath)) {
|
|
256
|
+
index[expName].push(relPath);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
return index;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Build a dependency map from the dependency graph
|
|
267
|
+
* @param {object} dependencyGraph - Dependency graph from analyzer
|
|
268
|
+
* @param {string} projectDir - Root project directory
|
|
269
|
+
* @returns {object} Dependency map with relative paths
|
|
270
|
+
*/
|
|
271
|
+
function buildDependencyMap(dependencyGraph, projectDir) {
|
|
272
|
+
if (!dependencyGraph || typeof dependencyGraph !== 'object') return {};
|
|
273
|
+
|
|
274
|
+
const map = {};
|
|
275
|
+
|
|
276
|
+
Object.keys(dependencyGraph).forEach(function (filePath) {
|
|
277
|
+
const deps = dependencyGraph[filePath];
|
|
278
|
+
if (!deps) return;
|
|
279
|
+
|
|
280
|
+
const relPath = projectDir
|
|
281
|
+
? path.relative(projectDir, filePath).replace(/\\/g, '/')
|
|
282
|
+
: filePath;
|
|
283
|
+
|
|
284
|
+
const relDeps = Array.isArray(deps)
|
|
285
|
+
? deps.map(function (dep) {
|
|
286
|
+
if (!dep || typeof dep !== 'string') return dep;
|
|
287
|
+
if (dep.startsWith('.') || path.isAbsolute(dep)) {
|
|
288
|
+
try {
|
|
289
|
+
return projectDir
|
|
290
|
+
? path.relative(projectDir, dep).replace(/\\/g, '/')
|
|
291
|
+
: dep;
|
|
292
|
+
} catch (e) {
|
|
293
|
+
return dep;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return dep;
|
|
297
|
+
}).filter(Boolean)
|
|
298
|
+
: [];
|
|
299
|
+
|
|
300
|
+
map[relPath] = relDeps;
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
return map;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Formats a bundle as a JSON string
|
|
308
|
+
* @param {object} bundle - Bundle object from createBundle
|
|
309
|
+
* @param {object} options - Formatting options
|
|
310
|
+
* @param {boolean} [options.pretty] - Whether to pretty-print the JSON (default: true)
|
|
311
|
+
* @returns {string} JSON string representation of the bundle
|
|
312
|
+
*/
|
|
313
|
+
function formatAsJSON(bundle, options) {
|
|
314
|
+
const opts = options || {};
|
|
315
|
+
const pretty = opts.pretty !== false;
|
|
316
|
+
|
|
317
|
+
if (!bundle || typeof bundle !== 'object') {
|
|
318
|
+
return pretty ? JSON.stringify({ error: 'Invalid bundle' }, null, 2) : JSON.stringify({ error: 'Invalid bundle' });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
return pretty ? JSON.stringify(bundle, null, 2) : JSON.stringify(bundle);
|
|
323
|
+
} catch (err) {
|
|
324
|
+
const fallback = { error: 'Failed to serialize bundle: ' + err.message };
|
|
325
|
+
return pretty ? JSON.stringify(fallback, null, 2) : JSON.stringify(fallback);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Formats a bundle as a Markdown document
|
|
331
|
+
* @param {object} bundle - Bundle object from createBundle
|
|
332
|
+
* @param {object} options - Formatting options
|
|
333
|
+
* @param {string} [options.title] - Document title (default: 'ContextPack Bundle')
|
|
334
|
+
* @param {boolean} [options.includeSymbolIndex] - Whether to include symbol index section
|
|
335
|
+
* @param {boolean} [options.includeDependencyMap] - Whether to include dependency map section
|
|
336
|
+
* @returns {string} Markdown string representation of the bundle
|
|
337
|
+
*/
|
|
338
|
+
function formatAsMarkdown(bundle, options) {
|
|
339
|
+
const opts = options || {};
|
|
340
|
+
const {
|
|
341
|
+
title = 'ContextPack Bundle',
|
|
342
|
+
includeSymbolIndex = true,
|
|
343
|
+
includeDependencyMap = true,
|
|
344
|
+
} = opts;
|
|
345
|
+
|
|
346
|
+
if (!bundle || typeof bundle !== 'object') {
|
|
347
|
+
return '# Error\n\nInvalid bundle provided.\n';
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const lines = [];
|
|
351
|
+
|
|
352
|
+
lines.push('# ' + title);
|
|
353
|
+
lines.push('');
|
|
354
|
+
|
|
355
|
+
const meta = bundle.metadata || {};
|
|
356
|
+
lines.push('## Metadata');
|
|
357
|
+
lines.push('');
|
|
358
|
+
lines.push('| Property | Value |');
|
|
359
|
+
lines.push('|----------|-------|');
|
|
360
|
+
lines.push('| Created | ' + (meta.createdAt || 'N/A') + ' |');
|
|
361
|
+
lines.push('| Project | ' + (meta.projectDir || 'N/A') + ' |');
|
|
362
|
+
lines.push('| Files | ' + (meta.fileCount != null ? meta.fileCount : 'N/A') + ' |');
|
|
363
|
+
lines.push('| Total Size | ' + formatBytes(meta.totalSize || 0) + ' |');
|
|
364
|
+
if (meta.version) {
|
|
365
|
+
lines.push('| Version | ' + meta.version + ' |');
|
|
366
|
+
}
|
|
367
|
+
if (meta.error) {
|
|
368
|
+
lines.push('| Error | ' + meta.error + ' |');
|
|
369
|
+
}
|
|
370
|
+
lines.push('');
|
|
371
|
+
|
|
372
|
+
const fileSummaries = Array.isArray(bundle.fileSummaries) ? bundle.fileSummaries : [];
|
|
373
|
+
lines.push('## File Summaries');
|
|
374
|
+
lines.push('');
|
|
375
|
+
|
|
376
|
+
if (fileSummaries.length === 0) {
|
|
377
|
+
lines.push('_No files found._');
|
|
378
|
+
lines.push('');
|
|
379
|
+
} else {
|
|
380
|
+
fileSummaries.forEach(function (file) {
|
|
381
|
+
if (!file) return;
|
|
382
|
+
lines.push('### `' + (file.path || 'unknown') + '`');
|
|
383
|
+
lines.push('');
|
|
384
|
+
lines.push('- **Size:** ' + formatBytes(file.size || 0));
|
|
385
|
+
lines.push('- **Type:** ' + ((file.extension || '').replace('.', '').toUpperCase() || 'Unknown'));
|
|
386
|
+
|
|
387
|
+
if (file.symbols && file.symbols.length > 0) {
|
|
388
|
+
lines.push('- **Symbols:** ' + file.symbols.join(', '));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (file.exports && file.exports.length > 0) {
|
|
392
|
+
const exportNames = file.exports.map(function (e) {
|
|
393
|
+
return (e && typeof e === 'object') ? (e.name || e.value || JSON.stringify(e)) : String(e);
|
|
394
|
+
});
|
|
395
|
+
lines.push('- **Exports:** ' + exportNames.join(', '));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (file.imports && file.imports.length > 0) {
|
|
399
|
+
const importSources = file.imports.map(function (imp) {
|
|
400
|
+
return (imp && typeof imp === 'object') ? (imp.source || imp.from || imp.module || JSON.stringify(imp)) : String(imp);
|
|
401
|
+
});
|
|
402
|
+
lines.push('- **Imports:** ' + importSources.join(', '));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (file.summary) {
|
|
406
|
+
lines.push('');
|
|
407
|
+
lines.push('> ' + file.summary.replace(/\n/g, ' '));
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
lines.push('');
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (includeSymbolIndex) {
|
|
415
|
+
const symbolIndex = (bundle.symbolIndex && typeof bundle.symbolIndex === 'object') ? bundle.symbolIndex : {};
|
|
416
|
+
const symbolKeys = Object.keys(symbolIndex);
|
|
417
|
+
|
|
418
|
+
lines.push('## Symbol Index');
|
|
419
|
+
lines.push('');
|
|
420
|
+
|
|
421
|
+
if (symbolKeys.length === 0) {
|
|
422
|
+
lines.push('_No symbols found._');
|
|
423
|
+
lines.push('');
|
|
424
|
+
} else {
|
|
425
|
+
lines.push('| Symbol | Defined In |');
|
|
426
|
+
lines.push('|--------|-----------|');
|
|
427
|
+
symbolKeys.sort().forEach(function (sym) {
|
|
428
|
+
const locations = Array.isArray(symbolIndex[sym]) ? symbolIndex[sym] : [symbolIndex[sym]];
|
|
429
|
+
lines.push('| `' + sym + '` | ' + locations.map(function (l) { return '`' + l + '`'; }).join(', ') + ' |');
|
|
430
|
+
});
|
|
431
|
+
lines.push('');
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (includeDependencyMap) {
|
|
436
|
+
const dependencyMap = (bundle.dependencyMap && typeof bundle.dependencyMap === 'object') ? bundle.dependencyMap : {};
|
|
437
|
+
const depKeys = Object.keys(dependencyMap);
|
|
438
|
+
|
|
439
|
+
lines.push('## Dependency Map');
|
|
440
|
+
lines.push('');
|
|
441
|
+
|
|
442
|
+
if (depKeys.length === 0) {
|
|
443
|
+
lines.push('_No dependencies found._');
|
|
444
|
+
lines.push('');
|
|
445
|
+
} else {
|
|
446
|
+
depKeys.sort().forEach(function (file) {
|
|
447
|
+
const deps = Array.isArray(dependencyMap[file]) ? dependencyMap[file] : [];
|
|
448
|
+
lines.push('**`' + file + '`**');
|
|
449
|
+
if (deps.length === 0) {
|
|
450
|
+
lines.push('- _(no dependencies)_');
|
|
451
|
+
} else {
|
|
452
|
+
deps.forEach(function (dep) {
|
|
453
|
+
lines.push('- `' + dep + '`');
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
lines.push('');
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return lines.join('\n');
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Format bytes into a human-readable string
|
|
466
|
+
* @param {number} bytes - Number of bytes
|
|
467
|
+
* @returns {string} Human-readable size string
|
|
468
|
+
*/
|
|
469
|
+
function formatBytes(bytes) {
|
|
470
|
+
if (!bytes || bytes === 0) return '0 B';
|
|
471
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
472
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
473
|
+
const val = (bytes / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 1);
|
|
474
|
+
return val + ' ' + (units[i] || 'B');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
module.exports = { createBundle, formatAsJSON, formatAsMarkdown };
|