@akanjs/cli 2.4.0 → 2.4.1-rc.1
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/agent.command-h4afc69n.js +26 -0
- package/application.command-47mj9qsy.js +165 -0
- package/applicationBuildRunner-yz144508.js +284 -0
- package/applicationReleasePackager-brvth6rs.js +245 -0
- package/capacitorApp-y0h6cgft.js +58 -0
- package/cloud.command-qgjxja3n.js +94 -0
- package/commandManifest.json +1 -0
- package/context.command-nqbtak4f.js +82 -0
- package/dependencyScanner-m4x5maek.js +9 -0
- package/guideline.command-ya0dh44f.js +356 -0
- package/incrementalBuilder.proc.js +89 -17394
- package/index-1xdrsbry.js +1447 -0
- package/index-45aj5ry0.js +990 -0
- package/index-5vvwc0cz.js +559 -0
- package/index-61keag0s.js +40 -0
- package/index-6pz1j0zj.js +62 -0
- package/index-73pr2cmy.js +534 -0
- package/index-76rn3g2c.js +76 -0
- package/index-77crfweb.js +1884 -0
- package/index-85msc0wg.js +161 -0
- package/index-8pkbzj26.js +840 -0
- package/index-8rc0bm04.js +514 -0
- package/index-a5rmdgy4.js +4359 -0
- package/index-a6sbyy0b.js +2769 -0
- package/index-fgc8r6dj.js +33 -0
- package/index-h6ca6qg0.js +2777 -0
- package/index-hdqztm58.js +758 -0
- package/index-hwzpw9c1.js +202 -0
- package/index-pmm9e2jf.js +120 -0
- package/index-qaq13qk3.js +80 -0
- package/index-qhtr07v8.js +1072 -0
- package/index-r24hmh0q.js +4 -0
- package/index-sgmas1fc.js +462 -0
- package/index-ss469dec.js +11 -0
- package/index-swf4bmbg.js +25 -0
- package/index-wnp7hwq7.js +193 -0
- package/index-wq8jwx8z.js +224 -0
- package/index-x53a5nya.js +301 -0
- package/index-y3hdhy4p.js +229 -0
- package/index-z9gvz7b0.js +83 -0
- package/index.js +54 -23340
- package/library.command-r15zdqvp.js +33 -0
- package/localRegistry.command-p1pgxw78.js +178 -0
- package/module.command-qrj3kmyz.js +54 -0
- package/package.command-r8sq5kzp.js +43 -0
- package/package.json +2 -2
- package/page.command-c6xdx0xm.js +24 -0
- package/primitive.command-pv9ssmtf.js +62 -0
- package/quality.command-es67wvdp.js +809 -0
- package/repair.command-677675vw.js +67 -0
- package/routeSourceValidator-wbhmbwpj.js +132 -0
- package/scalar.command-kabkd6wd.js +35 -0
- package/templates/facetIndex/index.ts +1 -1
- package/typeChecker-kravn7ns.js +8 -0
- package/typecheck.proc.js +4 -194
- package/workflow.command-64r6cw0w.js +108 -0
- package/workspace.command-xk68sd6c.js +435 -0
|
@@ -0,0 +1,840 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
PageScript
|
|
4
|
+
} from "./index-swf4bmbg.js";
|
|
5
|
+
import {
|
|
6
|
+
pluralizeName
|
|
7
|
+
} from "./index-ss469dec.js";
|
|
8
|
+
import {
|
|
9
|
+
Prompter
|
|
10
|
+
} from "./index-qaq13qk3.js";
|
|
11
|
+
import {
|
|
12
|
+
bilingualDescriptionForField,
|
|
13
|
+
bilingualLabelForField,
|
|
14
|
+
createPassedPrimitiveReport,
|
|
15
|
+
generatedFilesForSync,
|
|
16
|
+
moduleSourcePaths,
|
|
17
|
+
sourceFile
|
|
18
|
+
} from "./index-h6ca6qg0.js";
|
|
19
|
+
import {
|
|
20
|
+
AiSession
|
|
21
|
+
} from "./index-5vvwc0cz.js";
|
|
22
|
+
import {
|
|
23
|
+
runner,
|
|
24
|
+
script
|
|
25
|
+
} from "./index-hdqztm58.js";
|
|
26
|
+
import {
|
|
27
|
+
ModuleExecutor
|
|
28
|
+
} from "./index-a6sbyy0b.js";
|
|
29
|
+
import {
|
|
30
|
+
FileSys
|
|
31
|
+
} from "./index-61keag0s.js";
|
|
32
|
+
|
|
33
|
+
// pkgs/@akanjs/devkit/getRelatedCnsts.ts
|
|
34
|
+
import { readFileSync, realpathSync } from "fs";
|
|
35
|
+
import ora from "ora";
|
|
36
|
+
import * as ts from "typescript";
|
|
37
|
+
var tsTranspiler = new Bun.Transpiler({ loader: "ts" });
|
|
38
|
+
var tsxTranspiler = new Bun.Transpiler({ loader: "tsx" });
|
|
39
|
+
var getTranspiler = (filePath) => filePath.endsWith(".tsx") ? tsxTranspiler : tsTranspiler;
|
|
40
|
+
var scanModuleSpecifiers = (source, filePath, includeExports) => {
|
|
41
|
+
const scannedImports = getTranspiler(filePath).scanImports(source).map((imp) => imp.path).filter(Boolean);
|
|
42
|
+
if (includeExports) {
|
|
43
|
+
const specifiers = new Set(scannedImports);
|
|
44
|
+
const typeOnlyModuleRegex = /\b(?:import|export)\s+type\s+[\s\S]*?\s+from\s*["']([^"']+)["']/g;
|
|
45
|
+
for (const match of source.matchAll(typeOnlyModuleRegex)) {
|
|
46
|
+
const importPath = match[1];
|
|
47
|
+
if (importPath)
|
|
48
|
+
specifiers.add(importPath);
|
|
49
|
+
}
|
|
50
|
+
const exportFromRegex = /\bexport\s+(?:type\s+)?(?:\*|{[\s\S]*?})\s+from\s*["']([^"']+)["']/g;
|
|
51
|
+
for (const match of source.matchAll(exportFromRegex)) {
|
|
52
|
+
const importPath = match[1];
|
|
53
|
+
if (importPath)
|
|
54
|
+
specifiers.add(importPath);
|
|
55
|
+
}
|
|
56
|
+
return specifiers;
|
|
57
|
+
}
|
|
58
|
+
const importSpecifiers = new Set;
|
|
59
|
+
const importDeclarationRegex = /\bimport\s+(?:type\s+)?(?:["']([^"']+)["']|[\s\S]*?\s+from\s*["']([^"']+)["'])/g;
|
|
60
|
+
for (const match of source.matchAll(importDeclarationRegex)) {
|
|
61
|
+
const importPath = match[1] ?? match[2];
|
|
62
|
+
if (importPath && (scannedImports.includes(importPath) || match[0].startsWith("import type"))) {
|
|
63
|
+
importSpecifiers.add(importPath);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return importSpecifiers;
|
|
67
|
+
};
|
|
68
|
+
var parseTsConfig = (tsConfigPath = "./tsconfig.json") => {
|
|
69
|
+
const configFile = ts.readConfigFile(tsConfigPath, (path) => {
|
|
70
|
+
return ts.sys.readFile(path);
|
|
71
|
+
});
|
|
72
|
+
return ts.parseJsonConfigFileContent(configFile.config, ts.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
|
|
73
|
+
};
|
|
74
|
+
var collectImportedFiles = (constantFilePath, parsedConfig) => {
|
|
75
|
+
const allFilesToAnalyze = new Set([constantFilePath]);
|
|
76
|
+
const analyzedFiles = new Set;
|
|
77
|
+
const spinner = ora("Collecting related files...");
|
|
78
|
+
spinner.start();
|
|
79
|
+
function collectImported(filePath) {
|
|
80
|
+
if (analyzedFiles.has(filePath))
|
|
81
|
+
return;
|
|
82
|
+
analyzedFiles.add(filePath);
|
|
83
|
+
const source = readFileSync(filePath, "utf-8");
|
|
84
|
+
for (const importPath of scanModuleSpecifiers(source, filePath, false)) {
|
|
85
|
+
if (!importPath.startsWith("."))
|
|
86
|
+
continue;
|
|
87
|
+
const resolved = ts.resolveModuleName(importPath, filePath, parsedConfig.options, ts.sys).resolvedModule?.resolvedFileName;
|
|
88
|
+
if (resolved && !allFilesToAnalyze.has(resolved)) {
|
|
89
|
+
allFilesToAnalyze.add(resolved);
|
|
90
|
+
collectImported(resolved);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
collectImported(constantFilePath);
|
|
95
|
+
spinner.succeed(`Found ${allFilesToAnalyze.size} related files.`);
|
|
96
|
+
return {
|
|
97
|
+
allFilesToAnalyze,
|
|
98
|
+
analyzedFiles
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
var createTsProgram = (filePaths, options) => {
|
|
102
|
+
const spinner = ora("Creating TypeScript program for all files...");
|
|
103
|
+
spinner.start();
|
|
104
|
+
const program = ts.createProgram(Array.from(filePaths), options);
|
|
105
|
+
const checker = program.getTypeChecker();
|
|
106
|
+
spinner.succeed("TypeScript program created.");
|
|
107
|
+
return {
|
|
108
|
+
program,
|
|
109
|
+
checker
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
var createSymbolCache = (checker) => {
|
|
113
|
+
const symbolCache = new Map;
|
|
114
|
+
return (node) => {
|
|
115
|
+
const cacheKey = `${node.getSourceFile().fileName}:${node.pos}:${node.end}`;
|
|
116
|
+
if (!symbolCache.has(cacheKey)) {
|
|
117
|
+
symbolCache.set(cacheKey, checker.getSymbolAtLocation(node));
|
|
118
|
+
}
|
|
119
|
+
return symbolCache.get(cacheKey);
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
var analyzeProperties = (filesToAnalyze, program, checker) => {
|
|
123
|
+
const propertyMap = new Map;
|
|
124
|
+
const analyzedFiles = new Set;
|
|
125
|
+
const sourceLineCache = new Map;
|
|
126
|
+
const getCachedSymbol = createSymbolCache(checker);
|
|
127
|
+
const spinner = ora("Analyzing property relationships...");
|
|
128
|
+
spinner.start();
|
|
129
|
+
function analyzeFileProperties(filePath) {
|
|
130
|
+
if (analyzedFiles.has(filePath))
|
|
131
|
+
return;
|
|
132
|
+
analyzedFiles.add(filePath);
|
|
133
|
+
const source = program.getSourceFile(filePath);
|
|
134
|
+
if (!source)
|
|
135
|
+
return;
|
|
136
|
+
if (!sourceLineCache.has(filePath)) {
|
|
137
|
+
sourceLineCache.set(filePath, source.getFullText().split(`
|
|
138
|
+
`));
|
|
139
|
+
}
|
|
140
|
+
const sourceLines = sourceLineCache.get(filePath);
|
|
141
|
+
function visit(node) {
|
|
142
|
+
if (!source)
|
|
143
|
+
return;
|
|
144
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
145
|
+
const left = node.expression;
|
|
146
|
+
const right = node.name;
|
|
147
|
+
const { line } = ts.getLineAndCharacterOfPosition(source, node.getStart());
|
|
148
|
+
if (ts.isIdentifier(left) && sourceLines && sourceLines.length > line && sourceLines[line] && (sourceLines[line]?.includes(`@Field.Prop(() => ${left.text}.${right.text}`) || sourceLines[line].includes(`base.Filter(${left.text}.${right.text},`))) {
|
|
149
|
+
const symbol = getCachedSymbol(right);
|
|
150
|
+
if (symbol?.declarations && symbol.declarations.length > 0) {
|
|
151
|
+
const key = symbol.declarations[0]?.getSourceFile().fileName.split("/").pop()?.split(".")[0] ?? "";
|
|
152
|
+
const property = propertyMap.get(key);
|
|
153
|
+
const isScalar = symbol.declarations[0]?.getSourceFile().fileName.includes("_") ?? false;
|
|
154
|
+
const symbolFilePath = symbol.declarations[0]?.getSourceFile().fileName.replace(`${ts.sys.getCurrentDirectory()}/`, "");
|
|
155
|
+
if (!symbolFilePath)
|
|
156
|
+
throw new Error(`No symbol file path found for ${left.text}.${right.text}`);
|
|
157
|
+
if (property) {
|
|
158
|
+
propertyMap.set(`${left.text}.${right.text}`, {
|
|
159
|
+
filePath: symbolFilePath,
|
|
160
|
+
isLibModule: true,
|
|
161
|
+
isImport: false,
|
|
162
|
+
libName: left.text,
|
|
163
|
+
source: readFileSync(symbolFilePath, "utf-8"),
|
|
164
|
+
isScalar
|
|
165
|
+
});
|
|
166
|
+
} else {
|
|
167
|
+
propertyMap.set(key, {
|
|
168
|
+
filePath: symbolFilePath,
|
|
169
|
+
isLibModule: true,
|
|
170
|
+
isImport: false,
|
|
171
|
+
libName: left.text,
|
|
172
|
+
isScalar,
|
|
173
|
+
source: readFileSync(symbolFilePath, "utf-8")
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} else if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
179
|
+
const importPath = node.moduleSpecifier.text;
|
|
180
|
+
if (importPath.startsWith(".")) {
|
|
181
|
+
const resolved = ts.resolveModuleName(importPath, filePath, program.getCompilerOptions(), ts.sys).resolvedModule?.resolvedFileName;
|
|
182
|
+
const moduleName = importPath.split("/").pop()?.split(".")[0] ?? "";
|
|
183
|
+
const property = propertyMap.get(moduleName);
|
|
184
|
+
const isScalar = importPath.includes("_");
|
|
185
|
+
if (moduleName && resolved && (!property || property.filePath !== resolved)) {
|
|
186
|
+
propertyMap.set(moduleName, {
|
|
187
|
+
filePath: resolved,
|
|
188
|
+
isLibModule: false,
|
|
189
|
+
isImport: true,
|
|
190
|
+
isScalar,
|
|
191
|
+
source: readFileSync(resolved, "utf-8")
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
ts.forEachChild(node, visit);
|
|
197
|
+
}
|
|
198
|
+
visit(source);
|
|
199
|
+
}
|
|
200
|
+
for (const filePath of filesToAnalyze) {
|
|
201
|
+
analyzeFileProperties(filePath);
|
|
202
|
+
}
|
|
203
|
+
spinner.succeed(`Analysis complete. Found ${propertyMap.size} properties.`);
|
|
204
|
+
return propertyMap;
|
|
205
|
+
};
|
|
206
|
+
var getRelatedCnsts = (constantFilePath) => {
|
|
207
|
+
const parsedConfig = parseTsConfig();
|
|
208
|
+
const { allFilesToAnalyze } = collectImportedFiles(constantFilePath, parsedConfig);
|
|
209
|
+
const { program, checker } = createTsProgram(allFilesToAnalyze, parsedConfig.options);
|
|
210
|
+
const propertyMap = analyzeProperties(allFilesToAnalyze, program, checker);
|
|
211
|
+
return Array.from(propertyMap.entries()).map(([key, value]) => ({ key, ...value }));
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// pkgs/@akanjs/cli/module/module.script.ts
|
|
215
|
+
import { input } from "@inquirer/prompts";
|
|
216
|
+
import { capitalize as capitalize3, randomPicks } from "akanjs/common";
|
|
217
|
+
|
|
218
|
+
// pkgs/@akanjs/cli/module/module.request.ts
|
|
219
|
+
import { capitalize } from "akanjs/common";
|
|
220
|
+
|
|
221
|
+
// pkgs/@akanjs/cli/module/module.prompt.ts
|
|
222
|
+
var componentDefaultDescription = ({
|
|
223
|
+
modelName,
|
|
224
|
+
ModelName,
|
|
225
|
+
exampleFiles,
|
|
226
|
+
constant,
|
|
227
|
+
properties
|
|
228
|
+
}) => ``;
|
|
229
|
+
|
|
230
|
+
// pkgs/@akanjs/cli/module/module.request.ts
|
|
231
|
+
class ModuleRequest extends Prompter {
|
|
232
|
+
sysType;
|
|
233
|
+
sysName;
|
|
234
|
+
config;
|
|
235
|
+
modelName;
|
|
236
|
+
constructor(options) {
|
|
237
|
+
super();
|
|
238
|
+
this.sysType = options.sysType;
|
|
239
|
+
this.sysName = options.sysName;
|
|
240
|
+
this.config = options.config;
|
|
241
|
+
this.modelName = options.modelName;
|
|
242
|
+
}
|
|
243
|
+
async requestModelConstant({
|
|
244
|
+
modelDesc,
|
|
245
|
+
modelSchemaDesign,
|
|
246
|
+
boilerplate,
|
|
247
|
+
exampleFiles
|
|
248
|
+
}) {
|
|
249
|
+
return this.makeTsFileUpdatePrompt({
|
|
250
|
+
context: `
|
|
251
|
+
1. <model>.constant.ts \uD30C\uC77C\uC5D0 \uB300\uD55C \uAC1C\uC694
|
|
252
|
+
${await this.getDocumentation("modelConstant")}
|
|
253
|
+
|
|
254
|
+
2. <model>.constant.ts \uD30C\uC77C\uC758 Enum \uC791\uC131\uBC95
|
|
255
|
+
${await this.getDocumentation("enumConstant")}
|
|
256
|
+
|
|
257
|
+
3. <model>.constant.ts \uD30C\uC77C\uC758 Field \uC791\uC131\uBC95
|
|
258
|
+
${await this.getDocumentation("fieldRule")}
|
|
259
|
+
|
|
260
|
+
4. \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8 \uB0B4 \uB2E4\uB978 constant.ts \uD30C\uC77C\uB4E4\uC5D0 \uB300\uD55C \uC608\uC2DC
|
|
261
|
+
${exampleFiles.map((constant) => `
|
|
262
|
+
Example file: ${constant.filePath}
|
|
263
|
+
\`\`\`
|
|
264
|
+
${constant.content}
|
|
265
|
+
\`\`\`
|
|
266
|
+
`).join(`
|
|
267
|
+
`)}
|
|
268
|
+
`,
|
|
269
|
+
request: `
|
|
270
|
+
\uC704 \uB0B4\uC6A9\uB4E4\uC744 \uBC14\uD0D5\uC73C\uB85C \uD30C\uC2F1\uD558\uAE30 \uC27D\uAC8C \uC544\uB798\uC5D0 \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC918
|
|
271
|
+
|
|
272
|
+
Application name: ${this.sysName}
|
|
273
|
+
Model name: ${this.modelName}
|
|
274
|
+
Model description: ${modelDesc}
|
|
275
|
+
Model schema design: ${modelSchemaDesign}
|
|
276
|
+
|
|
277
|
+
Target filename: ${this.modelName}.constant.ts
|
|
278
|
+
\`\`\`typescript
|
|
279
|
+
// File: lib/${this.modelName}/${this.modelName}.constant.ts
|
|
280
|
+
${boilerplate}
|
|
281
|
+
\`\`\`
|
|
282
|
+
|
|
283
|
+
\uB9CC\uC57D, ${this.modelName}.constant.ts \uD30C\uC77C \uC678\uC5D0 \uB2E4\uB978 \uC2A4\uCE7C\uB77C \uBAA8\uB378\uC744 \uCD94\uAC00\uB85C \uC791\uC131\uD574\uC57C\uD55C\uB2E4\uBA74, \uB2E4\uC74C\uACFC \uAC19\uC740 \uD615\uC2DD\uC73C\uB85C \uCD94\uAC00\uB85C \uC791\uC131\uD574\uC918
|
|
284
|
+
\`\`\`typescript
|
|
285
|
+
// File: lib/__scalar/otherModel/otherModel.constant.ts
|
|
286
|
+
...\uD30C\uC77C \uB0B4\uC6A9
|
|
287
|
+
\`\`\`
|
|
288
|
+
`
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
var requestDictionary = ({
|
|
293
|
+
sysName,
|
|
294
|
+
modelName,
|
|
295
|
+
constant,
|
|
296
|
+
modelDesc,
|
|
297
|
+
modelSchemaDesign,
|
|
298
|
+
boilerplate,
|
|
299
|
+
exampleFiles
|
|
300
|
+
}) => `
|
|
301
|
+
|
|
302
|
+
You are writing an Akan.js model dictionary file.
|
|
303
|
+
|
|
304
|
+
Model constant:
|
|
305
|
+
\`\`\`typescript
|
|
306
|
+
// File: lib/${modelName}/${modelName}.constant.ts
|
|
307
|
+
${constant}
|
|
308
|
+
\`\`\`
|
|
309
|
+
|
|
310
|
+
Reference dictionary files:
|
|
311
|
+
${exampleFiles.map((exampleFile) => `
|
|
312
|
+
\`\`\`typescript
|
|
313
|
+
// File: ${exampleFile.filePath}
|
|
314
|
+
${exampleFile.content}
|
|
315
|
+
\`\`\`
|
|
316
|
+
`).join(`
|
|
317
|
+
`)}
|
|
318
|
+
|
|
319
|
+
Application name: ${sysName}
|
|
320
|
+
Model name: ${modelName}
|
|
321
|
+
Model description: ${modelDesc}
|
|
322
|
+
Model schema design: ${modelSchemaDesign}
|
|
323
|
+
|
|
324
|
+
Complete the dictionary according to the constant file and boilerplate below.
|
|
325
|
+
Return only file results in parseable code blocks.
|
|
326
|
+
|
|
327
|
+
\`\`\`typescript
|
|
328
|
+
// File: lib/${modelName}/${modelName}.dictionary.ts
|
|
329
|
+
${boilerplate}
|
|
330
|
+
\`\`\`
|
|
331
|
+
`;
|
|
332
|
+
var requestTemplate = ({
|
|
333
|
+
sysName,
|
|
334
|
+
modelName,
|
|
335
|
+
ModelName,
|
|
336
|
+
boilerplate,
|
|
337
|
+
constant,
|
|
338
|
+
properties,
|
|
339
|
+
exampleFiles
|
|
340
|
+
}) => `
|
|
341
|
+
${componentDefaultDescription({
|
|
342
|
+
sysName,
|
|
343
|
+
modelName,
|
|
344
|
+
ModelName: ModelName ?? capitalize(modelName),
|
|
345
|
+
exampleFiles,
|
|
346
|
+
constant,
|
|
347
|
+
properties
|
|
348
|
+
})}
|
|
349
|
+
\uC5ED\uD560\uBD80\uC5EC
|
|
350
|
+
- Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uC2DC\uB2C8\uC5B4 \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C\uC790.
|
|
351
|
+
|
|
352
|
+
\uCF54\uB529 \uADDC\uCE59
|
|
353
|
+
- \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
354
|
+
- \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
355
|
+
- CSS: tailwind, DaisyUI(card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 X) \uC0AC\uC6A9
|
|
356
|
+
- Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
357
|
+
- \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
358
|
+
\uCF54\uB4DC \uC2A4\uD0C0\uC77C
|
|
359
|
+
- \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
|
|
360
|
+
- \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
|
|
361
|
+
- \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
|
|
362
|
+
- \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
|
|
363
|
+
\uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
|
|
364
|
+
|
|
365
|
+
\uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
|
|
366
|
+
- UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
|
|
367
|
+
- \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
|
|
368
|
+
- \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
|
|
369
|
+
- dayjs \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB294 akanjs/base\uC5D0\uC11C \uB798\uD551\uD558\uC5EC \uC81C\uACF5\uD558\uACE0 \uC788\uC74C.
|
|
370
|
+
|
|
371
|
+
\uC694\uCCAD\uC0AC\uD56D
|
|
372
|
+
- \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
|
|
373
|
+
- ${ModelName}.Template.tsx \uCF54\uB4DC \uC791\uC131
|
|
374
|
+
- \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC740 \uBAA8\uB378 \uC774\uB984\uC740 \uC2A4\uD0A4\uB9C8\uC5D0 \uAE30\uBC18\uD55C \uAE30\uB2A5\uC5D0 \uCD08\uC810\uC744 \uB450\uACE0 \uC791\uC131
|
|
375
|
+
- \uC544\uB798 \uC81C\uACF5\uD560 \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uC788\uB294 General \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uB97C \uC81C\uC678\uD55C \uB514\uC790\uC778 \uCEF4\uD3EC\uB10C\uD2B8 4\uAC1C \uAC1C\uBC1C
|
|
376
|
+
- \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
|
|
377
|
+
- \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
|
|
378
|
+
-
|
|
379
|
+
|
|
380
|
+
Application name: ${sysName}
|
|
381
|
+
Model name: ${modelName}
|
|
382
|
+
Target filename: ${ModelName}.Template.tsx
|
|
383
|
+
\`\`\`
|
|
384
|
+
${boilerplate}
|
|
385
|
+
\`\`\`
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
`;
|
|
389
|
+
var requestView = ({
|
|
390
|
+
sysName,
|
|
391
|
+
modelName,
|
|
392
|
+
ModelName,
|
|
393
|
+
boilerplate,
|
|
394
|
+
constant,
|
|
395
|
+
properties,
|
|
396
|
+
exampleFiles
|
|
397
|
+
}) => `
|
|
398
|
+
${componentDefaultDescription({
|
|
399
|
+
sysName,
|
|
400
|
+
modelName,
|
|
401
|
+
ModelName: ModelName ?? capitalize(modelName),
|
|
402
|
+
exampleFiles,
|
|
403
|
+
constant,
|
|
404
|
+
properties
|
|
405
|
+
})}
|
|
406
|
+
|
|
407
|
+
\uC5ED\uD560\uBD80\uC5EC
|
|
408
|
+
- Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uC2DC\uB2C8\uC5B4 \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C\uC790.
|
|
409
|
+
|
|
410
|
+
\uCF54\uB529 \uADDC\uCE59
|
|
411
|
+
- \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
412
|
+
- \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
413
|
+
- CSS: tailwind, DaisyUI(card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 X) \uC0AC\uC6A9
|
|
414
|
+
- Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
415
|
+
- \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
416
|
+
\uCF54\uB4DC \uC2A4\uD0C0\uC77C
|
|
417
|
+
- \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
|
|
418
|
+
- \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
|
|
419
|
+
- \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
|
|
420
|
+
- \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
|
|
421
|
+
\uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
|
|
422
|
+
|
|
423
|
+
\uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
|
|
424
|
+
- UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
|
|
425
|
+
- \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
|
|
426
|
+
- \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
|
|
427
|
+
- dayjs \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB294 akanjs/base\uC5D0\uC11C \uB798\uD551\uD558\uC5EC \uC81C\uACF5\uD558\uACE0 \uC788\uC74C.
|
|
428
|
+
- util/ui \uC774\uC678\uC5D0 \uBA85\uC2DC\uB418\uC788\uC9C0 \uC54A\uC740 \uCEF4\uD3EC\uB10C\uD2B8 \uC808\uB300 \uC0AC\uC6A9 \uAE08\uC9C0.
|
|
429
|
+
|
|
430
|
+
\uC694\uCCAD\uC0AC\uD56D
|
|
431
|
+
- \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
|
|
432
|
+
- ${ModelName}.View.tsx \uCF54\uB4DC \uC791\uC131
|
|
433
|
+
- \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC5D0 ${ModelName}\uC740 \uC0DD\uB7B5\uD558\uBA70, \uB514\uC790\uC778 \uC911\uC810\uC758 \uC774\uB984\uC73C\uB85C \uC791\uC131
|
|
434
|
+
- \uC544\uB798 \uC81C\uACF5\uD560 \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uC788\uB294 General \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uB97C \uC81C\uC678\uD55C \uB514\uC790\uC778 \uCEF4\uD3EC\uB10C\uD2B8 4\uAC1C \uAC1C\uBC1C
|
|
435
|
+
- \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
|
|
436
|
+
- \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
|
|
437
|
+
- UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
Application name: ${sysName}
|
|
441
|
+
Model name: ${modelName}
|
|
442
|
+
|
|
443
|
+
Target filename: ${ModelName}.View.tsx
|
|
444
|
+
\`\`\`
|
|
445
|
+
${boilerplate}
|
|
446
|
+
\`\`\`
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
`;
|
|
450
|
+
var requestUnit = ({
|
|
451
|
+
sysName,
|
|
452
|
+
modelName,
|
|
453
|
+
ModelName,
|
|
454
|
+
constant,
|
|
455
|
+
properties,
|
|
456
|
+
boilerplate,
|
|
457
|
+
exampleFiles
|
|
458
|
+
}) => `
|
|
459
|
+
${componentDefaultDescription({
|
|
460
|
+
sysName,
|
|
461
|
+
modelName,
|
|
462
|
+
ModelName: ModelName ?? capitalize(modelName),
|
|
463
|
+
exampleFiles,
|
|
464
|
+
constant,
|
|
465
|
+
properties
|
|
466
|
+
})}
|
|
467
|
+
|
|
468
|
+
\uC5ED\uD560\uBD80\uC5EC
|
|
469
|
+
- Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uC2DC\uB2C8\uC5B4 \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C\uC790.
|
|
470
|
+
|
|
471
|
+
\uCF54\uB529 \uADDC\uCE59
|
|
472
|
+
- \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
473
|
+
- \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
474
|
+
- CSS: tailwind, DaisyUI(card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 X) \uC0AC\uC6A9
|
|
475
|
+
- Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
476
|
+
- \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
|
|
477
|
+
\uCF54\uB4DC \uC2A4\uD0C0\uC77C
|
|
478
|
+
- \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
|
|
479
|
+
- \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
|
|
480
|
+
- \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
|
|
481
|
+
- \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
|
|
482
|
+
\uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
|
|
483
|
+
|
|
484
|
+
\uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
|
|
485
|
+
- UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
|
|
486
|
+
- \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
|
|
487
|
+
- \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
|
|
488
|
+
- dayjs \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB294 akanjs/base\uC5D0\uC11C \uB798\uD551\uD558\uC5EC \uC81C\uACF5\uD558\uACE0 \uC788\uC74C.
|
|
489
|
+
|
|
490
|
+
\uC694\uCCAD\uC0AC\uD56D
|
|
491
|
+
- \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
|
|
492
|
+
- ${ModelName}.Unit.tsx \uCF54\uB4DC \uC791\uC131
|
|
493
|
+
- \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC5D0 ${ModelName}\uC740 \uC0DD\uB7B5\uD558\uBA70, \uB514\uC790\uC778 \uC911\uC810\uC758 \uC774\uB984\uC73C\uB85C \uC791\uC131
|
|
494
|
+
- \uC608\uC2DC\uD30C\uC77C \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAE30\uBC18\uD558\uC5EC \uC77C\uBC18\uC801\uC73C\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uC640 \uB514\uC790\uC778\uC801 \uC694\uC18C\uAC00 \uD3EC\uD568\uB41C \uCEF4\uD3EC\uB10C\uD2B8 3\uAC1C \uAC1C\uBC1C
|
|
495
|
+
- \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
|
|
496
|
+
- \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
Application name: ${sysName}
|
|
500
|
+
Model name: ${modelName}
|
|
501
|
+
|
|
502
|
+
Target filename: ${ModelName}.Unit.tsx
|
|
503
|
+
\`\`\`
|
|
504
|
+
${boilerplate}
|
|
505
|
+
\`\`\`
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
`;
|
|
509
|
+
|
|
510
|
+
// pkgs/@akanjs/cli/module/module.runner.ts
|
|
511
|
+
import { capitalize as capitalize2 } from "akanjs/common";
|
|
512
|
+
var purposeByModule = {
|
|
513
|
+
budget: "Budget represents planned or actual money allocated inside the app.",
|
|
514
|
+
project: "Project represents a project workspace or business initiative managed by the app.",
|
|
515
|
+
task: "Task represents work items that move through the app workflow."
|
|
516
|
+
};
|
|
517
|
+
var moduleAbstractContent = (moduleName) => {
|
|
518
|
+
const title = capitalize2(moduleName);
|
|
519
|
+
const label = bilingualLabelForField(moduleName);
|
|
520
|
+
const purpose = purposeByModule[moduleName] ?? `${title} represents ${label.en.toLowerCase()} records managed by the app.`;
|
|
521
|
+
return `# ${title} Module Abstract
|
|
522
|
+
|
|
523
|
+
## Purpose
|
|
524
|
+
|
|
525
|
+
${purpose}
|
|
526
|
+
|
|
527
|
+
## Domain Rules
|
|
528
|
+
|
|
529
|
+
- Keep ${label.en.toLowerCase()} data consistent with user-facing dictionary labels.
|
|
530
|
+
|
|
531
|
+
## Data Meaning
|
|
532
|
+
|
|
533
|
+
${label.en} (${label.ko}) is the primary business concept for this module.
|
|
534
|
+
|
|
535
|
+
## Workflows
|
|
536
|
+
|
|
537
|
+
No lifecycle workflow yet.
|
|
538
|
+
|
|
539
|
+
## Agent Notes
|
|
540
|
+
|
|
541
|
+
- Read this abstract before changing the module.
|
|
542
|
+
- Update this file when business invariants, workflows, or public behavior change.
|
|
543
|
+
- Do not update this file for formatting-only, import-only, or style-only changes.
|
|
544
|
+
|
|
545
|
+
## Related Modules
|
|
546
|
+
|
|
547
|
+
- None yet.
|
|
548
|
+
`;
|
|
549
|
+
};
|
|
550
|
+
var localModuleFilename = (moduleName, pathKey) => moduleSourcePaths(moduleName)[pathKey].replace(`lib/${moduleName}/`, "");
|
|
551
|
+
|
|
552
|
+
class ModuleRunner extends runner("module") {
|
|
553
|
+
async createService(module) {
|
|
554
|
+
const serviceName = module.name.replace(/^_+/, "");
|
|
555
|
+
await module.applyTemplate({
|
|
556
|
+
basePath: `.`,
|
|
557
|
+
template: "service",
|
|
558
|
+
dict: { model: serviceName, sysName: module.sys.name }
|
|
559
|
+
});
|
|
560
|
+
const [abstractContent, dictionaryContent, serviceContent, signalContent, storeContent] = await Promise.all([
|
|
561
|
+
module.readFile(`${serviceName}.abstract.md`),
|
|
562
|
+
module.readFile(`${serviceName}.dictionary.ts`),
|
|
563
|
+
module.readFile(`${serviceName}.service.ts`),
|
|
564
|
+
module.readFile(`${serviceName}.signal.ts`),
|
|
565
|
+
module.readFile(`${serviceName}.store.ts`)
|
|
566
|
+
]);
|
|
567
|
+
return {
|
|
568
|
+
abstract: { filename: `${serviceName}.abstract.md`, content: abstractContent },
|
|
569
|
+
dictionary: { filename: `${serviceName}.dictionary.ts`, content: dictionaryContent },
|
|
570
|
+
service: { filename: `${serviceName}.service.ts`, content: serviceContent },
|
|
571
|
+
signal: { filename: `${serviceName}.signal.ts`, content: signalContent },
|
|
572
|
+
store: { filename: `${serviceName}.store.ts`, content: storeContent }
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
async removeModule(module) {
|
|
576
|
+
await module.sys.removeDir(`lib/${module.name}`);
|
|
577
|
+
}
|
|
578
|
+
async createComponentTemplate(module, type) {
|
|
579
|
+
await module.sys.applyTemplate({
|
|
580
|
+
basePath: `./lib/${module.name}`,
|
|
581
|
+
template: `module/__Model__.${capitalize2(type)}.tsx`,
|
|
582
|
+
dict: { model: module.name, appName: module.sys.name }
|
|
583
|
+
});
|
|
584
|
+
return {
|
|
585
|
+
component: {
|
|
586
|
+
filename: `${capitalize2(module.name)}.${capitalize2(type)}.tsx`,
|
|
587
|
+
content: await module.sys.readFile(`lib/${module.name}/${capitalize2(module.name)}.${capitalize2(type)}.tsx`)
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
async createModuleTemplate(module) {
|
|
592
|
+
const names = pluralizeName(module.name);
|
|
593
|
+
const modelLabel = bilingualLabelForField(module.name);
|
|
594
|
+
const modelDescription = bilingualDescriptionForField(module.name);
|
|
595
|
+
const filenames = {
|
|
596
|
+
abstract: localModuleFilename(module.name, "abstract"),
|
|
597
|
+
constant: localModuleFilename(module.name, "constant"),
|
|
598
|
+
dictionary: localModuleFilename(module.name, "dictionary"),
|
|
599
|
+
service: localModuleFilename(module.name, "service"),
|
|
600
|
+
store: localModuleFilename(module.name, "store"),
|
|
601
|
+
signal: localModuleFilename(module.name, "signal"),
|
|
602
|
+
unit: localModuleFilename(module.name, "unit"),
|
|
603
|
+
view: localModuleFilename(module.name, "view"),
|
|
604
|
+
template: localModuleFilename(module.name, "template"),
|
|
605
|
+
zone: localModuleFilename(module.name, "zone"),
|
|
606
|
+
util: localModuleFilename(module.name, "util")
|
|
607
|
+
};
|
|
608
|
+
await module.applyTemplate({
|
|
609
|
+
basePath: `.`,
|
|
610
|
+
template: "module",
|
|
611
|
+
dict: {
|
|
612
|
+
model: module.name,
|
|
613
|
+
models: names,
|
|
614
|
+
sysName: module.sys.name,
|
|
615
|
+
modelLabelEn: modelLabel.en,
|
|
616
|
+
modelLabelKo: modelLabel.ko,
|
|
617
|
+
modelDescEn: modelDescription.en,
|
|
618
|
+
modelDescKo: modelDescription.ko
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
await module.writeFile(filenames.abstract, moduleAbstractContent(module.name));
|
|
622
|
+
const [
|
|
623
|
+
abstractContent,
|
|
624
|
+
constantContent,
|
|
625
|
+
dictionaryContent,
|
|
626
|
+
serviceContent,
|
|
627
|
+
storeContent,
|
|
628
|
+
signalContent,
|
|
629
|
+
unitContent,
|
|
630
|
+
viewContent,
|
|
631
|
+
templateContent,
|
|
632
|
+
zoneContent,
|
|
633
|
+
utilContent
|
|
634
|
+
] = await Promise.all([
|
|
635
|
+
module.readFile(filenames.abstract),
|
|
636
|
+
module.readFile(filenames.constant),
|
|
637
|
+
module.readFile(filenames.dictionary),
|
|
638
|
+
module.readFile(filenames.service),
|
|
639
|
+
module.readFile(filenames.store),
|
|
640
|
+
module.readFile(filenames.signal),
|
|
641
|
+
module.readFile(filenames.unit),
|
|
642
|
+
module.readFile(filenames.view),
|
|
643
|
+
module.readFile(filenames.template),
|
|
644
|
+
module.readFile(filenames.zone),
|
|
645
|
+
module.readFile(filenames.util)
|
|
646
|
+
]);
|
|
647
|
+
return {
|
|
648
|
+
abstract: { filename: filenames.abstract, content: abstractContent },
|
|
649
|
+
constant: { filename: filenames.constant, content: constantContent },
|
|
650
|
+
dictionary: { filename: filenames.dictionary, content: dictionaryContent },
|
|
651
|
+
service: { filename: filenames.service, content: serviceContent },
|
|
652
|
+
store: { filename: filenames.store, content: storeContent },
|
|
653
|
+
signal: { filename: filenames.signal, content: signalContent },
|
|
654
|
+
unit: { filename: filenames.unit, content: unitContent },
|
|
655
|
+
view: { filename: filenames.view, content: viewContent },
|
|
656
|
+
template: { filename: filenames.template, content: templateContent },
|
|
657
|
+
zone: { filename: filenames.zone, content: zoneContent },
|
|
658
|
+
util: { filename: filenames.util, content: utilContent }
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// pkgs/@akanjs/cli/module/module.script.ts
|
|
664
|
+
var moduleChangedFiles = (sys2, moduleName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/${moduleName}/${file.filename}`, "create", "Module source file was created."));
|
|
665
|
+
|
|
666
|
+
class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
|
|
667
|
+
async createModuleTemplate(sys2, name, { page = false } = {}) {
|
|
668
|
+
const mod = ModuleExecutor.from(sys2, name);
|
|
669
|
+
const files = await this.moduleRunner.createModuleTemplate(mod);
|
|
670
|
+
if (page && sys2.type === "app")
|
|
671
|
+
await this.pageScript.createCrudPage(mod, { app: sys2, basePath: null, single: false });
|
|
672
|
+
await sys2.scan();
|
|
673
|
+
return createPassedPrimitiveReport({
|
|
674
|
+
command: "create-module",
|
|
675
|
+
changedFiles: moduleChangedFiles(sys2, name, files),
|
|
676
|
+
generatedFiles: generatedFilesForSync(sys2, "Generated files were refreshed after module creation."),
|
|
677
|
+
target: sys2.name
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
async createModule(sys2, name, {
|
|
681
|
+
page = false,
|
|
682
|
+
description,
|
|
683
|
+
schemaDescription
|
|
684
|
+
} = {}) {
|
|
685
|
+
const isContinued = await sys2.exists(`lib/${name}/${name}.constant.ts`);
|
|
686
|
+
const session = new AiSession("createModule", { workspace: sys2.workspace, cacheKey: name, isContinued });
|
|
687
|
+
const moduleConstantExampleFiles = await sys2.workspace.getConstantFiles();
|
|
688
|
+
const executor = ModuleExecutor.from(sys2, name);
|
|
689
|
+
const files = await this.moduleRunner.createModuleTemplate(executor);
|
|
690
|
+
const { constant, dictionary } = files;
|
|
691
|
+
if (page && sys2.type === "app")
|
|
692
|
+
await this.pageScript.createCrudPage(executor, { app: sys2, basePath: null, single: false });
|
|
693
|
+
const modelDesc = description ?? await input({ message: "description of module" });
|
|
694
|
+
const modelSchemaDesign = schemaDescription ?? await input({ message: "schema description of module" });
|
|
695
|
+
const config = await sys2.getConfig();
|
|
696
|
+
const moduleRequest = new ModuleRequest({ sysType: sys2.type, sysName: sys2.name, modelName: name, config });
|
|
697
|
+
const constantRequestPrompt = await moduleRequest.requestModelConstant({
|
|
698
|
+
modelDesc,
|
|
699
|
+
modelSchemaDesign,
|
|
700
|
+
boilerplate: constant.content,
|
|
701
|
+
exampleFiles: randomPicks(moduleConstantExampleFiles, Math.min(10, moduleConstantExampleFiles.length))
|
|
702
|
+
});
|
|
703
|
+
const constantGuide = await ModuleRequest.getGuideJson("modelConstant");
|
|
704
|
+
const constantWrites = await session.writeTypescripts(constantRequestPrompt, sys2, {
|
|
705
|
+
validate: constantGuide.update.rules
|
|
706
|
+
});
|
|
707
|
+
for (const scalarName of this.#getCreatedScalarNames(constantWrites.map(({ filePath }) => filePath))) {
|
|
708
|
+
await sys2.applyTemplate({
|
|
709
|
+
basePath: "./lib/__scalar",
|
|
710
|
+
template: "__scalar",
|
|
711
|
+
dict: { model: scalarName, models: pluralizeName(scalarName), sysName: sys2.name },
|
|
712
|
+
overwrite: false
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
const constantContent = await sys2.readFile(`lib/${name}/${name}.constant.ts`);
|
|
716
|
+
const dictionaryGuide = await ModuleRequest.getGuideJson("modelDictionary");
|
|
717
|
+
await session.writeTypescripts(requestDictionary({
|
|
718
|
+
sysName: sys2.name,
|
|
719
|
+
modelName: name,
|
|
720
|
+
constant: constantContent,
|
|
721
|
+
modelDesc,
|
|
722
|
+
modelSchemaDesign,
|
|
723
|
+
boilerplate: dictionary.content,
|
|
724
|
+
exampleFiles: randomPicks(moduleConstantExampleFiles, Math.min(10, moduleConstantExampleFiles.length))
|
|
725
|
+
}), sys2, { validate: dictionaryGuide.update.rules });
|
|
726
|
+
await sys2.scan();
|
|
727
|
+
sys2.log(`Module ${name} created in ${sys2.type}s/${sys2.name}/lib/${name}`);
|
|
728
|
+
return createPassedPrimitiveReport({
|
|
729
|
+
command: "create-module",
|
|
730
|
+
changedFiles: [
|
|
731
|
+
...moduleChangedFiles(sys2, name, files),
|
|
732
|
+
...constantWrites.map((write) => sourceFile(sys2, write.filePath, "modify", "AI-assisted module constant source was updated."))
|
|
733
|
+
],
|
|
734
|
+
generatedFiles: generatedFilesForSync(sys2, "Generated files were refreshed after module creation."),
|
|
735
|
+
target: sys2.name
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
#getCreatedScalarNames(filePaths) {
|
|
739
|
+
return [
|
|
740
|
+
...new Set(filePaths.map((filePath) => /^lib\/__scalar\/([^/]+)\/[^/]+\.constant\.ts$/.exec(filePath)?.[1]).filter((scalarName) => !!scalarName))
|
|
741
|
+
];
|
|
742
|
+
}
|
|
743
|
+
async removeModule(mod) {
|
|
744
|
+
await this.moduleRunner.removeModule(mod);
|
|
745
|
+
}
|
|
746
|
+
async createService(sys2, name) {
|
|
747
|
+
const service = ModuleExecutor.from(sys2, `_${name}`);
|
|
748
|
+
const files = await this.moduleRunner.createService(service);
|
|
749
|
+
await sys2.scan();
|
|
750
|
+
return createPassedPrimitiveReport({
|
|
751
|
+
command: "create-service",
|
|
752
|
+
changedFiles: moduleChangedFiles(sys2, `_${name}`, files),
|
|
753
|
+
generatedFiles: generatedFilesForSync(sys2, "Generated files were refreshed after service creation."),
|
|
754
|
+
target: sys2.name
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
async createTest(workspace, name) {}
|
|
758
|
+
async createTemplate(mod) {
|
|
759
|
+
const { component: template } = await this.moduleRunner.createComponentTemplate(mod, "template");
|
|
760
|
+
const paths = moduleSourcePaths(mod.name);
|
|
761
|
+
const templateExampleFiles = (await mod.sys.getTemplatesSourceCode()).filter((f) => !f.filePath.includes(paths.template));
|
|
762
|
+
const Name = capitalize3(mod.name);
|
|
763
|
+
const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
764
|
+
const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
765
|
+
const session = new AiSession("createTemplate", { workspace: mod.sys.workspace, cacheKey: mod.name });
|
|
766
|
+
const promptRst = requestTemplate({
|
|
767
|
+
sysName: mod.sys.name,
|
|
768
|
+
modelName: mod.name,
|
|
769
|
+
ModelName: Name,
|
|
770
|
+
constant,
|
|
771
|
+
boilerplate: template.content,
|
|
772
|
+
properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
|
|
773
|
+
exampleFiles: randomPicks(templateExampleFiles, Math.min(20, templateExampleFiles.length))
|
|
774
|
+
});
|
|
775
|
+
const content = await session.editTypescript(promptRst);
|
|
776
|
+
//! 파일을 {name}.View.tsx에 저장.
|
|
777
|
+
await mod.writeFile(`${Name}.Template.tsx`, content);
|
|
778
|
+
return createPassedPrimitiveReport({
|
|
779
|
+
command: "create-template",
|
|
780
|
+
changedFiles: [
|
|
781
|
+
sourceFile(mod.sys, `lib/${mod.name}/${Name}.Template.tsx`, "modify", "Template UI source was created.")
|
|
782
|
+
],
|
|
783
|
+
target: mod.sys.name
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
async createUnit(mod) {
|
|
787
|
+
const { component: unit } = await this.moduleRunner.createComponentTemplate(mod, "unit");
|
|
788
|
+
const paths = moduleSourcePaths(mod.name);
|
|
789
|
+
const Name = capitalize3(mod.name);
|
|
790
|
+
const unitExampleFiles = (await mod.sys.getUnitsSourceCode()).filter((f) => !f.filePath.includes(paths.unit));
|
|
791
|
+
const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
792
|
+
const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
793
|
+
const session = new AiSession("createUnit", { workspace: mod.sys.workspace, cacheKey: mod.name });
|
|
794
|
+
const promptRst = requestUnit({
|
|
795
|
+
sysName: mod.sys.name,
|
|
796
|
+
modelName: mod.name,
|
|
797
|
+
ModelName: Name,
|
|
798
|
+
constant,
|
|
799
|
+
properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
|
|
800
|
+
exampleFiles: randomPicks(unitExampleFiles, Math.min(10, unitExampleFiles.length)),
|
|
801
|
+
boilerplate: unit.content
|
|
802
|
+
});
|
|
803
|
+
const content = await session.editTypescript(promptRst);
|
|
804
|
+
//! 파일을 {name}.Unit.tsx에 저장.
|
|
805
|
+
await mod.writeFile(`${Name}.Unit.tsx`, content);
|
|
806
|
+
return createPassedPrimitiveReport({
|
|
807
|
+
command: "create-unit",
|
|
808
|
+
changedFiles: [sourceFile(mod.sys, `lib/${mod.name}/${Name}.Unit.tsx`, "modify", "Unit UI source was created.")],
|
|
809
|
+
target: mod.sys.name
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
async createView(mod) {
|
|
813
|
+
const { component: view } = await this.moduleRunner.createComponentTemplate(mod, "view");
|
|
814
|
+
const paths = moduleSourcePaths(mod.name);
|
|
815
|
+
const viewExampleFiles = (await mod.sys.getViewsSourceCode()).filter((f) => !f.filePath.includes(paths.view));
|
|
816
|
+
const Name = capitalize3(mod.name);
|
|
817
|
+
const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
818
|
+
const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
819
|
+
const session = new AiSession("createView", { workspace: mod.sys.workspace, cacheKey: mod.name });
|
|
820
|
+
const promptRst = requestView({
|
|
821
|
+
sysName: mod.sys.name,
|
|
822
|
+
modelName: mod.name,
|
|
823
|
+
ModelName: Name,
|
|
824
|
+
constant,
|
|
825
|
+
boilerplate: view.content,
|
|
826
|
+
properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
|
|
827
|
+
exampleFiles: randomPicks(viewExampleFiles, Math.min(20, viewExampleFiles.length))
|
|
828
|
+
});
|
|
829
|
+
const content = await session.editTypescript(promptRst);
|
|
830
|
+
//! 파일을 {name}.View.tsx에 저장.
|
|
831
|
+
await mod.writeFile(`${Name}.View.tsx`, content);
|
|
832
|
+
return createPassedPrimitiveReport({
|
|
833
|
+
command: "create-view",
|
|
834
|
+
changedFiles: [sourceFile(mod.sys, `lib/${mod.name}/${Name}.View.tsx`, "modify", "View UI source was created.")],
|
|
835
|
+
target: mod.sys.name
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
export { ModuleScript };
|