@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.mjs
CHANGED
|
@@ -8737,6 +8737,415 @@ var extractApiProcedure = {
|
|
|
8737
8737
|
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.",
|
|
8738
8738
|
content: renderExtractApiProcedure()
|
|
8739
8739
|
};
|
|
8740
|
+
function renderCheckLinksProcedure() {
|
|
8741
|
+
return [
|
|
8742
|
+
"#!/usr/bin/env bash",
|
|
8743
|
+
"# check-links.sh \u2014 Link integrity checker for the Starlight docs site.",
|
|
8744
|
+
"#",
|
|
8745
|
+
"# Usage:",
|
|
8746
|
+
"# .claude/procedures/check-links.sh <mode> <docs-root> [<site-dir>]",
|
|
8747
|
+
"#",
|
|
8748
|
+
"# Where:",
|
|
8749
|
+
"# <mode> \u2014 one of `internal`, `external`, or `all`.",
|
|
8750
|
+
"# <docs-root> \u2014 path to the markdown content tree",
|
|
8751
|
+
"# (e.g. `docs/src/content/docs`). Walked recursively",
|
|
8752
|
+
"# for `*.md` / `*.mdx` files.",
|
|
8753
|
+
"# <site-dir> \u2014 (optional, internal/all only) path to the Starlight",
|
|
8754
|
+
"# project root that contains `astro.config.{ts,mjs,js}`.",
|
|
8755
|
+
"# Defaults to two levels up from `<docs-root>` so the",
|
|
8756
|
+
"# standard Starlight layout (`<site>/src/content/docs`)",
|
|
8757
|
+
"# resolves automatically.",
|
|
8758
|
+
"#",
|
|
8759
|
+
"# Wraps two external tools:",
|
|
8760
|
+
"# - `astro check` \u2014 internal links and content-collection refs",
|
|
8761
|
+
"# inside the Starlight site.",
|
|
8762
|
+
"# - `lychee` \u2014 external `https://\u2026` URLs across the docs tree",
|
|
8763
|
+
"# (https://github.com/lycheeverse/lychee).",
|
|
8764
|
+
"#",
|
|
8765
|
+
"# Output (stdout, single JSON array):",
|
|
8766
|
+
"#",
|
|
8767
|
+
"# [",
|
|
8768
|
+
"# {",
|
|
8769
|
+
'# "url": "<href as written in the markdown source>",',
|
|
8770
|
+
'# "docPath": "<path relative to <docs-root>>",',
|
|
8771
|
+
'# "line": <1-indexed line number>,',
|
|
8772
|
+
'# "kind": "internal" | "external",',
|
|
8773
|
+
'# "reason": "<short tool-supplied diagnostic>"',
|
|
8774
|
+
"# },",
|
|
8775
|
+
"# ...",
|
|
8776
|
+
"# ]",
|
|
8777
|
+
"#",
|
|
8778
|
+
"# An empty array (`[]`) is emitted when no broken links are found.",
|
|
8779
|
+
"# Detection is **data**, not failure: this helper exits 0 when a",
|
|
8780
|
+
"# tool ran successfully and reported zero or more broken links.",
|
|
8781
|
+
"# The downstream docs-sync scan phase (#519/#520) is responsible",
|
|
8782
|
+
"# for deciding which findings are advisory and which block.",
|
|
8783
|
+
"#",
|
|
8784
|
+
"# Exit codes:",
|
|
8785
|
+
"# 0 \u2014 every requested tool ran successfully; findings (zero or",
|
|
8786
|
+
"# more) are on stdout as a JSON array.",
|
|
8787
|
+
"# 1 \u2014 usage error (missing args, unreadable docs root).",
|
|
8788
|
+
"# 2 \u2014 a required external tool is not installed or not on PATH",
|
|
8789
|
+
"# (`astro` / `lychee` / `node` / `jq`).",
|
|
8790
|
+
"# 3 \u2014 a tool ran but exited non-zero for a reason other than",
|
|
8791
|
+
"# broken-link detection (config error, IO error, network",
|
|
8792
|
+
"# failure for lychee, etc.). Stderr carries the diagnostic.",
|
|
8793
|
+
"#",
|
|
8794
|
+
"# The script never edits files. Diagnostics flow to stderr; the",
|
|
8795
|
+
"# only thing on stdout is the normalized JSON array.",
|
|
8796
|
+
"",
|
|
8797
|
+
"set -uo pipefail",
|
|
8798
|
+
"",
|
|
8799
|
+
"err() {",
|
|
8800
|
+
' printf "check-links.sh: %s\\n" "$*" >&2',
|
|
8801
|
+
"}",
|
|
8802
|
+
"",
|
|
8803
|
+
'if [ "$#" -lt 2 ]; then',
|
|
8804
|
+
' err "usage: check-links.sh <internal|external|all> <docs-root> [<site-dir>]"',
|
|
8805
|
+
" exit 1",
|
|
8806
|
+
"fi",
|
|
8807
|
+
"",
|
|
8808
|
+
'mode="$1"',
|
|
8809
|
+
'docs_root="$2"',
|
|
8810
|
+
'site_dir="${3:-}"',
|
|
8811
|
+
"",
|
|
8812
|
+
'case "$mode" in',
|
|
8813
|
+
" internal | external | all) ;;",
|
|
8814
|
+
" *)",
|
|
8815
|
+
` err "unknown mode '$mode' \u2014 expected 'internal', 'external', or 'all'"`,
|
|
8816
|
+
" exit 1",
|
|
8817
|
+
" ;;",
|
|
8818
|
+
"esac",
|
|
8819
|
+
"",
|
|
8820
|
+
'if [ ! -d "$docs_root" ]; then',
|
|
8821
|
+
' err "docs root not found: $docs_root"',
|
|
8822
|
+
" exit 1",
|
|
8823
|
+
"fi",
|
|
8824
|
+
"",
|
|
8825
|
+
'if [ -z "$site_dir" ]; then',
|
|
8826
|
+
" # Standard Starlight layout: <site>/src/content/docs. Walk two",
|
|
8827
|
+
" # parents up from the content root to recover the site dir.",
|
|
8828
|
+
' site_dir="$(cd "$docs_root/../../.." && pwd)"',
|
|
8829
|
+
"fi",
|
|
8830
|
+
"",
|
|
8831
|
+
"# `jq` is required to merge the per-tool JSON output streams. Fail",
|
|
8832
|
+
"# fast with the dedicated tool-missing exit code if it is absent.",
|
|
8833
|
+
"if ! command -v jq >/dev/null 2>&1; then",
|
|
8834
|
+
' err "jq is required but not on PATH"',
|
|
8835
|
+
" exit 2",
|
|
8836
|
+
"fi",
|
|
8837
|
+
"",
|
|
8838
|
+
'tmp_dir="$(mktemp -d -t check-links-XXXXXX)"',
|
|
8839
|
+
"# shellcheck disable=SC2064",
|
|
8840
|
+
`trap "rm -rf '$tmp_dir'" EXIT`,
|
|
8841
|
+
"",
|
|
8842
|
+
'internal_json="$tmp_dir/internal.json"',
|
|
8843
|
+
'external_json="$tmp_dir/external.json"',
|
|
8844
|
+
'printf "[]" > "$internal_json"',
|
|
8845
|
+
'printf "[]" > "$external_json"',
|
|
8846
|
+
"",
|
|
8847
|
+
"# ---------------------------------------------------------------",
|
|
8848
|
+
"# Internal-link check",
|
|
8849
|
+
"# ---------------------------------------------------------------",
|
|
8850
|
+
"# `astro check` runs inside the Starlight project root and walks",
|
|
8851
|
+
"# content collections to validate internal references. It exits",
|
|
8852
|
+
"# non-zero when references are broken \u2014 that is a *finding* for",
|
|
8853
|
+
"# this helper, not a tool failure, so we capture stderr and parse",
|
|
8854
|
+
"# it into the normalized record shape rather than propagating the",
|
|
8855
|
+
"# non-zero exit.",
|
|
8856
|
+
'if [ "$mode" = "internal" ] || [ "$mode" = "all" ]; then',
|
|
8857
|
+
" if ! command -v node >/dev/null 2>&1; then",
|
|
8858
|
+
' err "node is required but not on PATH"',
|
|
8859
|
+
" exit 2",
|
|
8860
|
+
" fi",
|
|
8861
|
+
"",
|
|
8862
|
+
' if [ ! -d "$site_dir" ]; then',
|
|
8863
|
+
' err "site dir not found: $site_dir"',
|
|
8864
|
+
" exit 1",
|
|
8865
|
+
" fi",
|
|
8866
|
+
"",
|
|
8867
|
+
" # Locate the astro binary in the site's local node_modules first",
|
|
8868
|
+
" # so the version matches what `pnpm install` resolved for the",
|
|
8869
|
+
" # Starlight site. Fall back to a workspace-aware `pnpm exec`.",
|
|
8870
|
+
' if [ -x "$site_dir/node_modules/.bin/astro" ]; then',
|
|
8871
|
+
' astro_bin=("$site_dir/node_modules/.bin/astro")',
|
|
8872
|
+
" elif command -v pnpm >/dev/null 2>&1; then",
|
|
8873
|
+
' astro_bin=(pnpm --dir "$site_dir" exec astro)',
|
|
8874
|
+
" else",
|
|
8875
|
+
' err "astro binary not found in $site_dir/node_modules/.bin and pnpm is unavailable"',
|
|
8876
|
+
" exit 2",
|
|
8877
|
+
" fi",
|
|
8878
|
+
"",
|
|
8879
|
+
' astro_log="$tmp_dir/astro.log"',
|
|
8880
|
+
" set +e",
|
|
8881
|
+
' ( cd "$site_dir" && "${astro_bin[@]}" check ) > "$astro_log" 2>&1',
|
|
8882
|
+
" astro_status=$?",
|
|
8883
|
+
" set -e",
|
|
8884
|
+
"",
|
|
8885
|
+
" # `astro check` exits 1 on findings and >=2 on tool failure.",
|
|
8886
|
+
" # Translate to the normalized record stream regardless of exit.",
|
|
8887
|
+
' if [ "$astro_status" -ge 2 ]; then',
|
|
8888
|
+
' err "astro check exited with status $astro_status (tool failure)"',
|
|
8889
|
+
' cat "$astro_log" >&2 || true',
|
|
8890
|
+
" exit 3",
|
|
8891
|
+
" fi",
|
|
8892
|
+
"",
|
|
8893
|
+
" # Parse the line-oriented log into JSON records. The `astro",
|
|
8894
|
+
" # check` output for a broken reference looks like:",
|
|
8895
|
+
" #",
|
|
8896
|
+
" # src/content/docs/foo.md:42:8 - error <code>: <reason>",
|
|
8897
|
+
" #",
|
|
8898
|
+
" # The helper extracts `<docPath>`, `<line>`, and `<reason>` and",
|
|
8899
|
+
" # synthesises a `url` field by quoting the offending excerpt",
|
|
8900
|
+
" # when astro reports one (it does not always); when no excerpt",
|
|
8901
|
+
" # is available we fall back to an empty string so the schema",
|
|
8902
|
+
" # stays uniform.",
|
|
8903
|
+
` awk -v docs_root="$docs_root" -v site_dir="$site_dir" '`,
|
|
8904
|
+
" BEGIN {",
|
|
8905
|
+
' printf "[";',
|
|
8906
|
+
" first = 1;",
|
|
8907
|
+
" }",
|
|
8908
|
+
' # Match "<path>:<line>:<col> - <severity> <code>: <message>"',
|
|
8909
|
+
" /^.+:[0-9]+:[0-9]+ - / {",
|
|
8910
|
+
' split($0, head, " - ");',
|
|
8911
|
+
' n = split(head[1], loc, ":");',
|
|
8912
|
+
" file = loc[1];",
|
|
8913
|
+
' for (i = 2; i < n - 1; i++) { file = file ":" loc[i]; }',
|
|
8914
|
+
" line = loc[n - 1];",
|
|
8915
|
+
' message = "";',
|
|
8916
|
+
" for (i = 2; i <= length(head); i++) {",
|
|
8917
|
+
' if (i > 2) message = message " - ";',
|
|
8918
|
+
" message = message head[i];",
|
|
8919
|
+
" }",
|
|
8920
|
+
" # Resolve the file relative to docs_root for stability.",
|
|
8921
|
+
" abs = file;",
|
|
8922
|
+
' if (substr(abs, 1, 1) != "/") {',
|
|
8923
|
+
' abs = site_dir "/" abs;',
|
|
8924
|
+
" }",
|
|
8925
|
+
' sub(docs_root "/", "", abs);',
|
|
8926
|
+
' gsub(/"/, "\\\\\\"", message);',
|
|
8927
|
+
' gsub(/"/, "\\\\\\"", abs);',
|
|
8928
|
+
' if (!first) printf ",";',
|
|
8929
|
+
" first = 0;",
|
|
8930
|
+
' printf "{\\"url\\":\\"\\",\\"docPath\\":\\"%s\\",\\"line\\":%s,\\"kind\\":\\"internal\\",\\"reason\\":\\"%s\\"}", abs, line, message;',
|
|
8931
|
+
" }",
|
|
8932
|
+
" END {",
|
|
8933
|
+
' printf "]";',
|
|
8934
|
+
" }",
|
|
8935
|
+
` ' "$astro_log" > "$internal_json"`,
|
|
8936
|
+
"",
|
|
8937
|
+
" # Validate the JSON we synthesised. Catches awk escaping bugs",
|
|
8938
|
+
" # before we hand the payload to the merge step.",
|
|
8939
|
+
' if ! jq -e . "$internal_json" >/dev/null 2>&1; then',
|
|
8940
|
+
' err "failed to normalize astro check output to JSON"',
|
|
8941
|
+
' cat "$internal_json" >&2 || true',
|
|
8942
|
+
" exit 3",
|
|
8943
|
+
" fi",
|
|
8944
|
+
"fi",
|
|
8945
|
+
"",
|
|
8946
|
+
"# ---------------------------------------------------------------",
|
|
8947
|
+
"# External-link check",
|
|
8948
|
+
"# ---------------------------------------------------------------",
|
|
8949
|
+
"# `lychee` walks the markdown tree and reports unreachable URLs.",
|
|
8950
|
+
"# Its `--format json` output is structured per-link; we project",
|
|
8951
|
+
"# only the failed entries into the normalized record shape.",
|
|
8952
|
+
'if [ "$mode" = "external" ] || [ "$mode" = "all" ]; then',
|
|
8953
|
+
" if ! command -v lychee >/dev/null 2>&1; then",
|
|
8954
|
+
' err "lychee is required but not on PATH (https://github.com/lycheeverse/lychee)"',
|
|
8955
|
+
" exit 2",
|
|
8956
|
+
" fi",
|
|
8957
|
+
"",
|
|
8958
|
+
' lychee_json="$tmp_dir/lychee.json"',
|
|
8959
|
+
" set +e",
|
|
8960
|
+
" # `--no-progress` avoids interactive carriage-returns mangling",
|
|
8961
|
+
" # the JSON. `--format json` emits a single object whose `fail_map`",
|
|
8962
|
+
" # field lists the broken URLs grouped by source file.",
|
|
8963
|
+
" # `--exclude-mail` skips mailto: links (out of scope for this",
|
|
8964
|
+
" # helper).",
|
|
8965
|
+
' lychee --no-progress --format json --exclude-mail "$docs_root" > "$lychee_json" 2>"$tmp_dir/lychee.err"',
|
|
8966
|
+
" lychee_status=$?",
|
|
8967
|
+
" set -e",
|
|
8968
|
+
"",
|
|
8969
|
+
" # lychee exits 0 when every link resolved, 2 when one or more",
|
|
8970
|
+
" # links failed. Anything else is a tool failure (config, IO).",
|
|
8971
|
+
' if [ "$lychee_status" -ne 0 ] && [ "$lychee_status" -ne 2 ]; then',
|
|
8972
|
+
' err "lychee exited with status $lychee_status (tool failure)"',
|
|
8973
|
+
' cat "$tmp_dir/lychee.err" >&2 || true',
|
|
8974
|
+
" exit 3",
|
|
8975
|
+
" fi",
|
|
8976
|
+
"",
|
|
8977
|
+
" # Project lychee's `fail_map` into the normalized record shape.",
|
|
8978
|
+
' # The `fail_map` is `{ "<source-path>": [ { url, status: { details? } }, ... ] }`.',
|
|
8979
|
+
" # lychee does not report a line number for every match, so we",
|
|
8980
|
+
" # default to 0 when it is missing (downstream consumers treat",
|
|
8981
|
+
' # 0 as "unknown line").',
|
|
8982
|
+
` jq --arg root "$docs_root" '`,
|
|
8983
|
+
" def trim_root($p; $r):",
|
|
8984
|
+
' if ($p | startswith($r + "/")) then ($p | ltrimstr($r + "/"))',
|
|
8985
|
+
' elif ($p == $r) then ""',
|
|
8986
|
+
" else $p end;",
|
|
8987
|
+
" [",
|
|
8988
|
+
" ( .fail_map // {} ) as $map",
|
|
8989
|
+
" | $map | to_entries[]",
|
|
8990
|
+
" | .key as $src",
|
|
8991
|
+
" | .value[]",
|
|
8992
|
+
" | {",
|
|
8993
|
+
' url: (.url // ""),',
|
|
8994
|
+
" docPath: trim_root($src; $root),",
|
|
8995
|
+
" line: ((.line // 0) | tonumber? // 0),",
|
|
8996
|
+
' kind: "external",',
|
|
8997
|
+
' reason: ( (.status.details // .status.text // .status.code // "broken") | tostring )',
|
|
8998
|
+
" }",
|
|
8999
|
+
" ]",
|
|
9000
|
+
` ' "$lychee_json" > "$external_json" 2>"$tmp_dir/lychee.parse.err"`,
|
|
9001
|
+
"",
|
|
9002
|
+
' if ! jq -e . "$external_json" >/dev/null 2>&1; then',
|
|
9003
|
+
' err "failed to parse lychee JSON output"',
|
|
9004
|
+
' cat "$tmp_dir/lychee.parse.err" >&2 || true',
|
|
9005
|
+
' cat "$lychee_json" >&2 || true',
|
|
9006
|
+
" exit 3",
|
|
9007
|
+
" fi",
|
|
9008
|
+
"fi",
|
|
9009
|
+
"",
|
|
9010
|
+
"# ---------------------------------------------------------------",
|
|
9011
|
+
"# Merge & emit",
|
|
9012
|
+
"# ---------------------------------------------------------------",
|
|
9013
|
+
"# Sort records deterministically (kind, docPath, line, url) so",
|
|
9014
|
+
"# downstream consumers can compare reports across runs without",
|
|
9015
|
+
"# fighting platform-specific ordering noise.",
|
|
9016
|
+
"jq -s '",
|
|
9017
|
+
" add",
|
|
9018
|
+
" | sort_by(.kind, .docPath, .line, .url)",
|
|
9019
|
+
`' "$internal_json" "$external_json"`,
|
|
9020
|
+
"",
|
|
9021
|
+
"exit 0"
|
|
9022
|
+
].join("\n");
|
|
9023
|
+
}
|
|
9024
|
+
var checkLinksProcedure = {
|
|
9025
|
+
name: "check-links.sh",
|
|
9026
|
+
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.",
|
|
9027
|
+
content: renderCheckLinksProcedure()
|
|
9028
|
+
};
|
|
9029
|
+
function renderCheckDocSamplesProcedure() {
|
|
9030
|
+
const nodeScript = [
|
|
9031
|
+
"(async () => {",
|
|
9032
|
+
" const { compileFencedSamples } = await import('@codedrifters/configulator');",
|
|
9033
|
+
" const docsRoot = process.argv[1];",
|
|
9034
|
+
" const failures = compileFencedSamples({ docsRoot });",
|
|
9035
|
+
" process.stdout.write(JSON.stringify(failures));",
|
|
9036
|
+
"})().catch((err) => {",
|
|
9037
|
+
" console.error('check-doc-samples.sh: ' + (err && err.stack ? err.stack : err));",
|
|
9038
|
+
" process.exit(3);",
|
|
9039
|
+
"});"
|
|
9040
|
+
].join("\n");
|
|
9041
|
+
return [
|
|
9042
|
+
"#!/usr/bin/env bash",
|
|
9043
|
+
"# check-doc-samples.sh \u2014 Compile fenced TS/TSX samples in the Starlight docs.",
|
|
9044
|
+
"#",
|
|
9045
|
+
"# Usage:",
|
|
9046
|
+
"# .claude/procedures/check-doc-samples.sh <docs-root>",
|
|
9047
|
+
"#",
|
|
9048
|
+
"# Where:",
|
|
9049
|
+
"# <docs-root> \u2014 path to the markdown content tree",
|
|
9050
|
+
"# (e.g. `docs/src/content/docs`). Walked recursively",
|
|
9051
|
+
"# for `*.md` files.",
|
|
9052
|
+
"#",
|
|
9053
|
+
"# Wraps the `compileFencedSamples` API exported from",
|
|
9054
|
+
"# `@codedrifters/configulator`. For every fenced ` ```ts `,",
|
|
9055
|
+
"# ` ```typescript `, ` ```tsx `, or ` ```typescriptreact ` block in",
|
|
9056
|
+
"# the docs, the helper writes the sample to an in-memory file,",
|
|
9057
|
+
"# runs the TypeScript compiler against it (in-process, via the TS",
|
|
9058
|
+
"# compiler API), and reports any compilation diagnostics.",
|
|
9059
|
+
"#",
|
|
9060
|
+
"# Skipped samples (fence meta `skip` / `no-check`, or first line",
|
|
9061
|
+
"# `// @no-check`) are passed through unchecked.",
|
|
9062
|
+
"#",
|
|
9063
|
+
"# Output (stdout, single JSON array):",
|
|
9064
|
+
"#",
|
|
9065
|
+
"# [",
|
|
9066
|
+
"# {",
|
|
9067
|
+
'# "docPath": "<path relative to <docs-root>>",',
|
|
9068
|
+
'# "line": <1-indexed line number of the opening fence>,',
|
|
9069
|
+
'# "fenceIndex": <0-indexed fenced-block position within the doc>,',
|
|
9070
|
+
'# "lang": "ts" | "typescript" | "tsx" | "typescriptreact",',
|
|
9071
|
+
'# "diagnostics": [ "<TS<code>: <message>>", ... ]',
|
|
9072
|
+
"# },",
|
|
9073
|
+
"# ...",
|
|
9074
|
+
"# ]",
|
|
9075
|
+
"#",
|
|
9076
|
+
"# An empty array (`[]`) is emitted when every sample compiled.",
|
|
9077
|
+
"# Detection is **data**, not failure: this helper exits 0 when the",
|
|
9078
|
+
"# compilation phase ran successfully, regardless of how many",
|
|
9079
|
+
"# samples failed to compile. The downstream docs-sync scan phase",
|
|
9080
|
+
"# (#520) is responsible for treating failures as one of the two",
|
|
9081
|
+
"# hard-block cases per the parent epic.",
|
|
9082
|
+
"#",
|
|
9083
|
+
"# Exit codes:",
|
|
9084
|
+
"# 0 \u2014 compilation phase ran; failures (zero or more) are on",
|
|
9085
|
+
"# stdout as a JSON array.",
|
|
9086
|
+
"# 1 \u2014 usage error (missing args, unreadable docs root).",
|
|
9087
|
+
"# 2 \u2014 a required binary is not on PATH (`node` / `pnpm`).",
|
|
9088
|
+
"# 3 \u2014 the compilation phase threw an unhandled exception.",
|
|
9089
|
+
"# Stderr carries the diagnostic.",
|
|
9090
|
+
"#",
|
|
9091
|
+
"# Dependencies: expects `node` and `pnpm` on PATH and the workspace",
|
|
9092
|
+
"# `@codedrifters/configulator` package to be installed (the helper",
|
|
9093
|
+
"# resolves the API through `pnpm exec node` from the repo root).",
|
|
9094
|
+
"",
|
|
9095
|
+
"set -uo pipefail",
|
|
9096
|
+
"",
|
|
9097
|
+
"err() {",
|
|
9098
|
+
' printf "check-doc-samples.sh: %s\\n" "$*" >&2',
|
|
9099
|
+
"}",
|
|
9100
|
+
"",
|
|
9101
|
+
'if [ "$#" -lt 1 ]; then',
|
|
9102
|
+
' err "usage: check-doc-samples.sh <docs-root>"',
|
|
9103
|
+
" exit 1",
|
|
9104
|
+
"fi",
|
|
9105
|
+
"",
|
|
9106
|
+
'docs_root="$1"',
|
|
9107
|
+
"",
|
|
9108
|
+
'if [ ! -d "$docs_root" ]; then',
|
|
9109
|
+
' err "docs root not found: $docs_root"',
|
|
9110
|
+
" exit 1",
|
|
9111
|
+
"fi",
|
|
9112
|
+
"",
|
|
9113
|
+
"if ! command -v node >/dev/null 2>&1; then",
|
|
9114
|
+
' err "node is required but not on PATH"',
|
|
9115
|
+
" exit 2",
|
|
9116
|
+
"fi",
|
|
9117
|
+
"",
|
|
9118
|
+
"if ! command -v pnpm >/dev/null 2>&1; then",
|
|
9119
|
+
' err "pnpm is required but not on PATH"',
|
|
9120
|
+
" exit 2",
|
|
9121
|
+
"fi",
|
|
9122
|
+
"",
|
|
9123
|
+
"# Resolve docs_root to an absolute path so the inline node script",
|
|
9124
|
+
"# is independent of the cwd `pnpm exec` chooses.",
|
|
9125
|
+
'docs_root_abs="$(cd "$docs_root" && pwd)"',
|
|
9126
|
+
"",
|
|
9127
|
+
"# Run the inline node script through `pnpm exec` so module",
|
|
9128
|
+
"# resolution finds `@codedrifters/configulator` via the workspace",
|
|
9129
|
+
"# even when the helper is invoked from a sub-package directory.",
|
|
9130
|
+
"# The `--input-type=module` flag lets us use top-level `await` and",
|
|
9131
|
+
"# `import()` cleanly. Diagnostics from the script flow to stderr;",
|
|
9132
|
+
"# stdout carries only the JSON array.",
|
|
9133
|
+
'pnpm exec node --input-type=module -e "' + nodeScript.replace(/"/g, '\\"') + '" "$docs_root_abs"',
|
|
9134
|
+
"status=$?",
|
|
9135
|
+
"",
|
|
9136
|
+
'if [ "$status" -ne 0 ]; then',
|
|
9137
|
+
' err "compileFencedSamples exited with status $status"',
|
|
9138
|
+
" exit 3",
|
|
9139
|
+
"fi",
|
|
9140
|
+
"",
|
|
9141
|
+
"exit 0"
|
|
9142
|
+
].join("\n");
|
|
9143
|
+
}
|
|
9144
|
+
var checkDocSamplesProcedure = {
|
|
9145
|
+
name: "check-doc-samples.sh",
|
|
9146
|
+
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.",
|
|
9147
|
+
content: renderCheckDocSamplesProcedure()
|
|
9148
|
+
};
|
|
8740
9149
|
function buildDocsSyncBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
8741
9150
|
return {
|
|
8742
9151
|
name: "docs-sync",
|
|
@@ -8786,7 +9195,11 @@ function buildDocsSyncBundle(paths = DEFAULT_AGENT_PATHS) {
|
|
|
8786
9195
|
],
|
|
8787
9196
|
skills: [buildDocsSyncPrSkill(), buildDocsSyncAuditSkill()],
|
|
8788
9197
|
subAgents: [buildDocsSyncSubAgent(paths)],
|
|
8789
|
-
procedures: [
|
|
9198
|
+
procedures: [
|
|
9199
|
+
extractApiProcedure,
|
|
9200
|
+
checkLinksProcedure,
|
|
9201
|
+
checkDocSamplesProcedure
|
|
9202
|
+
],
|
|
8790
9203
|
labels: [
|
|
8791
9204
|
{
|
|
8792
9205
|
name: "type:docs-sync",
|
|
@@ -26530,8 +26943,8 @@ var FALLBACKS = {
|
|
|
26530
26943
|
monorepoLayoutSeedBlock: ""
|
|
26531
26944
|
};
|
|
26532
26945
|
var TEMPLATE_RE = /\{\{(\w+(?:\.\w+)*)\}\}/g;
|
|
26533
|
-
function getNestedValue(obj,
|
|
26534
|
-
const parts =
|
|
26946
|
+
function getNestedValue(obj, path6) {
|
|
26947
|
+
const parts = path6.split(".");
|
|
26535
26948
|
let current = obj;
|
|
26536
26949
|
for (const part of parts) {
|
|
26537
26950
|
if (current == null || typeof current !== "object") {
|
|
@@ -26757,20 +27170,20 @@ var ClaudeRenderer = class _ClaudeRenderer {
|
|
|
26757
27170
|
obj.excludedCommands = [...sandbox.excludedCommands];
|
|
26758
27171
|
}
|
|
26759
27172
|
if (sandbox.filesystem) {
|
|
26760
|
-
const
|
|
27173
|
+
const fs3 = {};
|
|
26761
27174
|
if (sandbox.filesystem.allowRead?.length) {
|
|
26762
|
-
|
|
27175
|
+
fs3.allowRead = [...sandbox.filesystem.allowRead];
|
|
26763
27176
|
}
|
|
26764
27177
|
if (sandbox.filesystem.denyRead?.length) {
|
|
26765
|
-
|
|
27178
|
+
fs3.denyRead = [...sandbox.filesystem.denyRead];
|
|
26766
27179
|
}
|
|
26767
27180
|
if (sandbox.filesystem.allowWrite?.length) {
|
|
26768
|
-
|
|
27181
|
+
fs3.allowWrite = [...sandbox.filesystem.allowWrite];
|
|
26769
27182
|
}
|
|
26770
27183
|
if (sandbox.filesystem.denyWrite?.length) {
|
|
26771
|
-
|
|
27184
|
+
fs3.denyWrite = [...sandbox.filesystem.denyWrite];
|
|
26772
27185
|
}
|
|
26773
|
-
if (Object.keys(
|
|
27186
|
+
if (Object.keys(fs3).length > 0) obj.filesystem = fs3;
|
|
26774
27187
|
}
|
|
26775
27188
|
if (sandbox.network) {
|
|
26776
27189
|
const net = {};
|
|
@@ -28632,10 +29045,357 @@ function stripYamlFrontmatter(source) {
|
|
|
28632
29045
|
return lines.join("\n");
|
|
28633
29046
|
}
|
|
28634
29047
|
|
|
28635
|
-
// src/docs-sync/
|
|
29048
|
+
// src/docs-sync/sample-compilation/compilation.ts
|
|
29049
|
+
import * as path4 from "path";
|
|
29050
|
+
import * as ts from "typescript";
|
|
29051
|
+
|
|
29052
|
+
// src/docs-sync/sample-compilation/extraction.ts
|
|
29053
|
+
import * as fs2 from "fs";
|
|
28636
29054
|
import * as path3 from "path";
|
|
29055
|
+
import { fromMarkdown as fromMarkdown2 } from "mdast-util-from-markdown";
|
|
29056
|
+
var SampleLang = {
|
|
29057
|
+
Ts: "ts",
|
|
29058
|
+
Typescript: "typescript",
|
|
29059
|
+
Tsx: "tsx",
|
|
29060
|
+
Typescriptreact: "typescriptreact"
|
|
29061
|
+
};
|
|
29062
|
+
var DEFAULT_DOCS_ROOT2 = "docs/src/content/docs";
|
|
29063
|
+
var PLAIN_LANGS = /* @__PURE__ */ new Set([SampleLang.Ts, SampleLang.Typescript]);
|
|
29064
|
+
var JSX_LANGS = /* @__PURE__ */ new Set([
|
|
29065
|
+
SampleLang.Tsx,
|
|
29066
|
+
SampleLang.Typescriptreact
|
|
29067
|
+
]);
|
|
29068
|
+
function extractFencedSamples(options = {}) {
|
|
29069
|
+
const docsRoot = path3.resolve(options.docsRoot ?? DEFAULT_DOCS_ROOT2);
|
|
29070
|
+
const includeTsx = options.includeTsx ?? true;
|
|
29071
|
+
if (!fs2.existsSync(docsRoot)) {
|
|
29072
|
+
return [];
|
|
29073
|
+
}
|
|
29074
|
+
const records = [];
|
|
29075
|
+
for (const absolutePath of walkMarkdownFiles2(docsRoot)) {
|
|
29076
|
+
const docPath = toPosix2(path3.relative(docsRoot, absolutePath));
|
|
29077
|
+
const source = fs2.readFileSync(absolutePath, "utf-8");
|
|
29078
|
+
const tree = fromMarkdown2(stripYamlFrontmatter2(source));
|
|
29079
|
+
let fenceIndex = 0;
|
|
29080
|
+
collectFencedCode(tree, (node) => {
|
|
29081
|
+
const lang = normalizeLang(node.lang);
|
|
29082
|
+
if (lang === void 0) {
|
|
29083
|
+
return;
|
|
29084
|
+
}
|
|
29085
|
+
if (!includeTsx && JSX_LANGS.has(lang)) {
|
|
29086
|
+
return;
|
|
29087
|
+
}
|
|
29088
|
+
if (!PLAIN_LANGS.has(lang) && !JSX_LANGS.has(lang)) {
|
|
29089
|
+
return;
|
|
29090
|
+
}
|
|
29091
|
+
const start = node.position?.start;
|
|
29092
|
+
if (!start) {
|
|
29093
|
+
return;
|
|
29094
|
+
}
|
|
29095
|
+
const skip = detectSkip(node);
|
|
29096
|
+
records.push({
|
|
29097
|
+
docPath,
|
|
29098
|
+
line: start.line,
|
|
29099
|
+
fenceIndex,
|
|
29100
|
+
lang,
|
|
29101
|
+
code: node.value ?? "",
|
|
29102
|
+
skipped: skip.skipped,
|
|
29103
|
+
skipReason: skip.reason
|
|
29104
|
+
});
|
|
29105
|
+
fenceIndex += 1;
|
|
29106
|
+
});
|
|
29107
|
+
}
|
|
29108
|
+
records.sort((a, b) => {
|
|
29109
|
+
if (a.docPath !== b.docPath) {
|
|
29110
|
+
return a.docPath.localeCompare(b.docPath);
|
|
29111
|
+
}
|
|
29112
|
+
if (a.line !== b.line) {
|
|
29113
|
+
return a.line - b.line;
|
|
29114
|
+
}
|
|
29115
|
+
return a.fenceIndex - b.fenceIndex;
|
|
29116
|
+
});
|
|
29117
|
+
return records;
|
|
29118
|
+
}
|
|
29119
|
+
function walkMarkdownFiles2(root) {
|
|
29120
|
+
const out = [];
|
|
29121
|
+
const stack = [root];
|
|
29122
|
+
while (stack.length > 0) {
|
|
29123
|
+
const dir = stack.pop();
|
|
29124
|
+
let entries;
|
|
29125
|
+
try {
|
|
29126
|
+
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
29127
|
+
} catch {
|
|
29128
|
+
continue;
|
|
29129
|
+
}
|
|
29130
|
+
for (const entry of entries) {
|
|
29131
|
+
const full = path3.join(dir, entry.name);
|
|
29132
|
+
if (entry.isDirectory()) {
|
|
29133
|
+
stack.push(full);
|
|
29134
|
+
} else if (entry.isFile() && full.toLowerCase().endsWith(".md")) {
|
|
29135
|
+
out.push(full);
|
|
29136
|
+
}
|
|
29137
|
+
}
|
|
29138
|
+
}
|
|
29139
|
+
out.sort();
|
|
29140
|
+
return out;
|
|
29141
|
+
}
|
|
29142
|
+
function collectFencedCode(tree, visit) {
|
|
29143
|
+
walk(tree);
|
|
29144
|
+
function walk(node) {
|
|
29145
|
+
if (node.type === "code") {
|
|
29146
|
+
visit(node);
|
|
29147
|
+
return;
|
|
29148
|
+
}
|
|
29149
|
+
if (node.type === "html") {
|
|
29150
|
+
return;
|
|
29151
|
+
}
|
|
29152
|
+
const parent = node;
|
|
29153
|
+
if (Array.isArray(parent.children)) {
|
|
29154
|
+
for (const child of parent.children) {
|
|
29155
|
+
walk(child);
|
|
29156
|
+
}
|
|
29157
|
+
}
|
|
29158
|
+
}
|
|
29159
|
+
}
|
|
29160
|
+
function normalizeLang(raw) {
|
|
29161
|
+
if (!raw) {
|
|
29162
|
+
return void 0;
|
|
29163
|
+
}
|
|
29164
|
+
const lower = raw.toLowerCase();
|
|
29165
|
+
switch (lower) {
|
|
29166
|
+
case "ts":
|
|
29167
|
+
return SampleLang.Ts;
|
|
29168
|
+
case "typescript":
|
|
29169
|
+
return SampleLang.Typescript;
|
|
29170
|
+
case "tsx":
|
|
29171
|
+
return SampleLang.Tsx;
|
|
29172
|
+
case "typescriptreact":
|
|
29173
|
+
return SampleLang.Typescriptreact;
|
|
29174
|
+
default:
|
|
29175
|
+
return void 0;
|
|
29176
|
+
}
|
|
29177
|
+
}
|
|
29178
|
+
var META_SKIP_PATTERN = /\b(skip|no-check)\b/i;
|
|
29179
|
+
var PRAGMA_NO_CHECK_PATTERN = /^\s*\/\/\s*@no-check\b/i;
|
|
29180
|
+
function detectSkip(node) {
|
|
29181
|
+
const meta = node.meta ?? "";
|
|
29182
|
+
const metaMatch = META_SKIP_PATTERN.exec(meta);
|
|
29183
|
+
if (metaMatch) {
|
|
29184
|
+
return { skipped: true, reason: `meta:${metaMatch[1].toLowerCase()}` };
|
|
29185
|
+
}
|
|
29186
|
+
const code = node.value ?? "";
|
|
29187
|
+
const firstLine = firstNonBlankLine(code);
|
|
29188
|
+
if (firstLine !== void 0 && PRAGMA_NO_CHECK_PATTERN.test(firstLine)) {
|
|
29189
|
+
return { skipped: true, reason: "pragma:@no-check" };
|
|
29190
|
+
}
|
|
29191
|
+
return { skipped: false, reason: "" };
|
|
29192
|
+
}
|
|
29193
|
+
function firstNonBlankLine(source) {
|
|
29194
|
+
for (const line of source.split("\n")) {
|
|
29195
|
+
if (line.trim().length > 0) {
|
|
29196
|
+
return line;
|
|
29197
|
+
}
|
|
29198
|
+
}
|
|
29199
|
+
return void 0;
|
|
29200
|
+
}
|
|
29201
|
+
function toPosix2(p) {
|
|
29202
|
+
return p.split(path3.sep).join("/");
|
|
29203
|
+
}
|
|
29204
|
+
function stripYamlFrontmatter2(source) {
|
|
29205
|
+
if (!source.startsWith("---")) {
|
|
29206
|
+
return source;
|
|
29207
|
+
}
|
|
29208
|
+
const lines = source.split("\n");
|
|
29209
|
+
if (lines[0] !== "---") {
|
|
29210
|
+
return source;
|
|
29211
|
+
}
|
|
29212
|
+
let endIndex = -1;
|
|
29213
|
+
for (let i = 1; i < lines.length; i++) {
|
|
29214
|
+
if (lines[i] === "---") {
|
|
29215
|
+
endIndex = i;
|
|
29216
|
+
break;
|
|
29217
|
+
}
|
|
29218
|
+
}
|
|
29219
|
+
if (endIndex < 0) {
|
|
29220
|
+
return source;
|
|
29221
|
+
}
|
|
29222
|
+
for (let i = 0; i <= endIndex; i++) {
|
|
29223
|
+
lines[i] = "";
|
|
29224
|
+
}
|
|
29225
|
+
return lines.join("\n");
|
|
29226
|
+
}
|
|
29227
|
+
|
|
29228
|
+
// src/docs-sync/sample-compilation/compilation.ts
|
|
29229
|
+
var DEFAULT_SAMPLE_COMPILER_OPTIONS = {
|
|
29230
|
+
target: ts.ScriptTarget.ES2022,
|
|
29231
|
+
module: ts.ModuleKind.ESNext,
|
|
29232
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
29233
|
+
lib: ["lib.es2022.d.ts", "lib.dom.d.ts"],
|
|
29234
|
+
strict: true,
|
|
29235
|
+
noUnusedLocals: false,
|
|
29236
|
+
noUnusedParameters: false,
|
|
29237
|
+
skipLibCheck: true,
|
|
29238
|
+
esModuleInterop: true,
|
|
29239
|
+
resolveJsonModule: true,
|
|
29240
|
+
isolatedModules: true,
|
|
29241
|
+
jsx: ts.JsxEmit.Preserve,
|
|
29242
|
+
noEmit: true,
|
|
29243
|
+
allowSyntheticDefaultImports: true
|
|
29244
|
+
};
|
|
29245
|
+
var REPO_RELATIVE_MODULE_PATTERN = /^['"](?:\.\.?\/|\/)/;
|
|
29246
|
+
function compileFencedSamples(options = {}) {
|
|
29247
|
+
const samples = extractFencedSamples(options);
|
|
29248
|
+
const tolerateRepoRelative = options.tolerateRepoRelativeMissingModule ?? true;
|
|
29249
|
+
const failures = [];
|
|
29250
|
+
for (const sample of samples) {
|
|
29251
|
+
if (sample.skipped) {
|
|
29252
|
+
continue;
|
|
29253
|
+
}
|
|
29254
|
+
const diagnostics = compileSingleSample(
|
|
29255
|
+
sample,
|
|
29256
|
+
options.compilerOptions,
|
|
29257
|
+
tolerateRepoRelative
|
|
29258
|
+
);
|
|
29259
|
+
if (diagnostics.length > 0) {
|
|
29260
|
+
failures.push({
|
|
29261
|
+
docPath: sample.docPath,
|
|
29262
|
+
line: sample.line,
|
|
29263
|
+
fenceIndex: sample.fenceIndex,
|
|
29264
|
+
lang: sample.lang,
|
|
29265
|
+
diagnostics
|
|
29266
|
+
});
|
|
29267
|
+
}
|
|
29268
|
+
}
|
|
29269
|
+
return failures;
|
|
29270
|
+
}
|
|
29271
|
+
function compileSingleSample(sample, overrides, tolerateRepoRelative) {
|
|
29272
|
+
const filename = sampleFilename(sample);
|
|
29273
|
+
const compilerOptions = mergeCompilerOptions(overrides);
|
|
29274
|
+
const host = createSingleFileHost(filename, sample.code, compilerOptions);
|
|
29275
|
+
const program = ts.createProgram({
|
|
29276
|
+
rootNames: [filename],
|
|
29277
|
+
options: compilerOptions,
|
|
29278
|
+
host
|
|
29279
|
+
});
|
|
29280
|
+
const sourceFile = program.getSourceFile(filename);
|
|
29281
|
+
const collected = [];
|
|
29282
|
+
if (sourceFile) {
|
|
29283
|
+
collected.push(...program.getSyntacticDiagnostics(sourceFile));
|
|
29284
|
+
collected.push(...program.getSemanticDiagnostics(sourceFile));
|
|
29285
|
+
} else {
|
|
29286
|
+
collected.push(...program.getSyntacticDiagnostics());
|
|
29287
|
+
collected.push(...program.getSemanticDiagnostics());
|
|
29288
|
+
}
|
|
29289
|
+
const diagnostics = [];
|
|
29290
|
+
for (const d of collected) {
|
|
29291
|
+
if (tolerateRepoRelative && isRepoRelativeMissingModule(d, sample.code)) {
|
|
29292
|
+
continue;
|
|
29293
|
+
}
|
|
29294
|
+
diagnostics.push(formatDiagnostic(d));
|
|
29295
|
+
}
|
|
29296
|
+
return diagnostics;
|
|
29297
|
+
}
|
|
29298
|
+
function sampleFilename(sample) {
|
|
29299
|
+
const safeDoc = sample.docPath.replace(/[^a-zA-Z0-9]+/g, "_");
|
|
29300
|
+
const ext = sample.lang === SampleLang.Tsx || sample.lang === SampleLang.Typescriptreact ? "tsx" : "ts";
|
|
29301
|
+
return path4.posix.join("/", `${safeDoc}__${sample.fenceIndex}.${ext}`);
|
|
29302
|
+
}
|
|
29303
|
+
function mergeCompilerOptions(overrides) {
|
|
29304
|
+
if (!overrides) {
|
|
29305
|
+
return { ...DEFAULT_SAMPLE_COMPILER_OPTIONS };
|
|
29306
|
+
}
|
|
29307
|
+
const merged = { ...DEFAULT_SAMPLE_COMPILER_OPTIONS };
|
|
29308
|
+
for (const key of Object.keys(overrides)) {
|
|
29309
|
+
const value = overrides[key];
|
|
29310
|
+
if (value === void 0) {
|
|
29311
|
+
delete merged[key];
|
|
29312
|
+
} else {
|
|
29313
|
+
merged[key] = value;
|
|
29314
|
+
}
|
|
29315
|
+
}
|
|
29316
|
+
return merged;
|
|
29317
|
+
}
|
|
29318
|
+
function createSingleFileHost(filename, source, compilerOptions) {
|
|
29319
|
+
const baseHost = ts.createCompilerHost(compilerOptions, true);
|
|
29320
|
+
return {
|
|
29321
|
+
...baseHost,
|
|
29322
|
+
getSourceFile(name, languageVersionOrOptions, onError, shouldCreate) {
|
|
29323
|
+
if (name === filename) {
|
|
29324
|
+
return ts.createSourceFile(
|
|
29325
|
+
name,
|
|
29326
|
+
source,
|
|
29327
|
+
languageVersionOrOptions,
|
|
29328
|
+
true
|
|
29329
|
+
);
|
|
29330
|
+
}
|
|
29331
|
+
return baseHost.getSourceFile(
|
|
29332
|
+
name,
|
|
29333
|
+
languageVersionOrOptions,
|
|
29334
|
+
onError,
|
|
29335
|
+
shouldCreate
|
|
29336
|
+
);
|
|
29337
|
+
},
|
|
29338
|
+
fileExists(name) {
|
|
29339
|
+
if (name === filename) {
|
|
29340
|
+
return true;
|
|
29341
|
+
}
|
|
29342
|
+
return baseHost.fileExists(name);
|
|
29343
|
+
},
|
|
29344
|
+
readFile(name) {
|
|
29345
|
+
if (name === filename) {
|
|
29346
|
+
return source;
|
|
29347
|
+
}
|
|
29348
|
+
return baseHost.readFile(name);
|
|
29349
|
+
},
|
|
29350
|
+
writeFile() {
|
|
29351
|
+
},
|
|
29352
|
+
getCanonicalFileName(name) {
|
|
29353
|
+
return baseHost.getCanonicalFileName(name);
|
|
29354
|
+
},
|
|
29355
|
+
useCaseSensitiveFileNames() {
|
|
29356
|
+
return baseHost.useCaseSensitiveFileNames();
|
|
29357
|
+
},
|
|
29358
|
+
getNewLine() {
|
|
29359
|
+
return baseHost.getNewLine();
|
|
29360
|
+
},
|
|
29361
|
+
getDefaultLibFileName(o) {
|
|
29362
|
+
return baseHost.getDefaultLibFileName(o);
|
|
29363
|
+
},
|
|
29364
|
+
getCurrentDirectory() {
|
|
29365
|
+
return baseHost.getCurrentDirectory();
|
|
29366
|
+
}
|
|
29367
|
+
};
|
|
29368
|
+
}
|
|
29369
|
+
function isRepoRelativeMissingModule(d, code) {
|
|
29370
|
+
if (d.code !== 2307) {
|
|
29371
|
+
return false;
|
|
29372
|
+
}
|
|
29373
|
+
const text = ts.flattenDiagnosticMessageText(d.messageText, "\n");
|
|
29374
|
+
const match = /'([^']+)'/.exec(text);
|
|
29375
|
+
if (!match) {
|
|
29376
|
+
return false;
|
|
29377
|
+
}
|
|
29378
|
+
const specifier = match[1];
|
|
29379
|
+
if (!specifier) {
|
|
29380
|
+
return false;
|
|
29381
|
+
}
|
|
29382
|
+
const inSourceSingle = `'${specifier}'`;
|
|
29383
|
+
const inSourceDouble = `"${specifier}"`;
|
|
29384
|
+
if (!code.includes(inSourceSingle) && !code.includes(inSourceDouble)) {
|
|
29385
|
+
return false;
|
|
29386
|
+
}
|
|
29387
|
+
return REPO_RELATIVE_MODULE_PATTERN.test(`'${specifier}'`);
|
|
29388
|
+
}
|
|
29389
|
+
function formatDiagnostic(d) {
|
|
29390
|
+
const text = ts.flattenDiagnosticMessageText(d.messageText, " ");
|
|
29391
|
+
const code = d.code === void 0 ? "" : `TS${d.code}: `;
|
|
29392
|
+
return `${code}${text}`.trim();
|
|
29393
|
+
}
|
|
29394
|
+
|
|
29395
|
+
// src/docs-sync/tsdoc-coverage/coverage.ts
|
|
29396
|
+
import * as path5 from "path";
|
|
28637
29397
|
import { TSDocParser } from "@microsoft/tsdoc";
|
|
28638
|
-
import * as
|
|
29398
|
+
import * as ts2 from "typescript";
|
|
28639
29399
|
var TsDocCoverageKind = {
|
|
28640
29400
|
Class: "Class",
|
|
28641
29401
|
Interface: "Interface",
|
|
@@ -28651,8 +29411,8 @@ var DEFAULT_THIN_SUMMARY_WORD_THRESHOLD = 4;
|
|
|
28651
29411
|
var DEFAULT_ENTRY_POINT = "src/index.ts";
|
|
28652
29412
|
function analyzeTsDocCoverage(options) {
|
|
28653
29413
|
const resolvedOptions = typeof options === "string" ? { packageRoot: options } : options;
|
|
28654
|
-
const packageRoot =
|
|
28655
|
-
const entryPoint =
|
|
29414
|
+
const packageRoot = path5.resolve(resolvedOptions.packageRoot);
|
|
29415
|
+
const entryPoint = path5.resolve(
|
|
28656
29416
|
packageRoot,
|
|
28657
29417
|
resolvedOptions.entryPoint ?? DEFAULT_ENTRY_POINT
|
|
28658
29418
|
);
|
|
@@ -28661,7 +29421,7 @@ function analyzeTsDocCoverage(options) {
|
|
|
28661
29421
|
packageRoot,
|
|
28662
29422
|
resolvedOptions.tsconfigPath
|
|
28663
29423
|
);
|
|
28664
|
-
const program =
|
|
29424
|
+
const program = ts2.createProgram({
|
|
28665
29425
|
rootNames: [entryPoint],
|
|
28666
29426
|
options: compilerOptions
|
|
28667
29427
|
});
|
|
@@ -28715,24 +29475,24 @@ function analyzeTsDocCoverage(options) {
|
|
|
28715
29475
|
}
|
|
28716
29476
|
function resolveCompilerOptions(packageRoot, tsconfigPath) {
|
|
28717
29477
|
if (tsconfigPath) {
|
|
28718
|
-
const absoluteTsconfig =
|
|
28719
|
-
const configFile =
|
|
29478
|
+
const absoluteTsconfig = path5.resolve(packageRoot, tsconfigPath);
|
|
29479
|
+
const configFile = ts2.readConfigFile(absoluteTsconfig, ts2.sys.readFile);
|
|
28720
29480
|
if (configFile.error) {
|
|
28721
29481
|
throw new Error(
|
|
28722
|
-
`analyzeTsDocCoverage: failed to read tsconfig at ${absoluteTsconfig}: ${
|
|
29482
|
+
`analyzeTsDocCoverage: failed to read tsconfig at ${absoluteTsconfig}: ${ts2.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
|
|
28723
29483
|
);
|
|
28724
29484
|
}
|
|
28725
|
-
const parsed =
|
|
29485
|
+
const parsed = ts2.parseJsonConfigFileContent(
|
|
28726
29486
|
configFile.config,
|
|
28727
|
-
|
|
28728
|
-
|
|
29487
|
+
ts2.sys,
|
|
29488
|
+
path5.dirname(absoluteTsconfig)
|
|
28729
29489
|
);
|
|
28730
29490
|
return { ...parsed.options, noEmit: true };
|
|
28731
29491
|
}
|
|
28732
29492
|
return {
|
|
28733
|
-
target:
|
|
28734
|
-
module:
|
|
28735
|
-
moduleResolution:
|
|
29493
|
+
target: ts2.ScriptTarget.ESNext,
|
|
29494
|
+
module: ts2.ModuleKind.NodeNext,
|
|
29495
|
+
moduleResolution: ts2.ModuleResolutionKind.NodeNext,
|
|
28736
29496
|
allowJs: false,
|
|
28737
29497
|
declaration: false,
|
|
28738
29498
|
noEmit: true,
|
|
@@ -28757,7 +29517,7 @@ function resolveAlias(symbol, checker) {
|
|
|
28757
29517
|
return current;
|
|
28758
29518
|
}
|
|
28759
29519
|
function isAlias(symbol) {
|
|
28760
|
-
return (symbol.flags &
|
|
29520
|
+
return (symbol.flags & ts2.SymbolFlags.Alias) !== 0;
|
|
28761
29521
|
}
|
|
28762
29522
|
function pickPrimaryDeclaration(symbol) {
|
|
28763
29523
|
const declarations = symbol.getDeclarations();
|
|
@@ -28765,53 +29525,53 @@ function pickPrimaryDeclaration(symbol) {
|
|
|
28765
29525
|
return void 0;
|
|
28766
29526
|
}
|
|
28767
29527
|
const concrete = declarations.find(
|
|
28768
|
-
(d) => !
|
|
29528
|
+
(d) => !ts2.isExportSpecifier(d) && !ts2.isExportAssignment(d) && !ts2.isExportDeclaration(d)
|
|
28769
29529
|
);
|
|
28770
29530
|
return concrete ?? declarations[0];
|
|
28771
29531
|
}
|
|
28772
29532
|
function classifyDeclaration(declaration) {
|
|
28773
|
-
if (
|
|
29533
|
+
if (ts2.isClassDeclaration(declaration)) {
|
|
28774
29534
|
return TsDocCoverageKind.Class;
|
|
28775
29535
|
}
|
|
28776
|
-
if (
|
|
29536
|
+
if (ts2.isInterfaceDeclaration(declaration)) {
|
|
28777
29537
|
return TsDocCoverageKind.Interface;
|
|
28778
29538
|
}
|
|
28779
|
-
if (
|
|
29539
|
+
if (ts2.isTypeAliasDeclaration(declaration)) {
|
|
28780
29540
|
return TsDocCoverageKind.TypeAlias;
|
|
28781
29541
|
}
|
|
28782
|
-
if (
|
|
29542
|
+
if (ts2.isEnumDeclaration(declaration)) {
|
|
28783
29543
|
return TsDocCoverageKind.Enum;
|
|
28784
29544
|
}
|
|
28785
|
-
if (
|
|
29545
|
+
if (ts2.isFunctionDeclaration(declaration) || ts2.isMethodDeclaration(declaration)) {
|
|
28786
29546
|
return TsDocCoverageKind.Function;
|
|
28787
29547
|
}
|
|
28788
|
-
if (
|
|
29548
|
+
if (ts2.isVariableDeclaration(declaration) || ts2.isVariableStatement(declaration)) {
|
|
28789
29549
|
return TsDocCoverageKind.Variable;
|
|
28790
29550
|
}
|
|
28791
|
-
if (
|
|
29551
|
+
if (ts2.isModuleDeclaration(declaration)) {
|
|
28792
29552
|
return TsDocCoverageKind.Module;
|
|
28793
29553
|
}
|
|
28794
|
-
if (
|
|
29554
|
+
if (ts2.isExportAssignment(declaration)) {
|
|
28795
29555
|
return TsDocCoverageKind.Default;
|
|
28796
29556
|
}
|
|
28797
29557
|
return TsDocCoverageKind.Other;
|
|
28798
29558
|
}
|
|
28799
29559
|
function resolveLocation(declaration) {
|
|
28800
|
-
const target =
|
|
29560
|
+
const target = ts2.isVariableDeclaration(declaration) ? declaration.parent.parent ?? declaration : declaration;
|
|
28801
29561
|
const source = target.getSourceFile();
|
|
28802
29562
|
const { line } = source.getLineAndCharacterOfPosition(target.getStart());
|
|
28803
29563
|
return { file: source.fileName, line: line + 1 };
|
|
28804
29564
|
}
|
|
28805
29565
|
function parseTsDocFor(declaration, parser) {
|
|
28806
|
-
const target =
|
|
29566
|
+
const target = ts2.isVariableDeclaration(declaration) ? declaration.parent.parent ?? declaration : declaration;
|
|
28807
29567
|
const sourceText = target.getSourceFile().getFullText();
|
|
28808
|
-
const ranges =
|
|
29568
|
+
const ranges = ts2.getLeadingCommentRanges(sourceText, target.getFullStart());
|
|
28809
29569
|
if (!ranges || ranges.length === 0) {
|
|
28810
29570
|
return void 0;
|
|
28811
29571
|
}
|
|
28812
29572
|
for (let i = ranges.length - 1; i >= 0; i--) {
|
|
28813
29573
|
const range = ranges[i];
|
|
28814
|
-
if (range.kind !==
|
|
29574
|
+
if (range.kind !== ts2.SyntaxKind.MultiLineCommentTrivia) {
|
|
28815
29575
|
continue;
|
|
28816
29576
|
}
|
|
28817
29577
|
const text = sourceText.slice(range.pos, range.end);
|
|
@@ -29034,14 +29794,14 @@ var LAYOUT_ROOT_BY_PROJECT_TYPE = {
|
|
|
29034
29794
|
};
|
|
29035
29795
|
function validateMonorepoLayout(root) {
|
|
29036
29796
|
const violations = [];
|
|
29037
|
-
const rootOutdir =
|
|
29797
|
+
const rootOutdir = toPosix3(root.outdir);
|
|
29038
29798
|
for (const sub of root.subprojects) {
|
|
29039
29799
|
const className = sub.constructor.name;
|
|
29040
29800
|
const expectedRoot = expectedRootFor(sub, className);
|
|
29041
29801
|
if (expectedRoot === void 0) {
|
|
29042
29802
|
continue;
|
|
29043
29803
|
}
|
|
29044
|
-
const relOutdir = relativeOutdir(rootOutdir,
|
|
29804
|
+
const relOutdir = relativeOutdir(rootOutdir, toPosix3(sub.outdir));
|
|
29045
29805
|
if (!outdirMatchesRoot(relOutdir, expectedRoot)) {
|
|
29046
29806
|
violations.push({
|
|
29047
29807
|
projectName: sub.name,
|
|
@@ -29100,7 +29860,7 @@ function outdirMatchesRoot(relOutdir, expectedRoot) {
|
|
|
29100
29860
|
}
|
|
29101
29861
|
return segments.length >= 2;
|
|
29102
29862
|
}
|
|
29103
|
-
function
|
|
29863
|
+
function toPosix3(p) {
|
|
29104
29864
|
return p.replace(/\\/g, "/");
|
|
29105
29865
|
}
|
|
29106
29866
|
function relativeOutdir(rootOutdir, subOutdir) {
|
|
@@ -29189,8 +29949,8 @@ var ResetTask = class _ResetTask extends Component14 {
|
|
|
29189
29949
|
const resetTask = this.project.tasks.addTask(this.taskName, {
|
|
29190
29950
|
description: "Delete build artifacts specified by pathsToRemove option, or artifactsDirectory if pathsToRemove is empty"
|
|
29191
29951
|
});
|
|
29192
|
-
this.pathsToRemove.forEach((
|
|
29193
|
-
resetTask.exec(`[ -e "${
|
|
29952
|
+
this.pathsToRemove.forEach((path6) => {
|
|
29953
|
+
resetTask.exec(`[ -e "${path6}" ] && rm -rf ${path6} || true`);
|
|
29194
29954
|
});
|
|
29195
29955
|
const rootHasTurbo = TurboRepo.of(this.project.root) !== void 0;
|
|
29196
29956
|
const isSubproject = this.project !== this.project.root;
|
|
@@ -31098,7 +31858,7 @@ export const collections = {
|
|
|
31098
31858
|
`;
|
|
31099
31859
|
|
|
31100
31860
|
// src/typescript/typescript-config.ts
|
|
31101
|
-
import { relative as
|
|
31861
|
+
import { relative as relative6 } from "path";
|
|
31102
31862
|
import { Component as Component19 } from "projen";
|
|
31103
31863
|
import { ensureRelativePathStartsWithDot } from "projen/lib/util/path";
|
|
31104
31864
|
var TypeScriptConfig = class extends Component19 {
|
|
@@ -31117,7 +31877,7 @@ var TypeScriptConfig = class extends Component19 {
|
|
|
31117
31877
|
...tsPaths,
|
|
31118
31878
|
[dep.name]: [
|
|
31119
31879
|
ensureRelativePathStartsWithDot(
|
|
31120
|
-
|
|
31880
|
+
relative6(project.outdir, subproject.outdir)
|
|
31121
31881
|
)
|
|
31122
31882
|
]
|
|
31123
31883
|
};
|
|
@@ -31175,6 +31935,7 @@ export {
|
|
|
31175
31935
|
DEFAULT_PROGRESS_FILES_STATE_DIR,
|
|
31176
31936
|
DEFAULT_REQUIRE_LINKED_ISSUE,
|
|
31177
31937
|
DEFAULT_REQUIRE_PRODUCT_CONTEXT,
|
|
31938
|
+
DEFAULT_SAMPLE_COMPILER_OPTIONS,
|
|
31178
31939
|
DEFAULT_SCHEDULED_TASKS_ROOT,
|
|
31179
31940
|
DEFAULT_SCHEDULED_TASK_ENTRIES,
|
|
31180
31941
|
DEFAULT_SHARED_EDITING_CONFLICT_STRATEGY,
|
|
@@ -31220,6 +31981,7 @@ export {
|
|
|
31220
31981
|
SCOPE_CLASS_VALUES,
|
|
31221
31982
|
SHARED_EDITING_CONFLICT_STRATEGY_VALUES,
|
|
31222
31983
|
STARLIGHT_ROLE,
|
|
31984
|
+
SampleLang,
|
|
31223
31985
|
StarlightProject,
|
|
31224
31986
|
TestRunner,
|
|
31225
31987
|
TsDocCoverageKind,
|
|
@@ -31262,13 +32024,17 @@ export {
|
|
|
31262
32024
|
buildStandardsResearchBundle,
|
|
31263
32025
|
buildUnblockDependentsProcedure,
|
|
31264
32026
|
businessModelsBundle,
|
|
32027
|
+
checkDocSamplesProcedure,
|
|
32028
|
+
checkLinksProcedure,
|
|
31265
32029
|
classifyIssueScope,
|
|
31266
32030
|
classifyRun,
|
|
31267
32031
|
companyProfileBundle,
|
|
32032
|
+
compileFencedSamples,
|
|
31268
32033
|
customerProfileBundle,
|
|
31269
32034
|
docsSyncBundle,
|
|
31270
32035
|
extractApiProcedure,
|
|
31271
32036
|
extractDocReferences,
|
|
32037
|
+
extractFencedSamples,
|
|
31272
32038
|
formatLayoutViolation,
|
|
31273
32039
|
formatStarlightSingletonViolation,
|
|
31274
32040
|
getLatestEligibleVersion,
|
|
@@ -31285,6 +32051,8 @@ export {
|
|
|
31285
32051
|
regulatoryResearchBundle,
|
|
31286
32052
|
renderAgentTierCaseStatement,
|
|
31287
32053
|
renderAgentTierSection,
|
|
32054
|
+
renderCheckDocSamplesProcedure,
|
|
32055
|
+
renderCheckLinksProcedure,
|
|
31288
32056
|
renderCustomDocSectionBlock,
|
|
31289
32057
|
renderCustomDocSections,
|
|
31290
32058
|
renderExtractApiProcedure,
|