@agiflowai/aicode-utils 0.4.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/LICENSE +661 -0
- package/dist/index.cjs +337 -0
- package/dist/index.d.cts +242 -0
- package/dist/index.d.ts +242 -0
- package/dist/index.js +315 -0
- package/package.json +54 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
let node_path = require("node:path");
|
|
25
|
+
node_path = __toESM(node_path);
|
|
26
|
+
let fs_extra = require("fs-extra");
|
|
27
|
+
fs_extra = __toESM(fs_extra);
|
|
28
|
+
|
|
29
|
+
//#region src/services/ProjectFinderService.ts
|
|
30
|
+
var ProjectFinderService = class {
|
|
31
|
+
projectCache = /* @__PURE__ */ new Map();
|
|
32
|
+
workspaceRoot;
|
|
33
|
+
constructor(workspaceRoot) {
|
|
34
|
+
this.workspaceRoot = workspaceRoot || process.cwd();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Find the project containing a given file by walking up the directory tree
|
|
38
|
+
*
|
|
39
|
+
* @param filePath - Absolute path to the file
|
|
40
|
+
* @returns Project configuration or null if not found
|
|
41
|
+
*/
|
|
42
|
+
async findProjectForFile(filePath) {
|
|
43
|
+
const normalizedPath = node_path.default.isAbsolute(filePath) ? filePath : node_path.default.join(this.workspaceRoot, filePath);
|
|
44
|
+
let currentDir = node_path.default.dirname(normalizedPath);
|
|
45
|
+
while (currentDir !== "/" && currentDir.startsWith(this.workspaceRoot)) {
|
|
46
|
+
const projectJsonPath = node_path.default.join(currentDir, "project.json");
|
|
47
|
+
try {
|
|
48
|
+
const project = await this.loadProjectConfig(projectJsonPath);
|
|
49
|
+
if (project) return project;
|
|
50
|
+
} catch {}
|
|
51
|
+
currentDir = node_path.default.dirname(currentDir);
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Find the project containing a given file (synchronous version)
|
|
57
|
+
*
|
|
58
|
+
* @param filePath - Absolute path to the file
|
|
59
|
+
* @returns Project configuration or null if not found
|
|
60
|
+
*/
|
|
61
|
+
findProjectForFileSync(filePath) {
|
|
62
|
+
const normalizedPath = node_path.default.isAbsolute(filePath) ? filePath : node_path.default.join(this.workspaceRoot, filePath);
|
|
63
|
+
let currentDir = node_path.default.dirname(normalizedPath);
|
|
64
|
+
while (currentDir !== "/" && currentDir.startsWith(this.workspaceRoot)) {
|
|
65
|
+
const projectJsonPath = node_path.default.join(currentDir, "project.json");
|
|
66
|
+
try {
|
|
67
|
+
const project = this.loadProjectConfigSync(projectJsonPath);
|
|
68
|
+
if (project) return project;
|
|
69
|
+
} catch {}
|
|
70
|
+
currentDir = node_path.default.dirname(currentDir);
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Load and parse a project.json file
|
|
76
|
+
*/
|
|
77
|
+
async loadProjectConfig(projectJsonPath) {
|
|
78
|
+
if (this.projectCache.has(projectJsonPath)) return this.projectCache.get(projectJsonPath);
|
|
79
|
+
try {
|
|
80
|
+
const content = await fs_extra.readFile(projectJsonPath, "utf-8");
|
|
81
|
+
const config = JSON.parse(content);
|
|
82
|
+
const projectConfig = {
|
|
83
|
+
name: config.name || node_path.default.basename(node_path.default.dirname(projectJsonPath)),
|
|
84
|
+
root: node_path.default.dirname(projectJsonPath),
|
|
85
|
+
sourceTemplate: config.sourceTemplate,
|
|
86
|
+
projectType: config.projectType
|
|
87
|
+
};
|
|
88
|
+
this.projectCache.set(projectJsonPath, projectConfig);
|
|
89
|
+
return projectConfig;
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Load and parse a project.json file (synchronous version)
|
|
96
|
+
*/
|
|
97
|
+
loadProjectConfigSync(projectJsonPath) {
|
|
98
|
+
if (this.projectCache.has(projectJsonPath)) return this.projectCache.get(projectJsonPath);
|
|
99
|
+
try {
|
|
100
|
+
const content = fs_extra.readFileSync(projectJsonPath, "utf-8");
|
|
101
|
+
const config = JSON.parse(content);
|
|
102
|
+
const projectConfig = {
|
|
103
|
+
name: config.name || node_path.default.basename(node_path.default.dirname(projectJsonPath)),
|
|
104
|
+
root: node_path.default.dirname(projectJsonPath),
|
|
105
|
+
sourceTemplate: config.sourceTemplate,
|
|
106
|
+
projectType: config.projectType
|
|
107
|
+
};
|
|
108
|
+
this.projectCache.set(projectJsonPath, projectConfig);
|
|
109
|
+
return projectConfig;
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the workspace root
|
|
116
|
+
*/
|
|
117
|
+
getWorkspaceRoot() {
|
|
118
|
+
return this.workspaceRoot;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Clear the project cache
|
|
122
|
+
*/
|
|
123
|
+
clearCache() {
|
|
124
|
+
this.projectCache.clear();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/services/ScaffoldProcessingService.ts
|
|
130
|
+
/**
|
|
131
|
+
* Shared service for common scaffolding operations like processing templates and tracking files
|
|
132
|
+
*/
|
|
133
|
+
var ScaffoldProcessingService = class {
|
|
134
|
+
constructor(fileSystem, variableReplacer) {
|
|
135
|
+
this.fileSystem = fileSystem;
|
|
136
|
+
this.variableReplacer = variableReplacer;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Process a target path for variable replacement, handling both files and directories
|
|
140
|
+
*/
|
|
141
|
+
async processTargetForVariableReplacement(targetPath, variables) {
|
|
142
|
+
if ((await this.fileSystem.stat(targetPath)).isDirectory()) await this.variableReplacer.processFilesForVariableReplacement(targetPath, variables);
|
|
143
|
+
else await this.variableReplacer.replaceVariablesInFile(targetPath, variables);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Track all created files, handling both single files and directories
|
|
147
|
+
*/
|
|
148
|
+
async trackCreatedFiles(targetPath, createdFiles) {
|
|
149
|
+
if ((await this.fileSystem.stat(targetPath)).isDirectory()) await this.trackCreatedFilesRecursive(targetPath, createdFiles);
|
|
150
|
+
else createdFiles.push(targetPath);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Track all existing files, handling both single files and directories
|
|
154
|
+
*/
|
|
155
|
+
async trackExistingFiles(targetPath, existingFiles) {
|
|
156
|
+
if ((await this.fileSystem.stat(targetPath)).isDirectory()) await this.trackExistingFilesRecursive(targetPath, existingFiles);
|
|
157
|
+
else existingFiles.push(targetPath);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Copy source to target, then process templates and track files
|
|
161
|
+
* Now supports tracking existing files separately from created files
|
|
162
|
+
*/
|
|
163
|
+
async copyAndProcess(sourcePath, targetPath, variables, createdFiles, existingFiles) {
|
|
164
|
+
await this.fileSystem.ensureDir(node_path.default.dirname(targetPath));
|
|
165
|
+
if (await this.fileSystem.pathExists(targetPath) && existingFiles) {
|
|
166
|
+
await this.trackExistingFiles(targetPath, existingFiles);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
await this.fileSystem.copy(sourcePath, targetPath);
|
|
170
|
+
await this.processTargetForVariableReplacement(targetPath, variables);
|
|
171
|
+
await this.trackCreatedFiles(targetPath, createdFiles);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Recursively collect all file paths in a directory for created files
|
|
175
|
+
*/
|
|
176
|
+
async trackCreatedFilesRecursive(dirPath, createdFiles) {
|
|
177
|
+
let items = [];
|
|
178
|
+
try {
|
|
179
|
+
items = await this.fileSystem.readdir(dirPath);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.warn(`Cannot read directory ${dirPath}: ${error}`);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
for (const item of items) {
|
|
185
|
+
if (!item) continue;
|
|
186
|
+
const itemPath = node_path.default.join(dirPath, item);
|
|
187
|
+
try {
|
|
188
|
+
const stat = await this.fileSystem.stat(itemPath);
|
|
189
|
+
if (stat.isDirectory()) await this.trackCreatedFilesRecursive(itemPath, createdFiles);
|
|
190
|
+
else if (stat.isFile()) createdFiles.push(itemPath);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.warn(`Cannot stat ${itemPath}: ${error}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Recursively collect all file paths in a directory for existing files
|
|
198
|
+
*/
|
|
199
|
+
async trackExistingFilesRecursive(dirPath, existingFiles) {
|
|
200
|
+
let items = [];
|
|
201
|
+
try {
|
|
202
|
+
items = await this.fileSystem.readdir(dirPath);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.warn(`Cannot read directory ${dirPath}: ${error}`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
for (const item of items) {
|
|
208
|
+
if (!item) continue;
|
|
209
|
+
const itemPath = node_path.default.join(dirPath, item);
|
|
210
|
+
try {
|
|
211
|
+
const stat = await this.fileSystem.stat(itemPath);
|
|
212
|
+
if (stat.isDirectory()) await this.trackExistingFilesRecursive(itemPath, existingFiles);
|
|
213
|
+
else if (stat.isFile()) existingFiles.push(itemPath);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
console.warn(`Cannot stat ${itemPath}: ${error}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/services/TemplatesManagerService.ts
|
|
223
|
+
var TemplatesManagerService = class TemplatesManagerService {
|
|
224
|
+
static SCAFFOLD_CONFIG_FILE = "scaffold.yaml";
|
|
225
|
+
static TEMPLATES_FOLDER = "templates";
|
|
226
|
+
static TOOLKIT_CONFIG_FILE = "toolkit.yaml";
|
|
227
|
+
/**
|
|
228
|
+
* Find the templates directory by searching upwards from the starting path.
|
|
229
|
+
*
|
|
230
|
+
* Algorithm:
|
|
231
|
+
* 1. Start from the provided path (default: current working directory)
|
|
232
|
+
* 2. Search upwards to find the workspace root (where .git exists or filesystem root)
|
|
233
|
+
* 3. Check if toolkit.yaml exists at workspace root
|
|
234
|
+
* - If yes, read templatesPath from toolkit.yaml
|
|
235
|
+
* - If no, default to 'templates' folder in workspace root
|
|
236
|
+
* 4. Verify the templates directory exists
|
|
237
|
+
*
|
|
238
|
+
* @param startPath - The path to start searching from (defaults to process.cwd())
|
|
239
|
+
* @returns The absolute path to the templates directory
|
|
240
|
+
* @throws Error if templates directory is not found
|
|
241
|
+
*/
|
|
242
|
+
static async findTemplatesPath(startPath = process.cwd()) {
|
|
243
|
+
const workspaceRoot = await TemplatesManagerService.findWorkspaceRoot(startPath);
|
|
244
|
+
const toolkitConfigPath = node_path.default.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE);
|
|
245
|
+
if (await fs_extra.pathExists(toolkitConfigPath)) {
|
|
246
|
+
const yaml = await import("js-yaml");
|
|
247
|
+
const content = await fs_extra.readFile(toolkitConfigPath, "utf-8");
|
|
248
|
+
const config = yaml.load(content);
|
|
249
|
+
if (config?.templatesPath) {
|
|
250
|
+
const templatesPath$1 = node_path.default.isAbsolute(config.templatesPath) ? config.templatesPath : node_path.default.join(workspaceRoot, config.templatesPath);
|
|
251
|
+
if (await fs_extra.pathExists(templatesPath$1)) return templatesPath$1;
|
|
252
|
+
else throw new Error(`Templates path specified in toolkit.yaml does not exist: ${templatesPath$1}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const templatesPath = node_path.default.join(workspaceRoot, TemplatesManagerService.TEMPLATES_FOLDER);
|
|
256
|
+
if (await fs_extra.pathExists(templatesPath)) return templatesPath;
|
|
257
|
+
throw new Error(`Templates folder not found at ${templatesPath}.\nEither create a 'templates' folder or specify templatesPath in toolkit.yaml`);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Find the workspace root by searching upwards for .git folder
|
|
261
|
+
*/
|
|
262
|
+
static async findWorkspaceRoot(startPath) {
|
|
263
|
+
let currentPath = node_path.default.resolve(startPath);
|
|
264
|
+
const rootPath = node_path.default.parse(currentPath).root;
|
|
265
|
+
while (true) {
|
|
266
|
+
const gitPath = node_path.default.join(currentPath, ".git");
|
|
267
|
+
if (await fs_extra.pathExists(gitPath)) return currentPath;
|
|
268
|
+
if (currentPath === rootPath) return process.cwd();
|
|
269
|
+
currentPath = node_path.default.dirname(currentPath);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get the templates path synchronously.
|
|
274
|
+
* Use this when you need immediate access and are sure templates exist.
|
|
275
|
+
*
|
|
276
|
+
* @param startPath - The path to start searching from (defaults to process.cwd())
|
|
277
|
+
* @returns The absolute path to the templates directory
|
|
278
|
+
* @throws Error if templates directory is not found
|
|
279
|
+
*/
|
|
280
|
+
static findTemplatesPathSync(startPath = process.cwd()) {
|
|
281
|
+
const workspaceRoot = TemplatesManagerService.findWorkspaceRootSync(startPath);
|
|
282
|
+
const toolkitConfigPath = node_path.default.join(workspaceRoot, TemplatesManagerService.TOOLKIT_CONFIG_FILE);
|
|
283
|
+
if (fs_extra.pathExistsSync(toolkitConfigPath)) {
|
|
284
|
+
const yaml = require("js-yaml");
|
|
285
|
+
const content = fs_extra.readFileSync(toolkitConfigPath, "utf-8");
|
|
286
|
+
const config = yaml.load(content);
|
|
287
|
+
if (config?.templatesPath) {
|
|
288
|
+
const templatesPath$1 = node_path.default.isAbsolute(config.templatesPath) ? config.templatesPath : node_path.default.join(workspaceRoot, config.templatesPath);
|
|
289
|
+
if (fs_extra.pathExistsSync(templatesPath$1)) return templatesPath$1;
|
|
290
|
+
else throw new Error(`Templates path specified in toolkit.yaml does not exist: ${templatesPath$1}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const templatesPath = node_path.default.join(workspaceRoot, TemplatesManagerService.TEMPLATES_FOLDER);
|
|
294
|
+
if (fs_extra.pathExistsSync(templatesPath)) return templatesPath;
|
|
295
|
+
throw new Error(`Templates folder not found at ${templatesPath}.\nEither create a 'templates' folder or specify templatesPath in toolkit.yaml`);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Find the workspace root synchronously by searching upwards for .git folder
|
|
299
|
+
*/
|
|
300
|
+
static findWorkspaceRootSync(startPath) {
|
|
301
|
+
let currentPath = node_path.default.resolve(startPath);
|
|
302
|
+
const rootPath = node_path.default.parse(currentPath).root;
|
|
303
|
+
while (true) {
|
|
304
|
+
const gitPath = node_path.default.join(currentPath, ".git");
|
|
305
|
+
if (fs_extra.pathExistsSync(gitPath)) return currentPath;
|
|
306
|
+
if (currentPath === rootPath) return process.cwd();
|
|
307
|
+
currentPath = node_path.default.dirname(currentPath);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Check if templates are initialized at the given path
|
|
312
|
+
*
|
|
313
|
+
* @param templatesPath - Path to check for templates
|
|
314
|
+
* @returns true if templates folder exists and is a directory
|
|
315
|
+
*/
|
|
316
|
+
static async isInitialized(templatesPath) {
|
|
317
|
+
if (!await fs_extra.pathExists(templatesPath)) return false;
|
|
318
|
+
return (await fs_extra.stat(templatesPath)).isDirectory();
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Get the scaffold config file name
|
|
322
|
+
*/
|
|
323
|
+
static getConfigFileName() {
|
|
324
|
+
return TemplatesManagerService.SCAFFOLD_CONFIG_FILE;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Get the templates folder name
|
|
328
|
+
*/
|
|
329
|
+
static getTemplatesFolderName() {
|
|
330
|
+
return TemplatesManagerService.TEMPLATES_FOLDER;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
//#endregion
|
|
335
|
+
exports.ProjectFinderService = ProjectFinderService;
|
|
336
|
+
exports.ScaffoldProcessingService = ScaffoldProcessingService;
|
|
337
|
+
exports.TemplatesManagerService = TemplatesManagerService;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
//#region src/types/index.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* @agiflowai/aicode-utils - Type Definitions
|
|
4
|
+
*
|
|
5
|
+
* DESIGN PATTERNS:
|
|
6
|
+
* - Interface segregation: Keep interfaces focused and minimal
|
|
7
|
+
* - Type composition: Build complex types from simple primitives
|
|
8
|
+
* - Generics: Use type parameters for reusable, type-safe abstractions
|
|
9
|
+
*
|
|
10
|
+
* CODING STANDARDS:
|
|
11
|
+
* - Use PascalCase for type/interface names
|
|
12
|
+
* - Prefix interfaces with 'I' only for abstract contracts
|
|
13
|
+
* - Document complex types with JSDoc comments
|
|
14
|
+
* - Export all public types
|
|
15
|
+
*
|
|
16
|
+
* AVOID:
|
|
17
|
+
* - Any type unless absolutely necessary
|
|
18
|
+
* - Overly complex type gymnastics
|
|
19
|
+
* - Coupling types to implementation details
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Project configuration from project.json
|
|
23
|
+
*/
|
|
24
|
+
interface ProjectConfig {
|
|
25
|
+
name: string;
|
|
26
|
+
root: string;
|
|
27
|
+
sourceTemplate?: string;
|
|
28
|
+
projectType?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Scaffold template include configuration
|
|
32
|
+
*/
|
|
33
|
+
interface ParsedInclude {
|
|
34
|
+
sourcePath: string;
|
|
35
|
+
targetPath: string;
|
|
36
|
+
conditions?: Record<string, string>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Result of a scaffold operation
|
|
40
|
+
*/
|
|
41
|
+
interface ScaffoldResult {
|
|
42
|
+
success: boolean;
|
|
43
|
+
message: string;
|
|
44
|
+
warnings?: string[];
|
|
45
|
+
createdFiles?: string[];
|
|
46
|
+
existingFiles?: string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Abstract interface for file system operations
|
|
50
|
+
*/
|
|
51
|
+
interface IFileSystemService {
|
|
52
|
+
pathExists(path: string): Promise<boolean>;
|
|
53
|
+
readFile(path: string, encoding?: BufferEncoding): Promise<string>;
|
|
54
|
+
readJson(path: string): Promise<any>;
|
|
55
|
+
writeFile(path: string, content: string, encoding?: BufferEncoding): Promise<void>;
|
|
56
|
+
ensureDir(path: string): Promise<void>;
|
|
57
|
+
copy(src: string, dest: string): Promise<void>;
|
|
58
|
+
readdir(path: string): Promise<string[]>;
|
|
59
|
+
stat(path: string): Promise<{
|
|
60
|
+
isDirectory(): boolean;
|
|
61
|
+
isFile(): boolean;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Abstract interface for variable replacement in templates
|
|
66
|
+
*/
|
|
67
|
+
interface IVariableReplacementService {
|
|
68
|
+
processFilesForVariableReplacement(dirPath: string, variables: Record<string, any>): Promise<void>;
|
|
69
|
+
replaceVariablesInFile(filePath: string, variables: Record<string, any>): Promise<void>;
|
|
70
|
+
isBinaryFile(filePath: string): boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Context object passed to generator functions
|
|
74
|
+
*/
|
|
75
|
+
interface GeneratorContext {
|
|
76
|
+
variables: Record<string, any>;
|
|
77
|
+
config: any;
|
|
78
|
+
targetPath: string;
|
|
79
|
+
templatePath: string;
|
|
80
|
+
fileSystem: IFileSystemService;
|
|
81
|
+
scaffoldConfigLoader: any;
|
|
82
|
+
variableReplacer: IVariableReplacementService;
|
|
83
|
+
ScaffoldProcessingService: any;
|
|
84
|
+
getRootPath: () => string;
|
|
85
|
+
getProjectPath: (projectPath: string) => string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Type definition for generator functions
|
|
89
|
+
*/
|
|
90
|
+
type GeneratorFunction = (context: GeneratorContext) => Promise<ScaffoldResult>;
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/services/ProjectFinderService.d.ts
|
|
93
|
+
declare class ProjectFinderService {
|
|
94
|
+
private projectCache;
|
|
95
|
+
private workspaceRoot;
|
|
96
|
+
constructor(workspaceRoot?: string);
|
|
97
|
+
/**
|
|
98
|
+
* Find the project containing a given file by walking up the directory tree
|
|
99
|
+
*
|
|
100
|
+
* @param filePath - Absolute path to the file
|
|
101
|
+
* @returns Project configuration or null if not found
|
|
102
|
+
*/
|
|
103
|
+
findProjectForFile(filePath: string): Promise<ProjectConfig | null>;
|
|
104
|
+
/**
|
|
105
|
+
* Find the project containing a given file (synchronous version)
|
|
106
|
+
*
|
|
107
|
+
* @param filePath - Absolute path to the file
|
|
108
|
+
* @returns Project configuration or null if not found
|
|
109
|
+
*/
|
|
110
|
+
findProjectForFileSync(filePath: string): ProjectConfig | null;
|
|
111
|
+
/**
|
|
112
|
+
* Load and parse a project.json file
|
|
113
|
+
*/
|
|
114
|
+
private loadProjectConfig;
|
|
115
|
+
/**
|
|
116
|
+
* Load and parse a project.json file (synchronous version)
|
|
117
|
+
*/
|
|
118
|
+
private loadProjectConfigSync;
|
|
119
|
+
/**
|
|
120
|
+
* Get the workspace root
|
|
121
|
+
*/
|
|
122
|
+
getWorkspaceRoot(): string;
|
|
123
|
+
/**
|
|
124
|
+
* Clear the project cache
|
|
125
|
+
*/
|
|
126
|
+
clearCache(): void;
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/services/ScaffoldProcessingService.d.ts
|
|
130
|
+
/**
|
|
131
|
+
* Shared service for common scaffolding operations like processing templates and tracking files
|
|
132
|
+
*/
|
|
133
|
+
declare class ScaffoldProcessingService {
|
|
134
|
+
private fileSystem;
|
|
135
|
+
private variableReplacer;
|
|
136
|
+
constructor(fileSystem: IFileSystemService, variableReplacer: IVariableReplacementService);
|
|
137
|
+
/**
|
|
138
|
+
* Process a target path for variable replacement, handling both files and directories
|
|
139
|
+
*/
|
|
140
|
+
processTargetForVariableReplacement(targetPath: string, variables: Record<string, any>): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Track all created files, handling both single files and directories
|
|
143
|
+
*/
|
|
144
|
+
trackCreatedFiles(targetPath: string, createdFiles: string[]): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Track all existing files, handling both single files and directories
|
|
147
|
+
*/
|
|
148
|
+
trackExistingFiles(targetPath: string, existingFiles: string[]): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* Copy source to target, then process templates and track files
|
|
151
|
+
* Now supports tracking existing files separately from created files
|
|
152
|
+
*/
|
|
153
|
+
copyAndProcess(sourcePath: string, targetPath: string, variables: Record<string, any>, createdFiles: string[], existingFiles?: string[]): Promise<void>;
|
|
154
|
+
/**
|
|
155
|
+
* Recursively collect all file paths in a directory for created files
|
|
156
|
+
*/
|
|
157
|
+
private trackCreatedFilesRecursive;
|
|
158
|
+
/**
|
|
159
|
+
* Recursively collect all file paths in a directory for existing files
|
|
160
|
+
*/
|
|
161
|
+
private trackExistingFilesRecursive;
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/services/TemplatesManagerService.d.ts
|
|
165
|
+
/**
|
|
166
|
+
* TemplatesManagerService
|
|
167
|
+
*
|
|
168
|
+
* DESIGN PATTERNS:
|
|
169
|
+
* - Class-based service pattern for encapsulating business logic
|
|
170
|
+
* - Static methods for utility-like functionality
|
|
171
|
+
* - File system traversal for workspace detection
|
|
172
|
+
* - Configuration-driven template path resolution
|
|
173
|
+
*
|
|
174
|
+
* CODING STANDARDS:
|
|
175
|
+
* - Service class names use PascalCase with 'Service' suffix
|
|
176
|
+
* - Method names use camelCase with descriptive verbs
|
|
177
|
+
* - Return types should be explicit (never use implicit any)
|
|
178
|
+
* - Use async/await for asynchronous operations
|
|
179
|
+
* - Handle errors with try-catch and throw descriptive Error objects
|
|
180
|
+
* - Document public methods with JSDoc comments
|
|
181
|
+
*
|
|
182
|
+
* AVOID:
|
|
183
|
+
* - Side effects in constructors (keep them lightweight)
|
|
184
|
+
* - Mixing concerns (keep services focused on single domain)
|
|
185
|
+
* - Direct coupling to other services (use dependency injection)
|
|
186
|
+
* - Exposing internal implementation details
|
|
187
|
+
*/
|
|
188
|
+
declare class TemplatesManagerService {
|
|
189
|
+
private static SCAFFOLD_CONFIG_FILE;
|
|
190
|
+
private static TEMPLATES_FOLDER;
|
|
191
|
+
private static TOOLKIT_CONFIG_FILE;
|
|
192
|
+
/**
|
|
193
|
+
* Find the templates directory by searching upwards from the starting path.
|
|
194
|
+
*
|
|
195
|
+
* Algorithm:
|
|
196
|
+
* 1. Start from the provided path (default: current working directory)
|
|
197
|
+
* 2. Search upwards to find the workspace root (where .git exists or filesystem root)
|
|
198
|
+
* 3. Check if toolkit.yaml exists at workspace root
|
|
199
|
+
* - If yes, read templatesPath from toolkit.yaml
|
|
200
|
+
* - If no, default to 'templates' folder in workspace root
|
|
201
|
+
* 4. Verify the templates directory exists
|
|
202
|
+
*
|
|
203
|
+
* @param startPath - The path to start searching from (defaults to process.cwd())
|
|
204
|
+
* @returns The absolute path to the templates directory
|
|
205
|
+
* @throws Error if templates directory is not found
|
|
206
|
+
*/
|
|
207
|
+
static findTemplatesPath(startPath?: string): Promise<string>;
|
|
208
|
+
/**
|
|
209
|
+
* Find the workspace root by searching upwards for .git folder
|
|
210
|
+
*/
|
|
211
|
+
private static findWorkspaceRoot;
|
|
212
|
+
/**
|
|
213
|
+
* Get the templates path synchronously.
|
|
214
|
+
* Use this when you need immediate access and are sure templates exist.
|
|
215
|
+
*
|
|
216
|
+
* @param startPath - The path to start searching from (defaults to process.cwd())
|
|
217
|
+
* @returns The absolute path to the templates directory
|
|
218
|
+
* @throws Error if templates directory is not found
|
|
219
|
+
*/
|
|
220
|
+
static findTemplatesPathSync(startPath?: string): string;
|
|
221
|
+
/**
|
|
222
|
+
* Find the workspace root synchronously by searching upwards for .git folder
|
|
223
|
+
*/
|
|
224
|
+
private static findWorkspaceRootSync;
|
|
225
|
+
/**
|
|
226
|
+
* Check if templates are initialized at the given path
|
|
227
|
+
*
|
|
228
|
+
* @param templatesPath - Path to check for templates
|
|
229
|
+
* @returns true if templates folder exists and is a directory
|
|
230
|
+
*/
|
|
231
|
+
static isInitialized(templatesPath: string): Promise<boolean>;
|
|
232
|
+
/**
|
|
233
|
+
* Get the scaffold config file name
|
|
234
|
+
*/
|
|
235
|
+
static getConfigFileName(): string;
|
|
236
|
+
/**
|
|
237
|
+
* Get the templates folder name
|
|
238
|
+
*/
|
|
239
|
+
static getTemplatesFolderName(): string;
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
export { GeneratorContext, GeneratorFunction, IFileSystemService, IVariableReplacementService, ParsedInclude, ProjectConfig, ProjectFinderService, ScaffoldProcessingService, ScaffoldResult, TemplatesManagerService };
|