scaffoldrite 2.0.4 → 2.0.6
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/package.json +1 -1
- package/dist/ast.js +0 -2
- package/dist/commandHandler.js +0 -632
- package/dist/constraints.js +0 -183
- package/dist/fsToAst.js +0 -47
- package/dist/generator.js +0 -152
- package/dist/history.js +0 -21
- package/dist/library/ai/ait.js +0 -129
- package/dist/library/ai/generateStructure.js +0 -28
- package/dist/library/ai/generateStructureWithGroq.js +0 -75
- package/dist/library/checkpage/checkPackageType.js +0 -25
- package/dist/library/sam.js +0 -2
- package/dist/parser.js +0 -103
- package/dist/progress.js +0 -63
- package/dist/structure.js +0 -84
- package/dist/utils/historyhelpers.js +0 -1
- package/dist/validateFS.js +0 -74
- package/dist/validator.js +0 -249
- package/dist/visitor.js +0 -22
package/dist/parser.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseStructure = parseStructure;
|
|
4
|
-
const constraints_1 = require("./constraints");
|
|
5
|
-
// No restrictions on file/folder name characters
|
|
6
|
-
const VALID_NAME_REGEX = /.+/;
|
|
7
|
-
function validateName(name, line) {
|
|
8
|
-
if (name === "__root__") {
|
|
9
|
-
throw new Error(`[Line ${line}] Reserved name "__root__" is not allowed`);
|
|
10
|
-
}
|
|
11
|
-
// Only validate that name is not empty
|
|
12
|
-
if (!VALID_NAME_REGEX.test(name)) {
|
|
13
|
-
throw new Error(`[Line ${line}] Name cannot be empty`);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
function parseStructure(input) {
|
|
17
|
-
const lines = input.split("\n");
|
|
18
|
-
const root = { type: "folder", name: "__root__", children: [] };
|
|
19
|
-
const stack = [root];
|
|
20
|
-
let constraints = [];
|
|
21
|
-
let inConstraints = false;
|
|
22
|
-
let braceDepth = 0;
|
|
23
|
-
for (let i = 0; i < lines.length; i++) {
|
|
24
|
-
const lineNumber = i + 1;
|
|
25
|
-
const raw = lines[i];
|
|
26
|
-
const line = raw.trim();
|
|
27
|
-
const codeLine = line.split("//")[0].trim();
|
|
28
|
-
if (codeLine.length === 0)
|
|
29
|
-
continue;
|
|
30
|
-
if (line.length === 0)
|
|
31
|
-
continue;
|
|
32
|
-
// Start constraints block
|
|
33
|
-
if (line === "constraints {") {
|
|
34
|
-
inConstraints = true;
|
|
35
|
-
braceDepth = 1;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
// If we are inside constraints block
|
|
39
|
-
if (inConstraints) {
|
|
40
|
-
if (line.includes("{"))
|
|
41
|
-
braceDepth++;
|
|
42
|
-
if (line.includes("}"))
|
|
43
|
-
braceDepth--;
|
|
44
|
-
// If braceDepth becomes 0, constraints ended
|
|
45
|
-
if (braceDepth === 0) {
|
|
46
|
-
inConstraints = false;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
constraints.push(line);
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
// Folder logic
|
|
53
|
-
if (line.startsWith("folder ")) {
|
|
54
|
-
const match = raw.match(/^\s*folder\s+(.+?)\s*\{\s*$/);
|
|
55
|
-
if (!match) {
|
|
56
|
-
throw new Error(`[Line ${lineNumber}] Invalid folder syntax: "${line}"`);
|
|
57
|
-
}
|
|
58
|
-
const name = match[1];
|
|
59
|
-
validateName(name, lineNumber);
|
|
60
|
-
const parent = stack[stack.length - 1];
|
|
61
|
-
if (parent.children.some(c => c.type === "folder" && c.name === name)) {
|
|
62
|
-
throw new Error(`[Line ${lineNumber}] Duplicate folder name "${name}" in the same scope`);
|
|
63
|
-
}
|
|
64
|
-
const folder = { type: "folder", name, children: [] };
|
|
65
|
-
parent.children.push(folder);
|
|
66
|
-
stack.push(folder);
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
// File logic
|
|
70
|
-
if (line.startsWith("file ")) {
|
|
71
|
-
const match = raw.match(/^\s*file\s+(.+?)\s*$/);
|
|
72
|
-
if (!match) {
|
|
73
|
-
throw new Error(`[Line ${lineNumber}] Invalid file syntax: "${line}"`);
|
|
74
|
-
}
|
|
75
|
-
const name = match[1];
|
|
76
|
-
validateName(name, lineNumber);
|
|
77
|
-
const parent = stack[stack.length - 1];
|
|
78
|
-
if (parent.children.some(c => c.type === "file" && c.name === name)) {
|
|
79
|
-
throw new Error(`[Line ${lineNumber}] Duplicate file name "${name}" in the same scope`);
|
|
80
|
-
}
|
|
81
|
-
const file = { type: "file", name };
|
|
82
|
-
parent.children.push(file);
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
// Close folder
|
|
86
|
-
if (line === "}") {
|
|
87
|
-
if (stack.length === 1) {
|
|
88
|
-
throw new Error(`[Line ${lineNumber}] Unexpected "}"`);
|
|
89
|
-
}
|
|
90
|
-
stack.pop();
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
throw new Error(`[Line ${lineNumber}] Unknown statement: "${line}"`);
|
|
94
|
-
}
|
|
95
|
-
if (stack.length !== 1) {
|
|
96
|
-
throw new Error(`Unclosed folder block`);
|
|
97
|
-
}
|
|
98
|
-
return {
|
|
99
|
-
root,
|
|
100
|
-
constraints: (0, constraints_1.parseConstraints)(constraints.join("\n")),
|
|
101
|
-
rawConstraints: constraints
|
|
102
|
-
};
|
|
103
|
-
}
|
package/dist/progress.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
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.createProgressBar = createProgressBar;
|
|
7
|
-
const cli_progress_1 = __importDefault(require("cli-progress"));
|
|
8
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
-
function createProgressBar() {
|
|
10
|
-
const bar = new cli_progress_1.default.SingleBar({
|
|
11
|
-
format: `${chalk_1.default.hex('#4cc9f0')("{bar}")} ` +
|
|
12
|
-
`${chalk_1.default.hex('#00b4d8').bold("{percentage}%")} ` +
|
|
13
|
-
`${chalk_1.default.hex('#4cc9f0')("|")} ` +
|
|
14
|
-
`${chalk_1.default.hex('#f8f9fa')("{value}")}${chalk_1.default.hex('#adb5bd')("/{total}")} ` +
|
|
15
|
-
`${chalk_1.default.hex('#4cc9f0')("|")} ` +
|
|
16
|
-
`${chalk_1.default.hex('#ffd166').bold("{type}")} ` +
|
|
17
|
-
`${chalk_1.default.hex('#8d99ae').italic("{path}")}`,
|
|
18
|
-
barCompleteChar: '█',
|
|
19
|
-
barIncompleteChar: '░',
|
|
20
|
-
hideCursor: true,
|
|
21
|
-
clearOnComplete: false,
|
|
22
|
-
stopOnComplete: true,
|
|
23
|
-
barGlue: `${chalk_1.default.hex('#1a1a2e').bold('[')}${chalk_1.default.hex('#1a1a2e').bold(']')}`,
|
|
24
|
-
barsize: 40,
|
|
25
|
-
}, cli_progress_1.default.Presets.shades_classic);
|
|
26
|
-
let totalOps = 0;
|
|
27
|
-
let startTime = Date.now();
|
|
28
|
-
return {
|
|
29
|
-
start(total) {
|
|
30
|
-
totalOps = total;
|
|
31
|
-
startTime = Date.now();
|
|
32
|
-
bar.start(total, 0, {
|
|
33
|
-
type: "",
|
|
34
|
-
path: "",
|
|
35
|
-
percentage: 0
|
|
36
|
-
});
|
|
37
|
-
},
|
|
38
|
-
update(e) {
|
|
39
|
-
const percentage = Math.floor((e.count / totalOps) * 100);
|
|
40
|
-
// Dynamically update format based on progress
|
|
41
|
-
if (percentage === 100) {
|
|
42
|
-
const elapsedSeconds = Math.round((Date.now() - startTime) / 1000);
|
|
43
|
-
bar.update(e.count, {
|
|
44
|
-
type: "COMPLETE",
|
|
45
|
-
path: `Finished in ${elapsedSeconds}s`,
|
|
46
|
-
percentage: percentage,
|
|
47
|
-
});
|
|
48
|
-
// Use a different format for completion
|
|
49
|
-
bar.setTotal(totalOps);
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
bar.update(e.count, {
|
|
53
|
-
type: e.type.toUpperCase(),
|
|
54
|
-
path: e.path,
|
|
55
|
-
percentage: percentage,
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
stop() {
|
|
60
|
-
bar.stop();
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
}
|
package/dist/structure.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.findNode = findNode;
|
|
4
|
-
exports.addNode = addNode;
|
|
5
|
-
exports.deleteNode = deleteNode;
|
|
6
|
-
exports.renameNode = renameNode;
|
|
7
|
-
function findNode(root, pathStr) {
|
|
8
|
-
const parts = pathStr.split("/").filter(Boolean);
|
|
9
|
-
let current = root;
|
|
10
|
-
for (let i = 0; i < parts.length; i++) {
|
|
11
|
-
const part = parts[i];
|
|
12
|
-
const child = current.children.find(c => c.name === part);
|
|
13
|
-
if (!child)
|
|
14
|
-
return null;
|
|
15
|
-
if (i === parts.length - 1)
|
|
16
|
-
return child;
|
|
17
|
-
if (child.type !== "folder")
|
|
18
|
-
return null;
|
|
19
|
-
current = child;
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
function addNode(root, pathStr, type, options = {}) {
|
|
24
|
-
const parts = pathStr.split("/").filter(Boolean);
|
|
25
|
-
const name = parts.pop();
|
|
26
|
-
if (!name) {
|
|
27
|
-
throw new Error("Invalid path: path is empty. Example: src/components/Button.ts");
|
|
28
|
-
}
|
|
29
|
-
let current = root;
|
|
30
|
-
for (const part of parts) {
|
|
31
|
-
let child = current.children.find(c => c.name === part && c.type === "folder");
|
|
32
|
-
if (!child) {
|
|
33
|
-
child = { type: "folder", name: part, children: [] };
|
|
34
|
-
current.children.push(child);
|
|
35
|
-
}
|
|
36
|
-
current = child;
|
|
37
|
-
}
|
|
38
|
-
const exists = current.children.some(c => c.name === name);
|
|
39
|
-
if (exists) {
|
|
40
|
-
if (options.ifNotExists)
|
|
41
|
-
return; // do nothing
|
|
42
|
-
if (!options.force) {
|
|
43
|
-
throw new Error(`Cannot create ${type}: '${pathStr}' already exists in the structure.sr file`);
|
|
44
|
-
}
|
|
45
|
-
// force = true -> replace existing node
|
|
46
|
-
const idx = current.children.findIndex(c => c.name === name);
|
|
47
|
-
current.children.splice(idx, 1);
|
|
48
|
-
}
|
|
49
|
-
const newNode = type === "folder"
|
|
50
|
-
? { type: "folder", name, children: [] }
|
|
51
|
-
: { type: "file", name };
|
|
52
|
-
current.children.push(newNode);
|
|
53
|
-
return current;
|
|
54
|
-
}
|
|
55
|
-
function deleteNode(root, pathStr) {
|
|
56
|
-
const parts = pathStr.split("/").filter(Boolean);
|
|
57
|
-
const name = parts.pop();
|
|
58
|
-
if (!name) {
|
|
59
|
-
throw new Error("Invalid path: path is empty. Example: src/components/Button.ts");
|
|
60
|
-
}
|
|
61
|
-
let current = root;
|
|
62
|
-
for (const part of parts) {
|
|
63
|
-
const child = current.children.find(c => c.name === part && c.type === "folder");
|
|
64
|
-
if (!child) {
|
|
65
|
-
throw new Error(`Path not found: '${parts.join("/")}' does not exist in the structure.`);
|
|
66
|
-
}
|
|
67
|
-
current = child;
|
|
68
|
-
}
|
|
69
|
-
const index = current.children.findIndex(c => c.name === name);
|
|
70
|
-
if (index === -1) {
|
|
71
|
-
throw new Error(`Node not found: '${pathStr}' does not exist in the structure.`);
|
|
72
|
-
}
|
|
73
|
-
current.children.splice(index, 1);
|
|
74
|
-
}
|
|
75
|
-
function renameNode(root, pathStr, newName) {
|
|
76
|
-
const node = findNode(root, pathStr);
|
|
77
|
-
if (!node) {
|
|
78
|
-
throw new Error(`Node not found: '${pathStr}' does not exist.`);
|
|
79
|
-
}
|
|
80
|
-
if (!newName || newName.trim().length === 0) {
|
|
81
|
-
throw new Error("Invalid new name: name cannot be empty.");
|
|
82
|
-
}
|
|
83
|
-
node.name = newName;
|
|
84
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";
|
package/dist/validateFS.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
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.validateFS = validateFS;
|
|
7
|
-
const fs_1 = __importDefault(require("fs"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const index_1 = require("./utils/index");
|
|
10
|
-
function validateFS(root, dir, options = {}) {
|
|
11
|
-
const { ignoreList = [], allowExtra = false, allowExtraPaths = [], currentPath = "", } = options;
|
|
12
|
-
if (!fs_1.default.existsSync(dir)) {
|
|
13
|
-
throw new Error(`Folder does not exist: ${dir}\n` +
|
|
14
|
-
`Expected folder according to structure.sr at: ${currentPath || "root"}`);
|
|
15
|
-
}
|
|
16
|
-
const actualItems = fs_1.default.readdirSync(dir);
|
|
17
|
-
// Check missing items in filesystem
|
|
18
|
-
for (const child of root.children) {
|
|
19
|
-
if ((0, index_1.isIgnored)(child.name, ignoreList))
|
|
20
|
-
continue;
|
|
21
|
-
const expectedPath = path_1.default.join(dir, child.name);
|
|
22
|
-
const expectedSrPath = path_1.default.join(currentPath, child.name);
|
|
23
|
-
if (!fs_1.default.existsSync(expectedPath)) {
|
|
24
|
-
const allowedExplicitly = allowExtraPaths.some((p) => {
|
|
25
|
-
const normalized = path_1.default.normalize(p);
|
|
26
|
-
return (expectedSrPath === normalized ||
|
|
27
|
-
expectedSrPath.endsWith(normalized));
|
|
28
|
-
});
|
|
29
|
-
if (allowExtra || allowedExplicitly)
|
|
30
|
-
continue;
|
|
31
|
-
throw new Error(`Missing in filesystem: ${expectedPath}\n` +
|
|
32
|
-
`Expected according to structure.sr at: ${expectedSrPath}`);
|
|
33
|
-
}
|
|
34
|
-
if (child.type === "folder") {
|
|
35
|
-
if (!fs_1.default.statSync(expectedPath).isDirectory()) {
|
|
36
|
-
throw new Error(`Expected folder but found file: ${expectedPath}\n` +
|
|
37
|
-
`structure.sr expects a folder at: ${expectedSrPath}`);
|
|
38
|
-
}
|
|
39
|
-
validateFS(child, expectedPath, {
|
|
40
|
-
ignoreList,
|
|
41
|
-
allowExtra,
|
|
42
|
-
allowExtraPaths,
|
|
43
|
-
currentPath: expectedSrPath,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
if (!fs_1.default.statSync(expectedPath).isFile()) {
|
|
48
|
-
throw new Error(`Expected file but found folder: ${expectedPath}\n` +
|
|
49
|
-
`structure.sr expects a file at: ${expectedSrPath}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// Check extra items in filesystem not in .sr
|
|
54
|
-
for (const item of actualItems) {
|
|
55
|
-
if ((0, index_1.isIgnored)(item, ignoreList))
|
|
56
|
-
continue;
|
|
57
|
-
const existsInSr = root.children.some((c) => c.name === item);
|
|
58
|
-
if (!existsInSr) {
|
|
59
|
-
const extraPath = path_1.default.join(dir, item);
|
|
60
|
-
const allowedExplicitly = allowExtraPaths.some((p) => {
|
|
61
|
-
const normalized = path_1.default.normalize(p);
|
|
62
|
-
const extraRel = path_1.default.relative(dir, extraPath);
|
|
63
|
-
const extraBasename = path_1.default.basename(extraPath);
|
|
64
|
-
return (extraBasename === normalized ||
|
|
65
|
-
extraRel === normalized ||
|
|
66
|
-
extraRel.endsWith(normalized));
|
|
67
|
-
});
|
|
68
|
-
if (allowExtra || allowedExplicitly)
|
|
69
|
-
continue;
|
|
70
|
-
throw new Error(`Extra file/folder found in filesystem: ${extraPath}\n` +
|
|
71
|
-
`Not defined in structure.sr at: ${currentPath || "root"}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
package/dist/validator.js
DELETED
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateConstraints = validateConstraints;
|
|
4
|
-
function normalizePath(path) {
|
|
5
|
-
if (!path || path.trim() === "")
|
|
6
|
-
return "__root__";
|
|
7
|
-
return path.trim().replace(/^\/+/, "").replace(/\/+$/, "");
|
|
8
|
-
}
|
|
9
|
-
function findNodeByPath(root, path) {
|
|
10
|
-
const normalizedPath = normalizePath(path);
|
|
11
|
-
const parts = normalizedPath.split("/").filter(Boolean);
|
|
12
|
-
let current = root;
|
|
13
|
-
if (parts[0] === root.name) {
|
|
14
|
-
parts.shift();
|
|
15
|
-
}
|
|
16
|
-
for (const part of parts) {
|
|
17
|
-
if (current.type !== "folder")
|
|
18
|
-
return null;
|
|
19
|
-
const next = current.children.find((c) => c.name === part);
|
|
20
|
-
if (!next)
|
|
21
|
-
return null;
|
|
22
|
-
current = next;
|
|
23
|
-
}
|
|
24
|
-
return current;
|
|
25
|
-
}
|
|
26
|
-
function countFiles(folder) {
|
|
27
|
-
return folder.children.filter((c) => c.type === "file").length;
|
|
28
|
-
}
|
|
29
|
-
function countFolders(folder) {
|
|
30
|
-
return folder.children.filter((c) => c.type === "folder").length;
|
|
31
|
-
}
|
|
32
|
-
function countFilesRecursive(folder, ext) {
|
|
33
|
-
let count = 0;
|
|
34
|
-
for (const child of folder.children) {
|
|
35
|
-
if (child.type === "file") {
|
|
36
|
-
if (!ext || child.name.endsWith(ext))
|
|
37
|
-
count++;
|
|
38
|
-
}
|
|
39
|
-
if (child.type === "folder")
|
|
40
|
-
count += countFilesRecursive(child, ext);
|
|
41
|
-
}
|
|
42
|
-
return count;
|
|
43
|
-
}
|
|
44
|
-
function countFoldersRecursive(folder) {
|
|
45
|
-
let count = 0;
|
|
46
|
-
for (const child of folder.children) {
|
|
47
|
-
if (child.type === "folder")
|
|
48
|
-
count++;
|
|
49
|
-
if (child.type === "folder")
|
|
50
|
-
count += countFoldersRecursive(child);
|
|
51
|
-
}
|
|
52
|
-
return count;
|
|
53
|
-
}
|
|
54
|
-
function maxDepth(folder, current = 0) {
|
|
55
|
-
let depth = current;
|
|
56
|
-
for (const child of folder.children) {
|
|
57
|
-
if (child.type === "folder") {
|
|
58
|
-
depth = Math.max(depth, maxDepth(child, current + 1));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return depth;
|
|
62
|
-
}
|
|
63
|
-
function getFoldersByScope(root, path, scope) {
|
|
64
|
-
const folder = findNodeByPath(root, path);
|
|
65
|
-
if (!folder || folder.type !== "folder")
|
|
66
|
-
return [];
|
|
67
|
-
const folders = [];
|
|
68
|
-
if (scope === "*") {
|
|
69
|
-
for (const child of folder.children) {
|
|
70
|
-
if (child.type === "folder")
|
|
71
|
-
folders.push(child);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (scope === "**") {
|
|
75
|
-
function traverse(f) {
|
|
76
|
-
for (const child of f.children) {
|
|
77
|
-
if (child.type === "folder") {
|
|
78
|
-
folders.push(child);
|
|
79
|
-
traverse(child);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
traverse(folder);
|
|
84
|
-
}
|
|
85
|
-
return folders;
|
|
86
|
-
}
|
|
87
|
-
function formatConstraintError(message, hint) {
|
|
88
|
-
return `Constraint failed:\n ${message}${hint ? `\nHint: ${hint}` : ""}`;
|
|
89
|
-
}
|
|
90
|
-
function validateConstraints(root, constraints) {
|
|
91
|
-
for (const c of constraints) {
|
|
92
|
-
if ((c.type !== "eachFolderMustContain" &&
|
|
93
|
-
c.type !== "eachFolderMustContainFile" &&
|
|
94
|
-
c.type !== "eachFolderMustContainFolder" &&
|
|
95
|
-
c.type !== "eachFolderMustHaveExt") &&
|
|
96
|
-
(!c.path || c.path.trim() === "")) {
|
|
97
|
-
throw new Error(formatConstraintError(`Path missing for constraint type "${c.type}"`, `Add a valid path like: constraints { ${c.type} src/index.ts }`));
|
|
98
|
-
}
|
|
99
|
-
if (c.type === "require") {
|
|
100
|
-
const node = findNodeByPath(root, c.path);
|
|
101
|
-
if (!node) {
|
|
102
|
-
throw new Error(formatConstraintError(`Required path not found: ${c.path}`, `Add this path to your structure.sr or remove this constraint`));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (c.type === "forbid") {
|
|
106
|
-
const node = findNodeByPath(root, c.path);
|
|
107
|
-
if (node) {
|
|
108
|
-
throw new Error(formatConstraintError(`Forbidden path exists: ${c.path}`, `Remove this path from your structure.sr or delete this constraint`));
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (c.type === "maxFiles") {
|
|
112
|
-
const folder = findNodeByPath(root, c.path);
|
|
113
|
-
if (!folder || folder.type !== "folder")
|
|
114
|
-
continue;
|
|
115
|
-
const fileCount = countFiles(folder);
|
|
116
|
-
if (fileCount > c.value)
|
|
117
|
-
throw new Error(formatConstraintError(`${c.path} has ${fileCount} files (max allowed ${c.value})`, `Remove extra files or increase maxFiles`));
|
|
118
|
-
}
|
|
119
|
-
if (c.type === "maxFilesRecursive") {
|
|
120
|
-
const folder = findNodeByPath(root, c.path);
|
|
121
|
-
if (!folder || folder.type !== "folder")
|
|
122
|
-
continue;
|
|
123
|
-
const fileCount = countFilesRecursive(folder);
|
|
124
|
-
if (fileCount > c.value)
|
|
125
|
-
throw new Error(formatConstraintError(`${c.path} has ${fileCount} files recursively (max allowed ${c.value})`, `Remove extra files or increase maxFilesRecursive`));
|
|
126
|
-
}
|
|
127
|
-
if (c.type === "maxFilesByExt") {
|
|
128
|
-
const folder = findNodeByPath(root, c.path);
|
|
129
|
-
if (!folder || folder.type !== "folder")
|
|
130
|
-
continue;
|
|
131
|
-
const fileCount = folder.children
|
|
132
|
-
.filter((x) => x.type === "file")
|
|
133
|
-
.filter((f) => f.name.endsWith(c.ext)).length;
|
|
134
|
-
if (fileCount > c.value)
|
|
135
|
-
throw new Error(formatConstraintError(`${c.path} has ${fileCount} files with extension "${c.ext}" (max allowed ${c.value})`, `Remove extra ${c.ext} files or increase maxFilesByExt`));
|
|
136
|
-
}
|
|
137
|
-
if (c.type === "maxFilesByExtRecursive") {
|
|
138
|
-
const folder = findNodeByPath(root, c.path);
|
|
139
|
-
if (!folder || folder.type !== "folder")
|
|
140
|
-
continue;
|
|
141
|
-
const extCount = countFilesRecursive(folder, c.ext);
|
|
142
|
-
if (extCount > c.value)
|
|
143
|
-
throw new Error(formatConstraintError(`${c.path} has ${extCount} files with extension "${c.ext}" recursively (max allowed ${c.value})`, `Remove extra ${c.ext} files or increase maxFilesByExtRecursive`));
|
|
144
|
-
}
|
|
145
|
-
if (c.type === "maxFolders") {
|
|
146
|
-
const folder = findNodeByPath(root, c.path);
|
|
147
|
-
if (!folder || folder.type !== "folder")
|
|
148
|
-
continue;
|
|
149
|
-
const folderCount = countFolders(folder);
|
|
150
|
-
if (folderCount > c.value)
|
|
151
|
-
throw new Error(formatConstraintError(`${c.path} has ${folderCount} folders (max allowed ${c.value})`, `Remove extra folders or increase maxFolders`));
|
|
152
|
-
}
|
|
153
|
-
if (c.type === "maxFoldersRecursive") {
|
|
154
|
-
const folder = findNodeByPath(root, c.path);
|
|
155
|
-
if (!folder || folder.type !== "folder")
|
|
156
|
-
continue;
|
|
157
|
-
const folderCount = countFoldersRecursive(folder);
|
|
158
|
-
if (folderCount > c.value)
|
|
159
|
-
throw new Error(formatConstraintError(`${c.path} has ${folderCount} folders recursively (max allowed ${c.value})`, `Remove extra folders or increase maxFoldersRecursive`));
|
|
160
|
-
}
|
|
161
|
-
if (c.type === "minFiles") {
|
|
162
|
-
const folder = findNodeByPath(root, c.path);
|
|
163
|
-
if (!folder || folder.type !== "folder")
|
|
164
|
-
continue;
|
|
165
|
-
const fileCount = countFiles(folder);
|
|
166
|
-
if (fileCount < c.value)
|
|
167
|
-
throw new Error(formatConstraintError(`${c.path} has ${fileCount} files (min required ${c.value})`, `Add more files to this folder`));
|
|
168
|
-
}
|
|
169
|
-
if (c.type === "minFolders") {
|
|
170
|
-
const folder = findNodeByPath(root, c.path);
|
|
171
|
-
if (!folder || folder.type !== "folder")
|
|
172
|
-
continue;
|
|
173
|
-
const folderCount = countFolders(folder);
|
|
174
|
-
if (folderCount < c.value)
|
|
175
|
-
throw new Error(formatConstraintError(`${c.path} has ${folderCount} folders (min required ${c.value})`, `Add more folders to this path`));
|
|
176
|
-
}
|
|
177
|
-
if (c.type === "mustContain") {
|
|
178
|
-
const folder = findNodeByPath(root, c.path);
|
|
179
|
-
if (!folder || folder.type !== "folder")
|
|
180
|
-
continue;
|
|
181
|
-
const exists = folder.children.some((x) => x.name === c.value);
|
|
182
|
-
if (!exists)
|
|
183
|
-
throw new Error(formatConstraintError(`${c.path} must contain "${c.value}"`, `Add ${c.value} inside ${c.path}`));
|
|
184
|
-
}
|
|
185
|
-
if (c.type === "fileNameRegex") {
|
|
186
|
-
const folder = findNodeByPath(root, c.path);
|
|
187
|
-
if (!folder || folder.type !== "folder")
|
|
188
|
-
continue;
|
|
189
|
-
const regex = new RegExp(c.regex);
|
|
190
|
-
for (const child of folder.children) {
|
|
191
|
-
if (child.type === "file" && !regex.test(child.name)) {
|
|
192
|
-
throw new Error(formatConstraintError(`${child.name} in ${c.path} does not match regex "${c.regex}"`, `Rename the file or update the regex constraint`));
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (c.type === "maxDepth") {
|
|
197
|
-
const folder = findNodeByPath(root, c.path);
|
|
198
|
-
if (!folder || folder.type !== "folder")
|
|
199
|
-
continue;
|
|
200
|
-
const depth = maxDepth(folder);
|
|
201
|
-
if (depth > c.value)
|
|
202
|
-
throw new Error(formatConstraintError(`${c.path} exceeds max depth of ${c.value} (current depth ${depth})`, `Remove nested folders or increase maxDepth`));
|
|
203
|
-
}
|
|
204
|
-
if (c.type === "mustHaveFile") {
|
|
205
|
-
const folder = findNodeByPath(root, c.path);
|
|
206
|
-
if (!folder || folder.type !== "folder")
|
|
207
|
-
continue;
|
|
208
|
-
const exists = folder.children.some((x) => x.type === "file" && x.name === c.value);
|
|
209
|
-
if (!exists)
|
|
210
|
-
throw new Error(formatConstraintError(`${c.path} must have file "${c.value}"`, `Add ${c.value} inside ${c.path}`));
|
|
211
|
-
}
|
|
212
|
-
if (c.type === "eachFolderMustContain") {
|
|
213
|
-
const folders = getFoldersByScope(root, c.path, c.scope);
|
|
214
|
-
for (const folder of folders) {
|
|
215
|
-
const exists = folder.children.some((x) => x.name === c.value);
|
|
216
|
-
if (!exists) {
|
|
217
|
-
throw new Error(formatConstraintError(`${folder.name} must contain "${c.value}"`, `Add ${c.value} to ${folder.name}`));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
if (c.type === "eachFolderMustContainFile") {
|
|
222
|
-
const folders = getFoldersByScope(root, c.path, c.scope);
|
|
223
|
-
for (const folder of folders) {
|
|
224
|
-
const exists = folder.children.some((x) => x.type === "file" && x.name === c.value);
|
|
225
|
-
if (!exists) {
|
|
226
|
-
throw new Error(formatConstraintError(`${folder.name} must contain file "${c.value}"`, `Add ${c.value} inside ${folder.name}`));
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
if (c.type === "eachFolderMustContainFolder") {
|
|
231
|
-
const folders = getFoldersByScope(root, c.path, c.scope);
|
|
232
|
-
for (const folder of folders) {
|
|
233
|
-
const exists = folder.children.some((x) => x.type === "folder" && x.name === c.value);
|
|
234
|
-
if (!exists) {
|
|
235
|
-
throw new Error(formatConstraintError(`${folder.name} must contain folder "${c.value}"`, `Add folder ${c.value} inside ${folder.name}`));
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (c.type === "eachFolderMustHaveExt") {
|
|
240
|
-
const folders = getFoldersByScope(root, c.path, c.scope);
|
|
241
|
-
for (const folder of folders) {
|
|
242
|
-
const exists = folder.children.some((x) => x.type === "file" && x.name.endsWith(c.ext));
|
|
243
|
-
if (!exists) {
|
|
244
|
-
throw new Error(formatConstraintError(`${folder.name} must contain a file with extension "${c.ext}"`, `Add a ${c.ext} file inside ${folder.name}`));
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
package/dist/visitor.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.visit = visit;
|
|
4
|
-
async function visit(node, visitor, currentPath = "") {
|
|
5
|
-
const isVirtualRoot = node.name === "__root__";
|
|
6
|
-
const nodePath = isVirtualRoot
|
|
7
|
-
? currentPath
|
|
8
|
-
: currentPath
|
|
9
|
-
? `${currentPath}/${node.name}`
|
|
10
|
-
: node.name;
|
|
11
|
-
if (!isVirtualRoot) {
|
|
12
|
-
await visitor.folder?.(node, nodePath);
|
|
13
|
-
}
|
|
14
|
-
for (const child of node.children) {
|
|
15
|
-
if (child.type === "folder") {
|
|
16
|
-
await visit(child, visitor, nodePath);
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
await visitor.file?.(child, `${nodePath}/${child.name}`);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|