@kt3k/tku 1.1.0 → 1.2.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.
Files changed (2) hide show
  1. package/dist/main.js +115 -4
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -163,10 +163,107 @@ 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 slashIdx = file.path.indexOf("/");
242
+ if (slashIdx === -1) continue;
243
+ const dir = file.path.slice(0, slashIdx);
244
+ dirMap.set(dir, (dirMap.get(dir) ?? 0) + file.tokens);
245
+ }
246
+ const files = [
247
+ ...dirMap.entries()
248
+ ].map(([path, tokens]) => ({
249
+ path,
250
+ tokens
251
+ }));
252
+ return {
253
+ ...result,
254
+ files
255
+ };
256
+ }
166
257
  function formatResult(result, options = {}) {
167
- const { json = false, top, sort = "tokens" } = options;
258
+ const { json = false, top, sort = "tokens", tree = false, dirs = false } = options;
259
+ if (tree) {
260
+ return formatTree(result, {
261
+ dirs
262
+ });
263
+ }
264
+ const effective = dirs ? summarizeByDir(result) : result;
168
265
  const sorted = [
169
- ...result.files
266
+ ...effective.files
170
267
  ];
171
268
  if (sort === "path") {
172
269
  sorted.sort((a, b) => a.path.localeCompare(b.path));
@@ -176,7 +273,7 @@ function formatResult(result, options = {}) {
176
273
  const filtered = top !== void 0 ? sorted.slice(0, top) : sorted;
177
274
  const omitted = sorted.length - filtered.length;
178
275
  const adjusted = {
179
- ...result,
276
+ ...effective,
180
277
  files: filtered
181
278
  };
182
279
  return json ? JSON.stringify(adjusted, null, 2) : formatTable(adjusted, omitted);
@@ -193,6 +290,9 @@ Options:
193
290
  --json Output results as JSON
194
291
  --top <n> Show only the top N files by token count
195
292
  --sort <field> Sort by "tokens" or "path" (default: tokens)
293
+ -t, --tree Display results as a directory tree
294
+ (ignores --top, --json, --sort)
295
+ --dirs Summarize by directory (table or tree)
196
296
  -h, --help Show this help message`);
197
297
  }
198
298
  async function main() {
@@ -223,6 +323,15 @@ async function main() {
223
323
  type: "string",
224
324
  default: "tokens"
225
325
  },
326
+ tree: {
327
+ type: "boolean",
328
+ short: "t",
329
+ default: false
330
+ },
331
+ dirs: {
332
+ type: "boolean",
333
+ default: false
334
+ },
226
335
  help: {
227
336
  type: "boolean",
228
337
  short: "h",
@@ -266,7 +375,9 @@ async function main() {
266
375
  const output = formatResult(result, {
267
376
  json: values.json,
268
377
  top,
269
- sort
378
+ sort,
379
+ tree: values.tree,
380
+ dirs: values.dirs
270
381
  });
271
382
  console.log(output);
272
383
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kt3k/tku",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "",
5
5
  "main": "dist/main.js",
6
6
  "bin": {