make-folder-txt 2.2.0 → 2.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.
@@ -449,18 +449,276 @@ function splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize
449
449
  return results;
450
450
  }
451
451
 
452
- // ── main ──────────────────────────────────────────────────────────────────────
452
+ // ── configuration ────────────────────────────────────────────────────────────────
453
+
454
+ function createInteractiveConfig() {
455
+ const readline = require('readline');
456
+ const fs = require('fs');
457
+ const path = require('path');
458
+ const os = require('os');
459
+
460
+ const rl = readline.createInterface({
461
+ input: process.stdin,
462
+ output: process.stdout
463
+ });
464
+
465
+ console.log('\n🔧 make-folder-txt Configuration Setup');
466
+ console.log('=====================================\n');
467
+
468
+ return new Promise((resolve) => {
469
+ const config = {
470
+ maxFileSize: '500KB',
471
+ splitMethod: 'none',
472
+ splitSize: '5MB',
473
+ copyToClipboard: false
474
+ };
475
+
476
+ let currentStep = 0;
477
+ const questions = [
478
+ {
479
+ key: 'maxFileSize',
480
+ question: 'Maximum file size to include (e.g., 500KB, 2MB, 1GB): ',
481
+ default: '500KB',
482
+ validate: (value) => {
483
+ if (!value.trim()) return true;
484
+ const validUnits = ['B', 'KB', 'MB', 'GB', 'TB'];
485
+ const match = value.match(/^(\d+(?:\.\d+)?)\s*([A-Z]+)$/i);
486
+ if (!match) return 'Please enter a valid size (e.g., 500KB, 2MB, 1GB)';
487
+ if (!validUnits.includes(match[2].toUpperCase())) return `Invalid unit. Use: ${validUnits.join(', ')}`;
488
+ return true;
489
+ }
490
+ },
491
+ {
492
+ key: 'splitMethod',
493
+ question: 'Split output method (none, folder, file, size): ',
494
+ default: 'none',
495
+ validate: (value) => {
496
+ const validMethods = ['none', 'folder', 'file', 'size'];
497
+ if (!validMethods.includes(value.toLowerCase())) return `Please choose: ${validMethods.join(', ')}`;
498
+ return true;
499
+ }
500
+ },
501
+ {
502
+ key: 'splitSize',
503
+ question: 'Split size when using size method (e.g., 5MB, 10MB): ',
504
+ default: '5MB',
505
+ ask: () => config.splitMethod === 'size',
506
+ validate: (value) => {
507
+ if (!value.trim()) return true;
508
+ const validUnits = ['B', 'KB', 'MB', 'GB', 'TB'];
509
+ const match = value.match(/^(\d+(?:\.\d+)?)\s*([A-Z]+)$/i);
510
+ if (!match) return 'Please enter a valid size (e.g., 5MB, 10MB)';
511
+ if (!validUnits.includes(match[2].toUpperCase())) return `Invalid unit. Use: ${validUnits.join(', ')}`;
512
+ return true;
513
+ }
514
+ },
515
+ {
516
+ key: 'copyToClipboard',
517
+ question: 'Copy to clipboard automatically? (y/n): ',
518
+ default: 'n',
519
+ validate: (value) => {
520
+ const answer = value.toLowerCase();
521
+ if (!['y', 'n', 'yes', 'no'].includes(answer)) return 'Please enter y/n or yes/no';
522
+ return true;
523
+ },
524
+ transform: (value) => ['y', 'yes'].includes(value.toLowerCase())
525
+ },
526
+ {
527
+ key: 'addToTxtIgnore',
528
+ question: 'Add ignore patterns to .txtignore file? (y/n): ',
529
+ default: 'n',
530
+ validate: (value) => {
531
+ const answer = value.toLowerCase();
532
+ if (!['y', 'n', 'yes', 'no'].includes(answer)) return 'Please enter y/n or yes/no';
533
+ return true;
534
+ },
535
+ transform: (value) => ['y', 'yes'].includes(value.toLowerCase())
536
+ },
537
+ {
538
+ key: 'ignoreFolders',
539
+ question: 'Ignore folders (comma-separated, or press Enter to skip): ',
540
+ default: '',
541
+ ask: () => config.addToTxtIgnore,
542
+ transform: (value) => {
543
+ if (!value || value.trim() === '') return [];
544
+ return value.split(',').map(f => f.trim()).filter(f => f);
545
+ }
546
+ },
547
+ {
548
+ key: 'ignoreFiles',
549
+ question: 'Ignore files (comma-separated, or press Enter to skip): ',
550
+ default: '',
551
+ ask: () => config.addToTxtIgnore,
552
+ transform: (value) => {
553
+ if (!value || value.trim() === '') return [];
554
+ return value.split(',').map(f => f.trim()).filter(f => f);
555
+ }
556
+ }
557
+ ];
558
+
559
+ function askQuestion() {
560
+ if (currentStep >= questions.length) {
561
+ // Save configuration
562
+ saveConfig();
563
+ return;
564
+ }
565
+
566
+ const q = questions[currentStep];
567
+
568
+ // Skip if conditional ask returns false
569
+ if (q.ask && !q.ask()) {
570
+ currentStep++;
571
+ askQuestion();
572
+ return;
573
+ }
574
+
575
+ const defaultValue = typeof q.default === 'function' ? q.default() : q.default;
576
+ rl.question(q.question + (defaultValue ? `(${defaultValue}) ` : ''), (answer) => {
577
+ const value = answer.trim() || defaultValue;
578
+
579
+ // Validate input
580
+ if (q.validate) {
581
+ const validation = q.validate(value);
582
+ if (validation !== true) {
583
+ console.log(`❌ ${validation}`);
584
+ askQuestion();
585
+ return;
586
+ }
587
+ }
588
+
589
+ // Transform value if needed
590
+ if (q.transform) {
591
+ config[q.key] = q.transform(value);
592
+ } else {
593
+ config[q.key] = value;
594
+ }
595
+
596
+ currentStep++;
597
+ askQuestion();
598
+ });
599
+ }
453
600
 
454
- const args = process.argv.slice(2);
601
+ function saveConfig() {
602
+ try {
603
+ // Create .txtconfig file with proper formatting
604
+ const configPath = path.join(process.cwd(), '.txtconfig');
605
+ const configContent = `{
606
+ "maxFileSize": "${config.maxFileSize}",
607
+ "splitMethod": "${config.splitMethod}",
608
+ "splitSize": "${config.splitSize}",
609
+ "copyToClipboard": ${config.copyToClipboard}
610
+ }`;
611
+
612
+ fs.writeFileSync(configPath, configContent);
613
+
614
+ // Update .txtignore if user wants to add ignore patterns
615
+ if (config.addToTxtIgnore && (config.ignoreFolders.length > 0 || config.ignoreFiles.length > 0)) {
616
+ const ignorePath = path.join(process.cwd(), '.txtignore');
617
+ let ignoreContent = '';
618
+
619
+ // Read existing content if file exists
620
+ if (fs.existsSync(ignorePath)) {
621
+ ignoreContent = fs.readFileSync(ignorePath, 'utf8');
622
+ if (!ignoreContent.endsWith('\n')) ignoreContent += '\n';
623
+ }
624
+
625
+ // Add new ignore patterns
626
+ const newPatterns = [
627
+ ...config.ignoreFolders.map(f => f.endsWith('/') ? f : `${f}/`),
628
+ ...config.ignoreFiles
629
+ ];
630
+
631
+ if (newPatterns.length > 0) {
632
+ ignoreContent += '\n# Added by make-folder-txt config\n';
633
+ ignoreContent += newPatterns.join('\n') + '\n';
634
+ fs.writeFileSync(ignorePath, ignoreContent);
635
+ }
636
+ }
637
+
638
+ console.log('\n✅ Configuration saved successfully!');
639
+ console.log(`📄 Config file: ${configPath}`);
640
+ if (config.addToTxtIgnore && (config.ignoreFolders.length > 0 || config.ignoreFiles.length > 0)) {
641
+ console.log(`📝 Ignore patterns added to .txtignore`);
642
+ }
643
+ console.log('\n💡 You can now run: make-folder-txt --use-config');
644
+
645
+ } catch (err) {
646
+ console.error('❌ Error saving configuration:', err.message);
647
+ } finally {
648
+ rl.close();
649
+ resolve();
650
+ }
651
+ }
455
652
 
456
- if (args.includes("-v") || args.includes("--version")) {
457
- console.log(`v${version}`);
458
- console.log("Built by Muhammad Saad Amin");
459
- process.exit(0);
653
+ askQuestion();
654
+ });
655
+ }
656
+
657
+ function loadConfig() {
658
+ const fs = require('fs');
659
+ const path = require('path');
660
+
661
+ try {
662
+ const configPath = path.join(process.cwd(), '.txtconfig');
663
+ if (!fs.existsSync(configPath)) {
664
+ console.error('❌ .txtconfig file not found. Run --make-config first.');
665
+ process.exit(1);
666
+ }
667
+
668
+ const configContent = fs.readFileSync(configPath, 'utf8');
669
+ return JSON.parse(configContent);
670
+ } catch (err) {
671
+ console.error('❌ Error loading configuration:', err.message);
672
+ process.exit(1);
673
+ }
460
674
  }
461
675
 
462
- if (args.includes("--help") || args.includes("-h")) {
463
- console.log(`
676
+ // ── main ──────────────────────────────────────────────────────────────────────
677
+
678
+ async function main() {
679
+ const args = process.argv.slice(2);
680
+
681
+ if (args.includes("-v") || args.includes("--version")) {
682
+ console.log(`v${version}`);
683
+ console.log("Built by Muhammad Saad Amin");
684
+ process.exit(0);
685
+ }
686
+
687
+ if (args.includes("--make-config")) {
688
+ await createInteractiveConfig();
689
+ process.exit(0);
690
+ }
691
+
692
+ if (args.includes("--use-config")) {
693
+ const config = loadConfig();
694
+
695
+ // Apply config to command line arguments
696
+ const newArgs = [];
697
+
698
+ // Add max file size if not default
699
+ if (config.maxFileSize !== '500KB') {
700
+ newArgs.push('--skip-large', config.maxFileSize);
701
+ }
702
+
703
+ // Add split method if not none
704
+ if (config.splitMethod !== 'none') {
705
+ newArgs.push('--split-method', config.splitMethod);
706
+ if (config.splitMethod === 'size') {
707
+ newArgs.push('--split-size', config.splitSize);
708
+ }
709
+ }
710
+
711
+ // Add copy to clipboard if true
712
+ if (config.copyToClipboard) {
713
+ newArgs.push('--copy');
714
+ }
715
+
716
+ // Replace args with config-based args
717
+ args.splice(0, args.length, ...newArgs);
718
+ }
719
+
720
+ if (args.includes("--help") || args.includes("-h")) {
721
+ console.log(`
464
722
  \x1b[33mmake-folder-txt\x1b[0m
465
723
  Dump an entire project folder into a single readable .txt file.
466
724
 
@@ -478,6 +736,8 @@ Dump an entire project folder into a single readable .txt file.
478
736
  --split-size <size> Split output when size exceeds limit (requires --split-method size)
479
737
  --copy Copy output to clipboard
480
738
  --force Include everything (overrides all ignore patterns)
739
+ --make-config Create interactive configuration
740
+ --use-config Use configuration from .txtconfig file
481
741
  --help, -h Show this help message
482
742
  --version, -v Show version information
483
743
 
@@ -499,6 +759,8 @@ Dump an entire project folder into a single readable .txt file.
499
759
  make-folder-txt -ofo src docs
500
760
  make-folder-txt --only-file package.json README.md
501
761
  make-folder-txt -ofi package.json README.md
762
+ make-folder-txt --make-config
763
+ make-folder-txt --use-config
502
764
 
503
765
  \x1b[33m.TXTIGNORE FILE\x1b[0m
504
766
  Create a .txtignore file in your project root to specify files/folders to ignore.
@@ -511,373 +773,372 @@ Dump an entire project folder into a single readable .txt file.
511
773
  coverage/
512
774
  LICENSE
513
775
  `);
514
- process.exit(0);
515
- }
776
+ process.exit(0);
777
+ }
516
778
 
517
- const ignoreDirs = new Set(IGNORE_DIRS);
518
- const ignoreFiles = new Set(IGNORE_FILES);
519
- const onlyFolders = new Set();
520
- const onlyFiles = new Set();
521
- let outputArg = null;
522
- let copyToClipboardFlag = false;
523
- let forceFlag = false;
524
- let maxFileSize = 500 * 1024; // Default 500KB
525
- let noSkipFlag = false;
526
- let splitMethod = null; // 'folder', 'file', 'size'
527
- let splitSize = null; // size in bytes
528
-
529
- for (let i = 0; i < args.length; i += 1) {
530
- const arg = args[i];
531
-
532
- if (arg === "--copy") {
533
- copyToClipboardFlag = true;
534
- continue;
535
- }
779
+ const ignoreDirs = new Set(IGNORE_DIRS);
780
+ const ignoreFiles = new Set(IGNORE_FILES);
781
+ const onlyFolders = new Set();
782
+ const onlyFiles = new Set();
783
+ let outputArg = null;
784
+ let copyToClipboardFlag = false;
785
+ let forceFlag = false;
786
+ let maxFileSize = 500 * 1024; // Default 500KB
787
+ let noSkipFlag = false;
788
+ let splitMethod = null; // 'folder', 'file', 'size'
789
+ let splitSize = null; // size in bytes
790
+
791
+ for (let i = 0; i < args.length; i += 1) {
792
+ const arg = args[i];
793
+
794
+ if (arg === "--copy") {
795
+ copyToClipboardFlag = true;
796
+ continue;
797
+ }
536
798
 
537
- if (arg === "--force") {
538
- forceFlag = true;
539
- continue;
540
- }
799
+ if (arg === "--force") {
800
+ forceFlag = true;
801
+ continue;
802
+ }
541
803
 
542
- if (arg === "--no-skip") {
543
- noSkipFlag = true;
544
- continue;
545
- }
804
+ if (arg === "--no-skip") {
805
+ noSkipFlag = true;
806
+ continue;
807
+ }
546
808
 
547
- if (arg === "--skip-large") {
548
- if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
549
- console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
550
- process.exit(1);
551
- }
552
- maxFileSize = parseFileSize(args[i + 1]);
553
- i += 1;
554
- continue;
555
- }
809
+ if (arg === "--skip-large") {
810
+ if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
811
+ console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
812
+ process.exit(1);
813
+ }
814
+ maxFileSize = parseFileSize(args[i + 1]);
815
+ i += 1;
816
+ continue;
817
+ }
556
818
 
557
- if (arg.startsWith("--skip-large=")) {
558
- const value = arg.slice("--skip-large=".length);
559
- if (!value) {
560
- console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
561
- process.exit(1);
562
- }
563
- maxFileSize = parseFileSize(value);
564
- continue;
565
- }
819
+ if (arg.startsWith("--skip-large=")) {
820
+ const value = arg.slice("--skip-large=".length);
821
+ if (!value) {
822
+ console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
823
+ process.exit(1);
824
+ }
825
+ maxFileSize = parseFileSize(value);
826
+ continue;
827
+ }
566
828
 
567
- if (arg === "--split-method") {
568
- if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
569
- console.error("Error: --split-method requires a method (folder, file, or size).");
570
- process.exit(1);
571
- }
572
- const method = args[i + 1].toLowerCase();
573
- if (!['folder', 'file', 'size'].includes(method)) {
574
- console.error("Error: --split-method must be one of: folder, file, size");
575
- process.exit(1);
576
- }
577
- splitMethod = method;
578
- i += 1;
579
- continue;
580
- }
829
+ if (arg === "--split-method") {
830
+ if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
831
+ console.error("Error: --split-method requires a method (folder, file, or size).");
832
+ process.exit(1);
833
+ }
834
+ const method = args[i + 1].toLowerCase();
835
+ if (!['folder', 'file', 'size'].includes(method)) {
836
+ console.error("Error: --split-method must be one of: folder, file, size");
837
+ process.exit(1);
838
+ }
839
+ splitMethod = method;
840
+ i += 1;
841
+ continue;
842
+ }
581
843
 
582
- if (arg.startsWith("--split-method=")) {
583
- const value = arg.slice("--split-method=".length);
584
- if (!value) {
585
- console.error("Error: --split-method requires a method (folder, file, or size).");
586
- process.exit(1);
587
- }
588
- const method = value.toLowerCase();
589
- if (!['folder', 'file', 'size'].includes(method)) {
590
- console.error("Error: --split-method must be one of: folder, file, size");
591
- process.exit(1);
592
- }
593
- splitMethod = method;
594
- continue;
595
- }
844
+ if (arg.startsWith("--split-method=")) {
845
+ const value = arg.slice("--split-method=".length);
846
+ if (!value) {
847
+ console.error("Error: --split-method requires a method (folder, file, or size).");
848
+ process.exit(1);
849
+ }
850
+ const method = value.toLowerCase();
851
+ if (!['folder', 'file', 'size'].includes(method)) {
852
+ console.error("Error: --split-method must be one of: folder, file, size");
853
+ process.exit(1);
854
+ }
855
+ splitMethod = method;
856
+ continue;
857
+ }
596
858
 
597
- if (arg === "--split-size") {
598
- if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
599
- console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
600
- process.exit(1);
601
- }
602
- splitSize = parseFileSize(args[i + 1]);
603
- i += 1;
604
- continue;
605
- }
859
+ if (arg === "--split-size") {
860
+ if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
861
+ console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
862
+ process.exit(1);
863
+ }
864
+ splitSize = parseFileSize(args[i + 1]);
865
+ i += 1;
866
+ continue;
867
+ }
606
868
 
607
- if (arg.startsWith("--split-size=")) {
608
- const value = arg.slice("--split-size=".length);
609
- if (!value) {
610
- console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
611
- process.exit(1);
612
- }
613
- splitSize = parseFileSize(value);
614
- continue;
615
- }
869
+ if (arg.startsWith("--split-size=")) {
870
+ const value = arg.slice("--split-size=".length);
871
+ if (!value) {
872
+ console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
873
+ process.exit(1);
874
+ }
875
+ splitSize = parseFileSize(value);
876
+ continue;
877
+ }
616
878
 
617
- if (arg === "--ignore-folder" || arg === "-ifo") {
618
- let consumed = 0;
619
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
620
- // Normalize the folder name: remove backslashes, trailing slashes, and leading ./
621
- let folderName = args[i + 1];
622
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
623
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
624
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
625
- ignoreDirs.add(folderName);
626
- i += 1;
627
- consumed += 1;
628
- }
629
- if (consumed === 0) {
630
- console.error("Error: --ignore-folder requires at least one folder name.");
631
- process.exit(1);
632
- }
633
- continue;
634
- }
879
+ if (arg === "--ignore-folder" || arg === "-ifo") {
880
+ let consumed = 0;
881
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
882
+ // Normalize the folder name: remove backslashes, trailing slashes, and leading ./
883
+ let folderName = args[i + 1];
884
+ folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
885
+ folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
886
+ folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
887
+ ignoreDirs.add(folderName);
888
+ i += 1;
889
+ consumed += 1;
890
+ }
891
+ if (consumed === 0) {
892
+ console.error("Error: --ignore-folder requires at least one folder name.");
893
+ process.exit(1);
894
+ }
895
+ continue;
896
+ }
635
897
 
636
- if (arg.startsWith("--ignore-folder=") || arg.startsWith("-ifo=")) {
637
- const value = arg.startsWith("--ignore-folder=")
638
- ? arg.slice("--ignore-folder=".length)
639
- : arg.slice("-ifo=".length);
640
- if (!value) {
641
- console.error("Error: --ignore-folder requires a folder name.");
642
- process.exit(1);
643
- }
644
- // Normalize the folder name
645
- let folderName = value;
646
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
647
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
648
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
649
- ignoreDirs.add(folderName);
650
- continue;
651
- }
898
+ if (arg.startsWith("--ignore-folder=") || arg.startsWith("-ifo=")) {
899
+ const value = arg.startsWith("--ignore-folder=")
900
+ ? arg.slice("--ignore-folder=".length)
901
+ : arg.slice("-ifo=".length);
902
+ if (!value) {
903
+ console.error("Error: --ignore-folder requires a folder name.");
904
+ process.exit(1);
905
+ }
906
+ // Normalize the folder name
907
+ let folderName = value;
908
+ folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
909
+ folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
910
+ folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
911
+ ignoreDirs.add(folderName);
912
+ continue;
913
+ }
652
914
 
653
- if (arg === "--ignore-file" || arg === "-ifi") {
654
- let consumed = 0;
655
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
656
- ignoreFiles.add(args[i + 1]);
657
- i += 1;
658
- consumed += 1;
659
- }
660
- if (consumed === 0) {
661
- console.error("Error: --ignore-file requires at least one file name.");
662
- process.exit(1);
663
- }
664
- continue;
665
- }
915
+ if (arg === "--ignore-file" || arg === "-ifi") {
916
+ let consumed = 0;
917
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
918
+ ignoreFiles.add(args[i + 1]);
919
+ i += 1;
920
+ consumed += 1;
921
+ }
922
+ if (consumed === 0) {
923
+ console.error("Error: --ignore-file requires at least one file name.");
924
+ process.exit(1);
925
+ }
926
+ continue;
927
+ }
666
928
 
667
- if (arg.startsWith("--ignore-file=") || arg.startsWith("-ifi=")) {
668
- const value = arg.startsWith("--ignore-file=")
669
- ? arg.slice("--ignore-file=".length)
670
- : arg.slice("-ifi=".length);
671
- if (!value) {
672
- console.error("Error: --ignore-file requires a file name.");
673
- process.exit(1);
674
- }
675
- ignoreFiles.add(value);
676
- continue;
677
- }
929
+ if (arg.startsWith("--ignore-file=") || arg.startsWith("-ifi=")) {
930
+ const value = arg.startsWith("--ignore-file=")
931
+ ? arg.slice("--ignore-file=".length)
932
+ : arg.slice("-ifi=".length);
933
+ if (!value) {
934
+ console.error("Error: --ignore-file requires a file name.");
935
+ process.exit(1);
936
+ }
937
+ ignoreFiles.add(value);
938
+ continue;
939
+ }
678
940
 
679
- if (arg === "--only-folder" || arg === "-ofo") {
680
- let consumed = 0;
681
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
682
- // Normalize the folder name
683
- let folderName = args[i + 1];
684
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
685
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
686
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
687
- onlyFolders.add(folderName);
688
- i += 1;
689
- consumed += 1;
690
- }
691
- if (consumed === 0) {
692
- console.error("Error: --only-folder requires at least one folder name.");
693
- process.exit(1);
694
- }
695
- continue;
696
- }
941
+ if (arg === "--only-folder" || arg === "-ofo") {
942
+ let consumed = 0;
943
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
944
+ // Normalize the folder name
945
+ let folderName = args[i + 1];
946
+ folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
947
+ folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
948
+ folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
949
+ onlyFolders.add(folderName);
950
+ i += 1;
951
+ consumed += 1;
952
+ }
953
+ if (consumed === 0) {
954
+ console.error("Error: --only-folder requires at least one folder name.");
955
+ process.exit(1);
956
+ }
957
+ continue;
958
+ }
959
+
960
+ if (arg.startsWith("--only-folder=") || arg.startsWith("-ofo=")) {
961
+ const value = arg.startsWith("--only-folder=")
962
+ ? arg.slice("--only-folder=".length)
963
+ : arg.slice("-ofo=".length);
964
+ if (!value) {
965
+ console.error("Error: --only-folder requires a folder name.");
966
+ process.exit(1);
967
+ }
968
+ // Normalize the folder name
969
+ let folderName = value;
970
+ folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
971
+ folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
972
+ folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
973
+ onlyFolders.add(folderName);
974
+ continue;
975
+ }
976
+
977
+ if (arg === "--only-file" || arg === "-ofi") {
978
+ let consumed = 0;
979
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
980
+ onlyFiles.add(args[i + 1]);
981
+ i += 1;
982
+ consumed += 1;
983
+ }
984
+ if (consumed === 0) {
985
+ console.error("Error: --only-file requires at least one file name.");
986
+ process.exit(1);
987
+ }
988
+ continue;
989
+ }
990
+
991
+ if (arg.startsWith("--only-file=") || arg.startsWith("-ofi=")) {
992
+ const value = arg.startsWith("--only-file=")
993
+ ? arg.slice("--only-file=".length)
994
+ : arg.slice("-ofi=".length);
995
+ if (!value) {
996
+ console.error("Error: --only-file requires a file name.");
997
+ process.exit(1);
998
+ }
999
+ onlyFiles.add(value);
1000
+ continue;
1001
+ }
697
1002
 
698
- if (arg.startsWith("--only-folder=") || arg.startsWith("-ofo=")) {
699
- const value = arg.startsWith("--only-folder=")
700
- ? arg.slice("--only-folder=".length)
701
- : arg.slice("-ofo=".length);
702
- if (!value) {
703
- console.error("Error: --only-folder requires a folder name.");
1003
+ // Unknown argument
1004
+ console.error(`Error: Unknown option "${arg}"`);
1005
+ console.error("Use --help for available options.");
704
1006
  process.exit(1);
705
1007
  }
706
- // Normalize the folder name
707
- let folderName = value;
708
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
709
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
710
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
711
- onlyFolders.add(folderName);
712
- continue;
713
- }
714
1008
 
715
- if (arg === "--only-file" || arg === "-ofi") {
716
- let consumed = 0;
717
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
718
- onlyFiles.add(args[i + 1]);
719
- i += 1;
720
- consumed += 1;
721
- }
722
- if (consumed === 0) {
723
- console.error("Error: --only-file requires at least one file name.");
1009
+ // Validate split options
1010
+ if (splitMethod === 'size' && !splitSize) {
1011
+ console.error("Error: --split-method size requires --split-size to be specified");
724
1012
  process.exit(1);
725
1013
  }
726
- continue;
727
- }
728
1014
 
729
- if (arg.startsWith("--only-file=") || arg.startsWith("-ofi=")) {
730
- const value = arg.startsWith("--only-file=")
731
- ? arg.slice("--only-file=".length)
732
- : arg.slice("-ofi=".length);
733
- if (!value) {
734
- console.error("Error: --only-file requires a file name.");
1015
+ if (splitSize && splitMethod !== 'size') {
1016
+ console.error("Error: --split-size can only be used with --split-method size");
735
1017
  process.exit(1);
736
1018
  }
737
- onlyFiles.add(value);
738
- continue;
739
- }
740
1019
 
741
- if (arg.startsWith("-")) {
742
- console.error(`Error: Unknown option "${arg}".`);
743
- process.exit(1);
744
- }
1020
+ // ── config ────────────────────────────────────────────────────────────────────────
745
1021
 
746
- if (!outputArg) {
747
- outputArg = arg;
748
- continue;
749
- }
1022
+ const folderPath = process.cwd();
1023
+ const rootName = path.basename(folderPath);
1024
+ const txtIgnore = readTxtIgnore(folderPath);
750
1025
 
751
- console.error(`Error: Unexpected argument "${arg}".`);
752
- process.exit(1);
753
- }
1026
+ // ── build tree & collect file paths ───────────────────────────────────────────────
754
1027
 
755
- // Validate split options
756
- if (splitMethod === 'size' && !splitSize) {
757
- console.error("Error: --split-method size requires --split-size to be specified.");
758
- process.exit(1);
759
- }
1028
+ const { lines: treeLines, filePaths } = collectFiles(
1029
+ folderPath,
1030
+ folderPath,
1031
+ ignoreDirs,
1032
+ ignoreFiles,
1033
+ onlyFolders,
1034
+ onlyFiles,
1035
+ { rootName, txtIgnore, force: forceFlag }
1036
+ );
760
1037
 
761
- if (splitSize && splitMethod !== 'size') {
762
- console.error("Error: --split-size can only be used with --split-method size.");
763
- process.exit(1);
764
- }
1038
+ // ── build output filename ───────────────────────────────────────────────────────────
1039
+
1040
+ let outputFile = outputArg || path.join(folderPath, `${rootName}.txt`);
1041
+
1042
+ // ── handle output splitting ─────────────────────────────────────────────────────────
765
1043
 
766
- const folderPath = process.cwd();
767
- const rootName = path.basename(folderPath);
1044
+ const effectiveMaxSize = noSkipFlag ? Infinity : maxFileSize;
1045
+
1046
+ if (splitMethod) {
1047
+ console.log(`🔧 Splitting output by: ${splitMethod}`);
1048
+
1049
+ let results;
1050
+
1051
+ if (splitMethod === 'folder') {
1052
+ results = splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceFlag);
1053
+ } else if (splitMethod === 'file') {
1054
+ results = splitByFiles(filePaths, rootName, effectiveMaxSize, forceFlag);
1055
+ } else if (splitMethod === 'size') {
1056
+ results = splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize, forceFlag);
1057
+ }
1058
+
1059
+ console.log(`✅ Done! Created ${results.length} split files:`);
1060
+ console.log('');
1061
+
1062
+ results.forEach((result, index) => {
1063
+ if (splitMethod === 'folder') {
1064
+ console.log(`📁 Folder: ${result.folder}`);
1065
+ } else if (splitMethod === 'file') {
1066
+ console.log(`📄 File: ${result.fileName}`);
1067
+ } else if (splitMethod === 'size') {
1068
+ console.log(`📦 Part ${result.part}`);
1069
+ }
1070
+ console.log(`📄 Output : ${result.file}`);
1071
+ console.log(`📊 Size : ${result.size} KB`);
1072
+ console.log(`🗂️ Files : ${result.files}`);
1073
+ console.log('');
1074
+ });
1075
+
1076
+ if (copyToClipboardFlag) {
1077
+ console.log('⚠️ --copy flag is not compatible with splitting - clipboard copy skipped');
1078
+ }
1079
+
1080
+ process.exit(0);
1081
+ }
768
1082
 
769
- // Read .txtignore file if it exists
770
- const txtIgnore = readTxtIgnore(folderPath);
1083
+ // ── build output (no splitting) ───────────────────────────────────────────────────
1084
+ const out = [];
1085
+ const divider = "=".repeat(80);
1086
+ const subDivider = "-".repeat(80);
771
1087
 
772
- const outputFile = outputArg
773
- ? path.resolve(outputArg)
774
- : path.join(process.cwd(), `${rootName}.txt`);
1088
+ out.push(divider);
1089
+ out.push(`START OF FOLDER: ${rootName}`);
1090
+ out.push(divider);
1091
+ out.push("");
775
1092
 
776
- console.log(`\n📂 Scanning: ${folderPath}`);
1093
+ out.push(divider);
1094
+ out.push("PROJECT STRUCTURE");
1095
+ out.push(divider);
1096
+ out.push(`Root: ${folderPath}\n`);
777
1097
 
778
- const hasOnlyFilters = onlyFolders.size > 0 || onlyFiles.size > 0;
779
- const { lines: treeLines, filePaths } = collectFiles(
780
- folderPath,
781
- folderPath,
782
- ignoreDirs,
783
- ignoreFiles,
784
- onlyFolders,
785
- onlyFiles,
786
- { hasOnlyFilters, rootName, txtIgnore, force: forceFlag },
787
- );
1098
+ out.push(`${rootName}/`);
1099
+ treeLines.forEach(l => out.push(l));
1100
+ out.push("");
1101
+ out.push(`Total files: ${filePaths.length}`);
1102
+ out.push("");
788
1103
 
789
- // ── handle splitting ──────────────────────────────────────────────────────────────
790
- const effectiveMaxSize = noSkipFlag ? Infinity : maxFileSize;
1104
+ out.push(divider);
1105
+ out.push("FILE CONTENTS");
1106
+ out.push(divider);
791
1107
 
792
- if (splitMethod) {
793
- console.log(`🔧 Splitting output by: ${splitMethod}`);
794
-
795
- let results;
796
-
797
- if (splitMethod === 'folder') {
798
- results = splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceFlag);
799
- } else if (splitMethod === 'file') {
800
- results = splitByFiles(filePaths, rootName, effectiveMaxSize, forceFlag);
801
- } else if (splitMethod === 'size') {
802
- results = splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize, forceFlag);
803
- }
804
-
805
- console.log(`✅ Done! Created ${results.length} split files:`);
806
- console.log('');
807
-
808
- results.forEach((result, index) => {
809
- if (splitMethod === 'folder') {
810
- console.log(`📁 Folder: ${result.folder}`);
811
- } else if (splitMethod === 'file') {
812
- console.log(`📄 File: ${result.fileName}`);
813
- } else if (splitMethod === 'size') {
814
- console.log(`📦 Part ${result.part}`);
815
- }
816
- console.log(`📄 Output : ${result.file}`);
817
- console.log(`📊 Size : ${result.size} KB`);
818
- console.log(`🗂️ Files : ${result.files}`);
819
- console.log('');
820
- });
821
-
822
- if (copyToClipboardFlag) {
823
- console.log('⚠️ --copy flag is not compatible with splitting - clipboard copy skipped');
824
- }
825
-
826
- process.exit(0);
827
- }
1108
+ filePaths.forEach(({ abs, rel }) => {
1109
+ out.push("");
1110
+ out.push(subDivider);
1111
+ out.push(`FILE: ${rel}`);
1112
+ out.push(subDivider);
1113
+ out.push(readContent(abs, forceFlag, effectiveMaxSize));
1114
+ });
828
1115
 
829
- // ── build output (no splitting) ───────────────────────────────────────────────────
830
- const out = [];
831
- const divider = "=".repeat(80);
832
- const subDivider = "-".repeat(80);
833
-
834
- out.push(divider);
835
- out.push(`START OF FOLDER: ${rootName}`);
836
- out.push(divider);
837
- out.push("");
838
-
839
- out.push(divider);
840
- out.push("PROJECT STRUCTURE");
841
- out.push(divider);
842
- out.push(`Root: ${folderPath}\n`);
843
- out.push(`${rootName}/`);
844
- treeLines.forEach(l => out.push(l));
845
- out.push("");
846
- out.push(`Total files: ${filePaths.length}`);
847
- out.push("");
848
-
849
- out.push(divider);
850
- out.push("FILE CONTENTS");
851
- out.push(divider);
852
-
853
- filePaths.forEach(({ abs, rel }) => {
854
- out.push("");
855
- out.push(subDivider);
856
- out.push(`FILE: ${rel}`);
857
- out.push(subDivider);
858
- const effectiveMaxSize = noSkipFlag ? Infinity : maxFileSize;
859
- out.push(readContent(abs, forceFlag, effectiveMaxSize));
860
- });
1116
+ out.push("");
1117
+ out.push(divider);
1118
+ out.push(`END OF FOLDER: ${rootName}`);
1119
+ out.push(divider);
861
1120
 
862
- out.push("");
863
- out.push(divider);
864
- out.push(`END OF FOLDER: ${rootName}`);
865
- out.push(divider);
1121
+ fs.writeFileSync(outputFile, out.join("\n"), "utf8");
866
1122
 
867
- fs.writeFileSync(outputFile, out.join("\n"), "utf8");
1123
+ const sizeKB = (fs.statSync(outputFile).size / 1024).toFixed(1);
1124
+ console.log(`✅ Done!`);
1125
+ console.log(`📄 Output : ${outputFile}`);
1126
+ console.log(`📊 Size : ${sizeKB} KB`);
1127
+ console.log(`🗂️ Files : ${filePaths.length}`);
868
1128
 
869
- const sizeKB = (fs.statSync(outputFile).size / 1024).toFixed(1);
870
- console.log(`✅ Done!`);
871
- console.log(`📄 Output : ${outputFile}`);
872
- console.log(`📊 Size : ${sizeKB} KB`);
873
- console.log(`🗂️ Files : ${filePaths.length}`);
1129
+ if (copyToClipboardFlag) {
1130
+ const content = fs.readFileSync(outputFile, 'utf8');
1131
+ const success = copyToClipboard(content);
1132
+ if (success) {
1133
+ console.log(`📋 Copied to clipboard!`);
1134
+ }
1135
+ }
874
1136
 
875
- if (copyToClipboardFlag) {
876
- const content = fs.readFileSync(outputFile, 'utf8');
877
- const success = copyToClipboard(content);
878
- if (success) {
879
- console.log(`📋 Copied to clipboard!`);
880
- }
1137
+ console.log('');
881
1138
  }
882
1139
 
883
- console.log('');
1140
+ // Run the main function
1141
+ main().catch(err => {
1142
+ console.error('❌ Error:', err.message);
1143
+ process.exit(1);
1144
+ });