ai-spec-dev 0.29.1 → 0.30.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/README.md +4 -3
- package/RELEASE_LOG.md +24 -0
- package/core/error-feedback.ts +102 -2
- package/core/frontend-context-loader.ts +117 -20
- package/dist/cli/index.js +113 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +113 -9
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.js +57 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +57 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/purpose.md +29 -4
package/dist/index.mjs
CHANGED
|
@@ -4910,6 +4910,41 @@ async function loadDslForSpec(specFilePath) {
|
|
|
4910
4910
|
// core/frontend-context-loader.ts
|
|
4911
4911
|
import * as fs7 from "fs-extra";
|
|
4912
4912
|
import * as path5 from "path";
|
|
4913
|
+
function parseImportStatements(content) {
|
|
4914
|
+
const stripped = content.replace(/\/\*[\s\S]*?\*\//g, (m) => "\n".repeat(m.split("\n").length - 1));
|
|
4915
|
+
const collapsed = stripped.replace(/import\s*\{[^}]*\}/gs, (m) => m.replace(/\n\s*/g, " "));
|
|
4916
|
+
const results = [];
|
|
4917
|
+
for (const rawLine of collapsed.split("\n")) {
|
|
4918
|
+
const line = rawLine.trim();
|
|
4919
|
+
if (!line) continue;
|
|
4920
|
+
if (/^import\s+type\b/.test(line)) continue;
|
|
4921
|
+
if (!line.startsWith("import")) continue;
|
|
4922
|
+
const match = line.match(/^(import\s+([\s\S]+?)\s+from\s+['"]([^'"]+)['"])/);
|
|
4923
|
+
if (!match) continue;
|
|
4924
|
+
results.push({
|
|
4925
|
+
line: match[1],
|
|
4926
|
+
modulePath: match[3],
|
|
4927
|
+
specifiers: match[2]
|
|
4928
|
+
});
|
|
4929
|
+
}
|
|
4930
|
+
return results;
|
|
4931
|
+
}
|
|
4932
|
+
var HTTP_MODULE_PATTERNS = [
|
|
4933
|
+
// Project path aliases (@/, @@/, ~/, #/) — catches '@/utils/request', '~/lib/http', etc.
|
|
4934
|
+
/^(?:@{1,2}|~|#)[/\\]/,
|
|
4935
|
+
// Well-known HTTP libraries (exact name match)
|
|
4936
|
+
/^(?:axios|ky(?:-universal)?|undici|node-fetch|cross-fetch|got|superagent|alova|openapi-fetch)$/,
|
|
4937
|
+
// Relative imports whose path contains an HTTP-utility keyword
|
|
4938
|
+
/\.{1,2}\/[^'"]*(?:http|request|fetch|client|api)[^'"]*/
|
|
4939
|
+
];
|
|
4940
|
+
function findHttpClientImport(content) {
|
|
4941
|
+
for (const stmt of parseImportStatements(content)) {
|
|
4942
|
+
if (HTTP_MODULE_PATTERNS.some((p) => p.test(stmt.modulePath))) {
|
|
4943
|
+
return stmt.line;
|
|
4944
|
+
}
|
|
4945
|
+
}
|
|
4946
|
+
return void 0;
|
|
4947
|
+
}
|
|
4913
4948
|
var STATE_MANAGEMENT_LIBS = [
|
|
4914
4949
|
"zustand",
|
|
4915
4950
|
"redux",
|
|
@@ -5098,13 +5133,12 @@ ${preview}`);
|
|
|
5098
5133
|
} catch {
|
|
5099
5134
|
}
|
|
5100
5135
|
}
|
|
5101
|
-
const httpImportRegex = /^import(?!\s+type)\s+(?:[\w*]+|\{[^}]+\})\s+from\s+['"]((?:@{1,2}|~|#)[/\\][^'"]+|\.{1,2}\/[^'"]*(?:http|request|fetch|client|api)[^'"]*|axios|ky(?:-universal)?|undici|node-fetch|cross-fetch|got|superagent|alova|openapi-fetch)['"]/im;
|
|
5102
5136
|
for (const relPath of ctx.existingApiFiles.slice(0, 5)) {
|
|
5103
5137
|
try {
|
|
5104
5138
|
const content = await fs7.readFile(path5.join(projectRoot, relPath), "utf-8");
|
|
5105
|
-
const
|
|
5106
|
-
if (
|
|
5107
|
-
ctx.httpClientImport =
|
|
5139
|
+
const found = findHttpClientImport(content);
|
|
5140
|
+
if (found) {
|
|
5141
|
+
ctx.httpClientImport = found;
|
|
5108
5142
|
break;
|
|
5109
5143
|
}
|
|
5110
5144
|
} catch {
|
|
@@ -5234,13 +5268,28 @@ async function extractRouteModuleContext(projectRoot, ctx) {
|
|
|
5234
5268
|
moduleFiles.push(...files);
|
|
5235
5269
|
}
|
|
5236
5270
|
if (moduleFiles.length === 0) return;
|
|
5237
|
-
const
|
|
5271
|
+
const dynamicLayoutRegex = /const\s+Layout\s*=\s*(?:defineAsyncComponent\s*\(\s*)?(?:\(\s*\))?\s*(?:=>|function[^(]*\()\s*(?:[^)]*\))?\s*(?:=>)?\s*import\s*\(\s*['"]([^'"]+)['"]\s*\)/;
|
|
5238
5272
|
for (const relPath of moduleFiles) {
|
|
5239
5273
|
try {
|
|
5240
5274
|
const content = await fs7.readFile(path5.join(projectRoot, relPath), "utf-8");
|
|
5241
|
-
const
|
|
5242
|
-
|
|
5243
|
-
|
|
5275
|
+
const stmts = parseImportStatements(content);
|
|
5276
|
+
const staticLayout = stmts.find(
|
|
5277
|
+
(s) => /\bLayout\b/.test(s.specifiers) && /layout/i.test(s.modulePath)
|
|
5278
|
+
);
|
|
5279
|
+
if (staticLayout) {
|
|
5280
|
+
ctx.layoutImport = staticLayout.line;
|
|
5281
|
+
const preview = content.split("\n").slice(0, 100).join("\n");
|
|
5282
|
+
ctx.routeModuleExample = { path: relPath, content: preview };
|
|
5283
|
+
break;
|
|
5284
|
+
}
|
|
5285
|
+
const singleLine = content.replace(
|
|
5286
|
+
/const\s+Layout\s*=[\s\S]*?import\s*\([^)]+\)/gm,
|
|
5287
|
+
(m) => m.replace(/\n\s*/g, " ")
|
|
5288
|
+
);
|
|
5289
|
+
const dynMatch = singleLine.match(dynamicLayoutRegex);
|
|
5290
|
+
if (dynMatch) {
|
|
5291
|
+
const constMatch = content.match(/^const\s+Layout\s*=.+/m);
|
|
5292
|
+
ctx.layoutImport = constMatch ? constMatch[0].trim() : dynMatch[0].trim();
|
|
5244
5293
|
const preview = content.split("\n").slice(0, 100).join("\n");
|
|
5245
5294
|
ctx.routeModuleExample = { path: relPath, content: preview };
|
|
5246
5295
|
break;
|