zshy 0.0.4 → 0.0.6

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/out/index.js CHANGED
@@ -1,22 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import { globby } from "globby";
3
- import { table } from "table";
4
2
  import * as fs from "node:fs";
5
3
  import * as path from "node:path/posix";
6
- import * as ts from "typescript";
7
4
  import parseArgs from "arg";
8
- import { getEntryPoints, compileProject, readTsconfig } from "./utils.js";
5
+ import { globby } from "globby";
6
+ import { table } from "table";
7
+ import * as ts from "typescript";
8
+ import { compileProject, formatForLog, getEntryPoints, readTsconfig } from "./utils.js";
9
9
  function isSourceFile(filePath) {
10
- return (filePath.endsWith(".ts") ||
11
- filePath.endsWith(".mts") ||
12
- filePath.endsWith(".cts") ||
13
- filePath.endsWith(".tsx"));
10
+ return (filePath.endsWith(".ts") || filePath.endsWith(".mts") || filePath.endsWith(".cts") || filePath.endsWith(".tsx"));
14
11
  }
15
12
  function removeExtension(filePath) {
16
13
  return filePath.split(".").slice(0, -1).join(".") || filePath;
17
14
  }
18
15
  async function main() {
19
- console.log("💎 Starting zshy build...");
20
16
  ///////////////////////////////////
21
17
  /// parse command line args ///
22
18
  ///////////////////////////////////
@@ -25,8 +21,12 @@ async function main() {
25
21
  try {
26
22
  args = parseArgs({
27
23
  "--help": Boolean,
24
+ "--verbose": Boolean,
25
+ "--project": String,
26
+ "--dry-run": Boolean,
28
27
  // Aliases
29
28
  "-h": "--help",
29
+ "-p": "--project",
30
30
  });
31
31
  }
32
32
  catch (error) {
@@ -43,12 +43,27 @@ Usage: zshy [options]
43
43
 
44
44
  Options:
45
45
  -h, --help Show this help message
46
+ -p, --project <path> Path to tsconfig.json file
47
+ --verbose Enable verbose output
48
+ --dry-run Don't write any files, just log what would be done
46
49
 
47
50
  Examples:
48
- zshy # Use ./tsconfig.json or package.json#zshy.tsconfig
51
+ zshy # Run build
52
+ zshy --project ./tsconfig.build.json # Use specific tsconfig file (defaults to tsconfig.json)
53
+ zshy --verbose # Enable verbose logging
54
+ zshy --dry-run # Preview changes without writing files
49
55
  `);
50
56
  process.exit(0);
51
57
  }
58
+ console.log("💎 Starting zshy build...");
59
+ const isVerbose = args["--verbose"];
60
+ const isDryRun = args["--dry-run"];
61
+ if (isVerbose) {
62
+ console.log("🗣️ Verbose mode enabled");
63
+ }
64
+ if (isDryRun) {
65
+ console.log("🔍 Dry run mode enabled - no files will be written");
66
+ }
52
67
  ///////////////////////////////////
53
68
  /// find and read pkg json ///
54
69
  ///////////////////////////////////
@@ -95,13 +110,26 @@ Examples:
95
110
  config.exports = { ".": config.exports };
96
111
  }
97
112
  else if (typeof config.exports === "undefined") {
98
- console.error(`❌ Missing "exports" key in package.json#${CONFIG_KEY}`);
113
+ console.error(`❌ Missing "exports" key in package.json#/${CONFIG_KEY}`);
99
114
  process.exit(1);
100
115
  }
101
116
  else if (typeof config.exports !== "object") {
102
- console.error(`❌ Invalid "exports" key in package.json#${CONFIG_KEY}`);
117
+ console.error(`❌ Invalid "exports" key in package.json#/${CONFIG_KEY}`);
103
118
  process.exit(1);
104
119
  }
120
+ // Validate bin field if present
121
+ if (config.bin !== undefined) {
122
+ if (typeof config.bin === "string") {
123
+ // Keep string format - we'll handle this in entry point extraction
124
+ }
125
+ else if (typeof config.bin === "object" && config.bin !== null) {
126
+ // Object format is valid
127
+ }
128
+ else {
129
+ console.error(`❌ Invalid "bin" key in package.json#/${CONFIG_KEY}, expected string or object`);
130
+ process.exit(1);
131
+ }
132
+ }
105
133
  // {
106
134
  // exports: { ".": pkgJson[CONFIG_KEY].exports },
107
135
  // };
@@ -114,13 +142,34 @@ Examples:
114
142
  console.error(`❌ Invalid "${CONFIG_KEY}" key in package.json, expected string or object`);
115
143
  process.exit(1);
116
144
  }
145
+ if (isVerbose) {
146
+ console.log("🔧 Parsed zshy config:", formatForLog(config));
147
+ }
117
148
  ///////////////////////////
118
149
  /// read tsconfig ///
119
150
  ///////////////////////////
120
151
  // Determine tsconfig.json path
121
152
  let tsconfigPath;
122
- if (config.tsconfig) {
123
- // Resolve the provided tsconfig path against package.json directory
153
+ if (args["--project"]) {
154
+ // CLI flag takes priority
155
+ const resolvedProjectPath = path.resolve(process.cwd(), args["--project"]);
156
+ if (fs.existsSync(resolvedProjectPath)) {
157
+ if (fs.statSync(resolvedProjectPath).isDirectory()) {
158
+ console.error(`❌ --project must point to a tsconfig.json file, not a directory: ${resolvedProjectPath}`);
159
+ process.exit(1);
160
+ }
161
+ else {
162
+ // Use the file directly
163
+ tsconfigPath = resolvedProjectPath;
164
+ }
165
+ }
166
+ else {
167
+ console.error(`❌ tsconfig.json file not found: ${resolvedProjectPath}`);
168
+ process.exit(1);
169
+ }
170
+ }
171
+ else if (config.tsconfig) {
172
+ // Fallback to package.json config
124
173
  const resolvedProjectPath = path.resolve(pkgJsonDir, config.tsconfig);
125
174
  if (fs.existsSync(resolvedProjectPath)) {
126
175
  if (fs.statSync(resolvedProjectPath).isDirectory()) {
@@ -166,6 +215,34 @@ Examples:
166
215
  const relOutDir = path.relative(pkgJsonDir, outDir);
167
216
  const declarationDir = path.resolve(pkgJsonDir, _parsedConfig?.declarationDir || relOutDir);
168
217
  const relDeclarationDir = path.relative(pkgJsonDir, declarationDir);
218
+ // clean up outDir and declarationDir
219
+ if (relOutDir !== "") {
220
+ console.log(`🗑️ Cleaning up outDir...`);
221
+ fs.rmSync(outDir, { recursive: true, force: true });
222
+ // // print success mesage in verbose mode
223
+ if (isVerbose) {
224
+ if (fs.existsSync(outDir)) {
225
+ console.error(`❌ Failed to clean up outDir: ${relOutDir}. Directory still exists.`);
226
+ }
227
+ }
228
+ }
229
+ else {
230
+ console.log(`🗑️ Skipping cleanup of outDir as it contains source files`);
231
+ }
232
+ if (relDeclarationDir !== relOutDir && relDeclarationDir !== "") {
233
+ console.log(`🗑️ Cleaning up declarationDir...`);
234
+ fs.rmSync(declarationDir, { recursive: true, force: true });
235
+ // // print success mesage in verbose mode
236
+ if (isVerbose) {
237
+ if (fs.existsSync(declarationDir)) {
238
+ console.error(`❌ Failed to clean up declarationDir: ${relDeclarationDir}. Directory still exists.`);
239
+ }
240
+ }
241
+ }
242
+ else {
243
+ if (isVerbose)
244
+ console.log(`🗑️ Skipping cleanup of declarationDir as it contains source files`);
245
+ }
169
246
  const tsconfigJson = {
170
247
  ..._parsedConfig,
171
248
  outDir,
@@ -180,14 +257,7 @@ Examples:
180
257
  if (relOutDir === "") {
181
258
  if (!pkgJson.files) {
182
259
  console.log('⚠️ You\'re building your code to the project root. This means your compiled files will be generated alongside your source files.\n ➜ Setting "files" in package.json to exclude TypeScript source from the published package.');
183
- pkgJson.files = [
184
- "**/*.js",
185
- "**/*.mjs",
186
- "**/*.cjs",
187
- "**/*.d.ts",
188
- "**/*.d.mts",
189
- "**/*.d.cts",
190
- ];
260
+ pkgJson.files = ["**/*.js", "**/*.mjs", "**/*.cjs", "**/*.d.ts", "**/*.d.mts", "**/*.d.cts"];
191
261
  }
192
262
  else {
193
263
  console.warn(`⚠️ You're building your code to the project root. This means your compiled files will be generated alongside your source files.
@@ -240,10 +310,7 @@ Examples:
240
310
  deep: 1,
241
311
  });
242
312
  entryPatterns.push(...wildcardFiles);
243
- rows.push([
244
- `"${cleanExportPath}"`,
245
- `${sourcePath} (${wildcardFiles.length} matches)`,
246
- ]);
313
+ rows.push([`"${cleanExportPath}"`, `${sourcePath} (${wildcardFiles.length} matches)`]);
247
314
  }
248
315
  else if (isSourceFile(sourcePath)) {
249
316
  entryPatterns.push(sourcePath);
@@ -251,7 +318,37 @@ Examples:
251
318
  }
252
319
  }
253
320
  }
254
- console.log(" " + table(rows).split("\n").join("\n ").trim());
321
+ // Extract bin entry points from zshy bin config
322
+ if (config.bin) {
323
+ if (typeof config.bin === "string") {
324
+ // Single bin entry
325
+ if (isSourceFile(config.bin)) {
326
+ entryPatterns.push(config.bin);
327
+ rows.push([`bin:${pkgJson.name}`, config.bin]);
328
+ }
329
+ }
330
+ else {
331
+ // Multiple bin entries
332
+ for (const [binName, sourcePath] of Object.entries(config.bin)) {
333
+ if (typeof sourcePath === "string" && isSourceFile(sourcePath)) {
334
+ entryPatterns.push(sourcePath);
335
+ rows.push([`bin:${binName}`, sourcePath]);
336
+ }
337
+ }
338
+ }
339
+ }
340
+ console.log(" " +
341
+ table(rows, {
342
+ drawHorizontalLine: (lineIndex, rowCount) => {
343
+ return (lineIndex === 0 ||
344
+ lineIndex === 1 ||
345
+ // lineIndex === rowCount - 1 ||
346
+ lineIndex === rowCount);
347
+ },
348
+ })
349
+ .split("\n")
350
+ .join("\n ")
351
+ .trim());
255
352
  ///////////////////////////////
256
353
  /// compute entry points ///
257
354
  ///////////////////////////////
@@ -263,7 +360,7 @@ Examples:
263
360
  process.exit(1);
264
361
  }
265
362
  if (entryPoints.length === 0) {
266
- console.error("❌ No entry points found matching the specified patterns in package.json#zshy exports");
363
+ console.error("❌ No entry points found matching the specified patterns in package.json#/zshy exports");
267
364
  process.exit(1);
268
365
  }
269
366
  ///////////////////////////////
@@ -285,37 +382,60 @@ Examples:
285
382
  const entryParts = entryDir.split(path.sep);
286
383
  const commonParts = commonDir.split(path.sep);
287
384
  let i = 0;
288
- while (i < entryParts.length &&
289
- i < commonParts.length &&
290
- entryParts[i] === commonParts[i]) {
385
+ while (i < entryParts.length && i < commonParts.length && entryParts[i] === commonParts[i]) {
291
386
  i++;
292
387
  }
293
388
  return commonParts.slice(0, i).join(path.sep) || path.sep;
294
389
  }, path.dirname(path.resolve(entryPoints[0])))
295
390
  : process.cwd();
296
391
  }
297
- // console.dir(_commonAncestor, { depth: null });
298
392
  const relRootDir = path.relative(pkgJsonDir, rootDir);
299
- console.log(`📂 Transpiling ${relRootDir ? `./${relRootDir}` : "."} (rootDir) to ${relOutDir ? `./${relOutDir}` : "."} (outDir)`);
393
+ // Display resolved paths table
394
+ console.log("🔧 Resolved build paths:");
395
+ const pathRows = [["Location", "Resolved path"]];
396
+ // pathRows.push([
397
+ // "package.json",
398
+ // `./${path.relative(pkgJsonDir, packageJsonPath)}`,
399
+ // ]);
400
+ // pathRows.push(["tsconfig", `./${path.relative(pkgJsonDir, tsconfigPath)}`]);
401
+ pathRows.push(["rootDir", relRootDir ? `./${relRootDir}` : "."]);
402
+ pathRows.push(["outDir", relOutDir ? `./${relOutDir}` : "."]);
300
403
  if (relDeclarationDir !== relOutDir) {
301
- console.log(`📂 Writing declaration files to ${relDeclarationDir ? `./${relDeclarationDir}` : "."} (declarationDir)`);
404
+ pathRows.push(["declarationDir", relDeclarationDir ? `./${relDeclarationDir}` : "."]);
302
405
  }
406
+ console.log(" " +
407
+ table(pathRows, {
408
+ drawHorizontalLine: (lineIndex, rowCount) => {
409
+ return (lineIndex === 0 ||
410
+ lineIndex === 1 ||
411
+ // lineIndex === rowCount - 1 ||
412
+ lineIndex === rowCount);
413
+ },
414
+ })
415
+ .split("\n")
416
+ .join("\n ")
417
+ .trim());
303
418
  const isTypeModule = pkgJson.type === "module";
304
419
  if (isTypeModule) {
305
- console.log(`🟨 Package is an ES module (package.json#type is \"module\")`);
420
+ console.log(`🟨 Package is an ES module (package.json#/type is \"module\")`);
306
421
  }
307
422
  else {
308
- console.log(`🐢 Package is a CommonJS module (${pkgJson.type === "commonjs" ? 'package.json#type is "commonjs"' : 'package.json#type not set to "module"'})`);
423
+ console.log(`🐢 Package is a CommonJS module (${pkgJson.type === "commonjs" ? 'package.json#/type is "commonjs"' : 'package.json#/type not set to "module"'})`);
309
424
  }
310
425
  ///////////////////////////////
311
426
  /// compile tsc ///
312
427
  ///////////////////////////////
313
428
  try {
429
+ // Track all written files
430
+ const allWrittenFiles = [];
314
431
  // CJS
315
432
  console.log(`🧱 Building CJS...${isTypeModule ? ` (rewriting .ts -> .cjs/.d.cts)` : ``}`);
316
- await compileProject({
433
+ const cjsFiles = await compileProject({
317
434
  configPath: tsconfigPath,
318
435
  mode: isTypeModule ? "cts" : "ts",
436
+ verbose: isVerbose,
437
+ dryRun: isDryRun,
438
+ packageRoot: pkgJsonDir,
319
439
  compilerOptions: {
320
440
  ...tsconfigJson,
321
441
  module: ts.ModuleKind.CommonJS,
@@ -323,11 +443,15 @@ Examples:
323
443
  outDir,
324
444
  },
325
445
  }, entryPoints);
446
+ allWrittenFiles.push(...cjsFiles);
326
447
  // ESM
327
448
  console.log(`🧱 Building ESM...${isTypeModule ? `` : ` (rewriting .ts -> .mjs/.d.mts)`}`);
328
- await compileProject({
449
+ const esmFiles = await compileProject({
329
450
  configPath: tsconfigPath,
330
451
  mode: isTypeModule ? "ts" : "mts",
452
+ verbose: isVerbose,
453
+ dryRun: isDryRun,
454
+ packageRoot: pkgJsonDir,
331
455
  compilerOptions: {
332
456
  ...tsconfigJson,
333
457
  module: ts.ModuleKind.ESNext,
@@ -335,11 +459,27 @@ Examples:
335
459
  outDir,
336
460
  },
337
461
  }, entryPoints);
462
+ allWrittenFiles.push(...esmFiles);
463
+ ///////////////////////////////////
464
+ /// display written files ///
465
+ ///////////////////////////////////
466
+ // Display files that were written or would be written (only in verbose mode)
467
+ if (isVerbose && allWrittenFiles.length > 0) {
468
+ console.log(`📜 ${isDryRun ? "[dryrun] " : ""}Writing files... (${allWrittenFiles.length} total):`);
469
+ // Sort files by relative path for consistent display
470
+ const sortedFiles = [...allWrittenFiles]
471
+ .map((file) => path.relative(pkgJsonDir, file))
472
+ .sort()
473
+ .map((relPath) => (relPath.startsWith(".") ? relPath : `./${relPath}`));
474
+ sortedFiles.forEach((file) => {
475
+ console.log(` ${file}`);
476
+ });
477
+ }
338
478
  ///////////////////////////////
339
479
  /// generate exports ///
340
480
  ///////////////////////////////
341
481
  // generate package.json exports
342
- console.log("📦 Updating package.json exports...");
482
+ console.log("📦 Updating package.json#/exports...");
343
483
  // Generate exports based on zshy config
344
484
  const newExports = {};
345
485
  const sourceDialects = config.sourceDialects || [];
@@ -348,15 +488,26 @@ Examples:
348
488
  newExports[exportPath] = sourcePath;
349
489
  continue;
350
490
  }
491
+ const absSourcePath = path.resolve(pkgJsonDir, sourcePath);
492
+ const relSourcePath = path.relative(rootDir, absSourcePath);
493
+ const absJsPath = path.resolve(outDir, relSourcePath);
494
+ const absDtsPath = path.resolve(declarationDir, relSourcePath);
495
+ const relJsPath = "./" + path.relative(pkgJsonDir, absJsPath);
496
+ const relDtsPath = "./" + path.relative(pkgJsonDir, absDtsPath);
351
497
  if (typeof sourcePath === "string") {
352
498
  if (sourcePath.endsWith("/*")) {
353
499
  // Handle wildcard exports
354
- const relSourcePath = "./" +
355
- path.relative(pkgJsonDir, path.resolve(outDir, sourcePath.slice(0, -2))) +
356
- "/*";
500
+ // const relSourcePath =
501
+ // "./" +
502
+ // path.relative(
503
+ // pkgJsonDir,
504
+ // path.resolve(outDir, sourcePath.slice(0, -2)),
505
+ // ) +
506
+ // "/*";
357
507
  newExports[exportPath] = {
358
- import: relSourcePath,
359
- require: relSourcePath,
508
+ types: relDtsPath,
509
+ import: relJsPath,
510
+ require: relJsPath,
360
511
  };
361
512
  for (const sd of sourceDialects) {
362
513
  newExports[exportPath] = {
@@ -366,44 +517,18 @@ Examples:
366
517
  }
367
518
  }
368
519
  else if (isSourceFile(sourcePath)) {
369
- // Handle regular TypeScript entry points
370
- // const basePath = sourcePath.slice(0, -3); // ./v4/index.ts
371
- const absSourcePath = path.resolve(pkgJsonDir, sourcePath); // /path/to/v4/index.ts
372
- const sourcePathRelativeToRootDir = path.relative(rootDir, absSourcePath); // index.ts
373
- const outPath = path.resolve(pkgJsonDir, outDir, sourcePathRelativeToRootDir); // /path/to/dist/index.ts
374
- const outPathNoExt = removeExtension(outPath); // /path/to/dist/index
375
- // For declaration files, use declarationDir if it's different from outDir
376
- const declarationPath = path.resolve(pkgJsonDir, declarationDir, sourcePathRelativeToRootDir); // /path/to/types/index.ts
377
- const declarationPathNoExt = removeExtension(declarationPath); // /path/to/types/index
378
- const esmFile = isTypeModule
379
- ? `${outPathNoExt}.js`
380
- : `${outPathNoExt}.mjs`; // ./v4/index.js or ./v4/index.mjs
381
- const cjsFile = isTypeModule
382
- ? `${outPathNoExt}.cjs`
383
- : `${outPathNoExt}.js`;
384
- const dtsFile = isTypeModule
385
- ? `${declarationPathNoExt}.d.cts`
386
- : `${declarationPathNoExt}.d.ts`;
387
- const relEsmFile = "./" + path.relative(pkgJsonDir, esmFile);
388
- const relCjsFile = "./" + path.relative(pkgJsonDir, cjsFile);
389
- const relDtsFile = "./" + path.relative(pkgJsonDir, dtsFile);
520
+ const esmPath = removeExtension(relJsPath) + (isTypeModule ? `.js` : `.mjs`); // ./v4/index.js or ./v4/index.mjs
521
+ const cjsPath = removeExtension(relJsPath) + (isTypeModule ? `.cjs` : `.js`);
522
+ const dtsPath = removeExtension(relDtsPath) + (isTypeModule ? `.d.cts` : `.d.ts`); // ./
390
523
  newExports[exportPath] = {
391
- types: relDtsFile,
392
- import: relEsmFile,
393
- require: relCjsFile,
394
- // import: {
395
- // types: relDtsFile,
396
- // default: relEsmFile,
397
- // },
398
- // require: {
399
- // types: relDtsFile,
400
- // default: relCjsFile,
401
- // },
524
+ types: dtsPath,
525
+ import: esmPath,
526
+ require: cjsPath,
402
527
  };
403
528
  if (exportPath === ".") {
404
- pkgJson.main = relCjsFile;
405
- pkgJson.module = relEsmFile;
406
- pkgJson.types = relDtsFile;
529
+ pkgJson.main = cjsPath;
530
+ pkgJson.module = esmPath;
531
+ pkgJson.types = dtsPath;
407
532
  }
408
533
  for (const sd of sourceDialects) {
409
534
  newExports[exportPath] = {
@@ -412,6 +537,45 @@ Examples:
412
537
  };
413
538
  }
414
539
  }
540
+ else {
541
+ console.error(`❌ Invalid entrypoint: ${sourcePath}`);
542
+ process.exit();
543
+ }
544
+ }
545
+ }
546
+ ///////////////////////////////
547
+ /// generate bin ///
548
+ ///////////////////////////////
549
+ // Generate bin field based on zshy bin config
550
+ if (config.bin) {
551
+ console.log("📦 Updating package.json#/bin...");
552
+ const newBin = {};
553
+ // Convert config.bin to object format for processing
554
+ const binEntries = typeof config.bin === "string" ? [[pkgJson.name, config.bin]] : Object.entries(config.bin);
555
+ for (const [binName, sourcePath] of binEntries) {
556
+ if (typeof sourcePath === "string" && isSourceFile(sourcePath)) {
557
+ const absSourcePath = path.resolve(pkgJsonDir, sourcePath);
558
+ const relSourcePath = path.relative(rootDir, absSourcePath);
559
+ const absJsPath = path.resolve(outDir, relSourcePath);
560
+ const relJsPath = "./" + path.relative(pkgJsonDir, absJsPath);
561
+ // Use CommonJS entrypoint for bin
562
+ const binPath = removeExtension(relJsPath) + (isTypeModule ? `.cjs` : `.js`);
563
+ newBin[binName] = binPath;
564
+ }
565
+ }
566
+ // If original config.bin was a string, output as string
567
+ if (typeof config.bin === "string") {
568
+ pkgJson.bin = Object.values(newBin)[0];
569
+ if (isVerbose) {
570
+ console.log(`🗣️ Updated package.json#/bin: "${Object.values(newBin)[0]}"`);
571
+ }
572
+ }
573
+ else {
574
+ // Output as object
575
+ pkgJson.bin = newBin;
576
+ if (isVerbose) {
577
+ console.log("🗣️ Updated package.json#/bin: " + formatForLog(newBin));
578
+ }
415
579
  }
416
580
  }
417
581
  ///////////////////////////////
@@ -419,9 +583,16 @@ Examples:
419
583
  ///////////////////////////////
420
584
  // Update package.json with new exports
421
585
  pkgJson.exports = newExports;
422
- fs.writeFileSync(packageJsonPath, JSON.stringify(pkgJson, null, 2) + "\n");
423
- // console.log("✅ Updating package.json#exports");
424
- console.log(" " + JSON.stringify(newExports, null, 2).split("\n").join("\n "));
586
+ if (isDryRun) {
587
+ console.log("🔍 Skipping package.json modification (dry-run)");
588
+ }
589
+ else {
590
+ fs.writeFileSync(packageJsonPath, JSON.stringify(pkgJson, null, 2) + "\n");
591
+ }
592
+ // console.log("✅ Updating package.json#/exports");
593
+ if (isVerbose) {
594
+ console.log(`🗣️ Updated package.json#/exports: ${formatForLog(newExports)}`);
595
+ }
425
596
  // // run `@arethetypeswrong/cli --pack .` to check types
426
597
  // console.log("🔍 Checking types with @arethetypeswrong/cli...");
427
598
  // const { execSync } = await import("node:child_process");