directory-lint 2.0.1 → 2.0.3

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/index.cjs CHANGED
@@ -91,32 +91,49 @@ var DirectoryLint = class {
91
91
  cwd,
92
92
  paths: {}
93
93
  };
94
- if (!this.backend.exists(cwd)) this.backend.makeDirectory(cwd, options?.recursive);
94
+ if (!this.backend.exists(cwd)) {
95
+ this.backend.makeDirectory(cwd, options?.recursive);
96
+ }
95
97
  for (const [name, node] of Object.entries(schema)) {
96
98
  const isRegexLiteral = name.startsWith("/") && name.endsWith("/");
97
99
  if (isRegexLiteral) throw new RegexNotSupported(name);
98
100
  const fullPath = (0, import_path.join)(cwd, name);
99
101
  if (node.type === "directory") {
100
- let childrenResult = void 0;
102
+ let childrenResult;
101
103
  if (!this.backend.exists(fullPath)) {
102
104
  this.backend.makeDirectory(fullPath, options?.recursive);
103
105
  }
104
106
  if (node.children) {
105
- childrenResult = await this.generate(fullPath, node.children, options);
107
+ childrenResult = await this.generate(
108
+ fullPath,
109
+ node.children,
110
+ options
111
+ );
106
112
  }
107
113
  result.paths[name] = {
108
- type: node.type,
114
+ type: "directory",
109
115
  path: fullPath,
110
- ...childrenResult?.paths && { children: childrenResult.paths }
116
+ ...childrenResult?.paths && {
117
+ children: childrenResult.paths
118
+ }
111
119
  };
112
- } else if (node.type === "file") {
120
+ }
121
+ if (node.type === "file") {
113
122
  if (!this.backend.exists(fullPath)) {
114
- this.backend.writeFile(fullPath, node.content === void 0 ? "" : node.content);
115
- } else {
116
- if (options?.overwrite) {
117
- this.backend.writeFile(fullPath, node.content === void 0 ? "" : node.content);
118
- }
123
+ this.backend.writeFile(
124
+ fullPath,
125
+ node.content ?? ""
126
+ );
127
+ } else if (options?.overwrite) {
128
+ this.backend.writeFile(
129
+ fullPath,
130
+ node.content ?? ""
131
+ );
119
132
  }
133
+ result.paths[name] = {
134
+ type: "file",
135
+ path: fullPath
136
+ };
120
137
  }
121
138
  }
122
139
  return result;
@@ -127,29 +144,61 @@ var DirectoryLint = class {
127
144
  paths: {}
128
145
  };
129
146
  const items = this.backend.getAllItems(cwd);
130
- for (const [pattern, node] of Object.entries(schema)) {
131
- const regex = this.patternToRegex(pattern);
132
- const matchedItems = items.filter((item) => regex.test(item.name));
133
- if (matchedItems.length === 0 && node.required !== false) throw new InvalidStructure(pattern);
147
+ const processedNames = /* @__PURE__ */ new Set();
148
+ const sortedEntries = Object.entries(schema).sort(([a], [b]) => {
149
+ const aHasWildcard = String(a).includes("*");
150
+ const bHasWildcard = String(b).includes("*");
151
+ if (!aHasWildcard && bHasWildcard) return -1;
152
+ if (aHasWildcard && !bHasWildcard) return 1;
153
+ return 0;
154
+ });
155
+ for (const [pattern, node] of sortedEntries) {
156
+ const regex = this.patternToRegex(String(pattern));
157
+ const matchedItems = items.filter(
158
+ (item) => regex.test(item.name) && !processedNames.has(item.name)
159
+ );
160
+ const isRequired = node.required ?? true;
161
+ if (matchedItems.length === 0 && isRequired) {
162
+ throw new InvalidStructure(String(pattern));
163
+ }
134
164
  for (const { name, type } of matchedItems) {
135
165
  if (options?.ignore.includes(name)) continue;
166
+ processedNames.add(name);
136
167
  const fullPath = (0, import_path.join)(cwd, name);
137
168
  if (node.type === "directory") {
138
- let childrenResult = void 0;
139
- if (type !== "directory") throw new InvalidStructure(fullPath);
140
- else if (node.children) {
141
- childrenResult = await this.validate(fullPath, node.children, options);
169
+ if (type !== "directory") {
170
+ throw new InvalidStructure(fullPath);
171
+ }
172
+ let childrenResult;
173
+ if (node.children) {
174
+ childrenResult = await this.validate(
175
+ fullPath,
176
+ node.children,
177
+ options
178
+ );
142
179
  }
143
180
  result.paths[name] = {
144
181
  path: fullPath,
145
182
  name,
146
- type: node.type,
147
- ...childrenResult?.paths && { children: childrenResult.paths }
183
+ type: "directory",
184
+ ...childrenResult?.paths && {
185
+ children: childrenResult.paths
186
+ }
148
187
  };
149
- } else if (node.type === "file") {
150
- if (type !== "file") throw new InvalidStructure(fullPath);
188
+ }
189
+ if (node.type === "file") {
190
+ if (type !== "file") {
191
+ throw new InvalidStructure(fullPath);
192
+ }
151
193
  const content = this.backend.readFile(fullPath);
152
- if (node.validate ? !node.validate(content) : false) throw new InvalidContent(fullPath);
194
+ if (node.validate && !node.validate(content)) {
195
+ throw new InvalidContent(fullPath);
196
+ }
197
+ result.paths[name] = {
198
+ path: fullPath,
199
+ name,
200
+ type: "file"
201
+ };
153
202
  }
154
203
  }
155
204
  }
package/dist/index.d.cts CHANGED
@@ -44,6 +44,16 @@ interface LintBackend {
44
44
  exists(path: string): boolean;
45
45
  }
46
46
 
47
+ type MapValidateSchema<T> = {
48
+ [K in keyof T]: T[K] extends {
49
+ type: "file";
50
+ } ? ValidateFileResult : T[K] extends {
51
+ type: "directory";
52
+ children?: infer C;
53
+ } ? ValidateDirectoryResult & {
54
+ children: C extends object ? MapValidateSchema<C> : undefined;
55
+ } : never;
56
+ };
47
57
  interface GenerateOptions {
48
58
  overwrite?: boolean;
49
59
  recursive?: boolean;
@@ -64,9 +74,9 @@ interface ValidateDirectoryResult {
64
74
  }
65
75
  type ValidateNodeResult = ValidateFileResult | ValidateDirectoryResult;
66
76
  type ValidatePathResult = Record<string, ValidateNodeResult>;
67
- interface ValidateResult {
77
+ interface ValidateResult<TSchema> {
68
78
  cwd: string;
69
- paths: ValidatePathResult;
79
+ paths: MapValidateSchema<TSchema>;
70
80
  }
71
81
  interface GenerateFileResult {
72
82
  type: "file";
@@ -88,7 +98,7 @@ declare class DirectoryLint {
88
98
  private readonly backend;
89
99
  constructor(backend?: LintBackend);
90
100
  generate(cwd: string, schema: GenerateSchema, options?: GenerateOptions): Promise<GenerateResult>;
91
- validate(cwd: string, schema: ValidateSchema, options?: ValidateOptions): Promise<ValidateResult>;
101
+ validate<TSchema extends ValidateSchema>(cwd: string, schema: TSchema, options?: ValidateOptions): Promise<ValidateResult<TSchema>>;
92
102
  private patternToRegex;
93
103
  }
94
104
 
package/dist/index.d.ts CHANGED
@@ -44,6 +44,16 @@ interface LintBackend {
44
44
  exists(path: string): boolean;
45
45
  }
46
46
 
47
+ type MapValidateSchema<T> = {
48
+ [K in keyof T]: T[K] extends {
49
+ type: "file";
50
+ } ? ValidateFileResult : T[K] extends {
51
+ type: "directory";
52
+ children?: infer C;
53
+ } ? ValidateDirectoryResult & {
54
+ children: C extends object ? MapValidateSchema<C> : undefined;
55
+ } : never;
56
+ };
47
57
  interface GenerateOptions {
48
58
  overwrite?: boolean;
49
59
  recursive?: boolean;
@@ -64,9 +74,9 @@ interface ValidateDirectoryResult {
64
74
  }
65
75
  type ValidateNodeResult = ValidateFileResult | ValidateDirectoryResult;
66
76
  type ValidatePathResult = Record<string, ValidateNodeResult>;
67
- interface ValidateResult {
77
+ interface ValidateResult<TSchema> {
68
78
  cwd: string;
69
- paths: ValidatePathResult;
79
+ paths: MapValidateSchema<TSchema>;
70
80
  }
71
81
  interface GenerateFileResult {
72
82
  type: "file";
@@ -88,7 +98,7 @@ declare class DirectoryLint {
88
98
  private readonly backend;
89
99
  constructor(backend?: LintBackend);
90
100
  generate(cwd: string, schema: GenerateSchema, options?: GenerateOptions): Promise<GenerateResult>;
91
- validate(cwd: string, schema: ValidateSchema, options?: ValidateOptions): Promise<ValidateResult>;
101
+ validate<TSchema extends ValidateSchema>(cwd: string, schema: TSchema, options?: ValidateOptions): Promise<ValidateResult<TSchema>>;
92
102
  private patternToRegex;
93
103
  }
94
104
 
package/dist/index.js CHANGED
@@ -51,32 +51,49 @@ var DirectoryLint = class {
51
51
  cwd,
52
52
  paths: {}
53
53
  };
54
- if (!this.backend.exists(cwd)) this.backend.makeDirectory(cwd, options?.recursive);
54
+ if (!this.backend.exists(cwd)) {
55
+ this.backend.makeDirectory(cwd, options?.recursive);
56
+ }
55
57
  for (const [name, node] of Object.entries(schema)) {
56
58
  const isRegexLiteral = name.startsWith("/") && name.endsWith("/");
57
59
  if (isRegexLiteral) throw new RegexNotSupported(name);
58
60
  const fullPath = join(cwd, name);
59
61
  if (node.type === "directory") {
60
- let childrenResult = void 0;
62
+ let childrenResult;
61
63
  if (!this.backend.exists(fullPath)) {
62
64
  this.backend.makeDirectory(fullPath, options?.recursive);
63
65
  }
64
66
  if (node.children) {
65
- childrenResult = await this.generate(fullPath, node.children, options);
67
+ childrenResult = await this.generate(
68
+ fullPath,
69
+ node.children,
70
+ options
71
+ );
66
72
  }
67
73
  result.paths[name] = {
68
- type: node.type,
74
+ type: "directory",
69
75
  path: fullPath,
70
- ...childrenResult?.paths && { children: childrenResult.paths }
76
+ ...childrenResult?.paths && {
77
+ children: childrenResult.paths
78
+ }
71
79
  };
72
- } else if (node.type === "file") {
80
+ }
81
+ if (node.type === "file") {
73
82
  if (!this.backend.exists(fullPath)) {
74
- this.backend.writeFile(fullPath, node.content === void 0 ? "" : node.content);
75
- } else {
76
- if (options?.overwrite) {
77
- this.backend.writeFile(fullPath, node.content === void 0 ? "" : node.content);
78
- }
83
+ this.backend.writeFile(
84
+ fullPath,
85
+ node.content ?? ""
86
+ );
87
+ } else if (options?.overwrite) {
88
+ this.backend.writeFile(
89
+ fullPath,
90
+ node.content ?? ""
91
+ );
79
92
  }
93
+ result.paths[name] = {
94
+ type: "file",
95
+ path: fullPath
96
+ };
80
97
  }
81
98
  }
82
99
  return result;
@@ -87,29 +104,61 @@ var DirectoryLint = class {
87
104
  paths: {}
88
105
  };
89
106
  const items = this.backend.getAllItems(cwd);
90
- for (const [pattern, node] of Object.entries(schema)) {
91
- const regex = this.patternToRegex(pattern);
92
- const matchedItems = items.filter((item) => regex.test(item.name));
93
- if (matchedItems.length === 0 && node.required !== false) throw new InvalidStructure(pattern);
107
+ const processedNames = /* @__PURE__ */ new Set();
108
+ const sortedEntries = Object.entries(schema).sort(([a], [b]) => {
109
+ const aHasWildcard = String(a).includes("*");
110
+ const bHasWildcard = String(b).includes("*");
111
+ if (!aHasWildcard && bHasWildcard) return -1;
112
+ if (aHasWildcard && !bHasWildcard) return 1;
113
+ return 0;
114
+ });
115
+ for (const [pattern, node] of sortedEntries) {
116
+ const regex = this.patternToRegex(String(pattern));
117
+ const matchedItems = items.filter(
118
+ (item) => regex.test(item.name) && !processedNames.has(item.name)
119
+ );
120
+ const isRequired = node.required ?? true;
121
+ if (matchedItems.length === 0 && isRequired) {
122
+ throw new InvalidStructure(String(pattern));
123
+ }
94
124
  for (const { name, type } of matchedItems) {
95
125
  if (options?.ignore.includes(name)) continue;
126
+ processedNames.add(name);
96
127
  const fullPath = join(cwd, name);
97
128
  if (node.type === "directory") {
98
- let childrenResult = void 0;
99
- if (type !== "directory") throw new InvalidStructure(fullPath);
100
- else if (node.children) {
101
- childrenResult = await this.validate(fullPath, node.children, options);
129
+ if (type !== "directory") {
130
+ throw new InvalidStructure(fullPath);
131
+ }
132
+ let childrenResult;
133
+ if (node.children) {
134
+ childrenResult = await this.validate(
135
+ fullPath,
136
+ node.children,
137
+ options
138
+ );
102
139
  }
103
140
  result.paths[name] = {
104
141
  path: fullPath,
105
142
  name,
106
- type: node.type,
107
- ...childrenResult?.paths && { children: childrenResult.paths }
143
+ type: "directory",
144
+ ...childrenResult?.paths && {
145
+ children: childrenResult.paths
146
+ }
108
147
  };
109
- } else if (node.type === "file") {
110
- if (type !== "file") throw new InvalidStructure(fullPath);
148
+ }
149
+ if (node.type === "file") {
150
+ if (type !== "file") {
151
+ throw new InvalidStructure(fullPath);
152
+ }
111
153
  const content = this.backend.readFile(fullPath);
112
- if (node.validate ? !node.validate(content) : false) throw new InvalidContent(fullPath);
154
+ if (node.validate && !node.validate(content)) {
155
+ throw new InvalidContent(fullPath);
156
+ }
157
+ result.paths[name] = {
158
+ path: fullPath,
159
+ name,
160
+ type: "file"
161
+ };
113
162
  }
114
163
  }
115
164
  }
package/package.json CHANGED
@@ -1,38 +1,38 @@
1
- {
2
- "name": "directory-lint",
3
- "version": "2.0.1",
4
- "description": "Directory Lint",
5
- "license": "MIT",
6
- "author": "Henry Vilani",
7
- "type": "module",
8
- "types": "./dist/index.d.ts",
9
- "main": "./dist/index.js",
10
- "exports": {
11
- ".": {
12
- "types": "./dist/index.d.ts",
13
- "import": "./dist/index.js",
14
- "require": "./dist/index.cjs"
15
- }
16
- },
17
- "files": [
18
- "dist"
19
- ],
20
- "scripts": {
21
- "build": "tsup src/index.ts --format cjs,esm --dts --clean",
22
- "example:basic": "ts-node examples/basic-generation.ts"
23
- },
24
- "keywords": [
25
- "linter",
26
- "validation",
27
- "filesystem",
28
- "directory-structure",
29
- "architecture",
30
- "lint"
31
- ],
32
- "devDependencies": {
33
- "@types/node": "^25.2.0",
34
- "ts-node": "^10.9.2",
35
- "tsup": "^8.5.1",
36
- "typescript": "^5.9.3"
37
- }
38
- }
1
+ {
2
+ "name": "directory-lint",
3
+ "version": "2.0.3",
4
+ "description": "Directory Lint",
5
+ "license": "MIT",
6
+ "author": "Henry Vilani",
7
+ "type": "module",
8
+ "types": "./dist/index.d.ts",
9
+ "main": "./dist/index.js",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
22
+ "example:basic": "ts-node examples/basic-generation.ts"
23
+ },
24
+ "keywords": [
25
+ "linter",
26
+ "validation",
27
+ "filesystem",
28
+ "directory-structure",
29
+ "architecture",
30
+ "lint"
31
+ ],
32
+ "devDependencies": {
33
+ "@types/node": "^25.2.0",
34
+ "ts-node": "^10.9.2",
35
+ "tsup": "^8.5.1",
36
+ "typescript": "^5.9.3"
37
+ }
38
+ }