make-folder-txt 2.2.6 → 2.2.9

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.
@@ -87,6 +87,7 @@ function collectFiles(
87
87
  rootName = "",
88
88
  txtIgnore = new Set(),
89
89
  force = false,
90
+ rootOnlyInclude = false,
90
91
  } = options;
91
92
 
92
93
  let entries;
@@ -126,30 +127,46 @@ function collectFiles(
126
127
  }
127
128
 
128
129
  const childPath = path.join(dir, entry.name);
129
- const childInSelectedFolder = inSelectedFolder || onlyFolders.has(entry.name);
130
+ // When rootOnlyInclude is active, a folder only "counts" if it's directly under root
131
+ const folderIsSelected = rootOnlyInclude
132
+ ? (dir === rootDir && onlyFolders.has(entry.name))
133
+ : onlyFolders.has(entry.name);
134
+
135
+ const childInSelectedFolder = inSelectedFolder || folderIsSelected;
130
136
  const childLines = [];
131
137
  const childFiles = [];
132
- const child = collectFiles(
133
- childPath,
134
- rootDir,
135
- ignoreDirs,
136
- ignoreFiles,
137
- onlyFolders,
138
- onlyFiles,
139
- {
140
- indent: childIndent,
141
- lines: childLines,
142
- filePaths: childFiles,
143
- inSelectedFolder: childInSelectedFolder,
144
- hasOnlyFilters,
145
- rootName,
146
- txtIgnore,
147
- force,
148
- },
149
- );
138
+
139
+ // If rootOnlyInclude is true, skip recursing into non-selected subdirectories
140
+ let child;
141
+ if (rootOnlyInclude && dir !== rootDir && !inSelectedFolder) {
142
+ // Don't recurse unless already inside a selected folder
143
+ child = { lines: [], filePaths: [], hasIncluded: false };
144
+ } else {
145
+ child = collectFiles(
146
+ childPath,
147
+ rootDir,
148
+ ignoreDirs,
149
+ ignoreFiles,
150
+ onlyFolders,
151
+ onlyFiles,
152
+ {
153
+ indent: childIndent,
154
+ lines: childLines,
155
+ filePaths: childFiles,
156
+ inSelectedFolder: childInSelectedFolder,
157
+ hasOnlyFilters,
158
+ rootName,
159
+ txtIgnore,
160
+ force,
161
+ rootOnlyInclude,
162
+ },
163
+ );
164
+ }
150
165
 
151
- const explicitlySelectedFolder = hasOnlyFilters && onlyFolders.has(entry.name);
152
- const shouldIncludeDir = !hasOnlyFilters || child.hasIncluded || explicitlySelectedFolder;
166
+ // Include the dir if: no filters active, or it's explicitly selected, or (non-root-only) a child matched
167
+ const shouldIncludeDir = !hasOnlyFilters ||
168
+ folderIsSelected ||
169
+ (!rootOnlyInclude && child.hasIncluded);
153
170
 
154
171
  if (shouldIncludeDir) {
155
172
  lines.push(`${indent}${connector}${entry.name}/`);
@@ -170,7 +187,12 @@ function collectFiles(
170
187
  // Ignore .txt files that match the folder name (e.g., foldername.txt) unless force is enabled
171
188
  if (!force && entry.name.endsWith('.txt') && entry.name === `${rootName}.txt`) return;
172
189
 
173
- const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder || onlyFiles.has(entry.name);
190
+ // Check if this file matches any of the onlyFiles patterns
191
+ const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder ||
192
+ onlyFiles.has(entry.name) ||
193
+ onlyFiles.has(relPathForIgnore) ||
194
+ onlyFiles.has(`/${relPathForIgnore}`);
195
+
174
196
  if (!shouldIncludeFile) return;
175
197
 
176
198
  lines.push(`${indent}${connector}${entry.name}`);
@@ -816,6 +838,7 @@ Dump an entire project folder into a single readable .txt file.
816
838
  let noSkipFlag = false;
817
839
  let splitMethod = null; // 'folder', 'file', 'size'
818
840
  let splitSize = null; // size in bytes
841
+ let rootOnlyInclude = false; // For /include flag
819
842
 
820
843
  for (let i = 0; i < args.length; i += 1) {
821
844
  const arg = args[i];
@@ -908,14 +931,21 @@ Dump an entire project folder into a single readable .txt file.
908
931
  if (arg === "--ignore-folder" || arg === "-ifo") {
909
932
  let consumed = 0;
910
933
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
911
- // Normalize the folder name: remove backslashes, trailing slashes, and leading ./
912
934
  let folderName = args[i + 1];
913
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
914
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
915
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
916
- ignoreDirs.add(folderName);
917
- i += 1;
918
- consumed += 1;
935
+
936
+ // Check for /folder syntax (root-only include)
937
+ if (folderName.startsWith("/")) {
938
+ const cleanFolderName = folderName.slice(1); // Remove leading /
939
+ onlyFolders.add(cleanFolderName);
940
+ rootOnlyInclude = true;
941
+ i += 1;
942
+ consumed += 1;
943
+ } else {
944
+ // Normal folder path (could be nested like folder/subfolder)
945
+ onlyFolders.add(folderName);
946
+ i += 1;
947
+ consumed += 1;
948
+ }
919
949
  }
920
950
  if (consumed === 0) {
921
951
  console.error("Error: --ignore-folder requires at least one folder name.");
@@ -932,12 +962,16 @@ Dump an entire project folder into a single readable .txt file.
932
962
  console.error("Error: --ignore-folder requires a folder name.");
933
963
  process.exit(1);
934
964
  }
935
- // Normalize the folder name
936
- let folderName = value;
937
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
938
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
939
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
940
- ignoreDirs.add(folderName);
965
+
966
+ // Check for /folder syntax (root-only include)
967
+ if (value.startsWith("/")) {
968
+ const cleanFolderName = value.slice(1); // Remove leading /
969
+ onlyFolders.add(cleanFolderName);
970
+ rootOnlyInclude = true;
971
+ } else {
972
+ // Normal folder path (could be nested like folder/subfolder)
973
+ onlyFolders.add(value);
974
+ }
941
975
  continue;
942
976
  }
943
977
 
@@ -970,9 +1004,12 @@ Dump an entire project folder into a single readable .txt file.
970
1004
  if (arg === "--only-folder" || arg === "-ofo") {
971
1005
  let consumed = 0;
972
1006
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
973
- // Normalize the folder name
974
1007
  let folderName = args[i + 1];
975
1008
  folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
1009
+ // Detect leading / as root-only signal (same as --only-file behavior)
1010
+ if (folderName.startsWith("/")) {
1011
+ rootOnlyInclude = true;
1012
+ }
976
1013
  folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
977
1014
  folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
978
1015
  onlyFolders.add(folderName);
@@ -994,9 +1031,12 @@ Dump an entire project folder into a single readable .txt file.
994
1031
  console.error("Error: --only-folder requires a folder name.");
995
1032
  process.exit(1);
996
1033
  }
997
- // Normalize the folder name
998
1034
  let folderName = value;
999
1035
  folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
1036
+ // Detect leading / as root-only signal
1037
+ if (folderName.startsWith("/")) {
1038
+ rootOnlyInclude = true;
1039
+ }
1000
1040
  folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
1001
1041
  folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
1002
1042
  onlyFolders.add(folderName);
@@ -1006,9 +1046,21 @@ Dump an entire project folder into a single readable .txt file.
1006
1046
  if (arg === "--only-file" || arg === "-ofi") {
1007
1047
  let consumed = 0;
1008
1048
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
1009
- onlyFiles.add(args[i + 1]);
1010
- i += 1;
1011
- consumed += 1;
1049
+ let fileName = args[i + 1];
1050
+
1051
+ // Check for /file syntax (root-only include)
1052
+ if (fileName.startsWith("/")) {
1053
+ const cleanFileName = fileName.slice(1); // Remove leading /
1054
+ onlyFiles.add(cleanFileName);
1055
+ rootOnlyInclude = true;
1056
+ i += 1;
1057
+ consumed += 1;
1058
+ } else {
1059
+ // Normal file path (could be nested like folder/file.ext)
1060
+ onlyFiles.add(fileName);
1061
+ i += 1;
1062
+ consumed += 1;
1063
+ }
1012
1064
  }
1013
1065
  if (consumed === 0) {
1014
1066
  console.error("Error: --only-file requires at least one file name.");
@@ -1025,7 +1077,16 @@ Dump an entire project folder into a single readable .txt file.
1025
1077
  console.error("Error: --only-file requires a file name.");
1026
1078
  process.exit(1);
1027
1079
  }
1028
- onlyFiles.add(value);
1080
+
1081
+ // Check for /file syntax (root-only include)
1082
+ if (value.startsWith("/")) {
1083
+ const cleanFileName = value.slice(1); // Remove leading /
1084
+ onlyFiles.add(cleanFileName);
1085
+ rootOnlyInclude = true;
1086
+ } else {
1087
+ // Normal file path (could be nested like folder/file.ext)
1088
+ onlyFiles.add(value);
1089
+ }
1029
1090
  continue;
1030
1091
  }
1031
1092
 
@@ -1065,7 +1126,8 @@ Dump an entire project folder into a single readable .txt file.
1065
1126
  rootName,
1066
1127
  txtIgnore,
1067
1128
  force: forceFlag,
1068
- hasOnlyFilters: onlyFolders.size > 0 || onlyFiles.size > 0
1129
+ hasOnlyFilters: onlyFolders.size > 0 || onlyFiles.size > 0,
1130
+ rootOnlyInclude
1069
1131
  }
1070
1132
  );
1071
1133
 
@@ -1175,4 +1237,4 @@ Dump an entire project folder into a single readable .txt file.
1175
1237
  main().catch(err => {
1176
1238
  console.error('❌ Error:', err.message);
1177
1239
  process.exit(1);
1178
- });
1240
+ });
@@ -10,9 +10,8 @@ Root: C:\Programming\make-folder-txt
10
10
  make-folder-txt/
11
11
  ├── bin/
12
12
  │ └── make-folder-txt.js
13
- ├── package.json
14
13
 
15
- Total files: 2
14
+ Total files: 1
16
15
 
17
16
  ================================================================================
18
17
  FILE CONTENTS
@@ -21,7 +20,7 @@ FILE CONTENTS
21
20
  --------------------------------------------------------------------------------
22
21
  FILE: /bin/make-folder-txt.js
23
22
  --------------------------------------------------------------------------------
24
- #!/usr/bin/env node
23
+ #!/usr/bin/env node
25
24
 
26
25
  const fs = require("fs");
27
26
  const path = require("path");
@@ -110,6 +109,7 @@ function collectFiles(
110
109
  rootName = "",
111
110
  txtIgnore = new Set(),
112
111
  force = false,
112
+ rootOnlyInclude = false,
113
113
  } = options;
114
114
 
115
115
  let entries;
@@ -152,27 +152,40 @@ function collectFiles(
152
152
  const childInSelectedFolder = inSelectedFolder || onlyFolders.has(entry.name);
153
153
  const childLines = [];
154
154
  const childFiles = [];
155
- const child = collectFiles(
156
- childPath,
157
- rootDir,
158
- ignoreDirs,
159
- ignoreFiles,
160
- onlyFolders,
161
- onlyFiles,
162
- {
163
- indent: childIndent,
164
- lines: childLines,
165
- filePaths: childFiles,
166
- inSelectedFolder: childInSelectedFolder,
167
- hasOnlyFilters,
168
- rootName,
169
- txtIgnore,
170
- force,
171
- },
172
- );
155
+
156
+ // If rootOnlyInclude is true, only include root-level items
157
+ let child;
158
+ if (rootOnlyInclude && dir !== rootDir) {
159
+ // Don't recurse into subdirectories when rootOnlyInclude is true
160
+ child = { lines: [], filePaths: [], hasIncluded: false };
161
+ } else {
162
+ child = collectFiles(
163
+ childPath,
164
+ rootDir,
165
+ ignoreDirs,
166
+ ignoreFiles,
167
+ onlyFolders,
168
+ onlyFiles,
169
+ {
170
+ indent: childIndent,
171
+ lines: childLines,
172
+ filePaths: childFiles,
173
+ inSelectedFolder: childInSelectedFolder,
174
+ hasOnlyFilters,
175
+ rootName,
176
+ txtIgnore,
177
+ force,
178
+ rootOnlyInclude,
179
+ },
180
+ );
181
+ }
173
182
 
174
183
  const explicitlySelectedFolder = hasOnlyFilters && onlyFolders.has(entry.name);
175
- const shouldIncludeDir = !hasOnlyFilters || child.hasIncluded || explicitlySelectedFolder;
184
+ // For root-only include, only include if we're at the root level and it matches
185
+ const shouldIncludeDir = !hasOnlyFilters ||
186
+ (rootOnlyInclude && dir === rootDir && onlyFolders.has(entry.name)) ||
187
+ (!rootOnlyInclude && child.hasIncluded) ||
188
+ explicitlySelectedFolder;
176
189
 
177
190
  if (shouldIncludeDir) {
178
191
  lines.push(`${indent}${connector}${entry.name}/`);
@@ -193,7 +206,12 @@ function collectFiles(
193
206
  // Ignore .txt files that match the folder name (e.g., foldername.txt) unless force is enabled
194
207
  if (!force && entry.name.endsWith('.txt') && entry.name === `${rootName}.txt`) return;
195
208
 
196
- const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder || onlyFiles.has(entry.name);
209
+ // Check if this file matches any of the onlyFiles patterns
210
+ const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder ||
211
+ onlyFiles.has(entry.name) ||
212
+ onlyFiles.has(relPathForIgnore) ||
213
+ onlyFiles.has(`/${relPathForIgnore}`);
214
+
197
215
  if (!shouldIncludeFile) return;
198
216
 
199
217
  lines.push(`${indent}${connector}${entry.name}`);
@@ -839,6 +857,7 @@ Dump an entire project folder into a single readable .txt file.
839
857
  let noSkipFlag = false;
840
858
  let splitMethod = null; // 'folder', 'file', 'size'
841
859
  let splitSize = null; // size in bytes
860
+ let rootOnlyInclude = false; // For /include flag
842
861
 
843
862
  for (let i = 0; i < args.length; i += 1) {
844
863
  const arg = args[i];
@@ -931,14 +950,21 @@ Dump an entire project folder into a single readable .txt file.
931
950
  if (arg === "--ignore-folder" || arg === "-ifo") {
932
951
  let consumed = 0;
933
952
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
934
- // Normalize the folder name: remove backslashes, trailing slashes, and leading ./
935
953
  let folderName = args[i + 1];
936
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
937
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
938
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
939
- ignoreDirs.add(folderName);
940
- i += 1;
941
- consumed += 1;
954
+
955
+ // Check for /folder syntax (root-only include)
956
+ if (folderName.startsWith("/")) {
957
+ const cleanFolderName = folderName.slice(1); // Remove leading /
958
+ onlyFolders.add(cleanFolderName);
959
+ rootOnlyInclude = true;
960
+ i += 1;
961
+ consumed += 1;
962
+ } else {
963
+ // Normal folder path (could be nested like folder/subfolder)
964
+ onlyFolders.add(folderName);
965
+ i += 1;
966
+ consumed += 1;
967
+ }
942
968
  }
943
969
  if (consumed === 0) {
944
970
  console.error("Error: --ignore-folder requires at least one folder name.");
@@ -955,12 +981,16 @@ Dump an entire project folder into a single readable .txt file.
955
981
  console.error("Error: --ignore-folder requires a folder name.");
956
982
  process.exit(1);
957
983
  }
958
- // Normalize the folder name
959
- let folderName = value;
960
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
961
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
962
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
963
- ignoreDirs.add(folderName);
984
+
985
+ // Check for /folder syntax (root-only include)
986
+ if (value.startsWith("/")) {
987
+ const cleanFolderName = value.slice(1); // Remove leading /
988
+ onlyFolders.add(cleanFolderName);
989
+ rootOnlyInclude = true;
990
+ } else {
991
+ // Normal folder path (could be nested like folder/subfolder)
992
+ onlyFolders.add(value);
993
+ }
964
994
  continue;
965
995
  }
966
996
 
@@ -1029,9 +1059,21 @@ Dump an entire project folder into a single readable .txt file.
1029
1059
  if (arg === "--only-file" || arg === "-ofi") {
1030
1060
  let consumed = 0;
1031
1061
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
1032
- onlyFiles.add(args[i + 1]);
1033
- i += 1;
1034
- consumed += 1;
1062
+ let fileName = args[i + 1];
1063
+
1064
+ // Check for /file syntax (root-only include)
1065
+ if (fileName.startsWith("/")) {
1066
+ const cleanFileName = fileName.slice(1); // Remove leading /
1067
+ onlyFiles.add(cleanFileName);
1068
+ rootOnlyInclude = true;
1069
+ i += 1;
1070
+ consumed += 1;
1071
+ } else {
1072
+ // Normal file path (could be nested like folder/file.ext)
1073
+ onlyFiles.add(fileName);
1074
+ i += 1;
1075
+ consumed += 1;
1076
+ }
1035
1077
  }
1036
1078
  if (consumed === 0) {
1037
1079
  console.error("Error: --only-file requires at least one file name.");
@@ -1048,7 +1090,16 @@ Dump an entire project folder into a single readable .txt file.
1048
1090
  console.error("Error: --only-file requires a file name.");
1049
1091
  process.exit(1);
1050
1092
  }
1051
- onlyFiles.add(value);
1093
+
1094
+ // Check for /file syntax (root-only include)
1095
+ if (value.startsWith("/")) {
1096
+ const cleanFileName = value.slice(1); // Remove leading /
1097
+ onlyFiles.add(cleanFileName);
1098
+ rootOnlyInclude = true;
1099
+ } else {
1100
+ // Normal file path (could be nested like folder/file.ext)
1101
+ onlyFiles.add(value);
1102
+ }
1052
1103
  continue;
1053
1104
  }
1054
1105
 
@@ -1088,7 +1139,8 @@ Dump an entire project folder into a single readable .txt file.
1088
1139
  rootName,
1089
1140
  txtIgnore,
1090
1141
  force: forceFlag,
1091
- hasOnlyFilters: onlyFolders.size > 0 || onlyFiles.size > 0
1142
+ hasOnlyFilters: onlyFolders.size > 0 || onlyFiles.size > 0,
1143
+ rootOnlyInclude
1092
1144
  }
1093
1145
  );
1094
1146
 
@@ -1201,37 +1253,6 @@ main().catch(err => {
1201
1253
  });
1202
1254
 
1203
1255
 
1204
- --------------------------------------------------------------------------------
1205
- FILE: /package.json
1206
- --------------------------------------------------------------------------------
1207
- {
1208
- "name": "make-folder-txt",
1209
- "version": "2.2.5",
1210
- "description": "Generate a single .txt file containing the full folder structure and file contents of any project, ignoring node_modules and other junk.",
1211
- "main": "bin/make-folder-txt.js",
1212
- "bin": {
1213
- "make-folder-txt": "bin/make-folder-txt.js"
1214
- },
1215
- "scripts": {
1216
- "test": "echo \"No tests yet\" && exit 0"
1217
- },
1218
- "keywords": [
1219
- "folder",
1220
- "dump",
1221
- "project",
1222
- "structure",
1223
- "txt",
1224
- "cli",
1225
- "export"
1226
- ],
1227
- "author": "Muhammad Saad Amin",
1228
- "license": "MIT",
1229
- "engines": {
1230
- "node": ">=14.0.0"
1231
- }
1232
- }
1233
-
1234
-
1235
1256
  ================================================================================
1236
1257
  END OF FOLDER: make-folder-txt
1237
1258
  ================================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-folder-txt",
3
- "version": "2.2.6",
3
+ "version": "2.2.9",
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": {