scaffoldrite 1.0.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/dist/ast.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/cli.js ADDED
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const readline_1 = __importDefault(require("readline"));
10
+ const validator_js_1 = require("./validator.js");
11
+ const parser_js_1 = require("./parser.js");
12
+ const generator_js_1 = require("./generator.js");
13
+ const structure_js_1 = require("./structure.js");
14
+ const validateFS_js_1 = require("./validateFS.js");
15
+ const fsToAst_js_1 = require("./fsToAst.js");
16
+ const index_js_1 = require("./data/index.js");
17
+ const structurePath = "./structure.sr";
18
+ function hasFlag(flag) {
19
+ return process.argv.includes(flag);
20
+ }
21
+ function getFlagValue(flagPrefix) {
22
+ return process.argv
23
+ .filter((arg) => arg.startsWith(flagPrefix))
24
+ .map((arg) => arg.split("=")[1])
25
+ .filter(Boolean);
26
+ }
27
+ function parseCSVFlag(flagName) {
28
+ return getFlagValue(flagName)
29
+ .flatMap((v) => v.split(","))
30
+ .map((v) => v.trim())
31
+ .filter(Boolean);
32
+ }
33
+ function getPassedFlags() {
34
+ return process.argv.filter((arg) => arg.startsWith("--"));
35
+ }
36
+ /* ===================== HELPERS ===================== */
37
+ function confirmProceed(dir) {
38
+ // Skip prompt if user asked for it
39
+ if (hasFlag("--yes") || hasFlag("-y"))
40
+ return Promise.resolve(true);
41
+ if (!fs_1.default.existsSync(dir))
42
+ return Promise.resolve(true);
43
+ if (fs_1.default.readdirSync(dir).length === 0)
44
+ return Promise.resolve(true);
45
+ console.warn("Output directory is not empty:", dir);
46
+ const rl = readline_1.default.createInterface({
47
+ input: process.stdin,
48
+ output: process.stdout,
49
+ });
50
+ return new Promise((resolve) => {
51
+ rl.question("Proceed and apply changes? (y/N): ", (answer) => {
52
+ rl.close();
53
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
54
+ });
55
+ });
56
+ }
57
+ /* ===================== STRUCTURE IO ===================== */
58
+ function saveStructure(root, rawConstraints, filePath) {
59
+ const lines = [];
60
+ function writeFolder(folder, indent = "") {
61
+ lines.push(`${indent}folder ${folder.name} {`);
62
+ for (const child of folder.children) {
63
+ if (child.type === "folder")
64
+ writeFolder(child, indent + " ");
65
+ else
66
+ lines.push(`${indent} file ${child.name}`);
67
+ }
68
+ lines.push(`${indent}}`);
69
+ }
70
+ for (const child of root.children) {
71
+ if (child.type === "folder")
72
+ writeFolder(child);
73
+ else
74
+ lines.push(`file ${child.name}`);
75
+ }
76
+ if (rawConstraints.length > 0) {
77
+ lines.push("");
78
+ lines.push("constraints {");
79
+ for (const c of rawConstraints) {
80
+ lines.push(` ${c}`);
81
+ }
82
+ lines.push("}");
83
+ }
84
+ fs_1.default.writeFileSync(filePath, lines.join("\n"));
85
+ }
86
+ function loadAST() {
87
+ const content = fs_1.default.readFileSync(structurePath, "utf-8");
88
+ return (0, parser_js_1.parseStructure)(content);
89
+ }
90
+ function printTree(root, indent = "") {
91
+ for (const child of root.children) {
92
+ if (child.type === "folder") {
93
+ console.log(`${indent} ${child.name}`);
94
+ printTree(child, indent + " ");
95
+ }
96
+ else {
97
+ console.log(`${indent} ${child.name}`);
98
+ }
99
+ }
100
+ }
101
+ /* ===================== CLI ===================== */
102
+ const ALLOWED_FLAGS = {
103
+ init: ["--force", "--empty", "--from-fs"],
104
+ validate: ["--allow-extra"],
105
+ generate: ["--yes"],
106
+ create: ["--force", "--if-not-exists", "--yes"],
107
+ delete: ["--yes"],
108
+ rename: ["--yes"],
109
+ list: [],
110
+ };
111
+ const command = process.argv[2];
112
+ const passedFlags = getPassedFlags();
113
+ const allowedFlags = ALLOWED_FLAGS[command];
114
+ if (!allowedFlags) {
115
+ console.error(`Unknown command: ${command}`);
116
+ process.exit(1);
117
+ }
118
+ const invalidFlags = passedFlags.filter((flag) => !allowedFlags.includes(flag));
119
+ if (invalidFlags.length > 0) {
120
+ console.error(`Unknown flag(s) for '${command}': ${invalidFlags.join(", ")}\n` +
121
+ `Run 'scaffoldrite ${command} --help' to see available options.`);
122
+ process.exit(1);
123
+ }
124
+ if (!command) {
125
+ console.log(`
126
+ Usage:
127
+ scaffoldrite init [--force] [--empty] [--from-fs <dir>]
128
+ scaffoldrite validate [dir] [--allow-extra] [--allow-extra <path1> <path2> ...]
129
+ scaffoldrite generate [dir] [--yes]
130
+ scaffoldrite list
131
+ scaffoldrite create <path> <file|folder> [dir] [--force] [--if-not-exists]
132
+ scaffoldrite delete <path> [dir] [--yes]
133
+ scaffoldrite rename <path> <newName> [dir] [--yes]
134
+ `);
135
+ process.exit(1);
136
+ }
137
+ function getFlagValuesAfter(flag) {
138
+ const index = process.argv.indexOf(flag);
139
+ if (index === -1)
140
+ return [];
141
+ const values = [];
142
+ for (let i = index + 1; i < process.argv.length; i++) {
143
+ const arg = process.argv[i];
144
+ if (arg.startsWith("--"))
145
+ break;
146
+ values.push(arg);
147
+ }
148
+ return values;
149
+ }
150
+ const force = hasFlag("--force");
151
+ const ifNotExists = hasFlag("--if-not-exists");
152
+ const allowExtraPaths = getFlagValuesAfter("--allow-extra");
153
+ const allowExtra = hasFlag("--allow-extra");
154
+ const args = process.argv.slice(3).filter((a) => !a.startsWith("--"));
155
+ const arg3 = args[0];
156
+ const arg4 = args[1];
157
+ const arg5 = args[2];
158
+ (async () => {
159
+ /* ===== INIT ===== */
160
+ if (command === "init") {
161
+ const empty = hasFlag("--empty");
162
+ const fromFs = hasFlag("--from-fs");
163
+ const ignorePath = "./.scaffoldignore";
164
+ // Prevent overwriting structure.sr unless --force
165
+ if (fs_1.default.existsSync(structurePath) && !force) {
166
+ console.error("structure.sr already exists. Use --force to overwrite.");
167
+ process.exit(1);
168
+ }
169
+ // Prevent overwriting scaffoldignore ALWAYS
170
+ if (fs_1.default.existsSync(ignorePath)) {
171
+ console.log(".scaffoldignore already exists. It will not be overwritten.");
172
+ }
173
+ // 1 Empty init
174
+ if (empty) {
175
+ fs_1.default.writeFileSync(structurePath, "constraints {\n}\n");
176
+ if (!fs_1.default.existsSync(ignorePath)) {
177
+ fs_1.default.writeFileSync(ignorePath, index_js_1.DEFAULT_IGNORE_TEMPLATE);
178
+ }
179
+ console.log("Empty structure.sr created");
180
+ return;
181
+ }
182
+ // 2 Init from filesystem
183
+ if (fromFs) {
184
+ const targetDir = path_1.default.resolve(args[0] ?? process.cwd());
185
+ const ignoreList = (0, fsToAst_js_1.getIgnoreList)();
186
+ const ast = (0, fsToAst_js_1.buildASTFromFS)(targetDir, ignoreList);
187
+ saveStructure(ast, [], structurePath);
188
+ if (!fs_1.default.existsSync(ignorePath)) {
189
+ fs_1.default.writeFileSync(ignorePath, index_js_1.DEFAULT_IGNORE_TEMPLATE);
190
+ }
191
+ console.log(`structure.sr generated from filesystem: ${targetDir}`);
192
+ return;
193
+ }
194
+ // Default init
195
+ fs_1.default.writeFileSync(structurePath, index_js_1.DEFAULT_TEMPLATE);
196
+ if (!fs_1.default.existsSync(ignorePath)) {
197
+ fs_1.default.writeFileSync(ignorePath, index_js_1.DEFAULT_IGNORE_TEMPLATE);
198
+ }
199
+ console.log("structure.sr created");
200
+ return;
201
+ }
202
+ /* ===== LIST ===== */
203
+ if (command === "list") {
204
+ const structure = loadAST();
205
+ console.log("Current structure:");
206
+ printTree(structure.root);
207
+ return;
208
+ }
209
+ /* ===== VALIDATE ===== */
210
+ if (command === "validate") {
211
+ const structure = loadAST();
212
+ // Parse allow-extra here only
213
+ const allowExtraPaths = getFlagValuesAfter("--allow-extra");
214
+ const allowExtra = hasFlag("--allow-extra") && allowExtraPaths.length === 0;
215
+ // Find real output dir
216
+ const outputDirArg = args.find((a) => {
217
+ if (a.startsWith("--"))
218
+ return false;
219
+ if (allowExtraPaths.includes(a))
220
+ return false;
221
+ return true;
222
+ });
223
+ const outputDir = path_1.default.resolve(outputDirArg ?? process.cwd());
224
+ try {
225
+ (0, validator_js_1.validateConstraints)(structure.root, structure.constraints);
226
+ (0, validateFS_js_1.validateFS)(structure.root, outputDir, allowExtra, allowExtraPaths);
227
+ console.log("All constraints and filesystem structure are valid");
228
+ }
229
+ catch (err) {
230
+ console.error("Validation failed:", err.message);
231
+ }
232
+ return;
233
+ }
234
+ /* ===== GENERATE ===== */
235
+ if (command === "generate") {
236
+ const structure = loadAST();
237
+ (0, validator_js_1.validateConstraints)(structure.root, structure.constraints);
238
+ const outputDir = path_1.default.resolve(arg3 ?? process.cwd());
239
+ if (!(await confirmProceed(outputDir))) {
240
+ console.log("Generation cancelled.");
241
+ return;
242
+ }
243
+ (0, generator_js_1.generateFS)(structure.root, outputDir);
244
+ console.log("Generated filesystem at:", outputDir);
245
+ return;
246
+ }
247
+ /* ===== CREATE ===== */
248
+ if (command === "create") {
249
+ if (!arg3 || !arg4) {
250
+ console.error("Usage: scaffoldrite create <path> <file|folder> [outputDir] [--force] [--if-not-exists] [--yes]");
251
+ process.exit(1);
252
+ }
253
+ const structure = loadAST();
254
+ (0, validator_js_1.validateConstraints)(structure.root, structure.constraints);
255
+ (0, structure_js_1.addNode)(structure.root, arg3, arg4, {
256
+ force,
257
+ ifNotExists,
258
+ });
259
+ (0, validator_js_1.validateConstraints)(structure.root, structure.constraints);
260
+ const outputDir = path_1.default.resolve(arg5 ?? process.cwd());
261
+ if (!(await confirmProceed(outputDir))) {
262
+ console.log("Creation cancelled.");
263
+ return;
264
+ }
265
+ saveStructure(structure.root, structure.rawConstraints, structurePath);
266
+ (0, generator_js_1.generateFS)(structure.root, outputDir);
267
+ console.log("Created successfully.");
268
+ return;
269
+ }
270
+ /* ===== DELETE ===== */
271
+ if (command === "delete") {
272
+ if (!arg3) {
273
+ console.error("Usage: scaffoldrite delete <path> [outputDir] [--yes]");
274
+ process.exit(1);
275
+ }
276
+ const structure = loadAST();
277
+ (0, validator_js_1.validateConstraints)(structure.root, structure.constraints);
278
+ (0, structure_js_1.deleteNode)(structure.root, arg3);
279
+ (0, validator_js_1.validateConstraints)(structure.root, structure.constraints);
280
+ const outputDir = path_1.default.resolve(arg4 ?? process.cwd());
281
+ if (!(await confirmProceed(outputDir))) {
282
+ console.log("Deletion cancelled.");
283
+ return;
284
+ }
285
+ saveStructure(structure.root, structure.rawConstraints, structurePath);
286
+ (0, generator_js_1.generateFS)(structure.root, outputDir);
287
+ console.log("Deleted successfully.");
288
+ return;
289
+ }
290
+ /* ===== RENAME ===== */
291
+ if (command === "rename") {
292
+ if (!arg3 || !arg4) {
293
+ console.error("Usage: scaffoldrite rename <path> <newName> [outputDir] [--yes]");
294
+ process.exit(1);
295
+ }
296
+ const structure = loadAST();
297
+ (0, validator_js_1.validateConstraints)(structure.root, structure.constraints);
298
+ (0, structure_js_1.renameNode)(structure.root, arg3, arg4);
299
+ (0, validator_js_1.validateConstraints)(structure.root, structure.constraints);
300
+ const outputDir = path_1.default.resolve(arg5 ?? process.cwd());
301
+ if (!(await confirmProceed(outputDir))) {
302
+ console.log("Rename cancelled.");
303
+ return;
304
+ }
305
+ saveStructure(structure.root, structure.rawConstraints, structurePath);
306
+ (0, generator_js_1.generateFS)(structure.root, outputDir);
307
+ console.log("Renamed successfully.");
308
+ return;
309
+ }
310
+ console.error(`Unknown command: ${command}`);
311
+ })();
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseConstraints = parseConstraints;
4
+ function splitArgs(line) {
5
+ const args = [];
6
+ let current = "";
7
+ let inQuotes = false;
8
+ for (let i = 0; i < line.length; i++) {
9
+ const ch = line[i];
10
+ if (ch === '"' || ch === "'") {
11
+ inQuotes = !inQuotes;
12
+ continue;
13
+ }
14
+ if (ch === " " && !inQuotes) {
15
+ if (current.length > 0) {
16
+ args.push(current);
17
+ current = "";
18
+ }
19
+ continue;
20
+ }
21
+ current += ch;
22
+ }
23
+ if (current.length > 0)
24
+ args.push(current);
25
+ return args;
26
+ }
27
+ function parseConstraints(input) {
28
+ const lines = input.split("\n");
29
+ const constraints = [];
30
+ for (let line of lines) {
31
+ line = line.trim();
32
+ if (!line)
33
+ continue;
34
+ if (line.startsWith("require ")) {
35
+ const path = line.replace("require ", "").trim();
36
+ constraints.push({ type: "require", path });
37
+ continue;
38
+ }
39
+ if (line.startsWith("forbid ")) {
40
+ const path = line.replace("forbid ", "").trim();
41
+ constraints.push({ type: "forbid", path });
42
+ continue;
43
+ }
44
+ if (line.startsWith("maxFilesByExtRecursive ")) {
45
+ const parts = splitArgs(line);
46
+ const ext = parts[1];
47
+ const value = Number(parts[2]);
48
+ const path = parts[3];
49
+ constraints.push({ type: "maxFilesByExtRecursive", path, value, ext });
50
+ continue;
51
+ }
52
+ if (line.startsWith("maxFilesRecursive ")) {
53
+ const parts = splitArgs(line);
54
+ const value = Number(parts[1]);
55
+ const path = parts[2];
56
+ constraints.push({ type: "maxFilesRecursive", path, value });
57
+ continue;
58
+ }
59
+ if (line.startsWith("maxFilesByExt ")) {
60
+ const parts = splitArgs(line);
61
+ const ext = parts[1];
62
+ const value = Number(parts[2]);
63
+ const path = parts[3];
64
+ constraints.push({ type: "maxFilesByExt", path, value, ext });
65
+ continue;
66
+ }
67
+ if (line.startsWith("maxFiles ")) {
68
+ const parts = splitArgs(line);
69
+ const value = Number(parts[1]);
70
+ const path = parts[2];
71
+ constraints.push({ type: "maxFiles", path, value });
72
+ continue;
73
+ }
74
+ if (line.startsWith("maxFoldersRecursive ")) {
75
+ const parts = splitArgs(line);
76
+ const value = Number(parts[1]);
77
+ const path = parts[2];
78
+ constraints.push({ type: "maxFoldersRecursive", path, value });
79
+ continue;
80
+ }
81
+ if (line.startsWith("maxFolders ")) {
82
+ const parts = splitArgs(line);
83
+ const value = Number(parts[1]);
84
+ const path = parts[2];
85
+ constraints.push({ type: "maxFolders", path, value });
86
+ continue;
87
+ }
88
+ if (line.startsWith("minFiles ")) {
89
+ const parts = splitArgs(line);
90
+ const value = Number(parts[1]);
91
+ const path = parts[2];
92
+ constraints.push({ type: "minFiles", path, value });
93
+ continue;
94
+ }
95
+ if (line.startsWith("minFolders ")) {
96
+ const parts = splitArgs(line);
97
+ const value = Number(parts[1]);
98
+ const path = parts[2];
99
+ constraints.push({ type: "minFolders", path, value });
100
+ continue;
101
+ }
102
+ // NEW RULES PARSING (WITH SCOPE)
103
+ if (line.startsWith("eachFolderMustContain ")) {
104
+ const parts = splitArgs(line);
105
+ const scope = parts[1];
106
+ // if only 3 parts, then path is root
107
+ const hasPath = parts.length === 4;
108
+ const path = hasPath ? parts[2] : "";
109
+ const value = hasPath ? parts[3] : parts[2];
110
+ constraints.push({ type: "eachFolderMustContain", path, value, scope });
111
+ console.log({ type: "eachFolderMustContain", path, value, scope });
112
+ continue;
113
+ }
114
+ if (line.startsWith("eachFolderMustContainFile ")) {
115
+ const parts = splitArgs(line);
116
+ const scope = parts[1];
117
+ const hasPath = parts.length === 4;
118
+ const path = hasPath ? parts[2] : "";
119
+ const value = hasPath ? parts[3] : parts[2];
120
+ constraints.push({ type: "eachFolderMustContainFile", path, value, scope });
121
+ continue;
122
+ }
123
+ if (line.startsWith("eachFolderMustContainFolder ")) {
124
+ const parts = splitArgs(line);
125
+ const scope = parts[1];
126
+ const hasPath = parts.length === 4;
127
+ const path = hasPath ? parts[2] : "";
128
+ const value = hasPath ? parts[3] : parts[2];
129
+ constraints.push({ type: "eachFolderMustContainFolder", path, value, scope });
130
+ continue;
131
+ }
132
+ if (line.startsWith("eachFolderMustHaveExt ")) {
133
+ const parts = splitArgs(line);
134
+ const scope = parts[1];
135
+ const hasPath = parts.length === 4;
136
+ const path = hasPath ? parts[2] : "";
137
+ const ext = hasPath ? parts[3] : parts[2];
138
+ constraints.push({ type: "eachFolderMustHaveExt", path, ext, scope });
139
+ continue;
140
+ }
141
+ // EXISTING RULES
142
+ if (line.startsWith("mustContain ")) {
143
+ const parts = splitArgs(line);
144
+ const path = parts[1];
145
+ const value = parts[2];
146
+ constraints.push({ type: "mustContain", path, value });
147
+ continue;
148
+ }
149
+ if (line.startsWith("fileNameRegex ")) {
150
+ const parts = splitArgs(line);
151
+ const path = parts[1];
152
+ const regex = parts[2];
153
+ constraints.push({ type: "fileNameRegex", path, regex });
154
+ continue;
155
+ }
156
+ if (line.startsWith("maxDepth ")) {
157
+ const parts = splitArgs(line);
158
+ const value = Number(parts[1]);
159
+ const path = parts[2];
160
+ constraints.push({ type: "maxDepth", path, value });
161
+ continue;
162
+ }
163
+ if (line.startsWith("mustHaveFile ")) {
164
+ const parts = splitArgs(line);
165
+ const path = parts[1];
166
+ const value = parts[2];
167
+ constraints.push({ type: "mustHaveFile", path, value });
168
+ continue;
169
+ }
170
+ throw new Error(`Unknown constraint: ${line}`);
171
+ }
172
+ return constraints;
173
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ function parseCSVFlag(flagName) {
3
+ return getFlagValue(flagName)
4
+ .flatMap((v) => v.split(","))
5
+ .map((v) => v.trim())
6
+ .filter(Boolean);
7
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_IGNORE_TEMPLATE = exports.DEFAULT_TEMPLATE = void 0;
4
+ exports.DEFAULT_TEMPLATE = `folder src {
5
+ file index.ts
6
+ }
7
+
8
+ constraints {
9
+ }
10
+ `;
11
+ exports.DEFAULT_IGNORE_TEMPLATE = `node_modules
12
+ .git
13
+ .next
14
+ dist
15
+ build
16
+ coverage
17
+ .turbo
18
+ `;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_IGNORES = void 0;
7
+ exports.loadIgnoreList = loadIgnoreList;
8
+ exports.getIgnoreList = getIgnoreList;
9
+ exports.buildASTFromFS = buildASTFromFS;
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const ignoreFilePath = "./.scaffoldignore";
13
+ exports.DEFAULT_IGNORES = [
14
+ "node_modules",
15
+ ".git",
16
+ ".next",
17
+ "dist",
18
+ "build",
19
+ "coverage",
20
+ ".turbo",
21
+ ];
22
+ function loadIgnoreList(filePath) {
23
+ if (!fs_1.default.existsSync(filePath))
24
+ return [];
25
+ const content = fs_1.default.readFileSync(filePath, "utf-8");
26
+ return content
27
+ .split("\n")
28
+ .map((x) => x.trim())
29
+ .map((x) => x.split("#")[0].trim())
30
+ .filter(Boolean);
31
+ }
32
+ function getIgnoreList() {
33
+ return fs_1.default.existsSync(ignoreFilePath)
34
+ ? loadIgnoreList(ignoreFilePath)
35
+ : exports.DEFAULT_IGNORES;
36
+ }
37
+ function buildASTFromFS(dir, ignoreList = []) {
38
+ if (!fs_1.default.existsSync(dir)) {
39
+ throw new Error(`Directory does not exist: ${dir}`);
40
+ }
41
+ const root = {
42
+ type: "folder",
43
+ name: path_1.default.basename(dir),
44
+ children: [],
45
+ };
46
+ function scan(folderPath, node) {
47
+ const items = fs_1.default.readdirSync(folderPath);
48
+ for (const item of items) {
49
+ if (ignoreList.includes(item))
50
+ continue;
51
+ const itemPath = path_1.default.join(folderPath, item);
52
+ const stat = fs_1.default.statSync(itemPath);
53
+ if (stat.isDirectory()) {
54
+ const childFolder = {
55
+ type: "folder",
56
+ name: item,
57
+ children: [],
58
+ };
59
+ node.children.push(childFolder);
60
+ scan(itemPath, childFolder);
61
+ }
62
+ else {
63
+ const childFile = {
64
+ type: "file",
65
+ name: item,
66
+ };
67
+ node.children.push(childFile);
68
+ }
69
+ }
70
+ }
71
+ scan(dir, root);
72
+ return root;
73
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateFS = generateFS;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const visitor_js_1 = require("./visitor.js");
10
+ function generateFS(ast, outputDir) {
11
+ const root = path_1.default.resolve(outputDir);
12
+ const expected = new Set();
13
+ function ensureFolder(folderPath) {
14
+ if (!fs_1.default.existsSync(folderPath)) {
15
+ fs_1.default.mkdirSync(folderPath, { recursive: true });
16
+ }
17
+ }
18
+ function ensureFile(filePath) {
19
+ if (!fs_1.default.existsSync(filePath)) {
20
+ fs_1.default.writeFileSync(filePath, "");
21
+ }
22
+ }
23
+ ensureFolder(root);
24
+ expected.add(root);
25
+ (0, visitor_js_1.visit)(ast, {
26
+ folder: (_, nodePath) => {
27
+ const fullPath = path_1.default.join(root, nodePath);
28
+ expected.add(fullPath);
29
+ ensureFolder(fullPath);
30
+ },
31
+ file: (_, nodePath) => {
32
+ const fullPath = path_1.default.join(root, nodePath);
33
+ expected.add(fullPath);
34
+ ensureFile(fullPath);
35
+ },
36
+ });
37
+ // 2️⃣ Remove extras (never root)
38
+ function clean(dir) {
39
+ for (const entry of fs_1.default.readdirSync(dir)) {
40
+ const fullPath = path_1.default.join(dir, entry);
41
+ if (!expected.has(fullPath)) {
42
+ fs_1.default.rmSync(fullPath, { recursive: true, force: true });
43
+ continue;
44
+ }
45
+ if (fs_1.default.statSync(fullPath).isDirectory()) {
46
+ clean(fullPath);
47
+ }
48
+ }
49
+ }
50
+ clean(root);
51
+ }