@farming-labs/docs 0.2.60 → 0.2.62
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 +1 -1
- package/package.json +11 -1
- package/dist/agent-BFqyqEnC.mjs +0 -4547
- package/dist/agent-DlxriaTs.mjs +0 -624
- package/dist/agent-evals-kJs2Y9xR.mjs +0 -2144
- package/dist/agent-export-BgUaiW8f.mjs +0 -869
- package/dist/agent-scope-CCaIY1aK.mjs +0 -283
- package/dist/agents-Djh-HXih.mjs +0 -219
- package/dist/analytics-Bx44lg6d.mjs +0 -177
- package/dist/cli/index.d.mts +0 -15
- package/dist/cli/index.mjs +0 -452
- package/dist/client/react.d.mts +0 -45
- package/dist/client/react.mjs +0 -223
- package/dist/cloud-BH_sHX64.mjs +0 -1615
- package/dist/cloud-analytics-CSyFE6SS.mjs +0 -132
- package/dist/cloud-ask-ai-B2WnG4fF.d.mts +0 -23
- package/dist/cloud-ask-ai-hnJfj8-X.mjs +0 -382
- package/dist/code-blocks-qe0T8-xe.mjs +0 -871
- package/dist/codeblocks-Bq67u32v.mjs +0 -250
- package/dist/config-DASewQ0x.mjs +0 -363
- package/dist/dev-DgY5xGl9.mjs +0 -1333
- package/dist/docs-cloud-server.d.mts +0 -70
- package/dist/docs-cloud-server.mjs +0 -310
- package/dist/doctor-CO1VMcF_.mjs +0 -1906
- package/dist/downgrade-BZs86NVr.mjs +0 -184
- package/dist/errors-CVqZ3kOO.mjs +0 -20
- package/dist/golden-evaluations-BN9u2wxw.mjs +0 -1483
- package/dist/i18n-CAlj1ADU.mjs +0 -40
- package/dist/index.d.mts +0 -1099
- package/dist/index.mjs +0 -9
- package/dist/init-Bd_k06bR.mjs +0 -1233
- package/dist/mcp-B_yXL5G5.mjs +0 -137
- package/dist/mcp.d.mts +0 -287
- package/dist/mcp.mjs +0 -4135
- package/dist/metadata-BDuewuzq.mjs +0 -237
- package/dist/package-version-qik_4J6C.mjs +0 -128
- package/dist/reading-time-BkEft6SD.mjs +0 -741
- package/dist/review-NC-sOdXn.mjs +0 -665
- package/dist/robots-DskPvGPw.mjs +0 -178
- package/dist/robots-ltltiLJF.mjs +0 -197
- package/dist/search-C1JitPwi.d.mts +0 -397
- package/dist/search-D57JXQLj.mjs +0 -1758
- package/dist/search-o4Ud6OXv.mjs +0 -102
- package/dist/server.d.mts +0 -341
- package/dist/server.mjs +0 -11
- package/dist/sitemap-Cq-Yj_iA.mjs +0 -247
- package/dist/sitemap-server-C1ibVKOy.mjs +0 -1137
- package/dist/templates-DNw15P-x.mjs +0 -2373
- package/dist/types-XHABMh_f.d.mts +0 -3248
- package/dist/upgrade-BCJTCW3O.mjs +0 -56
- package/dist/utils-6UCLxv4B.mjs +0 -225
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import { a as validateCodeBlocks, r as resolveDocsCodeBlocksValidateConfig } from "./code-blocks-qe0T8-xe.mjs";
|
|
2
|
-
import { d as readStringProperty, h as resolveDocsContentDir, i as loadDocsConfigModule, m as resolveDocsConfigPath, p as readTopLevelStringProperty, s as readBooleanProperty, t as extractNestedObjectLiteral, u as readNumberProperty } from "./config-DASewQ0x.mjs";
|
|
3
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
-
import pc from "picocolors";
|
|
5
|
-
|
|
6
|
-
//#region src/cli/codeblocks.ts
|
|
7
|
-
function parseInlineFlag(arg) {
|
|
8
|
-
const [rawKey, value] = arg.slice(2).split("=", 2);
|
|
9
|
-
return {
|
|
10
|
-
key: rawKey.trim(),
|
|
11
|
-
value
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
function parseCodeBlocksValidateArgs(argv) {
|
|
15
|
-
const parsed = {};
|
|
16
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
17
|
-
const arg = argv[index];
|
|
18
|
-
if (arg === "--help" || arg === "-h") {
|
|
19
|
-
parsed.help = true;
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
if (arg === "--json") {
|
|
23
|
-
parsed.json = true;
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
if (arg === "--plan") {
|
|
27
|
-
parsed.plan = true;
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (arg === "--run") {
|
|
31
|
-
parsed.run = true;
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
if (arg.startsWith("--config=")) {
|
|
35
|
-
const value = parseInlineFlag(arg).value;
|
|
36
|
-
if (!value) throw new Error("Missing value for --config.");
|
|
37
|
-
parsed.configPath = value;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (arg === "--config") {
|
|
41
|
-
const value = argv[index + 1];
|
|
42
|
-
if (!value || value.startsWith("--")) throw new Error("Missing value for --config.");
|
|
43
|
-
parsed.configPath = value;
|
|
44
|
-
index += 1;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
throw new Error(`Unknown codeblocks validate flag: ${arg}.`);
|
|
48
|
-
}
|
|
49
|
-
return parsed;
|
|
50
|
-
}
|
|
51
|
-
function printCodeBlocksValidateHelp() {
|
|
52
|
-
console.log(`
|
|
53
|
-
${pc.bold("@farming-labs/docs codeblocks validate")}
|
|
54
|
-
|
|
55
|
-
${pc.dim("Usage:")}
|
|
56
|
-
pnpm exec docs codeblocks validate
|
|
57
|
-
pnpm exec docs codeblocks validate --plan
|
|
58
|
-
pnpm exec docs codeblocks validate --json
|
|
59
|
-
pnpm exec docs codeblocks validate --config docs.config.ts
|
|
60
|
-
|
|
61
|
-
${pc.dim("Options:")}
|
|
62
|
-
${pc.cyan("--plan")} Build the execution plan without running code
|
|
63
|
-
${pc.cyan("--run")} Force execution even when config mode is ${pc.dim("\"plan\"")}
|
|
64
|
-
${pc.cyan("--json")} Print machine-readable output
|
|
65
|
-
${pc.cyan("--config <path>")} Use a custom docs config path instead of ${pc.dim("docs.config.ts[x]")}
|
|
66
|
-
${pc.cyan("-h, --help")} Show this help message
|
|
67
|
-
`);
|
|
68
|
-
}
|
|
69
|
-
async function runCodeBlocksValidate(options = {}) {
|
|
70
|
-
const rootDir = process.cwd();
|
|
71
|
-
const loaded = await loadDocsConfigModule(rootDir, options.configPath, { silent: options.json });
|
|
72
|
-
const configPath = loaded?.path ?? resolveDocsConfigPath(rootDir, options.configPath);
|
|
73
|
-
const configContent = existsSync(configPath) ? readFileSync(configPath, "utf-8") : "";
|
|
74
|
-
const entry = loaded?.config.entry ?? readTopLevelStringProperty(configContent, "entry") ?? "docs";
|
|
75
|
-
const contentDir = loaded?.config.contentDir ?? resolveDocsContentDir(rootDir, configContent, entry);
|
|
76
|
-
const config = resolveDocsCodeBlocksValidateConfig(loaded?.config.codeBlocks?.validate ?? readStaticCodeBlocksValidateConfig(configContent));
|
|
77
|
-
if (!config.enabled) {
|
|
78
|
-
const disabledReport = {
|
|
79
|
-
summary: {
|
|
80
|
-
total: 0,
|
|
81
|
-
planned: 0,
|
|
82
|
-
pass: 0,
|
|
83
|
-
skip: 0,
|
|
84
|
-
fail: 0
|
|
85
|
-
},
|
|
86
|
-
config,
|
|
87
|
-
targets: [],
|
|
88
|
-
plans: [],
|
|
89
|
-
results: []
|
|
90
|
-
};
|
|
91
|
-
if (options.json) console.log(JSON.stringify(redactReport(disabledReport), null, 2));
|
|
92
|
-
else console.log(pc.yellow("codeBlocks.validate is disabled. Add `codeBlocks: { validate: true }` to docs.config.ts."));
|
|
93
|
-
return disabledReport;
|
|
94
|
-
}
|
|
95
|
-
const effectiveConfig = {
|
|
96
|
-
...config,
|
|
97
|
-
mode: options.run ? "report" : config.mode
|
|
98
|
-
};
|
|
99
|
-
const planOnly = options.plan === true || effectiveConfig.mode === "plan";
|
|
100
|
-
const report = await validateCodeBlocks({
|
|
101
|
-
rootDir,
|
|
102
|
-
contentDir,
|
|
103
|
-
config: effectiveConfig,
|
|
104
|
-
planOnly: options.plan
|
|
105
|
-
});
|
|
106
|
-
if (options.json) console.log(JSON.stringify(redactReport(report), null, 2));
|
|
107
|
-
else printCodeBlocksReport(report, planOnly);
|
|
108
|
-
if (!options.plan && report.summary.fail > 0) process.exitCode = 1;
|
|
109
|
-
return report;
|
|
110
|
-
}
|
|
111
|
-
function readStaticCodeBlocksValidateConfig(content) {
|
|
112
|
-
const block = extractNestedObjectLiteral(content, ["codeBlocks"]);
|
|
113
|
-
if (!block) return void 0;
|
|
114
|
-
if (/\bvalidate\s*:\s*true\b/.test(block)) return true;
|
|
115
|
-
if (/\bvalidate\s*:\s*false\b/.test(block)) return false;
|
|
116
|
-
const validateBlock = extractNestedObjectLiteral(content, ["codeBlocks", "validate"]);
|
|
117
|
-
if (validateBlock) {
|
|
118
|
-
const config = {};
|
|
119
|
-
const enabled = readBooleanProperty(validateBlock, "enabled");
|
|
120
|
-
const mode = readStringProperty(validateBlock, "mode");
|
|
121
|
-
const missingEnv = readStringProperty(validateBlock, "missingEnv");
|
|
122
|
-
const unsupportedLanguage = readStringProperty(validateBlock, "unsupportedLanguage");
|
|
123
|
-
const envFile = readStringArrayProperty(validateBlock, "envFile");
|
|
124
|
-
if (enabled !== void 0) config.enabled = enabled;
|
|
125
|
-
if (mode === "plan" || mode === "report") config.mode = mode;
|
|
126
|
-
if (missingEnv === "skip" || missingEnv === "warn" || missingEnv === "error") config.missingEnv = missingEnv;
|
|
127
|
-
if (unsupportedLanguage === "skip" || unsupportedLanguage === "warn" || unsupportedLanguage === "error") config.unsupportedLanguage = unsupportedLanguage;
|
|
128
|
-
if (envFile) config.envFile = envFile;
|
|
129
|
-
const planner = readStaticPlannerConfig(extractNestedObjectLiteral(content, [
|
|
130
|
-
"codeBlocks",
|
|
131
|
-
"validate",
|
|
132
|
-
"planner"
|
|
133
|
-
]));
|
|
134
|
-
if (planner) config.planner = planner;
|
|
135
|
-
const runner = readStaticRunnerConfig(extractNestedObjectLiteral(content, [
|
|
136
|
-
"codeBlocks",
|
|
137
|
-
"validate",
|
|
138
|
-
"runner"
|
|
139
|
-
]));
|
|
140
|
-
if (runner) config.runner = runner;
|
|
141
|
-
const env = readStringRecord(extractNestedObjectLiteral(content, [
|
|
142
|
-
"codeBlocks",
|
|
143
|
-
"validate",
|
|
144
|
-
"env"
|
|
145
|
-
]));
|
|
146
|
-
if (env && Object.keys(env).length > 0) config.env = env;
|
|
147
|
-
return config;
|
|
148
|
-
}
|
|
149
|
-
if (/\bvalidate\s*:\s*\{/.test(block)) return true;
|
|
150
|
-
}
|
|
151
|
-
function readStaticPlannerConfig(block) {
|
|
152
|
-
if (!block) return void 0;
|
|
153
|
-
const provider = readStringProperty(block, "provider");
|
|
154
|
-
const model = readStringProperty(block, "model");
|
|
155
|
-
const baseUrl = readStringProperty(block, "baseUrl");
|
|
156
|
-
const baseUrlEnv = readStringProperty(block, "baseUrlEnv");
|
|
157
|
-
const apiKeyEnv = readStringProperty(block, "apiKeyEnv");
|
|
158
|
-
if (provider !== "metadata" && provider !== "openai" && provider !== "openai-compatible" && provider !== "cloud") return;
|
|
159
|
-
return {
|
|
160
|
-
provider,
|
|
161
|
-
...model ? { model } : {},
|
|
162
|
-
...baseUrl ? { baseUrl } : {},
|
|
163
|
-
...baseUrlEnv ? { baseUrlEnv } : {},
|
|
164
|
-
...apiKeyEnv ? { apiKeyEnv } : {}
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
function readStaticRunnerConfig(block) {
|
|
168
|
-
if (!block) return void 0;
|
|
169
|
-
const provider = readStringProperty(block, "provider");
|
|
170
|
-
const tokenEnv = readStringProperty(block, "tokenEnv");
|
|
171
|
-
const projectIdEnv = readStringProperty(block, "projectIdEnv");
|
|
172
|
-
const teamIdEnv = readStringProperty(block, "teamIdEnv");
|
|
173
|
-
const projectJson = readStringProperty(block, "projectJson");
|
|
174
|
-
const runtime = readStringProperty(block, "runtime");
|
|
175
|
-
const apiUrlEnv = readStringProperty(block, "apiUrlEnv");
|
|
176
|
-
const targetEnv = readStringProperty(block, "targetEnv");
|
|
177
|
-
const timeoutMs = readNumberProperty(block, "timeoutMs");
|
|
178
|
-
if (provider !== "local" && provider !== "vercel-sandbox" && provider !== "e2b" && provider !== "daytona" && provider !== "cloud") return;
|
|
179
|
-
return {
|
|
180
|
-
provider,
|
|
181
|
-
...tokenEnv ? { tokenEnv } : {},
|
|
182
|
-
...projectIdEnv ? { projectIdEnv } : {},
|
|
183
|
-
...teamIdEnv ? { teamIdEnv } : {},
|
|
184
|
-
...projectJson ? { projectJson } : {},
|
|
185
|
-
...runtime === "node24" || runtime === "node22" || runtime === "python3.13" ? { runtime } : {},
|
|
186
|
-
...apiUrlEnv ? { apiUrlEnv } : {},
|
|
187
|
-
...targetEnv ? { targetEnv } : {},
|
|
188
|
-
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
function readStringArrayProperty(content, key) {
|
|
192
|
-
const single = readStringProperty(content, key);
|
|
193
|
-
if (single) return [single];
|
|
194
|
-
const match = content.match(new RegExp(`\\b${key}\\b\\s*:\\s*\\[([\\s\\S]*?)\\]`));
|
|
195
|
-
if (!match) return void 0;
|
|
196
|
-
const values = [...match[1].matchAll(/["']([^"']+)["']/g)].map((item) => item[1]);
|
|
197
|
-
return values.length > 0 ? values : void 0;
|
|
198
|
-
}
|
|
199
|
-
function readStringRecord(block) {
|
|
200
|
-
if (!block) return void 0;
|
|
201
|
-
const record = {};
|
|
202
|
-
for (const pattern of [/(?:^|,)\s*([A-Za-z_$][\w$]*)\s*:\s*["']([^"']+)["']/g, /(?:^|,)\s*["']([^"']+)["']\s*:\s*["']([^"']+)["']/g]) {
|
|
203
|
-
let match;
|
|
204
|
-
while (match = pattern.exec(block)) record[match[1]] = match[2];
|
|
205
|
-
}
|
|
206
|
-
return record;
|
|
207
|
-
}
|
|
208
|
-
function printCodeBlocksReport(report, planOnly) {
|
|
209
|
-
const label = planOnly ? "Code block plan" : "Code block validation";
|
|
210
|
-
console.log(pc.bold(label));
|
|
211
|
-
console.log([
|
|
212
|
-
...report.summary.planned > 0 ? [`${pc.cyan(`${report.summary.planned} planned`)}`] : [],
|
|
213
|
-
`${pc.green(`${report.summary.pass} pass`)}`,
|
|
214
|
-
`${pc.yellow(`${report.summary.skip} skip`)}`,
|
|
215
|
-
`${report.summary.fail > 0 ? pc.red(`${report.summary.fail} fail`) : pc.dim("0 fail")}`,
|
|
216
|
-
`${report.targets.length} code blocks`
|
|
217
|
-
].join(pc.dim(" • ")));
|
|
218
|
-
if (report.results.length === 0) return;
|
|
219
|
-
console.log();
|
|
220
|
-
for (const result of report.results) {
|
|
221
|
-
const status = result.status === "PLAN" ? pc.cyan(result.status) : result.status === "PASS" ? pc.green(result.status) : result.status === "FAIL" ? pc.red(result.status) : pc.yellow(result.status);
|
|
222
|
-
const location = `${result.target.relativePath}:${result.target.lineStart}`;
|
|
223
|
-
const detail = result.reason ?? result.plan.reason ?? result.plan.template;
|
|
224
|
-
console.log(`${status} ${pc.cyan(location)} ${pc.dim(detail)}`);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
function redactReport(report) {
|
|
228
|
-
return {
|
|
229
|
-
...report,
|
|
230
|
-
config: {
|
|
231
|
-
...report.config,
|
|
232
|
-
planner: {
|
|
233
|
-
...report.config.planner,
|
|
234
|
-
apiKey: report.config.planner.apiKey ? "[REDACTED]" : void 0
|
|
235
|
-
}
|
|
236
|
-
},
|
|
237
|
-
results: report.results.map((result) => ({
|
|
238
|
-
...result,
|
|
239
|
-
stdout: trimOutput(result.stdout),
|
|
240
|
-
stderr: trimOutput(result.stderr)
|
|
241
|
-
}))
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
function trimOutput(value) {
|
|
245
|
-
if (!value) return value;
|
|
246
|
-
return value.length > 4e3 ? `${value.slice(0, 4e3)}...` : value;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
//#endregion
|
|
250
|
-
export { parseCodeBlocksValidateArgs, printCodeBlocksValidateHelp, runCodeBlocksValidate };
|
package/dist/config-DASewQ0x.mjs
DELETED
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
3
|
-
|
|
4
|
-
//#region src/cli/config.ts
|
|
5
|
-
const FILE_EXTS = [
|
|
6
|
-
"tsx",
|
|
7
|
-
"ts",
|
|
8
|
-
"jsx",
|
|
9
|
-
"js"
|
|
10
|
-
];
|
|
11
|
-
function escapeRegExp(value) {
|
|
12
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
|
-
}
|
|
14
|
-
function createPropertyPattern(key, valuePattern) {
|
|
15
|
-
return new RegExp(`\\b${escapeRegExp(key)}\\b\\s*:\\s*${valuePattern}`);
|
|
16
|
-
}
|
|
17
|
-
function findBalancedBraceEnd(content, braceStart) {
|
|
18
|
-
let depth = 0;
|
|
19
|
-
let stringQuote = null;
|
|
20
|
-
let escaped = false;
|
|
21
|
-
let lineComment = false;
|
|
22
|
-
let blockComment = false;
|
|
23
|
-
for (let index = braceStart; index < content.length; index += 1) {
|
|
24
|
-
const char = content[index];
|
|
25
|
-
const next = content[index + 1];
|
|
26
|
-
if (lineComment) {
|
|
27
|
-
if (char === "\n") lineComment = false;
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (blockComment) {
|
|
31
|
-
if (char === "*" && next === "/") {
|
|
32
|
-
blockComment = false;
|
|
33
|
-
index += 1;
|
|
34
|
-
}
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
if (stringQuote) {
|
|
38
|
-
if (escaped) {
|
|
39
|
-
escaped = false;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (char === "\\") {
|
|
43
|
-
escaped = true;
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
if (char === stringQuote) stringQuote = null;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (char === "/" && next === "/") {
|
|
50
|
-
lineComment = true;
|
|
51
|
-
index += 1;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (char === "/" && next === "*") {
|
|
55
|
-
blockComment = true;
|
|
56
|
-
index += 1;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (char === "\"" || char === "'" || char === "`") {
|
|
60
|
-
stringQuote = char;
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (char === "{") {
|
|
64
|
-
depth += 1;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (char !== "}") continue;
|
|
68
|
-
depth -= 1;
|
|
69
|
-
if (depth === 0) return index;
|
|
70
|
-
}
|
|
71
|
-
return -1;
|
|
72
|
-
}
|
|
73
|
-
function resolveDocsConfigPath(rootDir, explicitPath) {
|
|
74
|
-
if (explicitPath) {
|
|
75
|
-
const resolvedPath = resolve(rootDir, explicitPath);
|
|
76
|
-
if (!existsSync(resolvedPath)) throw new Error(`Could not find docs config at ${explicitPath}.`);
|
|
77
|
-
return resolvedPath;
|
|
78
|
-
}
|
|
79
|
-
for (const ext of FILE_EXTS) {
|
|
80
|
-
const configPath = join(rootDir, `docs.config.${ext}`);
|
|
81
|
-
if (existsSync(configPath)) return configPath;
|
|
82
|
-
}
|
|
83
|
-
throw new Error("Could not find docs.config.ts or docs.config.tsx in the current project. Use --config to point at a custom path.");
|
|
84
|
-
}
|
|
85
|
-
function readStringProperty(content, key) {
|
|
86
|
-
return content.match(createPropertyPattern(key, `["']([^"']+)["']`))?.[1];
|
|
87
|
-
}
|
|
88
|
-
function readEnvReferenceProperty(content, key) {
|
|
89
|
-
const patterns = [
|
|
90
|
-
createPropertyPattern(key, `process\\.env\\.([A-Za-z_$][\\w$]*)`),
|
|
91
|
-
createPropertyPattern(key, `process\\.env\\[['"]([^"'\\]]+)['"]\\]`),
|
|
92
|
-
createPropertyPattern(key, `import\\.meta\\.env\\.([A-Za-z_$][\\w$]*)`),
|
|
93
|
-
createPropertyPattern(key, `import\\.meta\\.env\\[['"]([^"'\\]]+)['"]\\]`)
|
|
94
|
-
];
|
|
95
|
-
for (const pattern of patterns) {
|
|
96
|
-
const match = content.match(pattern);
|
|
97
|
-
if (match?.[1]) return match[1];
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
function readBooleanProperty(content, key) {
|
|
101
|
-
const match = content.match(createPropertyPattern(key, "(true|false)"));
|
|
102
|
-
return match ? match[1] === "true" : void 0;
|
|
103
|
-
}
|
|
104
|
-
function readNumberProperty(content, key) {
|
|
105
|
-
const match = content.match(createPropertyPattern(key, "(-?\\d+(?:\\.\\d+)?)"));
|
|
106
|
-
if (!match) return void 0;
|
|
107
|
-
const parsed = Number.parseFloat(match[1]);
|
|
108
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
109
|
-
}
|
|
110
|
-
function extractObjectLiteral(content, key) {
|
|
111
|
-
const keyIndex = content.search(new RegExp(`${key}\\s*:\\s*\\{`));
|
|
112
|
-
if (keyIndex === -1) return void 0;
|
|
113
|
-
const braceStart = content.indexOf("{", keyIndex);
|
|
114
|
-
if (braceStart === -1) return void 0;
|
|
115
|
-
const braceEnd = findBalancedBraceEnd(content, braceStart);
|
|
116
|
-
return braceEnd === -1 ? void 0 : content.slice(braceStart + 1, braceEnd);
|
|
117
|
-
}
|
|
118
|
-
function stripLeadingPropertyTrivia(content) {
|
|
119
|
-
let current = content;
|
|
120
|
-
while (true) {
|
|
121
|
-
const trimmed = current.replace(/^\s+/, "");
|
|
122
|
-
if (trimmed.startsWith("//")) {
|
|
123
|
-
const lineEnd = trimmed.indexOf("\n");
|
|
124
|
-
current = lineEnd === -1 ? "" : trimmed.slice(lineEnd + 1);
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
if (trimmed.startsWith("/*")) {
|
|
128
|
-
const blockEnd = trimmed.indexOf("*/");
|
|
129
|
-
current = blockEnd === -1 ? trimmed : trimmed.slice(blockEnd + 2);
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
return trimmed;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
function extractTopLevelObjectLiteral(content, key) {
|
|
136
|
-
const propertyPattern = new RegExp(`^${escapeRegExp(key)}\\s*:\\s*\\{`);
|
|
137
|
-
for (const property of splitTopLevelProperties(content)) {
|
|
138
|
-
const normalizedProperty = stripLeadingPropertyTrivia(property);
|
|
139
|
-
if (!propertyPattern.test(normalizedProperty)) continue;
|
|
140
|
-
const braceStart = normalizedProperty.indexOf("{");
|
|
141
|
-
if (braceStart === -1) return void 0;
|
|
142
|
-
const braceEnd = findBalancedBraceEnd(normalizedProperty, braceStart);
|
|
143
|
-
return braceEnd === -1 ? void 0 : normalizedProperty.slice(braceStart + 1, braceEnd);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
function extractTopLevelConfigObject(content) {
|
|
147
|
-
for (const marker of ["defineDocs(", "export default"]) {
|
|
148
|
-
const markerIndex = content.indexOf(marker);
|
|
149
|
-
if (markerIndex === -1) continue;
|
|
150
|
-
const braceStart = content.indexOf("{", markerIndex);
|
|
151
|
-
if (braceStart === -1) continue;
|
|
152
|
-
const braceEnd = findBalancedBraceEnd(content, braceStart);
|
|
153
|
-
if (braceEnd !== -1) return content.slice(braceStart + 1, braceEnd);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
function extractNestedObjectLiteral(content, keys) {
|
|
157
|
-
if (keys.length === 0) return void 0;
|
|
158
|
-
let current = extractTopLevelConfigObject(content) ?? content;
|
|
159
|
-
for (const key of keys) {
|
|
160
|
-
const next = extractTopLevelObjectLiteral(current, key);
|
|
161
|
-
if (!next) return void 0;
|
|
162
|
-
current = next;
|
|
163
|
-
}
|
|
164
|
-
return current;
|
|
165
|
-
}
|
|
166
|
-
function splitTopLevelProperties(content) {
|
|
167
|
-
const properties = [];
|
|
168
|
-
let start = 0;
|
|
169
|
-
let stringQuote = null;
|
|
170
|
-
let escaped = false;
|
|
171
|
-
let braceDepth = 0;
|
|
172
|
-
let bracketDepth = 0;
|
|
173
|
-
let parenDepth = 0;
|
|
174
|
-
for (let index = 0; index < content.length; index += 1) {
|
|
175
|
-
const char = content[index];
|
|
176
|
-
if (stringQuote) {
|
|
177
|
-
if (escaped) {
|
|
178
|
-
escaped = false;
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
if (char === "\\") {
|
|
182
|
-
escaped = true;
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
if (char === stringQuote) stringQuote = null;
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
if (char === "\"" || char === "'" || char === "`") {
|
|
189
|
-
stringQuote = char;
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (char === "{") {
|
|
193
|
-
braceDepth += 1;
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
if (char === "}") {
|
|
197
|
-
braceDepth = Math.max(0, braceDepth - 1);
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
if (char === "[") {
|
|
201
|
-
bracketDepth += 1;
|
|
202
|
-
continue;
|
|
203
|
-
}
|
|
204
|
-
if (char === "]") {
|
|
205
|
-
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
if (char === "(") {
|
|
209
|
-
parenDepth += 1;
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
if (char === ")") {
|
|
213
|
-
parenDepth = Math.max(0, parenDepth - 1);
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (char === "," && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
|
|
217
|
-
properties.push(content.slice(start, index));
|
|
218
|
-
start = index + 1;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const trailing = content.slice(start);
|
|
222
|
-
if (trailing.trim().length > 0) properties.push(trailing);
|
|
223
|
-
return properties;
|
|
224
|
-
}
|
|
225
|
-
function readTopLevelStringProperty(content, key) {
|
|
226
|
-
const source = extractTopLevelConfigObject(content) ?? content;
|
|
227
|
-
const propertyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*:\\s*["']([^"']+)["']`);
|
|
228
|
-
for (const property of splitTopLevelProperties(source)) {
|
|
229
|
-
const match = property.trim().match(propertyPattern);
|
|
230
|
-
if (match) return match[1];
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
function readTopLevelBooleanProperty(content, key) {
|
|
234
|
-
const source = extractTopLevelConfigObject(content) ?? content;
|
|
235
|
-
const propertyPattern = new RegExp(`^\\s*${escapeRegExp(key)}\\s*:\\s*(true|false)\\b`);
|
|
236
|
-
for (const property of splitTopLevelProperties(source)) {
|
|
237
|
-
const match = property.trim().match(propertyPattern);
|
|
238
|
-
if (match) return match[1] === "true";
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
function readNavTitle(content) {
|
|
242
|
-
const block = extractObjectLiteral(extractTopLevelConfigObject(content) ?? content, "nav");
|
|
243
|
-
if (!block) return void 0;
|
|
244
|
-
return readStringProperty(block, "title");
|
|
245
|
-
}
|
|
246
|
-
function resolveDocsContentDir(rootDir, content, entry) {
|
|
247
|
-
const configuredContentDir = readTopLevelStringProperty(content, "contentDir");
|
|
248
|
-
if (configuredContentDir) return configuredContentDir;
|
|
249
|
-
const candidates = [
|
|
250
|
-
entry,
|
|
251
|
-
join("app", entry),
|
|
252
|
-
join("src", "app", entry)
|
|
253
|
-
];
|
|
254
|
-
for (const candidate of candidates) if (existsSync(join(rootDir, candidate))) return candidate;
|
|
255
|
-
return entry;
|
|
256
|
-
}
|
|
257
|
-
function parseEnvValue(rawValue) {
|
|
258
|
-
const value = rawValue.trim();
|
|
259
|
-
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
|
|
260
|
-
return value;
|
|
261
|
-
}
|
|
262
|
-
function loadProjectEnv(rootDir) {
|
|
263
|
-
const env = {};
|
|
264
|
-
for (const filename of [".env", ".env.local"]) {
|
|
265
|
-
const fullPath = join(rootDir, filename);
|
|
266
|
-
if (!existsSync(fullPath)) continue;
|
|
267
|
-
const lines = readFileSync(fullPath, "utf-8").split(/\r?\n/);
|
|
268
|
-
for (const line of lines) {
|
|
269
|
-
const trimmed = line.trim();
|
|
270
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
271
|
-
const equalsIndex = trimmed.indexOf("=");
|
|
272
|
-
if (equalsIndex === -1) continue;
|
|
273
|
-
const key = trimmed.slice(0, equalsIndex).trim();
|
|
274
|
-
const rawValue = trimmed.slice(equalsIndex + 1);
|
|
275
|
-
if (!key) continue;
|
|
276
|
-
env[key] = parseEnvValue(rawValue);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
return env;
|
|
280
|
-
}
|
|
281
|
-
async function loadDocsConfigModule(rootDir, explicitPath, options = {}) {
|
|
282
|
-
const result = await loadDocsConfigModuleResult(rootDir, explicitPath, options);
|
|
283
|
-
return result.status === "evaluated" ? {
|
|
284
|
-
path: result.path,
|
|
285
|
-
config: result.config
|
|
286
|
-
} : null;
|
|
287
|
-
}
|
|
288
|
-
function resolveDocsConfigModuleExport(loaded) {
|
|
289
|
-
if (!loaded || typeof loaded !== "object" || Array.isArray(loaded)) return loaded;
|
|
290
|
-
const record = loaded;
|
|
291
|
-
return Object.prototype.hasOwnProperty.call(record, "default") ? record.default : loaded;
|
|
292
|
-
}
|
|
293
|
-
function isPlainDocsConfig(value) {
|
|
294
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
295
|
-
const prototype = Object.getPrototypeOf(value);
|
|
296
|
-
return prototype === Object.prototype || prototype === null;
|
|
297
|
-
}
|
|
298
|
-
function resolveDocsConfigTsconfigPath(rootDir, configPath) {
|
|
299
|
-
const projectRoot = resolve(rootDir);
|
|
300
|
-
let currentDir = dirname(configPath);
|
|
301
|
-
while (true) {
|
|
302
|
-
const tsconfigPath = join(currentDir, "tsconfig.json");
|
|
303
|
-
if (existsSync(tsconfigPath)) return tsconfigPath;
|
|
304
|
-
if (currentDir === projectRoot) return false;
|
|
305
|
-
const parentDir = dirname(currentDir);
|
|
306
|
-
if (parentDir === currentDir) return false;
|
|
307
|
-
const relativeParent = relative(projectRoot, parentDir);
|
|
308
|
-
if (relativeParent === ".." || relativeParent.startsWith(`..${sep}`)) return false;
|
|
309
|
-
currentDir = parentDir;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Load docs.config with an explicit confidence signal for diagnostics.
|
|
314
|
-
* Existing callers can keep using `loadDocsConfigModule`, which preserves the
|
|
315
|
-
* previous evaluated-config-or-null contract.
|
|
316
|
-
*/
|
|
317
|
-
async function loadDocsConfigModuleResult(rootDir, explicitPath, options = {}) {
|
|
318
|
-
const configPath = resolveDocsConfigPath(rootDir, explicitPath);
|
|
319
|
-
try {
|
|
320
|
-
const { createJiti } = await import("jiti");
|
|
321
|
-
const config = resolveDocsConfigModuleExport(await createJiti(import.meta.url, {
|
|
322
|
-
moduleCache: false,
|
|
323
|
-
fsCache: false,
|
|
324
|
-
interopDefault: false,
|
|
325
|
-
jsx: { runtime: "automatic" },
|
|
326
|
-
tsconfigPaths: resolveDocsConfigTsconfigPath(rootDir, configPath)
|
|
327
|
-
}).import(configPath));
|
|
328
|
-
if (!isPlainDocsConfig(config)) return {
|
|
329
|
-
status: "static-fallback",
|
|
330
|
-
path: configPath,
|
|
331
|
-
error: "The config module did not export a plain object."
|
|
332
|
-
};
|
|
333
|
-
return {
|
|
334
|
-
status: "evaluated",
|
|
335
|
-
path: configPath,
|
|
336
|
-
config
|
|
337
|
-
};
|
|
338
|
-
} catch (error) {
|
|
339
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
340
|
-
if (!options.silent && process.env.NODE_ENV !== "test") console.warn(`[docs] Could not evaluate ${configPath} as a module; falling back to static parsing. ${message}`);
|
|
341
|
-
return {
|
|
342
|
-
status: "static-fallback",
|
|
343
|
-
path: configPath,
|
|
344
|
-
error: message
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
/** Evaluate docs.config after loading project-local env files, then restore process.env. */
|
|
349
|
-
async function loadDocsConfigModuleResultWithProjectEnv(rootDir, explicitPath, options = {}) {
|
|
350
|
-
const env = loadProjectEnv(rootDir);
|
|
351
|
-
const injectedKeys = Object.entries(env).filter(([key]) => process.env[key] === void 0).map(([key, value]) => {
|
|
352
|
-
process.env[key] = value;
|
|
353
|
-
return key;
|
|
354
|
-
});
|
|
355
|
-
try {
|
|
356
|
-
return await loadDocsConfigModuleResult(rootDir, explicitPath, options);
|
|
357
|
-
} finally {
|
|
358
|
-
for (const key of injectedKeys) delete process.env[key];
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
//#endregion
|
|
363
|
-
export { loadDocsConfigModuleResultWithProjectEnv as a, readEnvReferenceProperty as c, readStringProperty as d, readTopLevelBooleanProperty as f, resolveDocsContentDir as h, loadDocsConfigModule as i, readNavTitle as l, resolveDocsConfigPath as m, extractObjectLiteral as n, loadProjectEnv as o, readTopLevelStringProperty as p, extractTopLevelConfigObject as r, readBooleanProperty as s, extractNestedObjectLiteral as t, readNumberProperty as u };
|