make-folder-txt 2.2.10 → 2.3.0

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
@@ -25,6 +25,7 @@ Ever needed to share your entire project with ChatGPT, Claude, or a teammate —
25
25
  - ✅ Built-in help system with `--help` flag
26
26
  - ✅ File size control with `--skip-large` and `--no-skip`
27
27
  - ✅ Output splitting by folders, files, or size
28
+ - ✅ Reverse mode to recreate folders from generated `.txt` files
28
29
  - ✅ Copy to clipboard with `--copy` flag
29
30
  - ✅ Force include everything with `--force` flag
30
31
  - ✅ Generates a clean folder tree + every file's content
@@ -160,6 +161,16 @@ make-folder-txt --split-method size --split-size 2MB --ignore-folder node_module
160
161
 
161
162
  **Note**: Splitting is not compatible with `--copy` flag.
162
163
 
164
+ ### 🔄 Reverse a Txt Back Into a Folder
165
+
166
+ ```bash
167
+ make-folder-txt --reverse # Restore from <current-folder>.txt
168
+ make-folder-txt --reverse my-project.txt # Restore from a specific dump
169
+ make-folder-txt --reverse my-project.txt --force # Overwrite existing restored files
170
+ ```
171
+
172
+ The `--reverse` flag reads a `.txt` file created by `make-folder-txt` and recreates its files inside a folder named after the original dump. Existing files are skipped unless you add `--force`.
173
+
163
174
  Ignore specific folders/files by name:
164
175
 
165
176
  ```bash
@@ -847,9 +847,9 @@ function createInteractiveConfig() {
847
847
  });
848
848
  }
849
849
 
850
- function loadConfig() {
851
- const fs = require("fs");
852
- const path = require("path");
850
+ function loadConfig() {
851
+ const fs = require("fs");
852
+ const path = require("path");
853
853
 
854
854
  try {
855
855
  const configPath = path.join(process.cwd(), ".txtconfig");
@@ -863,12 +863,155 @@ function loadConfig() {
863
863
  } catch (err) {
864
864
  console.error("❌ Error loading configuration:", err.message);
865
865
  process.exit(1);
866
- }
867
- }
868
-
869
- // ── main ──────────────────────────────────────────────────────────────────────
870
-
871
- async function main() {
866
+ }
867
+ }
868
+
869
+ function sanitizeRootName(name) {
870
+ return name
871
+ .replace(/[<>:"/\\|?*\x00-\x1F]/g, "-")
872
+ .replace(/^\.+$/, "restored-folder")
873
+ .trim();
874
+ }
875
+
876
+ function getReverseTargetPath(args) {
877
+ const reverseIndex = args.indexOf("--reverse");
878
+ const nextArg = args[reverseIndex + 1];
879
+
880
+ if (nextArg && !nextArg.startsWith("-")) {
881
+ return path.resolve(process.cwd(), nextArg);
882
+ }
883
+
884
+ return path.join(process.cwd(), `${path.basename(process.cwd())}.txt`);
885
+ }
886
+
887
+ function parseReverseDump(content, txtPath) {
888
+ const lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
889
+ const dividerLine = /^={80}$/;
890
+ const fileDividerLine = /^-{80}$/;
891
+ const startLine = lines.find((line) => line.startsWith("START OF FOLDER: "));
892
+ const rootNameFromHeader = startLine
893
+ ? startLine
894
+ .slice("START OF FOLDER: ".length)
895
+ .replace(/\s+\(Part \d+\)$/, "")
896
+ .trim()
897
+ : "";
898
+ const rootName =
899
+ sanitizeRootName(rootNameFromHeader) ||
900
+ sanitizeRootName(path.basename(txtPath, path.extname(txtPath)));
901
+ const files = [];
902
+
903
+ for (let i = 0; i < lines.length; i += 1) {
904
+ if (
905
+ !lines[i].startsWith("FILE: ") ||
906
+ i === 0 ||
907
+ i + 1 >= lines.length ||
908
+ !fileDividerLine.test(lines[i - 1]) ||
909
+ !fileDividerLine.test(lines[i + 1])
910
+ ) {
911
+ continue;
912
+ }
913
+
914
+ const rel = lines[i]
915
+ .slice("FILE: ".length)
916
+ .replace(/\\/g, "/")
917
+ .replace(/^\/+/, "");
918
+
919
+ if (!rel || rel.includes("\0")) {
920
+ continue;
921
+ }
922
+
923
+ let end = lines.length;
924
+ for (let j = i + 2; j < lines.length; j += 1) {
925
+ const startsNextFile =
926
+ fileDividerLine.test(lines[j]) &&
927
+ j + 2 < lines.length &&
928
+ lines[j + 1].startsWith("FILE: ") &&
929
+ fileDividerLine.test(lines[j + 2]);
930
+ const startsFooter =
931
+ dividerLine.test(lines[j]) &&
932
+ j + 1 < lines.length &&
933
+ (lines[j + 1].startsWith("END OF FOLDER: ") ||
934
+ lines[j + 1].startsWith("END OF FILE: "));
935
+
936
+ if (startsNextFile || startsFooter) {
937
+ end = j;
938
+ break;
939
+ }
940
+ }
941
+
942
+ const contentLines = lines.slice(i + 2, end);
943
+ if (contentLines[contentLines.length - 1] === "") {
944
+ contentLines.pop();
945
+ }
946
+
947
+ files.push({
948
+ rel,
949
+ content: contentLines.join("\n"),
950
+ });
951
+ }
952
+
953
+ return { rootName, files };
954
+ }
955
+
956
+ function reverseTxtToFolder(txtPath, options = {}) {
957
+ if (!fs.existsSync(txtPath)) {
958
+ console.error(`Error: Reverse input file not found: ${txtPath}`);
959
+ process.exit(1);
960
+ }
961
+
962
+ const content = fs.readFileSync(txtPath, "utf8");
963
+ const { rootName, files } = parseReverseDump(content, txtPath);
964
+
965
+ if (files.length === 0) {
966
+ console.error(
967
+ "Error: No file content blocks found. Is this a make-folder-txt output file?",
968
+ );
969
+ process.exit(1);
970
+ }
971
+
972
+ const outputDir = path.join(process.cwd(), rootName);
973
+ const outputRoot = path.resolve(outputDir);
974
+ let written = 0;
975
+ let skipped = 0;
976
+
977
+ fs.mkdirSync(outputRoot, { recursive: true });
978
+
979
+ files.forEach(({ rel, content: fileContent }) => {
980
+ const destination = path.resolve(outputRoot, rel);
981
+
982
+ if (
983
+ destination !== outputRoot &&
984
+ !destination.startsWith(outputRoot + path.sep)
985
+ ) {
986
+ skipped += 1;
987
+ return;
988
+ }
989
+
990
+ if (fs.existsSync(destination) && !options.force) {
991
+ skipped += 1;
992
+ return;
993
+ }
994
+
995
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
996
+ fs.writeFileSync(destination, fileContent, "utf8");
997
+ written += 1;
998
+ });
999
+
1000
+ console.log(`✅ Done!`);
1001
+ console.log(`📁 Folder : ${outputRoot}`);
1002
+ console.log(`📄 Source : ${txtPath}`);
1003
+ console.log(`🗂️ Files : ${written}`);
1004
+
1005
+ if (skipped > 0) {
1006
+ console.log(
1007
+ `⚠️ Skipped: ${skipped} file${skipped === 1 ? "" : "s"} (use --force to overwrite existing files)`,
1008
+ );
1009
+ }
1010
+ }
1011
+
1012
+ // ── main ──────────────────────────────────────────────────────────────────────
1013
+
1014
+ async function main() {
872
1015
  const args = process.argv.slice(2);
873
1016
 
874
1017
  if (args.includes("-v") || args.includes("--version")) {
@@ -882,7 +1025,7 @@ async function main() {
882
1025
  process.exit(0);
883
1026
  }
884
1027
 
885
- if (args.includes("--delete-config")) {
1028
+ if (args.includes("--delete-config")) {
886
1029
  try {
887
1030
  const fs = require("fs");
888
1031
  const path = require("path");
@@ -898,10 +1041,16 @@ async function main() {
898
1041
  } catch (err) {
899
1042
  console.error("❌ Error deleting configuration:", err.message);
900
1043
  }
901
- process.exit(0);
902
- }
903
-
904
- // Auto-use config if it exists and no other flags are provided
1044
+ process.exit(0);
1045
+ }
1046
+
1047
+ if (args.includes("--reverse")) {
1048
+ const txtPath = getReverseTargetPath(args);
1049
+ reverseTxtToFolder(txtPath, { force: args.includes("--force") });
1050
+ process.exit(0);
1051
+ }
1052
+
1053
+ // Auto-use config if it exists and no other flags are provided
905
1054
  const configPath = path.join(process.cwd(), ".txtconfig");
906
1055
  const hasConfig = fs.existsSync(configPath);
907
1056
  const hasOtherFlags =
@@ -958,11 +1107,12 @@ Dump an entire project folder into a single readable .txt file.
958
1107
  --skip-large <size> Skip files larger than specified size (default: 500KB)
959
1108
  --no-skip Include all files regardless of size
960
1109
  --split-method <method> Split output: folder, file, or size
961
- --split-size <size> Split output when size exceeds limit (requires --split-method size)
962
- --copy Copy output to clipboard
963
- --force Include everything (overrides all ignore patterns)
964
- --make-config Create interactive configuration (with optional .gitignore setup)
965
- --delete-config Delete configuration and reset to defaults
1110
+ --split-size <size> Split output when size exceeds limit (requires --split-method size)
1111
+ --copy Copy output to clipboard
1112
+ --force Include everything (overrides all ignore patterns)
1113
+ --reverse [file.txt] Recreate a folder from a make-folder-txt output file
1114
+ --make-config Create interactive configuration (with optional .gitignore setup)
1115
+ --delete-config Delete configuration and reset to defaults
966
1116
  --help, -h Show this help message
967
1117
  --version, -v Show version information
968
1118
 
@@ -973,10 +1123,13 @@ Dump an entire project folder into a single readable .txt file.
973
1123
  make-folder-txt --skip-large 400KB
974
1124
  make-folder-txt --skip-large 5GB
975
1125
  make-folder-txt --no-skip
976
- make-folder-txt --split-method folder
977
- make-folder-txt --split-method file
978
- make-folder-txt --split-method size --split-size 5MB
979
- make-folder-txt --ignore-folder node_modules dist
1126
+ make-folder-txt --split-method folder
1127
+ make-folder-txt --split-method file
1128
+ make-folder-txt --split-method size --split-size 5MB
1129
+ make-folder-txt --reverse
1130
+ make-folder-txt --reverse my-project.txt
1131
+ make-folder-txt --reverse my-project.txt --force
1132
+ make-folder-txt --ignore-folder node_modules dist
980
1133
  make-folder-txt -ifo node_modules dist
981
1134
  make-folder-txt --ignore-file .env .env.local
982
1135
  make-folder-txt -ifi .env .env.local
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-folder-txt",
3
- "version": "2.2.10",
3
+ "version": "2.3.0",
4
4
  "description": "Generate a single .txt file containing the full folder structure and file contents of any project, ignoring node_modules and other junk.",
5
5
  "main": "bin/make-folder-txt.js",
6
6
  "bin": {