@reliverse/dler 1.7.14 → 1.7.16

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 CHANGED
@@ -356,6 +356,10 @@ merges multiple files into a single file. The command is built for both CI and i
356
356
  - includes backup functionality
357
357
  - validates file permissions and sizes
358
358
  - enforces output path conflict detection
359
+ - supports template generation with TypeScript type definitions
360
+ - handles both single file and directory output modes
361
+ - implements interactive prompts via `@reliverse/rempts`
362
+ - provides reporting with logging via `@reliverse/relinka`
359
363
 
360
364
  **usage examples:**
361
365
 
@@ -371,8 +375,49 @@ bun dler merge --s "src/templates" --d "templates/my-template.ts" --as-template
371
375
 
372
376
  # update mock template:
373
377
  bun dler merge --s "src/templates" --d "templates/my-template.ts" --as-template --update-template REACT_DLER_TEMPLATE
378
+
379
+ # generate multiple templates based on directory structure:
380
+ bun dler merge --s "src/templates" --d "templates" --as-template --template-multi --depth 2
381
+
382
+ # create separate files for each template with an aggregator:
383
+ bun dler merge --s "src/templates" --d "templates/index.ts" --as-template --template-per-file
374
384
  ```
375
385
 
386
+ **arguments:**
387
+
388
+ - `--s`: Input glob patterns (array)
389
+ - `--d`: Output file path or directory
390
+ - `--ignore`: Extra ignore patterns (array)
391
+ - `--format`: Fallback extension when output path is omitted (default: "txt")
392
+ - `--stdout`: Print to stdout
393
+ - `--noPath`: Don't inject relative path below each file
394
+ - `--pathAbove`: Print file path above each file's content (default: true)
395
+ - `--separator`: Custom separator (default: "\n\n")
396
+ - `--comment`: Custom comment prefix (e.g. '# ')
397
+ - `--forceComment`: Force custom comment prefix for all file types
398
+ - `--batch`: Disable interactive prompts (CI/non-interactive mode)
399
+ - `--recursive`: Recursively process all files in subdirectories (default: true)
400
+ - `--preserveStructure`: Preserve source directory structure in output (default: true)
401
+ - `--increment`: Attach an incrementing index to each output filename
402
+ - `--concurrency`: Number of concurrent file operations (default: 8)
403
+ - `--sort`: Sort files by: name, path, mtime, none (default: path)
404
+ - `--dryRun`: Show what would be done, but don't write files
405
+ - `--backup`: Backup output files before overwriting
406
+ - `--dedupe`: Remove duplicate file contents in merge
407
+ - `--header`: Header text to add at the start of merged output
408
+ - `--footer`: Footer text to add at the end of merged output
409
+ - `--select-files`: Prompt for file selection before merging
410
+ - `--interactive`: Enable interactive mode with prompts
411
+ - `--as-template`: Generate a TypeScript file with template structure
412
+ - `--custom-template-name`: Custom template name when using --as-template
413
+ - `--template-multi`: Create multiple templates based on directory structure (default: true)
414
+ - `--depth`: Depth level to start processing from (default: 0)
415
+ - `--template-per-file`: Create separate files for each template with an aggregator (default: false)
416
+ - `--whitelabel`: Custom prefix to use instead of 'DLER' in template generation (default: "DLER")
417
+ - `--sourcemap`: Generate source map for the merged output
418
+ - `--update-template`: Update specific template in existing mock template file
419
+ - `--dev`: Generate template for development
420
+
376
421
  **implementation details:**
377
422
 
378
423
  - uses `magic-string` for efficient string manipulation and source map generation
@@ -101,26 +101,40 @@ declare const _default: import("@reliverse/rempts").Command<{
101
101
  type: "boolean";
102
102
  description: string;
103
103
  };
104
+ depth: {
105
+ type: "number";
106
+ description: string;
107
+ default: number;
108
+ };
109
+ sourcemap: {
110
+ type: "boolean";
111
+ description: string;
112
+ };
104
113
  "as-template": {
105
114
  type: "boolean";
106
115
  description: string;
107
116
  };
108
- "custom-template-name": {
117
+ "template-whitelabel": {
109
118
  type: "string";
110
119
  description: string;
120
+ default: string;
121
+ dependencies: string[];
111
122
  };
112
- whitelabel: {
123
+ "template-update": {
113
124
  type: "string";
114
125
  description: string;
115
- default: string;
126
+ dependencies: string[];
116
127
  };
117
- sourcemap: {
128
+ "template-multi": {
118
129
  type: "boolean";
119
130
  description: string;
131
+ default: true;
132
+ dependencies: string[];
120
133
  };
121
- "update-template": {
122
- type: "string";
134
+ "template-per-file": {
135
+ type: "boolean";
123
136
  description: string;
137
+ default: false;
124
138
  dependencies: string[];
125
139
  };
126
140
  }>;
@@ -73,7 +73,7 @@ const maybePrompt = async (interactive, value, promptFn) => {
73
73
  if (!interactive || value !== void 0) return value;
74
74
  return promptFn();
75
75
  };
76
- const collectFiles = async (include, extraIgnore, recursive, sortBy) => {
76
+ const collectFiles = async (include, extraIgnore, recursive, sortBy, depth) => {
77
77
  try {
78
78
  const normalizedInclude = include.map(normalizeGlobPattern);
79
79
  const files = await glob(normalizedInclude, {
@@ -88,6 +88,19 @@ const collectFiles = async (include, extraIgnore, recursive, sortBy) => {
88
88
  await checkPermissions(file, "read");
89
89
  }
90
90
  let filtered = [...new Set(files)];
91
+ if (depth > 0) {
92
+ const fileGroups = /* @__PURE__ */ new Map();
93
+ for (const file of filtered) {
94
+ const relPath = path.relative(process.cwd(), file);
95
+ const parts = relPath.split(path.sep);
96
+ const groupKey = parts.slice(0, depth).join(path.sep);
97
+ if (!fileGroups.has(groupKey)) {
98
+ fileGroups.set(groupKey, []);
99
+ }
100
+ fileGroups.get(groupKey)?.push(file);
101
+ }
102
+ filtered = Array.from(fileGroups.values()).flat();
103
+ }
91
104
  if (sortBy === "name") {
92
105
  filtered.sort((a, b) => path.basename(a).localeCompare(path.basename(b)));
93
106
  } else if (sortBy === "path") {
@@ -263,6 +276,49 @@ const updateTemplateInFile = async (templateName, templateContent, targetFile, d
263
276
  handleError(error, "updateTemplateInFile");
264
277
  }
265
278
  };
279
+ const generateTemplateContent = (template, templateConstName, templateKey, whitelabel, isDev) => {
280
+ return `import type { Template } from "${isDev ? "~/libs/sdk/sdk-types" : "@reliverse/dler-sdk"}";
281
+ ${(() => {
282
+ const files = template.config.files;
283
+ if (!files) return "";
284
+ const hasPackageJson = Object.values(files).some(
285
+ (f) => f.type === "json" && f.content
286
+ );
287
+ const hasTSConfig = Object.values(files).some(
288
+ (f) => f.type === "json" && f.content
289
+ );
290
+ if (!hasPackageJson && !hasTSConfig) return "";
291
+ const imports = [];
292
+ if (hasPackageJson) imports.push("PackageJson");
293
+ if (hasTSConfig) imports.push("TSConfig");
294
+ return `import type { ${imports.join(", ")} } from "pkg-types";
295
+ `;
296
+ })()}
297
+ export const ${templateConstName}: Template = ${JSON.stringify(template, null, 2).replace(
298
+ /"([^"]+)":/g,
299
+ (_, key) => {
300
+ return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key) ? `${key}:` : `"${key}":`;
301
+ }
302
+ )};
303
+ export const ${whitelabel}_TEMPLATES = {
304
+ ${templateKey}: ${templateConstName},
305
+ } as const;
306
+ export type ${whitelabel}_TEMPLATE_NAMES = keyof typeof ${whitelabel}_TEMPLATES;
307
+ export const ${whitelabel.toLowerCase()}TemplatesMap: Record<string, ${whitelabel}_TEMPLATE_NAMES> = {
308
+ ${templateConstName}: "${templateKey}",
309
+ };`;
310
+ };
311
+ const generateAggregatorContent = (templateNames, whitelabel, isDev) => {
312
+ return `import type { Template } from "${isDev ? "~/libs/sdk/sdk-types" : "@reliverse/dler-sdk"}";
313
+ ${templateNames.map((name) => `import { ${name}, ${whitelabel}_TEMPLATES as ${name}_TEMPLATES, ${whitelabel}_TEMPLATE_NAMES as ${name}_TEMPLATE_NAMES, ${whitelabel.toLowerCase()}TemplatesMap as ${name.toLowerCase()}TemplatesMap } from "./${name}";`).join("\n")}
314
+ export const ${whitelabel}_TEMPLATES = {
315
+ ${templateNames.map((name) => ` ...${name}_TEMPLATES,`).join("\n")}
316
+ } as const;
317
+ export type ${whitelabel}_TEMPLATE_NAMES = ${templateNames.map((name) => `${name}_TEMPLATE_NAMES`).join(" | ")};
318
+ export const ${whitelabel.toLowerCase()}TemplatesMap: Record<string, ${whitelabel}_TEMPLATE_NAMES> = {
319
+ ${templateNames.map((name) => ` ...${name.toLowerCase()}TemplatesMap,`).join("\n")}
320
+ };`;
321
+ };
266
322
  export default defineCommand({
267
323
  meta: {
268
324
  name: "merge",
@@ -270,6 +326,7 @@ export default defineCommand({
270
326
  description: "Merge text files with optional commented path header/footer, skips binaries/media, built for CI & interactive use. Supports copy-like patterns and advanced options."
271
327
  },
272
328
  args: defineArgs({
329
+ /* ===== GENERAL ARGS ===== */
273
330
  dev: { type: "boolean", description: "Generate template for development" },
274
331
  s: { type: "array", description: "Input glob patterns" },
275
332
  d: { type: "string", description: "Output file path or directory" },
@@ -357,36 +414,51 @@ export default defineCommand({
357
414
  type: "boolean",
358
415
  description: "Enable interactive mode with prompts (default: false)"
359
416
  },
417
+ depth: {
418
+ type: "number",
419
+ description: "Depth level to start processing from (default: 0)",
420
+ default: 0
421
+ },
422
+ sourcemap: {
423
+ type: "boolean",
424
+ description: "Generate source map for the merged output"
425
+ },
426
+ /* ===== TEMPLATE GENERATION ARGS ===== */
360
427
  "as-template": {
361
428
  type: "boolean",
362
429
  description: "Generate a TypeScript file with MOCK_TEMPLATES structure"
363
430
  },
364
- "custom-template-name": {
365
- type: "string",
366
- description: "Custom template name when using --as-template"
367
- },
368
- whitelabel: {
431
+ "template-whitelabel": {
369
432
  type: "string",
370
433
  description: "Custom prefix to use instead of 'DLER' in template generation",
371
- default: "DLER"
372
- },
373
- sourcemap: {
374
- type: "boolean",
375
- description: "Generate source map for the merged output"
434
+ default: "DLER",
435
+ dependencies: ["as-template"]
376
436
  },
377
- "update-template": {
437
+ "template-update": {
378
438
  type: "string",
379
439
  description: "Update specific template in existing mock template file",
380
440
  dependencies: ["as-template"]
441
+ },
442
+ "template-multi": {
443
+ type: "boolean",
444
+ description: "Create multiple templates based on directory structure (default: true)",
445
+ default: true,
446
+ dependencies: ["as-template"]
447
+ },
448
+ "template-per-file": {
449
+ type: "boolean",
450
+ description: "Create separate files for each template with an aggregator (default: false)",
451
+ default: false,
452
+ dependencies: ["as-template", "template-multi"]
381
453
  }
382
454
  }),
383
455
  async run({ args }) {
384
- const customTemplateName = args["custom-template-name"];
385
456
  try {
386
457
  const timer = createPerfTimer();
387
458
  const interactive = args.interactive ?? false;
388
459
  const isDev = args.dev ?? false;
389
- const whitelabel = args.whitelabel ?? "DLER";
460
+ const whitelabel = args["template-whitelabel"] ?? "DLER";
461
+ const depth = args.depth ?? 0;
390
462
  let include = args.s ?? [];
391
463
  if (include.length === 0) {
392
464
  const raw = await maybePrompt(
@@ -476,7 +548,7 @@ export default defineCommand({
476
548
  const footer = args.footer;
477
549
  const selectFiles = args["select-files"] ?? false;
478
550
  const asTemplate = args["as-template"] ?? false;
479
- let files = await collectFiles(include, ignore, recursive, sortBy);
551
+ let files = await collectFiles(include, ignore, recursive, sortBy, depth);
480
552
  if (files.length === 0) {
481
553
  throw new Error("No text files matched given patterns (binary/media files are skipped)");
482
554
  }
@@ -493,11 +565,11 @@ export default defineCommand({
493
565
  throw new Error("No files selected for merging");
494
566
  }
495
567
  }
496
- if (args["update-template"]) {
568
+ if (args["template-update"]) {
497
569
  if (!outFile) {
498
570
  throw new Error("Output file path required for template update");
499
571
  }
500
- const templateName = args["update-template"];
572
+ const templateName = args["template-update"];
501
573
  const templateData = {
502
574
  name: templateName.replace(/_DLER_TEMPLATE$/, "").replace(/_/g, " ").toLowerCase(),
503
575
  // Convert REACT_DLER_TEMPLATE to "react"
@@ -519,31 +591,35 @@ export default defineCommand({
519
591
  if (ext === "json") {
520
592
  const jsonContent = JSON.parse(fileContent);
521
593
  if (fileName === "package.json") {
522
- content = {
523
- ...jsonContent
524
- };
594
+ content = jsonContent;
595
+ type = "json";
596
+ const magic = new MagicString(JSON.stringify(jsonContent, null, 2));
597
+ magic.append(" satisfies PackageJson");
598
+ content = magic.toString();
525
599
  } else if (fileName === "tsconfig.json") {
526
- content = {
527
- ...jsonContent
528
- };
600
+ content = jsonContent;
601
+ type = "json";
602
+ const magic = new MagicString(JSON.stringify(jsonContent, null, 2));
603
+ magic.append(" satisfies TSConfig");
604
+ content = magic.toString();
529
605
  } else {
530
606
  content = jsonContent;
607
+ type = "json";
531
608
  }
532
- type = "json";
533
609
  } else {
534
610
  content = fileContent;
535
611
  type = "text";
536
612
  }
537
613
  } catch (error) {
538
614
  type = "binary";
539
- if (asTemplate || args["update-template"]) {
615
+ if (asTemplate || args["template-update"]) {
540
616
  relinka(
541
617
  "warn",
542
618
  `Skipped file "${relPath}" due to error: ${error instanceof Error ? error.message : "unknown error"}`
543
619
  );
544
620
  }
545
621
  }
546
- } else if (asTemplate || args["update-template"]) {
622
+ } else if (asTemplate || args["template-update"]) {
547
623
  relinka("warn", `Skipped binary file "${relPath}"`);
548
624
  }
549
625
  templateData.config.files[relPath] = {
@@ -579,114 +655,215 @@ export default defineCommand({
579
655
  pausedAt: null,
580
656
  pausedDuration: 0
581
657
  };
658
+ const templateMulti = args["template-multi"] ?? true;
659
+ const templatePerFile = args["template-per-file"] ?? false;
582
660
  if (!outFile) {
583
- outFile = "template.ts";
661
+ outFile = templatePerFile ? "templates/index.ts" : "templates.ts";
584
662
  } else if (!outFile.endsWith(".ts")) {
585
663
  outFile = `${outFile}.ts`;
586
664
  }
587
- const templateName = customTemplateName || path.basename(outFile, ".ts");
588
- const templateConstName = templateName.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").replace(/[A-Z]/g, (letter) => `_${letter}`).replace(/^_/, "").toUpperCase() + `_${whitelabel}_TEMPLATE`;
589
- const templateKey = templateName.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase();
590
- const template = {
591
- name: templateName.toLowerCase(),
592
- // Ensure name is lowercase
593
- description: `Template generated from ${files.length} files`,
594
- config: {
595
- files: {}
665
+ const baseDir = path.dirname(outFile);
666
+ if (templateMulti) {
667
+ const fileGroups = /* @__PURE__ */ new Map();
668
+ const templateDepth = depth;
669
+ for (const file of files) {
670
+ const relPath = path.relative(process.cwd(), file);
671
+ const parts = relPath.split(path.sep);
672
+ const groupKey = parts.slice(0, templateDepth + 1).join(path.sep);
673
+ if (!fileGroups.has(groupKey)) {
674
+ fileGroups.set(groupKey, []);
675
+ }
676
+ fileGroups.get(groupKey)?.push(file);
596
677
  }
597
- };
598
- for (const file of files) {
599
- const relPath = path.relative(process.cwd(), file);
600
- const ext = path.extname(file).slice(1).toLowerCase();
601
- const isBinary = await isBinaryExt(file);
602
- const fileName = path.basename(file).toLowerCase();
603
- let content = "";
604
- let type = "binary";
605
- if (!isBinary) {
606
- try {
607
- const fileContent = await fs.readFile(file, "utf8");
608
- if (ext === "json") {
609
- const jsonContent = JSON.parse(fileContent);
610
- if (fileName === "package.json") {
611
- content = {
612
- ...jsonContent
613
- };
614
- } else if (fileName === "tsconfig.json") {
615
- content = {
616
- ...jsonContent
617
- };
618
- } else {
619
- content = jsonContent;
678
+ const templateNames = [];
679
+ for (const [groupKey, groupFiles] of fileGroups) {
680
+ const templateName = groupKey.split(path.sep).pop() || groupKey;
681
+ const templateConstName = templateName.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").replace(/[A-Z]/g, (letter) => `_${letter}`).replace(/^_/, "").toUpperCase() + `_${whitelabel}_TEMPLATE`;
682
+ const templateKey = templateName.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase();
683
+ const template = {
684
+ name: templateName.toLowerCase(),
685
+ description: `Template generated from ${groupFiles.length} files in ${groupKey}`,
686
+ config: {
687
+ files: {}
688
+ }
689
+ };
690
+ for (const file of groupFiles) {
691
+ const relPath = path.relative(process.cwd(), file);
692
+ const ext = path.extname(file).slice(1).toLowerCase();
693
+ const isBinary = await isBinaryExt(file);
694
+ const fileName = path.basename(file).toLowerCase();
695
+ let content = "";
696
+ let type = "binary";
697
+ if (!isBinary) {
698
+ try {
699
+ const fileContent = await fs.readFile(file, "utf8");
700
+ if (ext === "json") {
701
+ const jsonContent = JSON.parse(fileContent);
702
+ if (fileName === "package.json") {
703
+ content = jsonContent;
704
+ type = "json";
705
+ const magic = new MagicString(JSON.stringify(jsonContent, null, 2));
706
+ magic.append(" satisfies PackageJson");
707
+ content = magic.toString();
708
+ } else if (fileName === "tsconfig.json") {
709
+ content = jsonContent;
710
+ type = "json";
711
+ const magic = new MagicString(JSON.stringify(jsonContent, null, 2));
712
+ magic.append(" satisfies TSConfig");
713
+ content = magic.toString();
714
+ } else {
715
+ content = jsonContent;
716
+ type = "json";
717
+ }
718
+ } else {
719
+ content = fileContent;
720
+ type = "text";
721
+ }
722
+ } catch (error) {
723
+ type = "binary";
724
+ if (asTemplate || args["template-update"]) {
725
+ relinka(
726
+ "warn",
727
+ `Skipped file "${relPath}" due to error: ${error instanceof Error ? error.message : "unknown error"}`
728
+ );
729
+ }
620
730
  }
621
- type = "json";
731
+ } else if (asTemplate || args["template-update"]) {
732
+ relinka("warn", `Skipped binary file "${relPath}"`);
733
+ }
734
+ template.config.files[relPath] = {
735
+ content,
736
+ type
737
+ };
738
+ }
739
+ const templateContent = generateTemplateContent(
740
+ template,
741
+ templateConstName,
742
+ templateKey,
743
+ whitelabel,
744
+ isDev
745
+ );
746
+ if (templatePerFile) {
747
+ const templateFileName = `${templateKey}.ts`;
748
+ const templateFilePath = path.join(baseDir, templateFileName);
749
+ if (dryRun) {
750
+ relinka("verbose", `[DRY RUN] Would write template file: ${templateFilePath}`);
622
751
  } else {
623
- content = fileContent;
624
- type = "text";
752
+ await fs.ensureDir(path.dirname(templateFilePath));
753
+ if (backup && await fs.pathExists(templateFilePath)) {
754
+ const backupPath = `${templateFilePath}.${Date.now()}.bak`;
755
+ await fs.copyFile(templateFilePath, backupPath);
756
+ }
757
+ await fs.writeFile(templateFilePath, templateContent, "utf8");
625
758
  }
626
- } catch (error) {
627
- type = "binary";
628
- if (asTemplate || args["update-template"]) {
629
- relinka(
630
- "warn",
631
- `Skipped file "${relPath}" due to error: ${error instanceof Error ? error.message : "unknown error"}`
632
- );
759
+ templateNames.push(templateKey);
760
+ } else {
761
+ if (!dryRun) {
762
+ await fs.ensureDir(path.dirname(outFile));
763
+ if (backup && await fs.pathExists(outFile)) {
764
+ const backupPath = `${outFile}.${Date.now()}.bak`;
765
+ await fs.copyFile(outFile, backupPath);
766
+ }
767
+ await fs.appendFile(outFile, templateContent + "\n\n", "utf8");
633
768
  }
769
+ templateNames.push(templateKey);
634
770
  }
635
- } else if (asTemplate || args["update-template"]) {
636
- relinka("warn", `Skipped binary file "${relPath}"`);
637
771
  }
638
- template.config.files[relPath] = {
639
- content,
640
- type
772
+ if (templatePerFile) {
773
+ const aggregatorContent = generateAggregatorContent(templateNames, whitelabel, isDev);
774
+ if (dryRun) {
775
+ relinka("verbose", `[DRY RUN] Would write aggregator file: ${outFile}`);
776
+ } else {
777
+ await fs.ensureDir(path.dirname(outFile));
778
+ if (backup && await fs.pathExists(outFile)) {
779
+ const backupPath = `${outFile}.${Date.now()}.bak`;
780
+ await fs.copyFile(outFile, backupPath);
781
+ }
782
+ await fs.writeFile(outFile, aggregatorContent, "utf8");
783
+ }
784
+ }
785
+ } else {
786
+ const templateName = path.basename(outFile, ".ts");
787
+ const templateConstName = templateName.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").replace(/[A-Z]/g, (letter) => `_${letter}`).replace(/^_/, "").toUpperCase() + `_${whitelabel}_TEMPLATE`;
788
+ const templateKey = templateName.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").toLowerCase();
789
+ const template = {
790
+ name: templateName.toLowerCase(),
791
+ description: `Template generated from ${files.length} files`,
792
+ config: {
793
+ files: {}
794
+ }
641
795
  };
642
- }
643
- const tsContent = `import type { Template } from "${isDev ? "~/libs/sdk/sdk-types" : "@reliverse/dler-sdk"}";
644
- ${(() => {
645
- const files2 = template.config.files;
646
- if (!files2) return "";
647
- const hasPackageJson = Object.values(files2).some(
648
- (f) => f.type === "json" && f.content
649
- );
650
- const hasTSConfig = Object.values(files2).some(
651
- (f) => f.type === "json" && f.content
796
+ for (const file of files) {
797
+ const relPath = path.relative(process.cwd(), file);
798
+ const ext = path.extname(file).slice(1).toLowerCase();
799
+ const isBinary = await isBinaryExt(file);
800
+ const fileName = path.basename(file).toLowerCase();
801
+ let content = "";
802
+ let type = "binary";
803
+ if (!isBinary) {
804
+ try {
805
+ const fileContent = await fs.readFile(file, "utf8");
806
+ if (ext === "json") {
807
+ const jsonContent = JSON.parse(fileContent);
808
+ if (fileName === "package.json") {
809
+ content = jsonContent;
810
+ type = "json";
811
+ const magic = new MagicString(JSON.stringify(jsonContent, null, 2));
812
+ magic.append(" satisfies PackageJson");
813
+ content = magic.toString();
814
+ } else if (fileName === "tsconfig.json") {
815
+ content = jsonContent;
816
+ type = "json";
817
+ const magic = new MagicString(JSON.stringify(jsonContent, null, 2));
818
+ magic.append(" satisfies TSConfig");
819
+ content = magic.toString();
820
+ } else {
821
+ content = jsonContent;
822
+ type = "json";
823
+ }
824
+ } else {
825
+ content = fileContent;
826
+ type = "text";
827
+ }
828
+ } catch (error) {
829
+ type = "binary";
830
+ if (asTemplate || args["template-update"]) {
831
+ relinka(
832
+ "warn",
833
+ `Skipped file "${relPath}" due to error: ${error instanceof Error ? error.message : "unknown error"}`
834
+ );
835
+ }
836
+ }
837
+ } else if (asTemplate || args["template-update"]) {
838
+ relinka("warn", `Skipped binary file "${relPath}"`);
839
+ }
840
+ template.config.files[relPath] = {
841
+ content,
842
+ type
843
+ };
844
+ }
845
+ const templateContent = generateTemplateContent(
846
+ template,
847
+ templateConstName,
848
+ templateKey,
849
+ whitelabel,
850
+ isDev
652
851
  );
653
- if (!hasPackageJson && !hasTSConfig) return "";
654
- const imports = [];
655
- if (hasPackageJson) imports.push("PackageJson");
656
- if (hasTSConfig) imports.push("TSConfig");
657
- return `import type { ${imports.join(", ")} } from "pkg-types";
658
- `;
659
- })()}
660
- export const ${templateConstName}: Template = ${JSON.stringify(template, null, 2).replace(/"([^"]+)":/g, (_, key) => {
661
- return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key) ? `${key}:` : `"${key}":`;
662
- }).replace(/"([^"]+)":/g, (_, key) => {
663
- return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key) ? `${key}:` : `"${key}":`;
664
- }).replace(/"([^"]+)":/g, (_, key) => {
665
- return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key) ? `${key}:` : `"${key}":`;
666
- })};
667
- export const ${whitelabel}_TEMPLATES = {
668
- ${templateKey}: ${templateConstName},
669
- } as const;
670
- export type ${whitelabel}_TEMPLATE_NAMES = keyof typeof ${whitelabel}_TEMPLATES;
671
- export const ${whitelabel.toLowerCase()}TemplatesMap: Record<string, ${whitelabel}_TEMPLATE_NAMES> = {
672
- ${templateConstName}: "${templateKey}",
673
- };
674
- `;
675
- if (dryRun) {
676
- relinka("verbose", `[DRY RUN] Would write template file: ${outFile}`);
677
- return;
678
- }
679
- const dir = path.dirname(outFile);
680
- if (dir && dir !== ".") await fs.ensureDir(dir);
681
- if (backup && await fs.pathExists(outFile)) {
682
- const backupPath = `${outFile}.${Date.now()}.bak`;
683
- await fs.copyFile(outFile, backupPath);
852
+ if (dryRun) {
853
+ relinka("verbose", `[DRY RUN] Would write template file: ${outFile}`);
854
+ } else {
855
+ await fs.ensureDir(path.dirname(outFile));
856
+ if (backup && await fs.pathExists(outFile)) {
857
+ const backupPath = `${outFile}.${Date.now()}.bak`;
858
+ await fs.copyFile(outFile, backupPath);
859
+ }
860
+ await fs.writeFile(outFile, templateContent, "utf8");
861
+ }
684
862
  }
685
- await fs.writeFile(outFile, tsContent, "utf8");
686
863
  const elapsed2 = getElapsedPerfTime(timer2);
687
864
  relinka(
688
865
  "success",
689
- `Successfully ${dryRun ? "would generate" : "generated"} template file "${outFile}" (in ${prettyMilliseconds(elapsed2)})`
866
+ `Successfully ${dryRun ? "would generate" : "generated"} template file(s) (in ${prettyMilliseconds(elapsed2)})`
690
867
  );
691
868
  return;
692
869
  }
@@ -1,5 +1,5 @@
1
1
  import { endPrompt, startPrompt } from "@reliverse/rempts";
2
- const version = "1.7.14";
2
+ const version = "1.7.16";
3
3
  export async function showStartPrompt(isDev) {
4
4
  await startPrompt({
5
5
  titleColor: "inverse",
@@ -8,6 +8,7 @@ export async function library_publishLibrary(effectivePubRegistry, libName, npmO
8
8
  switch (effectivePubRegistry) {
9
9
  case "jsr":
10
10
  relinka("log", `Publishing lib ${libName} to JSR only...`);
11
+ relinka("null", "");
11
12
  await library_pubToJsr(
12
13
  jsrOutDir,
13
14
  distJsrDryRun,
@@ -21,10 +22,12 @@ export async function library_publishLibrary(effectivePubRegistry, libName, npmO
21
22
  break;
22
23
  case "npm":
23
24
  relinka("log", `Publishing lib ${libName} to NPM only...`);
25
+ relinka("null", "");
24
26
  await library_pubToNpm(npmOutDir, distJsrDryRun, distJsrFailOnWarn, libName, isDev, timer);
25
27
  break;
26
28
  case "npm-jsr": {
27
29
  relinka("log", `Publishing lib ${libName} to both NPM and JSR...`);
30
+ relinka("null", "");
28
31
  const publishTasks = [
29
32
  () => library_pubToNpm(npmOutDir, distJsrDryRun, distJsrFailOnWarn, libName, isDev, timer),
30
33
  () => library_pubToJsr(
@@ -54,6 +57,7 @@ async function library_pubToJsr(libOutDir, distJsrDryRun, distJsrFailOnWarn, dis
54
57
  if (timer) pausePerfTimer(timer);
55
58
  await withWorkingDirectory(libOutDir, async () => {
56
59
  relinka("log", `Publishing lib ${libName} to JSR from ${libOutDir}`);
60
+ relinka("null", "");
57
61
  const command = [
58
62
  "bun x jsr publish",
59
63
  distJsrDryRun ? "--dry-run" : "",
@@ -62,8 +66,9 @@ async function library_pubToJsr(libOutDir, distJsrDryRun, distJsrFailOnWarn, dis
62
66
  distJsrSlowTypes ? "--allow-slow-types" : ""
63
67
  ].filter(Boolean).join(" ");
64
68
  await execaCommand(command, { stdio: "inherit" });
69
+ relinka("null", "");
65
70
  relinka(
66
- "success",
71
+ "log",
67
72
  `Successfully ${distJsrDryRun ? "validated" : "published"} lib ${libName} to JSR registry`
68
73
  );
69
74
  });
@@ -79,10 +84,12 @@ async function library_pubToNpm(libOutDir, distJsrDryRun, _distJsrFailOnWarn, li
79
84
  if (timer) pausePerfTimer(timer);
80
85
  await withWorkingDirectory(libOutDir, async () => {
81
86
  relinka("log", `Publishing lib ${libName} to NPM from ${libOutDir}`);
87
+ relinka("null", "");
82
88
  const command = ["bun publish", distJsrDryRun ? "--dry-run" : ""].filter(Boolean).join(" ");
83
89
  await execaCommand(command, { stdio: "inherit" });
90
+ relinka("null", "");
84
91
  relinka(
85
- "success",
92
+ "log",
86
93
  `Successfully ${distJsrDryRun ? "validated" : "published"} lib ${libName} to NPM registry`
87
94
  );
88
95
  });
@@ -90,7 +97,5 @@ async function library_pubToNpm(libOutDir, distJsrDryRun, _distJsrFailOnWarn, li
90
97
  } catch (error) {
91
98
  if (timer) resumePerfTimer(timer);
92
99
  throw error;
93
- } finally {
94
- relinka("verbose", `Exiting library_pubToNpm for lib: ${libName}`);
95
100
  }
96
101
  }
@@ -20,10 +20,8 @@ export async function regular_pubToJsr(distJsrDryRun, distJsrFailOnWarn, _isDev,
20
20
  ].filter(Boolean).join(" ");
21
21
  relinka("verbose", `Running publish command: ${command}`);
22
22
  await execaCommand(command, { stdio: "inherit" });
23
- relinka(
24
- "success",
25
- `Successfully ${distJsrDryRun ? "validated" : "published"} to JSR registry`
26
- );
23
+ relinka("null", "");
24
+ relinka("log", `Successfully ${distJsrDryRun ? "validated" : "published"} to JSR registry`);
27
25
  });
28
26
  if (timer) resumePerfTimer(timer);
29
27
  }
@@ -42,10 +40,8 @@ export async function regular_pubToNpm(distJsrDryRun, _isDev, commonPubPause, di
42
40
  const command = ["bun publish", distJsrDryRun ? "--dry-run" : ""].filter(Boolean).join(" ");
43
41
  relinka("verbose", `Running publish command: ${command}`);
44
42
  await execaCommand(command, { stdio: "inherit" });
45
- relinka(
46
- "success",
47
- `Successfully ${distJsrDryRun ? "validated" : "published"} to NPM registry`
48
- );
43
+ relinka("null", "");
44
+ relinka("log", `Successfully ${distJsrDryRun ? "validated" : "published"} to NPM registry`);
49
45
  });
50
46
  if (timer) resumePerfTimer(timer);
51
47
  }
@@ -20,7 +20,7 @@ export async function withWorkingDirectory(transpileTargetDir, fn) {
20
20
  const originalDir = process.cwd();
21
21
  try {
22
22
  process.chdir(transpileTargetDir);
23
- relinka("info", `CWD (current working directory): ${originalDir} -> ${transpileTargetDir}`);
23
+ relinka("log", `CWD (current working directory): ${originalDir} -> ${transpileTargetDir}`);
24
24
  const result = await fn();
25
25
  return result;
26
26
  } catch (error) {
@@ -29,7 +29,7 @@ export async function withWorkingDirectory(transpileTargetDir, fn) {
29
29
  throw error;
30
30
  } finally {
31
31
  process.chdir(originalDir);
32
- relinka("info", `CWD (current working directory): ${transpileTargetDir} -> ${originalDir}`);
32
+ relinka("log", `CWD (current working directory): ${transpileTargetDir} -> ${originalDir}`);
33
33
  }
34
34
  }
35
35
  export async function validateDevCwd(isDev, paths, cliName, cliOrg = "") {
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "@reliverse/relico": "^1.1.2",
7
7
  "@reliverse/relifso": "^1.4.5",
8
8
  "@reliverse/relinka": "^1.4.7",
9
- "@reliverse/rempts": "^1.7.18",
9
+ "@reliverse/rempts": "^1.7.19",
10
10
  "@rollup/plugin-alias": "^5.1.1",
11
11
  "@rollup/plugin-commonjs": "^28.0.3",
12
12
  "@rollup/plugin-json": "^6.1.0",
@@ -44,7 +44,7 @@
44
44
  "license": "MIT",
45
45
  "name": "@reliverse/dler",
46
46
  "type": "module",
47
- "version": "1.7.14",
47
+ "version": "1.7.16",
48
48
  "keywords": [
49
49
  "reliverse",
50
50
  "cli",
@@ -1,31 +0,0 @@
1
- #root {
2
- max-width: 1280px;
3
- margin: 0 auto;
4
- padding: 2rem;
5
- text-align: center;
6
- }
7
-
8
- .card {
9
- padding: 2em;
10
- }
11
-
12
- button {
13
- border-radius: 8px;
14
- border: 1px solid transparent;
15
- padding: 0.6em 1.2em;
16
- font-size: 1em;
17
- font-weight: 500;
18
- font-family: inherit;
19
- background-color: #1a1a1a;
20
- cursor: pointer;
21
- transition: border-color 0.25s;
22
- }
23
-
24
- button:hover {
25
- border-color: #646cff;
26
- }
27
-
28
- button:focus,
29
- button:focus-visible {
30
- outline: 4px auto -webkit-focus-ring-color;
31
- }
@@ -1,21 +0,0 @@
1
- import { useState } from "react";
2
- import "./App.css";
3
-
4
- function App() {
5
- const [count, setCount] = useState(0);
6
-
7
- return (
8
- // @ts-expect-error mock
9
- <div className="App">
10
- {/* @ts-expect-error mock */}
11
- <h1>React TSX App</h1>
12
- {/* @ts-expect-error mock */}
13
- <div className="card">
14
- {/* @ts-expect-error mock */}
15
- <button onClick={() => setCount((count: number) => count + 1)}>count is {count}</button>
16
- </div>
17
- </div>
18
- );
19
- }
20
-
21
- export default App;
@@ -1,31 +0,0 @@
1
- # React TSX Project
2
-
3
- A modern React project with TypeScript and Vite.
4
-
5
- ## Features
6
-
7
- - React 19 with TypeScript
8
- - Vite for fast development and building
9
- - Modern CSS with CSS modules support
10
- - Hot Module Replacement (HMR)
11
- - ESLint and Prettier configuration
12
-
13
- ## Getting Started
14
-
15
- 1. Install dependencies:
16
-
17
- ```bash
18
- npm install
19
- ```
20
-
21
- 2. Start development server:
22
-
23
- ```bash
24
- npm run dev
25
- ```
26
-
27
- 3. Build for production:
28
-
29
- ```bash
30
- npm run build
31
- ```
@@ -1,27 +0,0 @@
1
- :root {
2
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
- line-height: 1.5;
4
- font-weight: 400;
5
-
6
- color-scheme: light dark;
7
- color: rgba(255, 255, 255, 0.87);
8
- background-color: #242424;
9
-
10
- font-synthesis: none;
11
- text-rendering: optimizeLegibility;
12
- -webkit-font-smoothing: antialiased;
13
- -moz-osx-font-smoothing: grayscale;
14
- }
15
-
16
- body {
17
- margin: 0;
18
- display: flex;
19
- place-items: center;
20
- min-width: 320px;
21
- min-height: 100vh;
22
- }
23
-
24
- h1 {
25
- font-size: 3.2em;
26
- line-height: 1.1;
27
- }
@@ -1 +0,0 @@
1
- console.log("Hello, world!");
@@ -1,12 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>React TSX App</title>
7
- </head>
8
- <body>
9
- <div id="root"></div>
10
- <script type="module" src="/src/main.tsx"></script>
11
- </body>
12
- </html>
@@ -1,11 +0,0 @@
1
- import React from "react";
2
- // @ts-expect-error mock
3
- import ReactDOM from "react-dom/client";
4
- import App from "./App";
5
- import "./index.css";
6
-
7
- ReactDOM.createRoot(document.getElementById("root")!).render(
8
- <React.StrictMode>
9
- <App />
10
- </React.StrictMode>,
11
- );