@codedrifters/configulator 0.0.272 → 0.0.274
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/lib/index.d.mts +279 -1
- package/lib/index.d.ts +280 -2
- package/lib/index.js +818 -42
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +812 -44
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -223,6 +223,7 @@ __export(index_exports, {
|
|
|
223
223
|
DEFAULT_PROGRESS_FILES_STATE_DIR: () => DEFAULT_PROGRESS_FILES_STATE_DIR,
|
|
224
224
|
DEFAULT_REQUIRE_LINKED_ISSUE: () => DEFAULT_REQUIRE_LINKED_ISSUE,
|
|
225
225
|
DEFAULT_REQUIRE_PRODUCT_CONTEXT: () => DEFAULT_REQUIRE_PRODUCT_CONTEXT,
|
|
226
|
+
DEFAULT_SAMPLE_COMPILER_OPTIONS: () => DEFAULT_SAMPLE_COMPILER_OPTIONS,
|
|
226
227
|
DEFAULT_SCHEDULED_TASKS_ROOT: () => DEFAULT_SCHEDULED_TASKS_ROOT,
|
|
227
228
|
DEFAULT_SCHEDULED_TASK_ENTRIES: () => DEFAULT_SCHEDULED_TASK_ENTRIES,
|
|
228
229
|
DEFAULT_SHARED_EDITING_CONFLICT_STRATEGY: () => DEFAULT_SHARED_EDITING_CONFLICT_STRATEGY,
|
|
@@ -268,6 +269,7 @@ __export(index_exports, {
|
|
|
268
269
|
SCOPE_CLASS_VALUES: () => SCOPE_CLASS_VALUES,
|
|
269
270
|
SHARED_EDITING_CONFLICT_STRATEGY_VALUES: () => SHARED_EDITING_CONFLICT_STRATEGY_VALUES,
|
|
270
271
|
STARLIGHT_ROLE: () => STARLIGHT_ROLE,
|
|
272
|
+
SampleLang: () => SampleLang,
|
|
271
273
|
StarlightProject: () => StarlightProject,
|
|
272
274
|
TestRunner: () => TestRunner,
|
|
273
275
|
TsDocCoverageKind: () => TsDocCoverageKind,
|
|
@@ -310,13 +312,17 @@ __export(index_exports, {
|
|
|
310
312
|
buildStandardsResearchBundle: () => buildStandardsResearchBundle,
|
|
311
313
|
buildUnblockDependentsProcedure: () => buildUnblockDependentsProcedure,
|
|
312
314
|
businessModelsBundle: () => businessModelsBundle,
|
|
315
|
+
checkDocSamplesProcedure: () => checkDocSamplesProcedure,
|
|
316
|
+
checkLinksProcedure: () => checkLinksProcedure,
|
|
313
317
|
classifyIssueScope: () => classifyIssueScope,
|
|
314
318
|
classifyRun: () => classifyRun,
|
|
315
319
|
companyProfileBundle: () => companyProfileBundle,
|
|
320
|
+
compileFencedSamples: () => compileFencedSamples,
|
|
316
321
|
customerProfileBundle: () => customerProfileBundle,
|
|
317
322
|
docsSyncBundle: () => docsSyncBundle,
|
|
318
323
|
extractApiProcedure: () => extractApiProcedure,
|
|
319
324
|
extractDocReferences: () => extractDocReferences,
|
|
325
|
+
extractFencedSamples: () => extractFencedSamples,
|
|
320
326
|
formatLayoutViolation: () => formatLayoutViolation,
|
|
321
327
|
formatStarlightSingletonViolation: () => formatStarlightSingletonViolation,
|
|
322
328
|
getLatestEligibleVersion: () => getLatestEligibleVersion,
|
|
@@ -333,6 +339,8 @@ __export(index_exports, {
|
|
|
333
339
|
regulatoryResearchBundle: () => regulatoryResearchBundle,
|
|
334
340
|
renderAgentTierCaseStatement: () => renderAgentTierCaseStatement,
|
|
335
341
|
renderAgentTierSection: () => renderAgentTierSection,
|
|
342
|
+
renderCheckDocSamplesProcedure: () => renderCheckDocSamplesProcedure,
|
|
343
|
+
renderCheckLinksProcedure: () => renderCheckLinksProcedure,
|
|
336
344
|
renderCustomDocSectionBlock: () => renderCustomDocSectionBlock,
|
|
337
345
|
renderCustomDocSections: () => renderCustomDocSections,
|
|
338
346
|
renderExtractApiProcedure: () => renderExtractApiProcedure,
|
|
@@ -8978,6 +8986,415 @@ var extractApiProcedure = {
|
|
|
8978
8986
|
description: "Run @microsoft/api-extractor for a single package and write the .api.md rollup to the scratch folder declared by the package's api-extractor.json. Rollups are regenerate-on-scan per docs-sync epic #448 resolved decision #3 \u2014 never committed to git.",
|
|
8979
8987
|
content: renderExtractApiProcedure()
|
|
8980
8988
|
};
|
|
8989
|
+
function renderCheckLinksProcedure() {
|
|
8990
|
+
return [
|
|
8991
|
+
"#!/usr/bin/env bash",
|
|
8992
|
+
"# check-links.sh \u2014 Link integrity checker for the Starlight docs site.",
|
|
8993
|
+
"#",
|
|
8994
|
+
"# Usage:",
|
|
8995
|
+
"# .claude/procedures/check-links.sh <mode> <docs-root> [<site-dir>]",
|
|
8996
|
+
"#",
|
|
8997
|
+
"# Where:",
|
|
8998
|
+
"# <mode> \u2014 one of `internal`, `external`, or `all`.",
|
|
8999
|
+
"# <docs-root> \u2014 path to the markdown content tree",
|
|
9000
|
+
"# (e.g. `docs/src/content/docs`). Walked recursively",
|
|
9001
|
+
"# for `*.md` / `*.mdx` files.",
|
|
9002
|
+
"# <site-dir> \u2014 (optional, internal/all only) path to the Starlight",
|
|
9003
|
+
"# project root that contains `astro.config.{ts,mjs,js}`.",
|
|
9004
|
+
"# Defaults to two levels up from `<docs-root>` so the",
|
|
9005
|
+
"# standard Starlight layout (`<site>/src/content/docs`)",
|
|
9006
|
+
"# resolves automatically.",
|
|
9007
|
+
"#",
|
|
9008
|
+
"# Wraps two external tools:",
|
|
9009
|
+
"# - `astro check` \u2014 internal links and content-collection refs",
|
|
9010
|
+
"# inside the Starlight site.",
|
|
9011
|
+
"# - `lychee` \u2014 external `https://\u2026` URLs across the docs tree",
|
|
9012
|
+
"# (https://github.com/lycheeverse/lychee).",
|
|
9013
|
+
"#",
|
|
9014
|
+
"# Output (stdout, single JSON array):",
|
|
9015
|
+
"#",
|
|
9016
|
+
"# [",
|
|
9017
|
+
"# {",
|
|
9018
|
+
'# "url": "<href as written in the markdown source>",',
|
|
9019
|
+
'# "docPath": "<path relative to <docs-root>>",',
|
|
9020
|
+
'# "line": <1-indexed line number>,',
|
|
9021
|
+
'# "kind": "internal" | "external",',
|
|
9022
|
+
'# "reason": "<short tool-supplied diagnostic>"',
|
|
9023
|
+
"# },",
|
|
9024
|
+
"# ...",
|
|
9025
|
+
"# ]",
|
|
9026
|
+
"#",
|
|
9027
|
+
"# An empty array (`[]`) is emitted when no broken links are found.",
|
|
9028
|
+
"# Detection is **data**, not failure: this helper exits 0 when a",
|
|
9029
|
+
"# tool ran successfully and reported zero or more broken links.",
|
|
9030
|
+
"# The downstream docs-sync scan phase (#519/#520) is responsible",
|
|
9031
|
+
"# for deciding which findings are advisory and which block.",
|
|
9032
|
+
"#",
|
|
9033
|
+
"# Exit codes:",
|
|
9034
|
+
"# 0 \u2014 every requested tool ran successfully; findings (zero or",
|
|
9035
|
+
"# more) are on stdout as a JSON array.",
|
|
9036
|
+
"# 1 \u2014 usage error (missing args, unreadable docs root).",
|
|
9037
|
+
"# 2 \u2014 a required external tool is not installed or not on PATH",
|
|
9038
|
+
"# (`astro` / `lychee` / `node` / `jq`).",
|
|
9039
|
+
"# 3 \u2014 a tool ran but exited non-zero for a reason other than",
|
|
9040
|
+
"# broken-link detection (config error, IO error, network",
|
|
9041
|
+
"# failure for lychee, etc.). Stderr carries the diagnostic.",
|
|
9042
|
+
"#",
|
|
9043
|
+
"# The script never edits files. Diagnostics flow to stderr; the",
|
|
9044
|
+
"# only thing on stdout is the normalized JSON array.",
|
|
9045
|
+
"",
|
|
9046
|
+
"set -uo pipefail",
|
|
9047
|
+
"",
|
|
9048
|
+
"err() {",
|
|
9049
|
+
' printf "check-links.sh: %s\\n" "$*" >&2',
|
|
9050
|
+
"}",
|
|
9051
|
+
"",
|
|
9052
|
+
'if [ "$#" -lt 2 ]; then',
|
|
9053
|
+
' err "usage: check-links.sh <internal|external|all> <docs-root> [<site-dir>]"',
|
|
9054
|
+
" exit 1",
|
|
9055
|
+
"fi",
|
|
9056
|
+
"",
|
|
9057
|
+
'mode="$1"',
|
|
9058
|
+
'docs_root="$2"',
|
|
9059
|
+
'site_dir="${3:-}"',
|
|
9060
|
+
"",
|
|
9061
|
+
'case "$mode" in',
|
|
9062
|
+
" internal | external | all) ;;",
|
|
9063
|
+
" *)",
|
|
9064
|
+
` err "unknown mode '$mode' \u2014 expected 'internal', 'external', or 'all'"`,
|
|
9065
|
+
" exit 1",
|
|
9066
|
+
" ;;",
|
|
9067
|
+
"esac",
|
|
9068
|
+
"",
|
|
9069
|
+
'if [ ! -d "$docs_root" ]; then',
|
|
9070
|
+
' err "docs root not found: $docs_root"',
|
|
9071
|
+
" exit 1",
|
|
9072
|
+
"fi",
|
|
9073
|
+
"",
|
|
9074
|
+
'if [ -z "$site_dir" ]; then',
|
|
9075
|
+
" # Standard Starlight layout: <site>/src/content/docs. Walk two",
|
|
9076
|
+
" # parents up from the content root to recover the site dir.",
|
|
9077
|
+
' site_dir="$(cd "$docs_root/../../.." && pwd)"',
|
|
9078
|
+
"fi",
|
|
9079
|
+
"",
|
|
9080
|
+
"# `jq` is required to merge the per-tool JSON output streams. Fail",
|
|
9081
|
+
"# fast with the dedicated tool-missing exit code if it is absent.",
|
|
9082
|
+
"if ! command -v jq >/dev/null 2>&1; then",
|
|
9083
|
+
' err "jq is required but not on PATH"',
|
|
9084
|
+
" exit 2",
|
|
9085
|
+
"fi",
|
|
9086
|
+
"",
|
|
9087
|
+
'tmp_dir="$(mktemp -d -t check-links-XXXXXX)"',
|
|
9088
|
+
"# shellcheck disable=SC2064",
|
|
9089
|
+
`trap "rm -rf '$tmp_dir'" EXIT`,
|
|
9090
|
+
"",
|
|
9091
|
+
'internal_json="$tmp_dir/internal.json"',
|
|
9092
|
+
'external_json="$tmp_dir/external.json"',
|
|
9093
|
+
'printf "[]" > "$internal_json"',
|
|
9094
|
+
'printf "[]" > "$external_json"',
|
|
9095
|
+
"",
|
|
9096
|
+
"# ---------------------------------------------------------------",
|
|
9097
|
+
"# Internal-link check",
|
|
9098
|
+
"# ---------------------------------------------------------------",
|
|
9099
|
+
"# `astro check` runs inside the Starlight project root and walks",
|
|
9100
|
+
"# content collections to validate internal references. It exits",
|
|
9101
|
+
"# non-zero when references are broken \u2014 that is a *finding* for",
|
|
9102
|
+
"# this helper, not a tool failure, so we capture stderr and parse",
|
|
9103
|
+
"# it into the normalized record shape rather than propagating the",
|
|
9104
|
+
"# non-zero exit.",
|
|
9105
|
+
'if [ "$mode" = "internal" ] || [ "$mode" = "all" ]; then',
|
|
9106
|
+
" if ! command -v node >/dev/null 2>&1; then",
|
|
9107
|
+
' err "node is required but not on PATH"',
|
|
9108
|
+
" exit 2",
|
|
9109
|
+
" fi",
|
|
9110
|
+
"",
|
|
9111
|
+
' if [ ! -d "$site_dir" ]; then',
|
|
9112
|
+
' err "site dir not found: $site_dir"',
|
|
9113
|
+
" exit 1",
|
|
9114
|
+
" fi",
|
|
9115
|
+
"",
|
|
9116
|
+
" # Locate the astro binary in the site's local node_modules first",
|
|
9117
|
+
" # so the version matches what `pnpm install` resolved for the",
|
|
9118
|
+
" # Starlight site. Fall back to a workspace-aware `pnpm exec`.",
|
|
9119
|
+
' if [ -x "$site_dir/node_modules/.bin/astro" ]; then',
|
|
9120
|
+
' astro_bin=("$site_dir/node_modules/.bin/astro")',
|
|
9121
|
+
" elif command -v pnpm >/dev/null 2>&1; then",
|
|
9122
|
+
' astro_bin=(pnpm --dir "$site_dir" exec astro)',
|
|
9123
|
+
" else",
|
|
9124
|
+
' err "astro binary not found in $site_dir/node_modules/.bin and pnpm is unavailable"',
|
|
9125
|
+
" exit 2",
|
|
9126
|
+
" fi",
|
|
9127
|
+
"",
|
|
9128
|
+
' astro_log="$tmp_dir/astro.log"',
|
|
9129
|
+
" set +e",
|
|
9130
|
+
' ( cd "$site_dir" && "${astro_bin[@]}" check ) > "$astro_log" 2>&1',
|
|
9131
|
+
" astro_status=$?",
|
|
9132
|
+
" set -e",
|
|
9133
|
+
"",
|
|
9134
|
+
" # `astro check` exits 1 on findings and >=2 on tool failure.",
|
|
9135
|
+
" # Translate to the normalized record stream regardless of exit.",
|
|
9136
|
+
' if [ "$astro_status" -ge 2 ]; then',
|
|
9137
|
+
' err "astro check exited with status $astro_status (tool failure)"',
|
|
9138
|
+
' cat "$astro_log" >&2 || true',
|
|
9139
|
+
" exit 3",
|
|
9140
|
+
" fi",
|
|
9141
|
+
"",
|
|
9142
|
+
" # Parse the line-oriented log into JSON records. The `astro",
|
|
9143
|
+
" # check` output for a broken reference looks like:",
|
|
9144
|
+
" #",
|
|
9145
|
+
" # src/content/docs/foo.md:42:8 - error <code>: <reason>",
|
|
9146
|
+
" #",
|
|
9147
|
+
" # The helper extracts `<docPath>`, `<line>`, and `<reason>` and",
|
|
9148
|
+
" # synthesises a `url` field by quoting the offending excerpt",
|
|
9149
|
+
" # when astro reports one (it does not always); when no excerpt",
|
|
9150
|
+
" # is available we fall back to an empty string so the schema",
|
|
9151
|
+
" # stays uniform.",
|
|
9152
|
+
` awk -v docs_root="$docs_root" -v site_dir="$site_dir" '`,
|
|
9153
|
+
" BEGIN {",
|
|
9154
|
+
' printf "[";',
|
|
9155
|
+
" first = 1;",
|
|
9156
|
+
" }",
|
|
9157
|
+
' # Match "<path>:<line>:<col> - <severity> <code>: <message>"',
|
|
9158
|
+
" /^.+:[0-9]+:[0-9]+ - / {",
|
|
9159
|
+
' split($0, head, " - ");',
|
|
9160
|
+
' n = split(head[1], loc, ":");',
|
|
9161
|
+
" file = loc[1];",
|
|
9162
|
+
' for (i = 2; i < n - 1; i++) { file = file ":" loc[i]; }',
|
|
9163
|
+
" line = loc[n - 1];",
|
|
9164
|
+
' message = "";',
|
|
9165
|
+
" for (i = 2; i <= length(head); i++) {",
|
|
9166
|
+
' if (i > 2) message = message " - ";',
|
|
9167
|
+
" message = message head[i];",
|
|
9168
|
+
" }",
|
|
9169
|
+
" # Resolve the file relative to docs_root for stability.",
|
|
9170
|
+
" abs = file;",
|
|
9171
|
+
' if (substr(abs, 1, 1) != "/") {',
|
|
9172
|
+
' abs = site_dir "/" abs;',
|
|
9173
|
+
" }",
|
|
9174
|
+
' sub(docs_root "/", "", abs);',
|
|
9175
|
+
' gsub(/"/, "\\\\\\"", message);',
|
|
9176
|
+
' gsub(/"/, "\\\\\\"", abs);',
|
|
9177
|
+
' if (!first) printf ",";',
|
|
9178
|
+
" first = 0;",
|
|
9179
|
+
' printf "{\\"url\\":\\"\\",\\"docPath\\":\\"%s\\",\\"line\\":%s,\\"kind\\":\\"internal\\",\\"reason\\":\\"%s\\"}", abs, line, message;',
|
|
9180
|
+
" }",
|
|
9181
|
+
" END {",
|
|
9182
|
+
' printf "]";',
|
|
9183
|
+
" }",
|
|
9184
|
+
` ' "$astro_log" > "$internal_json"`,
|
|
9185
|
+
"",
|
|
9186
|
+
" # Validate the JSON we synthesised. Catches awk escaping bugs",
|
|
9187
|
+
" # before we hand the payload to the merge step.",
|
|
9188
|
+
' if ! jq -e . "$internal_json" >/dev/null 2>&1; then',
|
|
9189
|
+
' err "failed to normalize astro check output to JSON"',
|
|
9190
|
+
' cat "$internal_json" >&2 || true',
|
|
9191
|
+
" exit 3",
|
|
9192
|
+
" fi",
|
|
9193
|
+
"fi",
|
|
9194
|
+
"",
|
|
9195
|
+
"# ---------------------------------------------------------------",
|
|
9196
|
+
"# External-link check",
|
|
9197
|
+
"# ---------------------------------------------------------------",
|
|
9198
|
+
"# `lychee` walks the markdown tree and reports unreachable URLs.",
|
|
9199
|
+
"# Its `--format json` output is structured per-link; we project",
|
|
9200
|
+
"# only the failed entries into the normalized record shape.",
|
|
9201
|
+
'if [ "$mode" = "external" ] || [ "$mode" = "all" ]; then',
|
|
9202
|
+
" if ! command -v lychee >/dev/null 2>&1; then",
|
|
9203
|
+
' err "lychee is required but not on PATH (https://github.com/lycheeverse/lychee)"',
|
|
9204
|
+
" exit 2",
|
|
9205
|
+
" fi",
|
|
9206
|
+
"",
|
|
9207
|
+
' lychee_json="$tmp_dir/lychee.json"',
|
|
9208
|
+
" set +e",
|
|
9209
|
+
" # `--no-progress` avoids interactive carriage-returns mangling",
|
|
9210
|
+
" # the JSON. `--format json` emits a single object whose `fail_map`",
|
|
9211
|
+
" # field lists the broken URLs grouped by source file.",
|
|
9212
|
+
" # `--exclude-mail` skips mailto: links (out of scope for this",
|
|
9213
|
+
" # helper).",
|
|
9214
|
+
' lychee --no-progress --format json --exclude-mail "$docs_root" > "$lychee_json" 2>"$tmp_dir/lychee.err"',
|
|
9215
|
+
" lychee_status=$?",
|
|
9216
|
+
" set -e",
|
|
9217
|
+
"",
|
|
9218
|
+
" # lychee exits 0 when every link resolved, 2 when one or more",
|
|
9219
|
+
" # links failed. Anything else is a tool failure (config, IO).",
|
|
9220
|
+
' if [ "$lychee_status" -ne 0 ] && [ "$lychee_status" -ne 2 ]; then',
|
|
9221
|
+
' err "lychee exited with status $lychee_status (tool failure)"',
|
|
9222
|
+
' cat "$tmp_dir/lychee.err" >&2 || true',
|
|
9223
|
+
" exit 3",
|
|
9224
|
+
" fi",
|
|
9225
|
+
"",
|
|
9226
|
+
" # Project lychee's `fail_map` into the normalized record shape.",
|
|
9227
|
+
' # The `fail_map` is `{ "<source-path>": [ { url, status: { details? } }, ... ] }`.',
|
|
9228
|
+
" # lychee does not report a line number for every match, so we",
|
|
9229
|
+
" # default to 0 when it is missing (downstream consumers treat",
|
|
9230
|
+
' # 0 as "unknown line").',
|
|
9231
|
+
` jq --arg root "$docs_root" '`,
|
|
9232
|
+
" def trim_root($p; $r):",
|
|
9233
|
+
' if ($p | startswith($r + "/")) then ($p | ltrimstr($r + "/"))',
|
|
9234
|
+
' elif ($p == $r) then ""',
|
|
9235
|
+
" else $p end;",
|
|
9236
|
+
" [",
|
|
9237
|
+
" ( .fail_map // {} ) as $map",
|
|
9238
|
+
" | $map | to_entries[]",
|
|
9239
|
+
" | .key as $src",
|
|
9240
|
+
" | .value[]",
|
|
9241
|
+
" | {",
|
|
9242
|
+
' url: (.url // ""),',
|
|
9243
|
+
" docPath: trim_root($src; $root),",
|
|
9244
|
+
" line: ((.line // 0) | tonumber? // 0),",
|
|
9245
|
+
' kind: "external",',
|
|
9246
|
+
' reason: ( (.status.details // .status.text // .status.code // "broken") | tostring )',
|
|
9247
|
+
" }",
|
|
9248
|
+
" ]",
|
|
9249
|
+
` ' "$lychee_json" > "$external_json" 2>"$tmp_dir/lychee.parse.err"`,
|
|
9250
|
+
"",
|
|
9251
|
+
' if ! jq -e . "$external_json" >/dev/null 2>&1; then',
|
|
9252
|
+
' err "failed to parse lychee JSON output"',
|
|
9253
|
+
' cat "$tmp_dir/lychee.parse.err" >&2 || true',
|
|
9254
|
+
' cat "$lychee_json" >&2 || true',
|
|
9255
|
+
" exit 3",
|
|
9256
|
+
" fi",
|
|
9257
|
+
"fi",
|
|
9258
|
+
"",
|
|
9259
|
+
"# ---------------------------------------------------------------",
|
|
9260
|
+
"# Merge & emit",
|
|
9261
|
+
"# ---------------------------------------------------------------",
|
|
9262
|
+
"# Sort records deterministically (kind, docPath, line, url) so",
|
|
9263
|
+
"# downstream consumers can compare reports across runs without",
|
|
9264
|
+
"# fighting platform-specific ordering noise.",
|
|
9265
|
+
"jq -s '",
|
|
9266
|
+
" add",
|
|
9267
|
+
" | sort_by(.kind, .docPath, .line, .url)",
|
|
9268
|
+
`' "$internal_json" "$external_json"`,
|
|
9269
|
+
"",
|
|
9270
|
+
"exit 0"
|
|
9271
|
+
].join("\n");
|
|
9272
|
+
}
|
|
9273
|
+
var checkLinksProcedure = {
|
|
9274
|
+
name: "check-links.sh",
|
|
9275
|
+
description: "Link integrity checker that wraps `astro check` (internal links) and `lychee` (external URLs) and normalizes their output into a single JSON-array stream of { url, docPath, line, kind, reason } records. Detection is data: the helper exits 0 when a tool ran successfully regardless of how many broken links it reported. Non-zero exits are reserved for tool-level failures.",
|
|
9276
|
+
content: renderCheckLinksProcedure()
|
|
9277
|
+
};
|
|
9278
|
+
function renderCheckDocSamplesProcedure() {
|
|
9279
|
+
const nodeScript = [
|
|
9280
|
+
"(async () => {",
|
|
9281
|
+
" const { compileFencedSamples } = await import('@codedrifters/configulator');",
|
|
9282
|
+
" const docsRoot = process.argv[1];",
|
|
9283
|
+
" const failures = compileFencedSamples({ docsRoot });",
|
|
9284
|
+
" process.stdout.write(JSON.stringify(failures));",
|
|
9285
|
+
"})().catch((err) => {",
|
|
9286
|
+
" console.error('check-doc-samples.sh: ' + (err && err.stack ? err.stack : err));",
|
|
9287
|
+
" process.exit(3);",
|
|
9288
|
+
"});"
|
|
9289
|
+
].join("\n");
|
|
9290
|
+
return [
|
|
9291
|
+
"#!/usr/bin/env bash",
|
|
9292
|
+
"# check-doc-samples.sh \u2014 Compile fenced TS/TSX samples in the Starlight docs.",
|
|
9293
|
+
"#",
|
|
9294
|
+
"# Usage:",
|
|
9295
|
+
"# .claude/procedures/check-doc-samples.sh <docs-root>",
|
|
9296
|
+
"#",
|
|
9297
|
+
"# Where:",
|
|
9298
|
+
"# <docs-root> \u2014 path to the markdown content tree",
|
|
9299
|
+
"# (e.g. `docs/src/content/docs`). Walked recursively",
|
|
9300
|
+
"# for `*.md` files.",
|
|
9301
|
+
"#",
|
|
9302
|
+
"# Wraps the `compileFencedSamples` API exported from",
|
|
9303
|
+
"# `@codedrifters/configulator`. For every fenced ` ```ts `,",
|
|
9304
|
+
"# ` ```typescript `, ` ```tsx `, or ` ```typescriptreact ` block in",
|
|
9305
|
+
"# the docs, the helper writes the sample to an in-memory file,",
|
|
9306
|
+
"# runs the TypeScript compiler against it (in-process, via the TS",
|
|
9307
|
+
"# compiler API), and reports any compilation diagnostics.",
|
|
9308
|
+
"#",
|
|
9309
|
+
"# Skipped samples (fence meta `skip` / `no-check`, or first line",
|
|
9310
|
+
"# `// @no-check`) are passed through unchecked.",
|
|
9311
|
+
"#",
|
|
9312
|
+
"# Output (stdout, single JSON array):",
|
|
9313
|
+
"#",
|
|
9314
|
+
"# [",
|
|
9315
|
+
"# {",
|
|
9316
|
+
'# "docPath": "<path relative to <docs-root>>",',
|
|
9317
|
+
'# "line": <1-indexed line number of the opening fence>,',
|
|
9318
|
+
'# "fenceIndex": <0-indexed fenced-block position within the doc>,',
|
|
9319
|
+
'# "lang": "ts" | "typescript" | "tsx" | "typescriptreact",',
|
|
9320
|
+
'# "diagnostics": [ "<TS<code>: <message>>", ... ]',
|
|
9321
|
+
"# },",
|
|
9322
|
+
"# ...",
|
|
9323
|
+
"# ]",
|
|
9324
|
+
"#",
|
|
9325
|
+
"# An empty array (`[]`) is emitted when every sample compiled.",
|
|
9326
|
+
"# Detection is **data**, not failure: this helper exits 0 when the",
|
|
9327
|
+
"# compilation phase ran successfully, regardless of how many",
|
|
9328
|
+
"# samples failed to compile. The downstream docs-sync scan phase",
|
|
9329
|
+
"# (#520) is responsible for treating failures as one of the two",
|
|
9330
|
+
"# hard-block cases per the parent epic.",
|
|
9331
|
+
"#",
|
|
9332
|
+
"# Exit codes:",
|
|
9333
|
+
"# 0 \u2014 compilation phase ran; failures (zero or more) are on",
|
|
9334
|
+
"# stdout as a JSON array.",
|
|
9335
|
+
"# 1 \u2014 usage error (missing args, unreadable docs root).",
|
|
9336
|
+
"# 2 \u2014 a required binary is not on PATH (`node` / `pnpm`).",
|
|
9337
|
+
"# 3 \u2014 the compilation phase threw an unhandled exception.",
|
|
9338
|
+
"# Stderr carries the diagnostic.",
|
|
9339
|
+
"#",
|
|
9340
|
+
"# Dependencies: expects `node` and `pnpm` on PATH and the workspace",
|
|
9341
|
+
"# `@codedrifters/configulator` package to be installed (the helper",
|
|
9342
|
+
"# resolves the API through `pnpm exec node` from the repo root).",
|
|
9343
|
+
"",
|
|
9344
|
+
"set -uo pipefail",
|
|
9345
|
+
"",
|
|
9346
|
+
"err() {",
|
|
9347
|
+
' printf "check-doc-samples.sh: %s\\n" "$*" >&2',
|
|
9348
|
+
"}",
|
|
9349
|
+
"",
|
|
9350
|
+
'if [ "$#" -lt 1 ]; then',
|
|
9351
|
+
' err "usage: check-doc-samples.sh <docs-root>"',
|
|
9352
|
+
" exit 1",
|
|
9353
|
+
"fi",
|
|
9354
|
+
"",
|
|
9355
|
+
'docs_root="$1"',
|
|
9356
|
+
"",
|
|
9357
|
+
'if [ ! -d "$docs_root" ]; then',
|
|
9358
|
+
' err "docs root not found: $docs_root"',
|
|
9359
|
+
" exit 1",
|
|
9360
|
+
"fi",
|
|
9361
|
+
"",
|
|
9362
|
+
"if ! command -v node >/dev/null 2>&1; then",
|
|
9363
|
+
' err "node is required but not on PATH"',
|
|
9364
|
+
" exit 2",
|
|
9365
|
+
"fi",
|
|
9366
|
+
"",
|
|
9367
|
+
"if ! command -v pnpm >/dev/null 2>&1; then",
|
|
9368
|
+
' err "pnpm is required but not on PATH"',
|
|
9369
|
+
" exit 2",
|
|
9370
|
+
"fi",
|
|
9371
|
+
"",
|
|
9372
|
+
"# Resolve docs_root to an absolute path so the inline node script",
|
|
9373
|
+
"# is independent of the cwd `pnpm exec` chooses.",
|
|
9374
|
+
'docs_root_abs="$(cd "$docs_root" && pwd)"',
|
|
9375
|
+
"",
|
|
9376
|
+
"# Run the inline node script through `pnpm exec` so module",
|
|
9377
|
+
"# resolution finds `@codedrifters/configulator` via the workspace",
|
|
9378
|
+
"# even when the helper is invoked from a sub-package directory.",
|
|
9379
|
+
"# The `--input-type=module` flag lets us use top-level `await` and",
|
|
9380
|
+
"# `import()` cleanly. Diagnostics from the script flow to stderr;",
|
|
9381
|
+
"# stdout carries only the JSON array.",
|
|
9382
|
+
'pnpm exec node --input-type=module -e "' + nodeScript.replace(/"/g, '\\"') + '" "$docs_root_abs"',
|
|
9383
|
+
"status=$?",
|
|
9384
|
+
"",
|
|
9385
|
+
'if [ "$status" -ne 0 ]; then',
|
|
9386
|
+
' err "compileFencedSamples exited with status $status"',
|
|
9387
|
+
" exit 3",
|
|
9388
|
+
"fi",
|
|
9389
|
+
"",
|
|
9390
|
+
"exit 0"
|
|
9391
|
+
].join("\n");
|
|
9392
|
+
}
|
|
9393
|
+
var checkDocSamplesProcedure = {
|
|
9394
|
+
name: "check-doc-samples.sh",
|
|
9395
|
+
description: "Fenced-sample compilation checker that wraps the `compileFencedSamples` API and emits a JSON-array stream of { docPath, line, fenceIndex, lang, diagnostics } failure records. Skipped samples (meta `skip`/`no-check`, or first-line `// @no-check`) are passed through unchecked. Detection is data: the helper exits 0 whenever compilation ran successfully regardless of failure count.",
|
|
9396
|
+
content: renderCheckDocSamplesProcedure()
|
|
9397
|
+
};
|
|
8981
9398
|
function buildDocsSyncBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
8982
9399
|
return {
|
|
8983
9400
|
name: "docs-sync",
|
|
@@ -9027,7 +9444,11 @@ function buildDocsSyncBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
9027
9444
|
],
|
|
9028
9445
|
skills: [buildDocsSyncPrSkill(), buildDocsSyncAuditSkill()],
|
|
9029
9446
|
subAgents: [buildDocsSyncSubAgent(paths)],
|
|
9030
|
-
procedures: [
|
|
9447
|
+
procedures: [
|
|
9448
|
+
extractApiProcedure,
|
|
9449
|
+
checkLinksProcedure,
|
|
9450
|
+
checkDocSamplesProcedure
|
|
9451
|
+
],
|
|
9031
9452
|
labels: [
|
|
9032
9453
|
{
|
|
9033
9454
|
name: "type:docs-sync",
|
|
@@ -26771,8 +27192,8 @@ var FALLBACKS = {
|
|
|
26771
27192
|
monorepoLayoutSeedBlock: ""
|
|
26772
27193
|
};
|
|
26773
27194
|
var TEMPLATE_RE = /\{\{(\w+(?:\.\w+)*)\}\}/g;
|
|
26774
|
-
function getNestedValue(obj,
|
|
26775
|
-
const parts =
|
|
27195
|
+
function getNestedValue(obj, path6) {
|
|
27196
|
+
const parts = path6.split(".");
|
|
26776
27197
|
let current = obj;
|
|
26777
27198
|
for (const part of parts) {
|
|
26778
27199
|
if (current == null || typeof current !== "object") {
|
|
@@ -26998,20 +27419,20 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
26998
27419
|
obj.excludedCommands = [...sandbox.excludedCommands];
|
|
26999
27420
|
}
|
|
27000
27421
|
if (sandbox.filesystem) {
|
|
27001
|
-
const
|
|
27422
|
+
const fs3 = {};
|
|
27002
27423
|
if (sandbox.filesystem.allowRead?.length) {
|
|
27003
|
-
|
|
27424
|
+
fs3.allowRead = [...sandbox.filesystem.allowRead];
|
|
27004
27425
|
}
|
|
27005
27426
|
if (sandbox.filesystem.denyRead?.length) {
|
|
27006
|
-
|
|
27427
|
+
fs3.denyRead = [...sandbox.filesystem.denyRead];
|
|
27007
27428
|
}
|
|
27008
27429
|
if (sandbox.filesystem.allowWrite?.length) {
|
|
27009
|
-
|
|
27430
|
+
fs3.allowWrite = [...sandbox.filesystem.allowWrite];
|
|
27010
27431
|
}
|
|
27011
27432
|
if (sandbox.filesystem.denyWrite?.length) {
|
|
27012
|
-
|
|
27433
|
+
fs3.denyWrite = [...sandbox.filesystem.denyWrite];
|
|
27013
27434
|
}
|
|
27014
|
-
if (Object.keys(
|
|
27435
|
+
if (Object.keys(fs3).length > 0) obj.filesystem = fs3;
|
|
27015
27436
|
}
|
|
27016
27437
|
if (sandbox.network) {
|
|
27017
27438
|
const net = {};
|
|
@@ -28873,10 +29294,357 @@ function stripYamlFrontmatter(source) {
|
|
|
28873
29294
|
return lines.join("\n");
|
|
28874
29295
|
}
|
|
28875
29296
|
|
|
28876
|
-
// src/docs-sync/
|
|
29297
|
+
// src/docs-sync/sample-compilation/compilation.ts
|
|
29298
|
+
var path4 = __toESM(require("path"));
|
|
29299
|
+
var ts = __toESM(require("typescript"));
|
|
29300
|
+
|
|
29301
|
+
// src/docs-sync/sample-compilation/extraction.ts
|
|
29302
|
+
var fs2 = __toESM(require("fs"));
|
|
28877
29303
|
var path3 = __toESM(require("path"));
|
|
29304
|
+
var import_mdast_util_from_markdown2 = require("mdast-util-from-markdown");
|
|
29305
|
+
var SampleLang = {
|
|
29306
|
+
Ts: "ts",
|
|
29307
|
+
Typescript: "typescript",
|
|
29308
|
+
Tsx: "tsx",
|
|
29309
|
+
Typescriptreact: "typescriptreact"
|
|
29310
|
+
};
|
|
29311
|
+
var DEFAULT_DOCS_ROOT2 = "docs/src/content/docs";
|
|
29312
|
+
var PLAIN_LANGS = /* @__PURE__ */ new Set([SampleLang.Ts, SampleLang.Typescript]);
|
|
29313
|
+
var JSX_LANGS = /* @__PURE__ */ new Set([
|
|
29314
|
+
SampleLang.Tsx,
|
|
29315
|
+
SampleLang.Typescriptreact
|
|
29316
|
+
]);
|
|
29317
|
+
function extractFencedSamples(options = {}) {
|
|
29318
|
+
const docsRoot = path3.resolve(options.docsRoot ?? DEFAULT_DOCS_ROOT2);
|
|
29319
|
+
const includeTsx = options.includeTsx ?? true;
|
|
29320
|
+
if (!fs2.existsSync(docsRoot)) {
|
|
29321
|
+
return [];
|
|
29322
|
+
}
|
|
29323
|
+
const records = [];
|
|
29324
|
+
for (const absolutePath of walkMarkdownFiles2(docsRoot)) {
|
|
29325
|
+
const docPath = toPosix2(path3.relative(docsRoot, absolutePath));
|
|
29326
|
+
const source = fs2.readFileSync(absolutePath, "utf-8");
|
|
29327
|
+
const tree = (0, import_mdast_util_from_markdown2.fromMarkdown)(stripYamlFrontmatter2(source));
|
|
29328
|
+
let fenceIndex = 0;
|
|
29329
|
+
collectFencedCode(tree, (node) => {
|
|
29330
|
+
const lang = normalizeLang(node.lang);
|
|
29331
|
+
if (lang === void 0) {
|
|
29332
|
+
return;
|
|
29333
|
+
}
|
|
29334
|
+
if (!includeTsx && JSX_LANGS.has(lang)) {
|
|
29335
|
+
return;
|
|
29336
|
+
}
|
|
29337
|
+
if (!PLAIN_LANGS.has(lang) && !JSX_LANGS.has(lang)) {
|
|
29338
|
+
return;
|
|
29339
|
+
}
|
|
29340
|
+
const start = node.position?.start;
|
|
29341
|
+
if (!start) {
|
|
29342
|
+
return;
|
|
29343
|
+
}
|
|
29344
|
+
const skip = detectSkip(node);
|
|
29345
|
+
records.push({
|
|
29346
|
+
docPath,
|
|
29347
|
+
line: start.line,
|
|
29348
|
+
fenceIndex,
|
|
29349
|
+
lang,
|
|
29350
|
+
code: node.value ?? "",
|
|
29351
|
+
skipped: skip.skipped,
|
|
29352
|
+
skipReason: skip.reason
|
|
29353
|
+
});
|
|
29354
|
+
fenceIndex += 1;
|
|
29355
|
+
});
|
|
29356
|
+
}
|
|
29357
|
+
records.sort((a, b) => {
|
|
29358
|
+
if (a.docPath !== b.docPath) {
|
|
29359
|
+
return a.docPath.localeCompare(b.docPath);
|
|
29360
|
+
}
|
|
29361
|
+
if (a.line !== b.line) {
|
|
29362
|
+
return a.line - b.line;
|
|
29363
|
+
}
|
|
29364
|
+
return a.fenceIndex - b.fenceIndex;
|
|
29365
|
+
});
|
|
29366
|
+
return records;
|
|
29367
|
+
}
|
|
29368
|
+
function walkMarkdownFiles2(root) {
|
|
29369
|
+
const out = [];
|
|
29370
|
+
const stack = [root];
|
|
29371
|
+
while (stack.length > 0) {
|
|
29372
|
+
const dir = stack.pop();
|
|
29373
|
+
let entries;
|
|
29374
|
+
try {
|
|
29375
|
+
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
29376
|
+
} catch {
|
|
29377
|
+
continue;
|
|
29378
|
+
}
|
|
29379
|
+
for (const entry of entries) {
|
|
29380
|
+
const full = path3.join(dir, entry.name);
|
|
29381
|
+
if (entry.isDirectory()) {
|
|
29382
|
+
stack.push(full);
|
|
29383
|
+
} else if (entry.isFile() && full.toLowerCase().endsWith(".md")) {
|
|
29384
|
+
out.push(full);
|
|
29385
|
+
}
|
|
29386
|
+
}
|
|
29387
|
+
}
|
|
29388
|
+
out.sort();
|
|
29389
|
+
return out;
|
|
29390
|
+
}
|
|
29391
|
+
function collectFencedCode(tree, visit) {
|
|
29392
|
+
walk(tree);
|
|
29393
|
+
function walk(node) {
|
|
29394
|
+
if (node.type === "code") {
|
|
29395
|
+
visit(node);
|
|
29396
|
+
return;
|
|
29397
|
+
}
|
|
29398
|
+
if (node.type === "html") {
|
|
29399
|
+
return;
|
|
29400
|
+
}
|
|
29401
|
+
const parent = node;
|
|
29402
|
+
if (Array.isArray(parent.children)) {
|
|
29403
|
+
for (const child of parent.children) {
|
|
29404
|
+
walk(child);
|
|
29405
|
+
}
|
|
29406
|
+
}
|
|
29407
|
+
}
|
|
29408
|
+
}
|
|
29409
|
+
function normalizeLang(raw) {
|
|
29410
|
+
if (!raw) {
|
|
29411
|
+
return void 0;
|
|
29412
|
+
}
|
|
29413
|
+
const lower = raw.toLowerCase();
|
|
29414
|
+
switch (lower) {
|
|
29415
|
+
case "ts":
|
|
29416
|
+
return SampleLang.Ts;
|
|
29417
|
+
case "typescript":
|
|
29418
|
+
return SampleLang.Typescript;
|
|
29419
|
+
case "tsx":
|
|
29420
|
+
return SampleLang.Tsx;
|
|
29421
|
+
case "typescriptreact":
|
|
29422
|
+
return SampleLang.Typescriptreact;
|
|
29423
|
+
default:
|
|
29424
|
+
return void 0;
|
|
29425
|
+
}
|
|
29426
|
+
}
|
|
29427
|
+
var META_SKIP_PATTERN = /\b(skip|no-check)\b/i;
|
|
29428
|
+
var PRAGMA_NO_CHECK_PATTERN = /^\s*\/\/\s*@no-check\b/i;
|
|
29429
|
+
function detectSkip(node) {
|
|
29430
|
+
const meta = node.meta ?? "";
|
|
29431
|
+
const metaMatch = META_SKIP_PATTERN.exec(meta);
|
|
29432
|
+
if (metaMatch) {
|
|
29433
|
+
return { skipped: true, reason: `meta:${metaMatch[1].toLowerCase()}` };
|
|
29434
|
+
}
|
|
29435
|
+
const code = node.value ?? "";
|
|
29436
|
+
const firstLine = firstNonBlankLine(code);
|
|
29437
|
+
if (firstLine !== void 0 && PRAGMA_NO_CHECK_PATTERN.test(firstLine)) {
|
|
29438
|
+
return { skipped: true, reason: "pragma:@no-check" };
|
|
29439
|
+
}
|
|
29440
|
+
return { skipped: false, reason: "" };
|
|
29441
|
+
}
|
|
29442
|
+
function firstNonBlankLine(source) {
|
|
29443
|
+
for (const line of source.split("\n")) {
|
|
29444
|
+
if (line.trim().length > 0) {
|
|
29445
|
+
return line;
|
|
29446
|
+
}
|
|
29447
|
+
}
|
|
29448
|
+
return void 0;
|
|
29449
|
+
}
|
|
29450
|
+
function toPosix2(p) {
|
|
29451
|
+
return p.split(path3.sep).join("/");
|
|
29452
|
+
}
|
|
29453
|
+
function stripYamlFrontmatter2(source) {
|
|
29454
|
+
if (!source.startsWith("---")) {
|
|
29455
|
+
return source;
|
|
29456
|
+
}
|
|
29457
|
+
const lines = source.split("\n");
|
|
29458
|
+
if (lines[0] !== "---") {
|
|
29459
|
+
return source;
|
|
29460
|
+
}
|
|
29461
|
+
let endIndex = -1;
|
|
29462
|
+
for (let i = 1; i < lines.length; i++) {
|
|
29463
|
+
if (lines[i] === "---") {
|
|
29464
|
+
endIndex = i;
|
|
29465
|
+
break;
|
|
29466
|
+
}
|
|
29467
|
+
}
|
|
29468
|
+
if (endIndex < 0) {
|
|
29469
|
+
return source;
|
|
29470
|
+
}
|
|
29471
|
+
for (let i = 0; i <= endIndex; i++) {
|
|
29472
|
+
lines[i] = "";
|
|
29473
|
+
}
|
|
29474
|
+
return lines.join("\n");
|
|
29475
|
+
}
|
|
29476
|
+
|
|
29477
|
+
// src/docs-sync/sample-compilation/compilation.ts
|
|
29478
|
+
var DEFAULT_SAMPLE_COMPILER_OPTIONS = {
|
|
29479
|
+
target: ts.ScriptTarget.ES2022,
|
|
29480
|
+
module: ts.ModuleKind.ESNext,
|
|
29481
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
29482
|
+
lib: ["lib.es2022.d.ts", "lib.dom.d.ts"],
|
|
29483
|
+
strict: true,
|
|
29484
|
+
noUnusedLocals: false,
|
|
29485
|
+
noUnusedParameters: false,
|
|
29486
|
+
skipLibCheck: true,
|
|
29487
|
+
esModuleInterop: true,
|
|
29488
|
+
resolveJsonModule: true,
|
|
29489
|
+
isolatedModules: true,
|
|
29490
|
+
jsx: ts.JsxEmit.Preserve,
|
|
29491
|
+
noEmit: true,
|
|
29492
|
+
allowSyntheticDefaultImports: true
|
|
29493
|
+
};
|
|
29494
|
+
var REPO_RELATIVE_MODULE_PATTERN = /^['"](?:\.\.?\/|\/)/;
|
|
29495
|
+
function compileFencedSamples(options = {}) {
|
|
29496
|
+
const samples = extractFencedSamples(options);
|
|
29497
|
+
const tolerateRepoRelative = options.tolerateRepoRelativeMissingModule ?? true;
|
|
29498
|
+
const failures = [];
|
|
29499
|
+
for (const sample of samples) {
|
|
29500
|
+
if (sample.skipped) {
|
|
29501
|
+
continue;
|
|
29502
|
+
}
|
|
29503
|
+
const diagnostics = compileSingleSample(
|
|
29504
|
+
sample,
|
|
29505
|
+
options.compilerOptions,
|
|
29506
|
+
tolerateRepoRelative
|
|
29507
|
+
);
|
|
29508
|
+
if (diagnostics.length > 0) {
|
|
29509
|
+
failures.push({
|
|
29510
|
+
docPath: sample.docPath,
|
|
29511
|
+
line: sample.line,
|
|
29512
|
+
fenceIndex: sample.fenceIndex,
|
|
29513
|
+
lang: sample.lang,
|
|
29514
|
+
diagnostics
|
|
29515
|
+
});
|
|
29516
|
+
}
|
|
29517
|
+
}
|
|
29518
|
+
return failures;
|
|
29519
|
+
}
|
|
29520
|
+
function compileSingleSample(sample, overrides, tolerateRepoRelative) {
|
|
29521
|
+
const filename = sampleFilename(sample);
|
|
29522
|
+
const compilerOptions = mergeCompilerOptions(overrides);
|
|
29523
|
+
const host = createSingleFileHost(filename, sample.code, compilerOptions);
|
|
29524
|
+
const program = ts.createProgram({
|
|
29525
|
+
rootNames: [filename],
|
|
29526
|
+
options: compilerOptions,
|
|
29527
|
+
host
|
|
29528
|
+
});
|
|
29529
|
+
const sourceFile = program.getSourceFile(filename);
|
|
29530
|
+
const collected = [];
|
|
29531
|
+
if (sourceFile) {
|
|
29532
|
+
collected.push(...program.getSyntacticDiagnostics(sourceFile));
|
|
29533
|
+
collected.push(...program.getSemanticDiagnostics(sourceFile));
|
|
29534
|
+
} else {
|
|
29535
|
+
collected.push(...program.getSyntacticDiagnostics());
|
|
29536
|
+
collected.push(...program.getSemanticDiagnostics());
|
|
29537
|
+
}
|
|
29538
|
+
const diagnostics = [];
|
|
29539
|
+
for (const d of collected) {
|
|
29540
|
+
if (tolerateRepoRelative && isRepoRelativeMissingModule(d, sample.code)) {
|
|
29541
|
+
continue;
|
|
29542
|
+
}
|
|
29543
|
+
diagnostics.push(formatDiagnostic(d));
|
|
29544
|
+
}
|
|
29545
|
+
return diagnostics;
|
|
29546
|
+
}
|
|
29547
|
+
function sampleFilename(sample) {
|
|
29548
|
+
const safeDoc = sample.docPath.replace(/[^a-zA-Z0-9]+/g, "_");
|
|
29549
|
+
const ext = sample.lang === SampleLang.Tsx || sample.lang === SampleLang.Typescriptreact ? "tsx" : "ts";
|
|
29550
|
+
return path4.posix.join("/", `${safeDoc}__${sample.fenceIndex}.${ext}`);
|
|
29551
|
+
}
|
|
29552
|
+
function mergeCompilerOptions(overrides) {
|
|
29553
|
+
if (!overrides) {
|
|
29554
|
+
return { ...DEFAULT_SAMPLE_COMPILER_OPTIONS };
|
|
29555
|
+
}
|
|
29556
|
+
const merged = { ...DEFAULT_SAMPLE_COMPILER_OPTIONS };
|
|
29557
|
+
for (const key of Object.keys(overrides)) {
|
|
29558
|
+
const value = overrides[key];
|
|
29559
|
+
if (value === void 0) {
|
|
29560
|
+
delete merged[key];
|
|
29561
|
+
} else {
|
|
29562
|
+
merged[key] = value;
|
|
29563
|
+
}
|
|
29564
|
+
}
|
|
29565
|
+
return merged;
|
|
29566
|
+
}
|
|
29567
|
+
function createSingleFileHost(filename, source, compilerOptions) {
|
|
29568
|
+
const baseHost = ts.createCompilerHost(compilerOptions, true);
|
|
29569
|
+
return {
|
|
29570
|
+
...baseHost,
|
|
29571
|
+
getSourceFile(name, languageVersionOrOptions, onError, shouldCreate) {
|
|
29572
|
+
if (name === filename) {
|
|
29573
|
+
return ts.createSourceFile(
|
|
29574
|
+
name,
|
|
29575
|
+
source,
|
|
29576
|
+
languageVersionOrOptions,
|
|
29577
|
+
true
|
|
29578
|
+
);
|
|
29579
|
+
}
|
|
29580
|
+
return baseHost.getSourceFile(
|
|
29581
|
+
name,
|
|
29582
|
+
languageVersionOrOptions,
|
|
29583
|
+
onError,
|
|
29584
|
+
shouldCreate
|
|
29585
|
+
);
|
|
29586
|
+
},
|
|
29587
|
+
fileExists(name) {
|
|
29588
|
+
if (name === filename) {
|
|
29589
|
+
return true;
|
|
29590
|
+
}
|
|
29591
|
+
return baseHost.fileExists(name);
|
|
29592
|
+
},
|
|
29593
|
+
readFile(name) {
|
|
29594
|
+
if (name === filename) {
|
|
29595
|
+
return source;
|
|
29596
|
+
}
|
|
29597
|
+
return baseHost.readFile(name);
|
|
29598
|
+
},
|
|
29599
|
+
writeFile() {
|
|
29600
|
+
},
|
|
29601
|
+
getCanonicalFileName(name) {
|
|
29602
|
+
return baseHost.getCanonicalFileName(name);
|
|
29603
|
+
},
|
|
29604
|
+
useCaseSensitiveFileNames() {
|
|
29605
|
+
return baseHost.useCaseSensitiveFileNames();
|
|
29606
|
+
},
|
|
29607
|
+
getNewLine() {
|
|
29608
|
+
return baseHost.getNewLine();
|
|
29609
|
+
},
|
|
29610
|
+
getDefaultLibFileName(o) {
|
|
29611
|
+
return baseHost.getDefaultLibFileName(o);
|
|
29612
|
+
},
|
|
29613
|
+
getCurrentDirectory() {
|
|
29614
|
+
return baseHost.getCurrentDirectory();
|
|
29615
|
+
}
|
|
29616
|
+
};
|
|
29617
|
+
}
|
|
29618
|
+
function isRepoRelativeMissingModule(d, code) {
|
|
29619
|
+
if (d.code !== 2307) {
|
|
29620
|
+
return false;
|
|
29621
|
+
}
|
|
29622
|
+
const text = ts.flattenDiagnosticMessageText(d.messageText, "\n");
|
|
29623
|
+
const match = /'([^']+)'/.exec(text);
|
|
29624
|
+
if (!match) {
|
|
29625
|
+
return false;
|
|
29626
|
+
}
|
|
29627
|
+
const specifier = match[1];
|
|
29628
|
+
if (!specifier) {
|
|
29629
|
+
return false;
|
|
29630
|
+
}
|
|
29631
|
+
const inSourceSingle = `'${specifier}'`;
|
|
29632
|
+
const inSourceDouble = `"${specifier}"`;
|
|
29633
|
+
if (!code.includes(inSourceSingle) && !code.includes(inSourceDouble)) {
|
|
29634
|
+
return false;
|
|
29635
|
+
}
|
|
29636
|
+
return REPO_RELATIVE_MODULE_PATTERN.test(`'${specifier}'`);
|
|
29637
|
+
}
|
|
29638
|
+
function formatDiagnostic(d) {
|
|
29639
|
+
const text = ts.flattenDiagnosticMessageText(d.messageText, " ");
|
|
29640
|
+
const code = d.code === void 0 ? "" : `TS${d.code}: `;
|
|
29641
|
+
return `${code}${text}`.trim();
|
|
29642
|
+
}
|
|
29643
|
+
|
|
29644
|
+
// src/docs-sync/tsdoc-coverage/coverage.ts
|
|
29645
|
+
var path5 = __toESM(require("path"));
|
|
28878
29646
|
var import_tsdoc = require("@microsoft/tsdoc");
|
|
28879
|
-
var
|
|
29647
|
+
var ts2 = __toESM(require("typescript"));
|
|
28880
29648
|
var TsDocCoverageKind = {
|
|
28881
29649
|
Class: "Class",
|
|
28882
29650
|
Interface: "Interface",
|
|
@@ -28892,8 +29660,8 @@ var DEFAULT_THIN_SUMMARY_WORD_THRESHOLD = 4;
|
|
|
28892
29660
|
var DEFAULT_ENTRY_POINT = "src/index.ts";
|
|
28893
29661
|
function analyzeTsDocCoverage(options) {
|
|
28894
29662
|
const resolvedOptions = typeof options === "string" ? { packageRoot: options } : options;
|
|
28895
|
-
const packageRoot =
|
|
28896
|
-
const entryPoint =
|
|
29663
|
+
const packageRoot = path5.resolve(resolvedOptions.packageRoot);
|
|
29664
|
+
const entryPoint = path5.resolve(
|
|
28897
29665
|
packageRoot,
|
|
28898
29666
|
resolvedOptions.entryPoint ?? DEFAULT_ENTRY_POINT
|
|
28899
29667
|
);
|
|
@@ -28902,7 +29670,7 @@ function analyzeTsDocCoverage(options) {
|
|
|
28902
29670
|
packageRoot,
|
|
28903
29671
|
resolvedOptions.tsconfigPath
|
|
28904
29672
|
);
|
|
28905
|
-
const program =
|
|
29673
|
+
const program = ts2.createProgram({
|
|
28906
29674
|
rootNames: [entryPoint],
|
|
28907
29675
|
options: compilerOptions
|
|
28908
29676
|
});
|
|
@@ -28956,24 +29724,24 @@ function analyzeTsDocCoverage(options) {
|
|
|
28956
29724
|
}
|
|
28957
29725
|
function resolveCompilerOptions(packageRoot, tsconfigPath) {
|
|
28958
29726
|
if (tsconfigPath) {
|
|
28959
|
-
const absoluteTsconfig =
|
|
28960
|
-
const configFile =
|
|
29727
|
+
const absoluteTsconfig = path5.resolve(packageRoot, tsconfigPath);
|
|
29728
|
+
const configFile = ts2.readConfigFile(absoluteTsconfig, ts2.sys.readFile);
|
|
28961
29729
|
if (configFile.error) {
|
|
28962
29730
|
throw new Error(
|
|
28963
|
-
`analyzeTsDocCoverage: failed to read tsconfig at ${absoluteTsconfig}: ${
|
|
29731
|
+
`analyzeTsDocCoverage: failed to read tsconfig at ${absoluteTsconfig}: ${ts2.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
|
|
28964
29732
|
);
|
|
28965
29733
|
}
|
|
28966
|
-
const parsed =
|
|
29734
|
+
const parsed = ts2.parseJsonConfigFileContent(
|
|
28967
29735
|
configFile.config,
|
|
28968
|
-
|
|
28969
|
-
|
|
29736
|
+
ts2.sys,
|
|
29737
|
+
path5.dirname(absoluteTsconfig)
|
|
28970
29738
|
);
|
|
28971
29739
|
return { ...parsed.options, noEmit: true };
|
|
28972
29740
|
}
|
|
28973
29741
|
return {
|
|
28974
|
-
target:
|
|
28975
|
-
module:
|
|
28976
|
-
moduleResolution:
|
|
29742
|
+
target: ts2.ScriptTarget.ESNext,
|
|
29743
|
+
module: ts2.ModuleKind.NodeNext,
|
|
29744
|
+
moduleResolution: ts2.ModuleResolutionKind.NodeNext,
|
|
28977
29745
|
allowJs: false,
|
|
28978
29746
|
declaration: false,
|
|
28979
29747
|
noEmit: true,
|
|
@@ -28998,7 +29766,7 @@ function resolveAlias(symbol, checker) {
|
|
|
28998
29766
|
return current;
|
|
28999
29767
|
}
|
|
29000
29768
|
function isAlias(symbol) {
|
|
29001
|
-
return (symbol.flags &
|
|
29769
|
+
return (symbol.flags & ts2.SymbolFlags.Alias) !== 0;
|
|
29002
29770
|
}
|
|
29003
29771
|
function pickPrimaryDeclaration(symbol) {
|
|
29004
29772
|
const declarations = symbol.getDeclarations();
|
|
@@ -29006,53 +29774,53 @@ function pickPrimaryDeclaration(symbol) {
|
|
|
29006
29774
|
return void 0;
|
|
29007
29775
|
}
|
|
29008
29776
|
const concrete = declarations.find(
|
|
29009
|
-
(d) => !
|
|
29777
|
+
(d) => !ts2.isExportSpecifier(d) && !ts2.isExportAssignment(d) && !ts2.isExportDeclaration(d)
|
|
29010
29778
|
);
|
|
29011
29779
|
return concrete ?? declarations[0];
|
|
29012
29780
|
}
|
|
29013
29781
|
function classifyDeclaration(declaration) {
|
|
29014
|
-
if (
|
|
29782
|
+
if (ts2.isClassDeclaration(declaration)) {
|
|
29015
29783
|
return TsDocCoverageKind.Class;
|
|
29016
29784
|
}
|
|
29017
|
-
if (
|
|
29785
|
+
if (ts2.isInterfaceDeclaration(declaration)) {
|
|
29018
29786
|
return TsDocCoverageKind.Interface;
|
|
29019
29787
|
}
|
|
29020
|
-
if (
|
|
29788
|
+
if (ts2.isTypeAliasDeclaration(declaration)) {
|
|
29021
29789
|
return TsDocCoverageKind.TypeAlias;
|
|
29022
29790
|
}
|
|
29023
|
-
if (
|
|
29791
|
+
if (ts2.isEnumDeclaration(declaration)) {
|
|
29024
29792
|
return TsDocCoverageKind.Enum;
|
|
29025
29793
|
}
|
|
29026
|
-
if (
|
|
29794
|
+
if (ts2.isFunctionDeclaration(declaration) || ts2.isMethodDeclaration(declaration)) {
|
|
29027
29795
|
return TsDocCoverageKind.Function;
|
|
29028
29796
|
}
|
|
29029
|
-
if (
|
|
29797
|
+
if (ts2.isVariableDeclaration(declaration) || ts2.isVariableStatement(declaration)) {
|
|
29030
29798
|
return TsDocCoverageKind.Variable;
|
|
29031
29799
|
}
|
|
29032
|
-
if (
|
|
29800
|
+
if (ts2.isModuleDeclaration(declaration)) {
|
|
29033
29801
|
return TsDocCoverageKind.Module;
|
|
29034
29802
|
}
|
|
29035
|
-
if (
|
|
29803
|
+
if (ts2.isExportAssignment(declaration)) {
|
|
29036
29804
|
return TsDocCoverageKind.Default;
|
|
29037
29805
|
}
|
|
29038
29806
|
return TsDocCoverageKind.Other;
|
|
29039
29807
|
}
|
|
29040
29808
|
function resolveLocation(declaration) {
|
|
29041
|
-
const target =
|
|
29809
|
+
const target = ts2.isVariableDeclaration(declaration) ? declaration.parent.parent ?? declaration : declaration;
|
|
29042
29810
|
const source = target.getSourceFile();
|
|
29043
29811
|
const { line } = source.getLineAndCharacterOfPosition(target.getStart());
|
|
29044
29812
|
return { file: source.fileName, line: line + 1 };
|
|
29045
29813
|
}
|
|
29046
29814
|
function parseTsDocFor(declaration, parser) {
|
|
29047
|
-
const target =
|
|
29815
|
+
const target = ts2.isVariableDeclaration(declaration) ? declaration.parent.parent ?? declaration : declaration;
|
|
29048
29816
|
const sourceText = target.getSourceFile().getFullText();
|
|
29049
|
-
const ranges =
|
|
29817
|
+
const ranges = ts2.getLeadingCommentRanges(sourceText, target.getFullStart());
|
|
29050
29818
|
if (!ranges || ranges.length === 0) {
|
|
29051
29819
|
return void 0;
|
|
29052
29820
|
}
|
|
29053
29821
|
for (let i = ranges.length - 1; i >= 0; i--) {
|
|
29054
29822
|
const range = ranges[i];
|
|
29055
|
-
if (range.kind !==
|
|
29823
|
+
if (range.kind !== ts2.SyntaxKind.MultiLineCommentTrivia) {
|
|
29056
29824
|
continue;
|
|
29057
29825
|
}
|
|
29058
29826
|
const text = sourceText.slice(range.pos, range.end);
|
|
@@ -29275,14 +30043,14 @@ var LAYOUT_ROOT_BY_PROJECT_TYPE = {
|
|
|
29275
30043
|
};
|
|
29276
30044
|
function validateMonorepoLayout(root) {
|
|
29277
30045
|
const violations = [];
|
|
29278
|
-
const rootOutdir =
|
|
30046
|
+
const rootOutdir = toPosix3(root.outdir);
|
|
29279
30047
|
for (const sub of root.subprojects) {
|
|
29280
30048
|
const className = sub.constructor.name;
|
|
29281
30049
|
const expectedRoot = expectedRootFor(sub, className);
|
|
29282
30050
|
if (expectedRoot === void 0) {
|
|
29283
30051
|
continue;
|
|
29284
30052
|
}
|
|
29285
|
-
const relOutdir = relativeOutdir(rootOutdir,
|
|
30053
|
+
const relOutdir = relativeOutdir(rootOutdir, toPosix3(sub.outdir));
|
|
29286
30054
|
if (!outdirMatchesRoot(relOutdir, expectedRoot)) {
|
|
29287
30055
|
violations.push({
|
|
29288
30056
|
projectName: sub.name,
|
|
@@ -29341,7 +30109,7 @@ function outdirMatchesRoot(relOutdir, expectedRoot) {
|
|
|
29341
30109
|
}
|
|
29342
30110
|
return segments.length >= 2;
|
|
29343
30111
|
}
|
|
29344
|
-
function
|
|
30112
|
+
function toPosix3(p) {
|
|
29345
30113
|
return p.replace(/\\/g, "/");
|
|
29346
30114
|
}
|
|
29347
30115
|
function relativeOutdir(rootOutdir, subOutdir) {
|
|
@@ -29421,8 +30189,8 @@ var ResetTask = class _ResetTask extends import_projen14.Component {
|
|
|
29421
30189
|
const resetTask = this.project.tasks.addTask(this.taskName, {
|
|
29422
30190
|
description: "Delete build artifacts specified by pathsToRemove option, or artifactsDirectory if pathsToRemove is empty"
|
|
29423
30191
|
});
|
|
29424
|
-
this.pathsToRemove.forEach((
|
|
29425
|
-
resetTask.exec(`[ -e "${
|
|
30192
|
+
this.pathsToRemove.forEach((path6) => {
|
|
30193
|
+
resetTask.exec(`[ -e "${path6}" ] && rm -rf ${path6} || true`);
|
|
29426
30194
|
});
|
|
29427
30195
|
const rootHasTurbo = TurboRepo.of(this.project.root) !== void 0;
|
|
29428
30196
|
const isSubproject = this.project !== this.project.root;
|
|
@@ -31404,6 +32172,7 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
31404
32172
|
DEFAULT_PROGRESS_FILES_STATE_DIR,
|
|
31405
32173
|
DEFAULT_REQUIRE_LINKED_ISSUE,
|
|
31406
32174
|
DEFAULT_REQUIRE_PRODUCT_CONTEXT,
|
|
32175
|
+
DEFAULT_SAMPLE_COMPILER_OPTIONS,
|
|
31407
32176
|
DEFAULT_SCHEDULED_TASKS_ROOT,
|
|
31408
32177
|
DEFAULT_SCHEDULED_TASK_ENTRIES,
|
|
31409
32178
|
DEFAULT_SHARED_EDITING_CONFLICT_STRATEGY,
|
|
@@ -31449,6 +32218,7 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
31449
32218
|
SCOPE_CLASS_VALUES,
|
|
31450
32219
|
SHARED_EDITING_CONFLICT_STRATEGY_VALUES,
|
|
31451
32220
|
STARLIGHT_ROLE,
|
|
32221
|
+
SampleLang,
|
|
31452
32222
|
StarlightProject,
|
|
31453
32223
|
TestRunner,
|
|
31454
32224
|
TsDocCoverageKind,
|
|
@@ -31491,13 +32261,17 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
31491
32261
|
buildStandardsResearchBundle,
|
|
31492
32262
|
buildUnblockDependentsProcedure,
|
|
31493
32263
|
businessModelsBundle,
|
|
32264
|
+
checkDocSamplesProcedure,
|
|
32265
|
+
checkLinksProcedure,
|
|
31494
32266
|
classifyIssueScope,
|
|
31495
32267
|
classifyRun,
|
|
31496
32268
|
companyProfileBundle,
|
|
32269
|
+
compileFencedSamples,
|
|
31497
32270
|
customerProfileBundle,
|
|
31498
32271
|
docsSyncBundle,
|
|
31499
32272
|
extractApiProcedure,
|
|
31500
32273
|
extractDocReferences,
|
|
32274
|
+
extractFencedSamples,
|
|
31501
32275
|
formatLayoutViolation,
|
|
31502
32276
|
formatStarlightSingletonViolation,
|
|
31503
32277
|
getLatestEligibleVersion,
|
|
@@ -31514,6 +32288,8 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
31514
32288
|
regulatoryResearchBundle,
|
|
31515
32289
|
renderAgentTierCaseStatement,
|
|
31516
32290
|
renderAgentTierSection,
|
|
32291
|
+
renderCheckDocSamplesProcedure,
|
|
32292
|
+
renderCheckLinksProcedure,
|
|
31517
32293
|
renderCustomDocSectionBlock,
|
|
31518
32294
|
renderCustomDocSections,
|
|
31519
32295
|
renderExtractApiProcedure,
|