@kt3k/tku 1.2.0 → 1.2.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.
Files changed (2) hide show
  1. package/dist/main.js +117 -4
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -163,10 +163,109 @@ function formatTable(result, omitted = 0) {
163
163
  lines.push(`${totalDisplay.padStart(maxWidth)} total (${result.totalFiles} files)`);
164
164
  return lines.join("\n");
165
165
  }
166
+ function buildTree(files) {
167
+ const root = {
168
+ name: ".",
169
+ tokens: 0,
170
+ children: []
171
+ };
172
+ for (const file of files) {
173
+ const parts = file.path.split("/");
174
+ let node = root;
175
+ for (let i = 0; i < parts.length; i++) {
176
+ const name = parts[i];
177
+ const isFile = i === parts.length - 1;
178
+ let child = node.children.find((c) => c.name === name);
179
+ if (!child) {
180
+ child = {
181
+ name,
182
+ tokens: 0,
183
+ children: []
184
+ };
185
+ node.children.push(child);
186
+ }
187
+ if (isFile) {
188
+ child.tokens = file.tokens;
189
+ }
190
+ node = child;
191
+ }
192
+ }
193
+ function aggregate(node) {
194
+ if (node.children.length === 0) return node.tokens;
195
+ node.tokens = node.children.reduce((sum, c) => sum + aggregate(c), 0);
196
+ return node.tokens;
197
+ }
198
+ aggregate(root);
199
+ function sortChildren(node) {
200
+ for (const child of node.children) sortChildren(child);
201
+ const dirs = node.children.filter((c) => c.children.length > 0).sort((a, b) => b.tokens - a.tokens);
202
+ const leaves = node.children.filter((c) => c.children.length === 0).sort((a, b) => b.tokens - a.tokens);
203
+ node.children = [
204
+ ...dirs,
205
+ ...leaves
206
+ ];
207
+ }
208
+ sortChildren(root);
209
+ return root;
210
+ }
211
+ function formatTree(result, options = {}) {
212
+ const tree = buildTree(result.files);
213
+ const dirsOnly = options.dirs ?? false;
214
+ const lines = [];
215
+ const allTokens = [];
216
+ function collectTokens(node) {
217
+ if (dirsOnly && node.children.length === 0) return;
218
+ allTokens.push(node.tokens);
219
+ for (const child of node.children) collectTokens(child);
220
+ }
221
+ collectTokens(tree);
222
+ const maxWidth = Math.max("tokens".length, ...allTokens.map((t) => formatTokenCount(t).length));
223
+ function render(node, prefix, isLast, isRoot) {
224
+ const display = formatTokenCount(node.tokens);
225
+ const connector = isRoot ? "" : isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
226
+ const name = node.children.length > 0 && !isRoot ? `${node.name}/` : node.name;
227
+ lines.push(`${display.padStart(maxWidth)} ${prefix}${connector}${name}`);
228
+ const visibleChildren = dirsOnly ? node.children.filter((c) => c.children.length > 0) : node.children;
229
+ const childPrefix = isRoot ? "" : prefix + (isLast ? " " : "\u2502 ");
230
+ for (let i = 0; i < visibleChildren.length; i++) {
231
+ render(visibleChildren[i], childPrefix, i === visibleChildren.length - 1, false);
232
+ }
233
+ }
234
+ lines.push(`${"tokens".padStart(maxWidth)} path`);
235
+ render(tree, "", true, true);
236
+ return lines.join("\n");
237
+ }
238
+ function summarizeByDir(result) {
239
+ const dirMap = /* @__PURE__ */ new Map();
240
+ for (const file of result.files) {
241
+ const parts = file.path.split("/");
242
+ if (parts.length < 2) continue;
243
+ for (let i = 1; i < parts.length; i++) {
244
+ const dir = parts.slice(0, i).join("/");
245
+ dirMap.set(dir, (dirMap.get(dir) ?? 0) + file.tokens);
246
+ }
247
+ }
248
+ const files = [
249
+ ...dirMap.entries()
250
+ ].map(([path, tokens]) => ({
251
+ path,
252
+ tokens
253
+ }));
254
+ return {
255
+ ...result,
256
+ files
257
+ };
258
+ }
166
259
  function formatResult(result, options = {}) {
167
- const { json = false, top, sort = "tokens" } = options;
260
+ const { json = false, top, sort = "tokens", tree = false, dirs = false } = options;
261
+ if (tree) {
262
+ return formatTree(result, {
263
+ dirs
264
+ });
265
+ }
266
+ const effective = dirs ? summarizeByDir(result) : result;
168
267
  const sorted = [
169
- ...result.files
268
+ ...effective.files
170
269
  ];
171
270
  if (sort === "path") {
172
271
  sorted.sort((a, b) => a.path.localeCompare(b.path));
@@ -176,7 +275,7 @@ function formatResult(result, options = {}) {
176
275
  const filtered = top !== void 0 ? sorted.slice(0, top) : sorted;
177
276
  const omitted = sorted.length - filtered.length;
178
277
  const adjusted = {
179
- ...result,
278
+ ...effective,
180
279
  files: filtered
181
280
  };
182
281
  return json ? JSON.stringify(adjusted, null, 2) : formatTable(adjusted, omitted);
@@ -193,6 +292,9 @@ Options:
193
292
  --json Output results as JSON
194
293
  --top <n> Show only the top N files by token count
195
294
  --sort <field> Sort by "tokens" or "path" (default: tokens)
295
+ -t, --tree Display results as a directory tree
296
+ (ignores --top, --json, --sort)
297
+ --dirs Summarize by directory (table or tree)
196
298
  -h, --help Show this help message`);
197
299
  }
198
300
  async function main() {
@@ -223,6 +325,15 @@ async function main() {
223
325
  type: "string",
224
326
  default: "tokens"
225
327
  },
328
+ tree: {
329
+ type: "boolean",
330
+ short: "t",
331
+ default: false
332
+ },
333
+ dirs: {
334
+ type: "boolean",
335
+ default: false
336
+ },
226
337
  help: {
227
338
  type: "boolean",
228
339
  short: "h",
@@ -266,7 +377,9 @@ async function main() {
266
377
  const output = formatResult(result, {
267
378
  json: values.json,
268
379
  top,
269
- sort
380
+ sort,
381
+ tree: values.tree,
382
+ dirs: values.dirs
270
383
  });
271
384
  console.log(output);
272
385
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kt3k/tku",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "",
5
5
  "main": "dist/main.js",
6
6
  "bin": {