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 +11 -0
- package/bin/make-folder-txt.js +176 -23
- package/package.json +1 -1
- package/make-folder-txt.txt +0 -1258
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
|
package/bin/make-folder-txt.js
CHANGED
|
@@ -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
|
-
|
|
870
|
-
|
|
871
|
-
|
|
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
|
-
|
|
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
|
-
--
|
|
965
|
-
--
|
|
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 --
|
|
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.
|
|
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": {
|