From d7580f30de8e92e2204a478711ec58bb1b6e3197 Mon Sep 17 00:00:00 2001 From: David Neumann Date: Tue, 4 Aug 2026 23:02:19 +0200 Subject: [PATCH] Added the ProperDocs Documentation Website MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a documentation website, built with ProperDocs (`properdocs.yml`, `requirements.txt`) on top of the existing hand-written `docs/` tree, so `docs_dir` needs no separate source folder. The site gets a generated "API Reference" section built at deploy time from the framework's XML documentation comments, via a new `gen-files` hook (`docs/build/generate-api-docs.py`) that shells out to the `xmldoc2md` local tool (newly pinned in `.config/dotnet-tools.json`). Because Roslyn never expands `` in the emitted XML documentation file, `clinet-core` now also references `SauceControl.InheritDoc` (`PrivateAssets="all"`) to rewrite it into real text as a build step, so the generated API docs are not full of empty members. Three more build hooks handle the smaller pieces: copying the favicon to the site root, rewriting links that point outside `docs_dir` into absolute GitHub URLs, and injecting the "API Reference" nav entry into the overview page. A new light/dark logo and favicon set was added under `docs/assets/`, along with CSS that teaches the site's Material theme to honor GitHub's `#gh-dark-mode-only` / `#gh-light-mode-only` image suffixes and to swap the header logo with the color scheme. A new "Docs" GitHub Actions workflow (`.github/workflows/docs.yml`) builds and deploys the site to GitHub Pages on every push to `main`. Restructured the documentation to make room for this. Merged `docs/developer-manual/architecture/` into a single `docs/developer-manual/architecture.md`, renamed `contributing.md` to `contribution-guide.md` and `tooling/formatting-dprint.md` to `tooling/code-formatting-dprint.md`, and moved `design/DESIGN.md` to `docs/developer-manual/logo-design.md`, adding a small new `design/README.md` that just lists the asset files left behind. Added a new "Documentation Website" article describing all of the above, and updated `CLAUDE.md` and the various indexes to match. The website's own build output goes into `build/documentation-website/` (`site_dir` in `properdocs.yml`), which meant a top-level `build/` directory now existed in the repository for the first time. While touching that, the C# projects' `bin/`/`obj/` output was redirected into it too, so all build output — the website's and the .NET projects' alike — lives in the same place: a new `Directory.Build.props` under `source/` (shared by `clinet-core` and `sample-app`, keyed off each project's own folder name) and another under `tests/unit-tests` do the redirecting. `.gitignore`, the shared VS Code settings, and the CSpell ignore list were all updated from the old `bin/`/`obj/` patterns to `build/`, and `.gitignore` also picks up `node_modules/`, `.venv/`, and `__pycache__/` for the new NPM and Python tooling the website needed. Added shared VS Code `launch.json` and `tasks.json` files with a "Debug Sample App" launch configuration and its matching build task. The website's Python dependencies needed pinning in a new `requirements.txt` to match this project's policy of never using version ranges (see Dependency Management), and since that pinning had to be introduced anyway, the linters got the same treatment on the NPM side: a new `package.json` / `package-lock.json` pins `dprint`, `cspell`, and `markdownlint-cli2` instead of installing global, unlocked versions in CI and in the pre-commit hooks in `.claude/settings.json` — both now run `npm ci` once and invoke the linters through `npx`. Finally, added a `CLAUDE.md` golden rule that changes are never staged or committed automatically — only the user does that, by hand — matching a new "Never Stage or Commit Automatically" section in the commit-messages article. This work was done with the help of Claude Code, which built the ProperDocs site and its build hooks, wrote the GitHub Pages workflow, redirected the build output, migrated the linters to `npm`, and updated the documentation to match. Co-Authored-By: Claude --- .claude/settings.json | 4 +- .config/dotnet-tools.json | 7 + .github/workflows/docs.yml | 97 + .github/workflows/linters.yml | 28 +- .gitignore | 15 +- .vscode/launch.shared.json | 18 + .vscode/settings.shared.json | 13 +- .vscode/tasks.shared.json | 210 ++ CLAUDE.md | 20 +- README.md | 6 +- design/README.md | 19 + docs/README.md | 4 +- docs/assets/favicon/favicon-dark.svg | 1 + docs/assets/favicon/favicon-light.svg | 1 + docs/assets/favicon/favicon.ico | Bin 0 -> 15086 bytes .../assets/images/header-logo-dark.png | Bin .../assets/images/header-logo-light.png | Bin docs/assets/images/logo-dark.png | Bin 0 -> 6611 bytes docs/assets/images/logo-light.png | Bin 0 -> 5543 bytes docs/assets/styles/gh-mode-only-images.css | 11 + docs/assets/styles/site-logo.css | 9 + docs/assets/templates/main.html | 10 + docs/build/copy-favicon-to-site-root.py | 24 + docs/build/generate-api-docs.py | 153 + docs/build/inject-api-reference-link.py | 47 + docs/build/rewrite-repo-links.py | 97 + docs/developer-manual/README.md | 19 +- docs/developer-manual/architecture.md | 48 + docs/developer-manual/architecture/README.md | 5 - .../developer-manual/architecture/overview.md | 39 - ...{contributing.md => contribution-guide.md} | 2 +- docs/developer-manual/conventions/README.md | 2 +- .../conventions/commit-messages.md | 3 +- .../conventions/csharp-style.md | 14 +- .../conventions/dependency-management.md | 19 +- .../conventions/file-naming-conventions.md | 6 +- docs/developer-manual/conventions/testing.md | 8 +- .../developer-manual/logo-design.md | 8 +- docs/developer-manual/tooling/README.md | 11 +- ...ng-dprint.md => code-formatting-dprint.md} | 6 +- .../tooling/continuous-integration.md | 19 +- .../tooling/developer-setup.md | 52 +- .../tooling/documentation-website.md | 134 + .../tooling/linting-markdownlint.md | 8 +- .../tooling/spell-checking-cspell.md | 6 +- .../tooling/testing-and-code-coverage.md | 2 +- .../tooling/vscode-integration.md | 28 +- docs/user-manual/README.md | 4 +- docs/user-manual/building-applications.md | 2 +- package-lock.json | 2759 +++++++++++++++++ package.json | 9 + properdocs.yml | 132 + requirements.txt | 9 + source/Directory.Build.props | 14 + source/clinet-core/CLI.NET Core.csproj | 9 + source/clinet-core/packages.lock.json | 6 + tests/linters/.cspell.json | 18 +- tests/unit-tests/Directory.Build.props | 12 + 58 files changed, 4045 insertions(+), 162 deletions(-) create mode 100644 .github/workflows/docs.yml create mode 100644 .vscode/launch.shared.json create mode 100644 .vscode/tasks.shared.json create mode 100644 design/README.md create mode 100644 docs/assets/favicon/favicon-dark.svg create mode 100644 docs/assets/favicon/favicon-light.svg create mode 100644 docs/assets/favicon/favicon.ico rename design/readme-header-dark.png => docs/assets/images/header-logo-dark.png (100%) rename design/readme-header-light.png => docs/assets/images/header-logo-light.png (100%) create mode 100644 docs/assets/images/logo-dark.png create mode 100644 docs/assets/images/logo-light.png create mode 100644 docs/assets/styles/gh-mode-only-images.css create mode 100644 docs/assets/styles/site-logo.css create mode 100644 docs/assets/templates/main.html create mode 100644 docs/build/copy-favicon-to-site-root.py create mode 100644 docs/build/generate-api-docs.py create mode 100644 docs/build/inject-api-reference-link.py create mode 100644 docs/build/rewrite-repo-links.py create mode 100644 docs/developer-manual/architecture.md delete mode 100644 docs/developer-manual/architecture/README.md delete mode 100644 docs/developer-manual/architecture/overview.md rename docs/developer-manual/{contributing.md => contribution-guide.md} (96%) rename design/DESIGN.md => docs/developer-manual/logo-design.md (86%) rename docs/developer-manual/tooling/{formatting-dprint.md => code-formatting-dprint.md} (67%) create mode 100644 docs/developer-manual/tooling/documentation-website.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 properdocs.yml create mode 100644 requirements.txt create mode 100644 source/Directory.Build.props create mode 100644 tests/unit-tests/Directory.Build.props diff --git a/.claude/settings.json b/.claude/settings.json index ef9b267..b779ec4 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -9,7 +9,7 @@ "hooks": [ { "type": "command", - "command": "f=$(jq -r '.tool_input.file_path // .tool_input.notebook_path // empty'); case \"$f\" in \"\"|*/bin/*|*/obj/*) exit 0;; esac; case \"$f\" in *.md|*.json|*.jsonc|*.yml|*.yaml|*.toml|*.xml|*.csproj|*.slnx) ;; *) exit 0;; esac; command -v dprint >/dev/null 2>&1 && [ -f \"$f\" ] && dprint fmt \"$f\" 2>&1; true", + "command": "f=$(jq -r '.tool_input.file_path // .tool_input.notebook_path // empty'); case \"$f\" in \"\"|*/build/*) exit 0;; esac; case \"$f\" in *.md|*.json|*.jsonc|*.yml|*.yaml|*.toml|*.xml|*.csproj|*.slnx) ;; *) exit 0;; esac; command -v dprint >/dev/null 2>&1 && [ -f \"$f\" ] && dprint fmt \"$f\" 2>&1; true", "statusMessage": "Formatting (dprint)..." }, { @@ -19,7 +19,7 @@ }, { "type": "command", - "command": "f=$(jq -r '.tool_input.file_path // .tool_input.notebook_path // empty'); case \"$f\" in \"\"|*/bin/*|*/obj/*|*.svg) exit 0;; esac; command -v npx >/dev/null 2>&1 || exit 0; if [ -f \"$f\" ]; then out=$(npx --yes cspell@10.0.1 lint --config tests/linters/.cspell.json --no-progress \"$f\" 2>&1); [ $? -ne 0 ] && { echo \"$out\" >&2; exit 2; }; fi; true", + "command": "f=$(jq -r '.tool_input.file_path // .tool_input.notebook_path // empty'); case \"$f\" in \"\"|*/build/*|*.svg) exit 0;; esac; command -v npx >/dev/null 2>&1 || exit 0; if [ -f \"$f\" ]; then out=$(npx --yes cspell@10.0.1 lint --config tests/linters/.cspell.json --no-progress \"$f\" 2>&1); [ $? -ne 0 ] && { echo \"$out\" >&2; exit 2; }; fi; true", "statusMessage": "Spell checking (CSpell)..." } ] diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 91bf031..06bb6a1 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -8,6 +8,13 @@ "reportgenerator" ], "rollForward": false + }, + "xmldoc2markdown": { + "version": "6.0.0", + "commands": [ + "xmldoc2md" + ], + "rollForward": false } } } diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..1fb2f64 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,97 @@ +# The display name of the workflow, shown in the "Actions" tab of the GitHub repository and in the checks of a commit or pull request +name: Docs + +# Defines the events that trigger this workflow; unlike "Linters" and "Tests", which run on every push to any branch, this workflow only runs on +# pushes to "main" - it deploys the documentation website to GitHub Pages, so it only ever needs to run against what is actually published +on: + push: + branches: + - main + +# Grants the workflow only the permissions the GitHub Pages deployment needs: reading the repository contents, writing to the Pages deployment, +# and requesting the OpenID Connect token GitHub Pages uses to verify the deployment came from this workflow +permissions: + contents: read + pages: write + id-token: write + +# Ensures that at most one deployment runs at a time and that a newer push is never overtaken by an older one still in flight; unlike most +# concurrency groups, an in-progress Pages deployment is allowed to finish rather than being cancelled, since a cancelled deployment can leave the +# site in a partially updated state +concurrency: + group: pages + cancel-in-progress: false + +# Defines the jobs that make up the workflow; building the site and deploying it are two separate jobs so that the deployment step only runs +# through the dedicated "github-pages" environment, which is what makes the deployment show up in the repository's "Environments" and gives it +# its own audit trail, separate from the build +jobs: + # Builds the ProperDocs site, including the generated "api" section (see docs/developer-manual/tooling/documentation-website.md), and uploads it as + # a Pages artifact; runs on the latest Ubuntu image, which is the fastest and cheapest of the available runners + build: + name: Build Site + runs-on: ubuntu-latest + + steps: + # Checks out the repository into the runner's working directory; the "actions/checkout" action is pinned to a major version to receive + # non-breaking updates automatically + - name: Checkout Repository + uses: actions/checkout@v7 + + # Installs the .NET SDK; the version is read from "global.json" in the repository root, so this workflow can never drift from the exact + # version pinned there (see Dependency Management); the generator script (below) shells out to "dotnet" to build the framework and to run + # the pinned xmldoc2md local tool + - name: Set Up .NET + uses: actions/setup-dotnet@v6 + with: + global-json-file: global.json + + # Restores the local .NET tools declared in ".config/dotnet-tools.json", which is where xmldoc2md, the C# XML documentation to Markdown + # generator the "api" section uses, is pinned (see Documentation Website and Dependency Management) + - name: Restore .NET Tools + run: dotnet tool restore + + # Installs Python, pinned to an exact patch version, so that ProperDocs and its plugins run in a stable and reproducible environment; the + # "actions/setup-python" action is pinned to a major version to receive non-breaking updates automatically + - name: Set Up Python + uses: actions/setup-python@v7 + with: + python-version: "3.13.14" + + # Installs ProperDocs, the Material theme, and the plugins that generate the site's navigation and its "api" section, all pinned to exact + # versions in "requirements.txt" (see Dependency Management) + - name: Install Documentation Dependencies + run: python -m pip install --requirement requirements.txt + + # Builds the static site into "build/documentation-website/" (see "site_dir" in "properdocs.yml"); this regenerates the "docs/api" section + # from the framework's current XML documentation comments as part of the build (see Documentation Website) - nothing under "docs/api" is + # ever committed, so this step is what produces it, every time + - name: Build Documentation Website + run: properdocs build + + # Configures GitHub Pages for the repository, so the following upload step knows where to send the built site + - name: Configure Pages + uses: actions/configure-pages@v6 + + # Uploads the built site as the artifact that the "deploy" job below publishes to GitHub Pages + - name: Upload Pages Artifact + uses: actions/upload-pages-artifact@v5 + with: + path: build/documentation-website + + # Publishes the artifact the "build" job uploaded to GitHub Pages, through the "github-pages" deployment environment + deploy: + name: Deploy to GitHub Pages + needs: build + runs-on: ubuntu-latest + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + # Publishes the previously uploaded artifact to GitHub Pages; "id: deployment" lets the "environment.url" above surface the live URL on + # the workflow run and in the repository's "Environments" tab + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 061a299..fdcad79 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -29,17 +29,18 @@ jobs: with: node-version: 24 - # Installs the linters and the code formatter globally, so that they are available as commands in the subsequent steps; installing them in a - # dedicated step keeps the linter steps below focused on just running the tools + # Installs the exact versions of the linters and the code formatter declared in "package.json" and locked in "package-lock.json" (see + # Dependency Management); "ci" (rather than "install") fails instead of silently updating the lock file if it would no longer match + # "package.json", the same guarantee "--locked-mode" gives the NuGet restore in the Tests workflow - name: Install Linters - run: npm install --global dprint@0.55.1 cspell@10.0.1 markdownlint-cli2@0.23.0 + run: npm ci # Runs the dprint code formatter in check mode; "check" only verifies that all files are already formatted and exits with a non-zero status code - # if any file is not, but unlike "dprint fmt" it never modifies any file; dprint automatically discovers the "dprint.json" stub in the - # repository root, which extends the real configuration in "tests/linters", so no configuration path has to be passed explicitly, and the - # "**/*" glob makes dprint check the whole repository (relative to the working directory) + # if any file is not, but unlike "dprint fmt" it never modifies any file; dprint automatically discovers "dprint.json" in the repository root, + # so no configuration path has to be passed explicitly, and the "**/*" glob makes dprint check the whole repository (relative to the working + # directory); "npx" resolves the version "npm ci" just installed into "node_modules/.bin" rather than downloading one - name: Check Formatting (dprint) - run: dprint check "**/*" + run: npx dprint check "**/*" # Runs the CSpell spell checker over every file in the repository; the "lint" command reports spelling mistakes and exits with a non-zero status # code if any are found, the configuration path is passed explicitly, and "--no-progress" suppresses the per-file progress output to keep the @@ -47,12 +48,13 @@ jobs: # exited with a non-zero exit code, so that the job runs all linters instead of stopping at the first failure - name: Spell Check (CSpell) if: ${{ !cancelled() }} - run: cspell lint --config tests/linters/.cspell.json --no-progress "**/*" + run: npx cspell lint --config tests/linters/.cspell.json --no-progress "**/*" - # Runs the MarkdownLint linter over all Markdown files in the repository; the configuration path is passed explicitly via "--config", and the - # build output directories are excluded using markdownlint-cli2's "#"-prefixed negated globs (markdownlint-cli2 has no "--ignore" flag), so that - # only the project's own documentation is linted; the "if" ensures that the step runs (unless the workflow was cancelled), even if the previous - # linter reported an error and exited with a non-zero exit code, so that the job runs all linters instead of stopping at the first failure + # Runs the MarkdownLint linter over all Markdown files in the repository; the configuration path is passed explicitly via "--config", and build + # output and "node_modules" (unlike dprint and CSpell, markdownlint-cli2 does not ignore it by default) are excluded using markdownlint-cli2's + # "#"-prefixed negated globs (it has no "--ignore" flag), so that only the project's own documentation is linted; the "if" ensures that the + # step runs (unless the workflow was cancelled), even if the previous linter reported an error and exited with a non-zero exit code, so that + # the job runs all linters instead of stopping at the first failure - name: Lint Markdown (MarkdownLint) if: ${{ !cancelled() }} - run: markdownlint-cli2 --config tests/linters/.markdownlint.yml "**/*.md" "#**/bin/**" "#**/obj/**" + run: npx markdownlint-cli2 --config tests/linters/.markdownlint.yml "**/*.md" "#**/build/**" "#**/node_modules/**" "#**/.venv/**" diff --git a/.gitignore b/.gitignore index ae53b8a..be4d428 100644 --- a/.gitignore +++ b/.gitignore @@ -16,11 +16,18 @@ Thumbs.db !.vscode/tasks.shared.json *.launch *.sublime-workspace -/.idea +.idea -# Build output -bin/ -obj/ +# Installed packages +node_modules/ +.venv/ + +# Build output (redirected here from every project's own folder, and from the documentation website, see Architecture and Documentation Website) +/build/ +__pycache__/ + +# Personal, unreviewed NPM install-script policy (see docs/developer-manual/tooling/developer-setup.md) +/.npmrc # Test results and code coverage reports TestResults/ diff --git a/.vscode/launch.shared.json b/.vscode/launch.shared.json new file mode 100644 index 0000000..ee2234d --- /dev/null +++ b/.vscode/launch.shared.json @@ -0,0 +1,18 @@ +{ + "version": "0.2.0", + "configurations": [ + // Debugs the sample app, this builds the project via the "Build Sample App" task (see "tasks.shared.json") before attaching the debugger to + // the resulting executable + { + "name": "Debug Sample App", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "Build Sample App", + "program": "${workspaceFolder}/build/sample-app/bin/Debug/net10.0/CLI.NET Core Sample App.dll", + "args": [], + "cwd": "${workspaceFolder}/source/sample-app", + "console": "integratedTerminal", + "stopAtEntry": false, + }, + ], +} diff --git a/.vscode/settings.shared.json b/.vscode/settings.shared.json index c6c3cdf..6db6ff3 100644 --- a/.vscode/settings.shared.json +++ b/.vscode/settings.shared.json @@ -20,22 +20,23 @@ // Excludes certain files from being displayed in the file explorer "files.exclude": { ".VSCodeCounter": true, + "**/__pycache__": true, "**/.DS_Store": true, "**/.git": true, - "**/bin": true, - "**/obj": true, + "**/.venv": true, + "**/node_modules": true, "**/Thumbs.db": true, }, // Excludes certain files from being watched by the file watcher, this is required, because if the data directory is inside of the workspace, then // Visual Studio Code will watch the entire directory, which can not only cause performance issues, but will also cause the file watcher to run - // out of file handles, which are limited by the operating system, that are necessary to watch the files + // out of file handles, which are limited by the operating system, that are necessary to watch the files; "/build" is anchored to the workspace + // root, the same as in "files.exclude" above, so "docs/build/" is still watched "files.watcherExclude": { ".VSCodeCounter": true, "**/.git/objects/**": true, "**/.git/subtree-cache/**": true, - "**/bin": true, - "**/obj": true, + "/build": true, }, // Specifies which Git branches are protected in this repository, Visual Studio will prevent users from committing to these branches (this only @@ -69,7 +70,7 @@ "cSpell.import": ["${workspaceFolder}/tests/linters/.cspell.json"], // Configures the Markdown linter MarkdownLint to use our custom configuration - "markdownlint.config": { "extends": "${workspaceFolder}/tests/linters/.markdownlint.yml" }, + "markdownlint.configFile": "${workspaceFolder}/tests/linters/.markdownlint.yml", // Configures the code formatter dprint "dprint.verbose": true, diff --git a/.vscode/tasks.shared.json b/.vscode/tasks.shared.json new file mode 100644 index 0000000..a986252 --- /dev/null +++ b/.vscode/tasks.shared.json @@ -0,0 +1,210 @@ +{ + "version": "2.0.0", + "tasks": [ + // Restores the Node.js dependencies of the project (dprint, CSpell, and MarkdownLint, see Dependency Management); "ci" (rather than + // "install") fails instead of silently updating "package-lock.json" if it no longer matches "package.json" + { + "label": "Install Node Dependencies", + "type": "shell", + "command": "npm ci", + "problemMatcher": [], + "icon": { + "id": "desktop-download", + "color": "terminal.ansiBlue", + }, + }, + + // Restores the pinned local dotnet tools declared in ".config/dotnet-tools.json" (ReportGenerator and xmldoc2md, see Dependency Management) + { + "label": "Restore .NET Tools", + "type": "shell", + "command": "dotnet tool restore", + "problemMatcher": [], + "icon": { + "id": "desktop-download", + "color": "terminal.ansiBlue", + }, + }, + + // Installs the pinned Python dependencies that build the documentation website (ProperDocs, the Material theme, and its plugins, see + // Documentation Website) + { + "label": "Install Python Dependencies", + "type": "shell", + "command": "python -m pip install --requirement requirements.txt", + "problemMatcher": [], + "icon": { + "id": "desktop-download", + "color": "terminal.ansiBlue", + }, + }, + + // Installs every dependency of the project in one go; if you only use Visual Studio Code tasks, you do not have to run this task manually, + // all other tasks that require up-to-date dependencies run the relevant one of the three tasks above automatically as a dependency + { + "label": "Install Dependencies", + "dependsOn": ["Install Node Dependencies", "Restore .NET Tools", "Install Python Dependencies"], + "dependsOrder": "parallel", + "problemMatcher": [], + "icon": { + "id": "desktop-download", + "color": "terminal.ansiBlue", + }, + }, + + // Runs the dprint code formatter on the entire code base + { + "label": "Format", + "type": "shell", + "command": "npx dprint fmt", + "dependsOn": ["Install Node Dependencies"], + "problemMatcher": [], + "icon": { + "id": "case-sensitive", + "color": "terminal.ansiYellow", + }, + }, + + // Runs CSpell on the entire codebase + { + "label": "Lint Spelling", + "type": "shell", + "command": "npx cspell lint --config tests/linters/.cspell.json --no-progress \"**/*\"", + "dependsOn": ["Install Node Dependencies"], + "problemMatcher": [ + { + "owner": "cspell", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": { + "regexp": "^(.+?):(\\d+):(\\d+)\\s+-\\s+(.+)$", + "file": 1, + "line": 2, + "column": 3, + "message": 4, + }, + }, + ], + "group": "test", + "icon": { + "id": "shield", + "color": "terminal.ansiCyan", + }, + }, + + // Runs MarkdownLint on the entire codebase + { + "label": "Lint Markdown", + "type": "shell", + "command": "npx markdownlint-cli2 --config tests/linters/.markdownlint.yml \"**/*.md\" \"#**/build/**\" \"#**/node_modules/**\" \"#**/.venv/**\"", + "dependsOn": ["Install Node Dependencies"], + "problemMatcher": [ + { + "owner": "markdownlint", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": { + "regexp": "^(.+?):(\\d+)(?::(\\d+))?\\s+(error|warning)\\s+(MD\\d+\\S*)\\s+(.+)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "code": 5, + "message": 6, + }, + }, + ], + "group": "test", + "icon": { + "id": "shield", + "color": "terminal.ansiCyan", + }, + }, + + // Runs every linter used by this project (dprint in check mode, CSpell, and MarkdownLint) on the entire codebase, the same three checks the + // Linters GitHub Actions workflow runs + { + "label": "Lint", + "type": "shell", + "command": "npx dprint check \"**/*\" && npx cspell lint --config tests/linters/.cspell.json --no-progress \"**/*\" && npx markdownlint-cli2 --config tests/linters/.markdownlint.yml \"**/*.md\" \"#**/build/**\" \"#**/node_modules/**\" \"#**/.venv/**\"", + "dependsOn": ["Install Node Dependencies"], + "problemMatcher": [ + { + "owner": "cspell", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": { + "regexp": "^(.+?):(\\d+):(\\d+)\\s+-\\s+(.+)$", + "file": 1, + "line": 2, + "column": 3, + "message": 4, + }, + }, + { + "owner": "markdownlint", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": { + "regexp": "^(.+?):(\\d+)(?::(\\d+))?\\s+(error|warning)\\s+(MD\\d+\\S*)\\s+(.+)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "code": 5, + "message": 6, + }, + }, + ], + "group": { + "kind": "test", + "isDefault": true, + }, + "icon": { + "id": "shield", + "color": "terminal.ansiCyan", + }, + }, + + // Builds the sample app, this is the "preLaunchTask" of the "Debug Sample App" launch configuration in "launch.shared.json" + { + "label": "Build Sample App", + "type": "shell", + "command": "dotnet build \"source/sample-app/CLI.NET Core Sample App.csproj\"", + "problemMatcher": ["$msCompile"], + "group": "build", + "icon": { + "id": "tools", + "color": "terminal.ansiRed", + }, + }, + + // Runs the xUnit unit tests of the framework + { + "label": "Test", + "type": "shell", + "command": "dotnet test \"source/CLI.NET Core.slnx\"", + "problemMatcher": ["$msCompile"], + "group": { + "kind": "test", + "isDefault": false, + }, + "icon": { + "id": "beaker", + "color": "terminal.ansiCyan", + }, + }, + + // Builds the ProperDocs documentation website, including generating the "docs/api" C# API reference from the framework's current XML + // documentation comments (see Documentation Website); requires both the Python and the .NET dependencies to be installed + { + "label": "Build Docs", + "type": "shell", + "command": "properdocs build", + "dependsOn": ["Install Python Dependencies", "Restore .NET Tools"], + "dependsOrder": "parallel", + "problemMatcher": [], + "group": "build", + "icon": { + "id": "tools", + "color": "terminal.ansiRed", + }, + }, + ], +} diff --git a/CLAUDE.md b/CLAUDE.md index 1232561..9a3004c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,18 +6,19 @@ Guidance for AI assistants working on CLI.NET Core. Humans should read the [docu CLI.NET Core is a .NET command line application framework, built in the style of ASP.NET Core: it lets consumers define commands and command-line arguments much the same way they would define actions and parameters for a Web API. The repository holds one solution, [`source/CLI.NET Core.slnx`](source/CLI.NET%20Core.slnx), with three projects — the framework itself (`clinet-core`), a sample app that references it (`sample-app`), and its xUnit unit test project (`tests/unit-tests`). All three target `net10.0` with nullable reference types and implicit usings enabled. -Start from [docs/developer-manual/architecture/overview.md](docs/developer-manual/architecture/overview.md). +Start from [docs/developer-manual/architecture.md](docs/developer-manual/architecture.md). ## Golden Rules - **Match the surrounding code and the documented conventions** ([docs/developer-manual/conventions/](docs/developer-manual/conventions/csharp-style.md)). This code is heavily and consistently documented — follow it. -- **Formatting is owned by dprint** ([docs/developer-manual/tooling/formatting-dprint.md](docs/developer-manual/tooling/formatting-dprint.md)) for Markdown, JSON, XML (including `.csproj`/`.slnx`), YAML, and TOML — never add a lint rule that reformats one of these. Run `dprint fmt` before finishing. **C# is not covered by dprint** (there is no C# plugin configured); its whitespace comes from [`.editorconfig`](.editorconfig) and its style from [C# Style](docs/developer-manual/conventions/csharp-style.md) alone, so review it by eye. -- **Run the linters before finishing** — there is no NPM-script wrapper, so invoke them directly with the versions [Continuous Integration](docs/developer-manual/tooling/continuous-integration.md) pins: +- **Formatting is owned by dprint** ([docs/developer-manual/tooling/code-formatting-dprint.md](docs/developer-manual/tooling/code-formatting-dprint.md)) for Markdown, JSON, XML (including `.csproj`/`.slnx`), YAML, and TOML — never add a lint rule that reformats one of these. Run `dprint fmt` before finishing. **C# is not covered by dprint** (there is no C# plugin configured); its whitespace comes from [`.editorconfig`](.editorconfig) and its style from [C# Style](docs/developer-manual/conventions/csharp-style.md) alone, so review it by eye. +- **Run the linters before finishing** — `dprint`, `cspell`, and `markdownlint-cli2` are pinned in [`package.json`](package.json) and locked in `package-lock.json`; there is no NPM-script wrapper, so restore them with `npm ci` once, then invoke each directly through `npx`: ```shell - dprint check "**/*" - npx --yes cspell@10.0.1 lint --config tests/linters/.cspell.json --no-progress "**/*" - npx --yes markdownlint-cli2@0.23.0 --config tests/linters/.markdownlint.yml "**/*.md" "#**/bin/**" "#**/obj/**" + npm ci + npx dprint check "**/*" + npx cspell lint --config tests/linters/.cspell.json --no-progress "**/*" + npx markdownlint-cli2 --config tests/linters/.markdownlint.yml "**/*.md" "#**/build/**" "#**/node_modules/**" "#**/.venv/**" ``` The docs and this file are Markdown and are linted too. @@ -27,7 +28,7 @@ Start from [docs/developer-manual/architecture/overview.md](docs/developer-manua - **C# structure**: file-scoped namespaces, an XML documentation comment (``, ``, ``) on **every member, including `private` ones** (not just public and internal — this is for readers of the source, not only consumers of the compiled output), `` for members that implement an interface or override a base member, `sealed` classes by default (composition over inheritance for anything that would otherwise need to extend a sealed framework type), explicit `this.` on member access, expression-bodied members where the implementation is a single expression, and `camelCase` private fields with no underscore. Full detail: [C# Style](docs/developer-manual/conventions/csharp-style.md). - **Nullable reference types and implicit usings are enabled everywhere.** Write genuinely null-safe code rather than silencing the analyzer. - **Files and directories are kebab-case**, except well-known and tool-mandated names (`README.md`, `LICENSE`, `.editorconfig`, ...). Inside a C# project's own source tree, directories and files switch to PascalCase, one type per file. Full detail: [File Naming Conventions](docs/developer-manual/conventions/file-naming-conventions.md). -- **Every dependency is pinned to an exact version — never a range.** NuGet packages, dprint plugins, the linter versions CI installs, and the .NET SDK (`global.json`), and local `dotnet` tools (`.config/dotnet-tools.json`) are all pinned exactly. Every C# project sets `RestorePackagesWithLockFile`, so a `PackageReference` version bump must be followed by `dotnet restore` and the resulting `packages.lock.json` change committed alongside it. Full detail: [Dependency Management](docs/developer-manual/conventions/dependency-management.md). +- **Every dependency is pinned to an exact version — never a range.** NuGet packages, dprint plugins, the linters (`package.json`/`package-lock.json`), the .NET SDK (`global.json`), local `dotnet` tools (`.config/dotnet-tools.json`), and the Python packages that build the documentation website with ProperDocs (`requirements.txt`) are all pinned exactly. Every C# project sets `RestorePackagesWithLockFile`, so a `PackageReference` version bump must be followed by `dotnet restore` and the resulting `packages.lock.json` change committed alongside it. Full detail: [Dependency Management](docs/developer-manual/conventions/dependency-management.md). - **Every new feature is unit tested, in the same change.** A bug fix adds a test that reproduces the bug. Test classes live in `tests/unit-tests`, mirror the source namespace and folder they cover, and are named `Tests`; `internal` types are tested directly (the core project grants the test assembly `InternalsVisibleTo`). Full detail: [Testing](docs/developer-manual/conventions/testing.md). Run `dotnet test "source/CLI.NET Core.slnx"` before finishing; see [Testing and Code Coverage](docs/developer-manual/tooling/testing-and-code-coverage.md) for coverage collection. - **Write commit messages by the rules** ([docs/developer-manual/conventions/commit-messages.md](docs/developer-manual/conventions/commit-messages.md)) — the 50/72 rule, a title-cased, past-tense subject, and a prose body. State whether AI was involved and, if it was, what exactly the AI did — this project is developed openly with AI assistance and the commit history is where that is tracked (see the "Use of AI" section of the [root README](README.md)). When you did any of the work, add the trailer this project uses (not a model-specific one): @@ -36,13 +37,16 @@ Start from [docs/developer-manual/architecture/overview.md](docs/developer-manua ``` - **Delegating to subagents is pre-approved.** `.claude/settings.json` allows the agent/subagent tool by default, so use one whenever a task genuinely benefits from parallel or isolated work, without asking first. +- **Never stage or commit changes yourself.** Leave every change unstaged (no `git add`) — the user reviews and stages everything by hand. Writing a commit message is not permission to commit: only run `git commit` when the user explicitly asks for a commit in that same turn, never as a follow-on to drafting the message or to any other request. Full detail: [Commit Messages](docs/developer-manual/conventions/commit-messages.md#never-stage-or-commit-automatically). ## When Working on X, Read Y -- **Repository layout, the solution, the projects** → [docs/developer-manual/architecture/](docs/developer-manual/architecture/overview.md). +- **Repository layout, the solution, the projects** → [docs/developer-manual/architecture.md](docs/developer-manual/architecture.md). +- **The logo, brand assets, `design/`** → [docs/developer-manual/logo-design.md](docs/developer-manual/logo-design.md). - **C# source code** → [docs/developer-manual/conventions/csharp-style.md](docs/developer-manual/conventions/csharp-style.md). - **Installing the SDK, Node.js, or the editor before working on the project** → [docs/developer-manual/tooling/developer-setup.md](docs/developer-manual/tooling/developer-setup.md). - **Linting, formatting, spell checking, CI, editor setup** → [docs/developer-manual/tooling/](docs/developer-manual/tooling/README.md). +- **The ProperDocs documentation website, its navigation, or the generated `docs/api` C# API reference** → [docs/developer-manual/tooling/documentation-website.md](docs/developer-manual/tooling/documentation-website.md). - **Markdown and documentation style** → [docs/developer-manual/conventions/markdown-style.md](docs/developer-manual/conventions/markdown-style.md). - **Writing commit messages** → [docs/developer-manual/conventions/commit-messages.md](docs/developer-manual/conventions/commit-messages.md). - **Naming a new file or directory** → [docs/developer-manual/conventions/file-naming-conventions.md](docs/developer-manual/conventions/file-naming-conventions.md). diff --git a/README.md b/README.md index 16f7b2b..18efb68 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# ![CLI.NET Core](design/readme-header-dark.png#gh-dark-mode-only) ![CLI.NET Core](design/readme-header-light.png#gh-light-mode-only) +# ![CLI.NET Core](docs/assets/images/header-logo-dark.png#gh-dark-mode-only) ![CLI.NET Core](docs/assets/images/header-logo-light.png#gh-light-mode-only)
-[![Tests](https://github.com/lecode-official/command-line-parser/actions/workflows/tests.yml/badge.svg)](https://github.com/lecode-official/command-line-parser/actions/workflows/tests.yml) [![Linters](https://github.com/lecode-official/command-line-parser/actions/workflows/linters.yml/badge.svg)](https://github.com/lecode-official/command-line-parser/actions/workflows/linters.yml) +[![Tests](https://github.com/lecode-official/command-line-parser/actions/workflows/tests.yml/badge.svg)](https://github.com/lecode-official/command-line-parser/actions/workflows/tests.yml) [![Linters](https://github.com/lecode-official/command-line-parser/actions/workflows/linters.yml/badge.svg)](https://github.com/lecode-official/command-line-parser/actions/workflows/linters.yml) [![Documentation](https://img.shields.io/badge/Docs-GitHub%20Pages-blue)](https://lecode-official.github.io/command-line-parser/)
@@ -15,7 +15,7 @@ The repository is organized into a few top-level directories: - **`source/`** — The source code of the CLI.NET Core framework itself. - **`tests/`** — The unit tests and integration tests that verify the behavior of the framework, as well as the linters and code formatters that enforce a consistent and high-quality codebase. - **`docs/`** — The project documentation. Start with [`docs/README.md`](docs/README.md). -- **`design/`** — Design assets related to the project and the [design guide](design/DESIGN.md). +- **`design/`** — Logo and brand assets; see [`design/README.md`](design/README.md) for the file list and the [Logo Design](docs/developer-manual/logo-design.md) guide that governs them. ## Development diff --git a/design/README.md b/design/README.md new file mode 100644 index 0000000..a0221f7 --- /dev/null +++ b/design/README.md @@ -0,0 +1,19 @@ +# Design Assets + +Logo and brand assets for CLI.NET Core. Usage, rationale, and dos/don'ts are documented in the [Logo Design](../docs/developer-manual/logo-design.md) guide — this folder only holds the files. + +## Core Vector Marks + +- `mark-light.svg` / `mark-dark.svg` — the aperture-C mark on its own, for light and dark backgrounds. + +## App/Package Badges + +- `badge-glass-light.svg` / `-dark.svg` — the primary frosted-glass badge. +- `badge-flat-light.svg` / `-dark.svg` — flat badges for small favicons and anywhere filter effects may be stripped. +- `mini-badge-flat-light.svg` / `-dark.svg` — the flat badge at a smaller footprint. + +## Wordmark Lockups + +- `wordmark-light.svg` / `-dark.svg` — compact bar (mark + text). +- `readme-header-light.svg` / `-dark.svg` — tall horizontal lockup for READMEs. +- `wordmark.html` — responsive, fill-width HTML version of the bar. diff --git a/docs/README.md b/docs/README.md index 4b7850b..acdcf84 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ -# Documentation +# CLI.NET Core Documentation -![CLI.NET Core Logo](../design/readme-header-dark.png#gh-dark-mode-only) ![CLI.NET Core Logo](../design/readme-header-light.png#gh-light-mode-only) +![CLI.NET Core Logo](assets/images/header-logo-dark.png#gh-dark-mode-only) ![CLI.NET Core Logo](assets/images/header-logo-light.png#gh-light-mode-only) Welcome to the documentation of CLI.NET Core, a .NET command line application framework in the style of ASP.NET Core. It allows you to define commands and command line arguments in much the same way you would define actions and arguments for a Web API in ASP.NET Core. This documentation is split into two parts: diff --git a/docs/assets/favicon/favicon-dark.svg b/docs/assets/favicon/favicon-dark.svg new file mode 100644 index 0000000..8029f68 --- /dev/null +++ b/docs/assets/favicon/favicon-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/favicon/favicon-light.svg b/docs/assets/favicon/favicon-light.svg new file mode 100644 index 0000000..a8e79c4 --- /dev/null +++ b/docs/assets/favicon/favicon-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/favicon/favicon.ico b/docs/assets/favicon/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..68a2d57644460685ddd5d687c47b1205f1274525 GIT binary patch literal 15086 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 Y@zd`H{<&PZoVeV$9JyRIIjeI2FA&nI_W%F@ literal 0 HcmV?d00001 diff --git a/design/readme-header-dark.png b/docs/assets/images/header-logo-dark.png similarity index 100% rename from design/readme-header-dark.png rename to docs/assets/images/header-logo-dark.png diff --git a/design/readme-header-light.png b/docs/assets/images/header-logo-light.png similarity index 100% rename from design/readme-header-light.png rename to docs/assets/images/header-logo-light.png diff --git a/docs/assets/images/logo-dark.png b/docs/assets/images/logo-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..9482812034bd8bfac938b7746d66ad86b0b9df1e GIT binary patch literal 6611 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 R+Nl5l002ovPDHLkV1nea+WY_j literal 0 HcmV?d00001 diff --git a/docs/assets/images/logo-light.png b/docs/assets/images/logo-light.png new file mode 100644 index 0000000000000000000000000000000000000000..e68ae41622772b0da7acb2dc469c03baffd0c679 GIT binary patch literal 5543 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 element (see + * docs/developer-manual/tooling/documentation-website.md), so the same Markdown image pair that already switches with GitHub's own color + * scheme also switches with mkdocs-material's - keyed off the "data-md-color-scheme" attribute the theme's light/dark toggle sets, exactly + * like mkdocs-material's own built-in "#only-light" / "#only-dark" suffixes (see the "img[src$=...]" rules mkdocs-material ships for those). */ +[data-md-color-scheme="slate"] img[src$="#gh-light-mode-only"] { + display: none; +} + +[data-md-color-scheme="default"] img[src$="#gh-dark-mode-only"] { + display: none; +} diff --git a/docs/assets/styles/site-logo.css b/docs/assets/styles/site-logo.css new file mode 100644 index 0000000..1f06e30 --- /dev/null +++ b/docs/assets/styles/site-logo.css @@ -0,0 +1,9 @@ +/* "theme.logo" in properdocs.yml points at "assets/images/logo-light.png" as the fallback mkdocs-material renders into every "" it puts + * inside a ".md-logo" element (the header logo and the mobile drawer's copy of it - see docs/developer-manual/tooling/documentation-website.md). + * Since "theme.logo" itself is a single file with no light/dark option, this replaces that rendered image with the dark variant whenever the + * "data-md-color-scheme" attribute mkdocs-material's light/dark toggle sets is "slate" - the same color-scheme switch + * "gh-mode-only-images.css" applies to a pair of "" elements, applied here to the CSS "content" property instead, since only one "" + * exists for this logo. */ +[data-md-color-scheme="slate"] .md-logo img { + content: url("../images/logo-dark.png"); +} diff --git a/docs/assets/templates/main.html b/docs/assets/templates/main.html new file mode 100644 index 0000000..dda41e2 --- /dev/null +++ b/docs/assets/templates/main.html @@ -0,0 +1,10 @@ + +{% extends "base.html" %} {% block extrahead %} {{ super() }} + + +{% endblock %} diff --git a/docs/build/copy-favicon-to-site-root.py b/docs/build/copy-favicon-to-site-root.py new file mode 100644 index 0000000..d9307f9 --- /dev/null +++ b/docs/build/copy-favicon-to-site-root.py @@ -0,0 +1,24 @@ +"""MkDocs "hooks" module (see properdocs.yml) that copies the built favicon.ico to the site's root once a ProperDocs build has finished. + +Browsers and crawlers request "/favicon.ico" at the domain root as a fallback, independent of whatever "" tags a page's "" +carries (see docs/developer-manual/tooling/documentation-website.md). "theme.favicon" in properdocs.yml only ever writes the icon to the path +given there - "docs/assets/favicon/favicon.ico", kept there so the favicon sits alongside the site's other static assets under "docs/assets/" - +never to the site root, so this hook copies the already-built file there after the rest of the site has been written. +""" + +import pathlib +import shutil +from typing import Any + + +def on_post_build(config: Any, **kwargs: Any) -> None: + """Copies "assets/favicon/favicon.ico" from the built site into the site's own root directory. + + Args: + config: The ProperDocs configuration for the build that just finished, whose "site_dir" is where the site was written. + kwargs: Additional arguments the "on_post_build" event may be called with; unused, but required by the hook's signature. + """ + + site_directory = pathlib.Path(config['site_dir']) + built_favicon_path = site_directory / 'assets' / 'favicon' / 'favicon.ico' + shutil.copyfile(built_favicon_path, site_directory / 'favicon.ico') diff --git a/docs/build/generate-api-docs.py b/docs/build/generate-api-docs.py new file mode 100644 index 0000000..0c4d935 --- /dev/null +++ b/docs/build/generate-api-docs.py @@ -0,0 +1,153 @@ +"""Generates the "api" section of the documentation website at ProperDocs build time from CLI.NET Core's XML documentation comments. + +Nothing this script produces ever touches the real "docs" directory: xmldoc2md writes its Markdown files to a temporary directory, which this +script reads and re-emits through mkdocs_gen_files.open() into the in-memory build, and the temporary directory is discarded once the build has +consumed it. See docs/developer-manual/tooling/documentation-website.md for the full explanation of this flow. +""" + +import pathlib +import re +import subprocess +import tempfile + +import mkdocs_gen_files + +# The repository root, resolved relative to this script rather than the current working directory, so the script works regardless of where +# "properdocs build" or "properdocs serve" is invoked from; this script lives inside "docs_dir"'s "build" subdirectory (see properdocs.yml's +# "exclude_docs"), two levels below the repository root +repository_root: pathlib.Path = pathlib.Path(__file__).resolve().parent.parent.parent + +# The project whose build output is fed to xmldoc2md; the sample app is used instead of "clinet-core" directly because a class library's own +# build output does not include its NuGet dependencies (they are only resolved, not copied), and xmldoc2md needs every dependency of the +# framework assembly loadable via reflection; building the sample app transitively builds "clinet-core" and copies the framework assembly, its +# XML documentation file, and the full dependency closure into one output folder +documented_project: pathlib.Path = repository_root / 'source' / 'sample-app' / 'CLI.NET Core Sample App.csproj' +build_configuration: str = 'Release' + +# "build/sample-app/bin/", not "source/sample-app/bin/" - the repository root's "Directory.Build.props" redirects every project's own "bin/" and +# "obj/" into "build//" instead (see Architecture) +build_output_directory: pathlib.Path = repository_root / 'build' / 'sample-app' / 'bin' / build_configuration / 'net10.0' +framework_assembly_path: pathlib.Path = build_output_directory / 'CLI.NET Core.dll' +framework_xml_documentation_path: pathlib.Path = build_output_directory / 'CLI.NET Core.xml' + + +def ensure_framework_is_built() -> None: + """Builds the sample app (and, transitively, the framework) so that the framework's XML documentation file exists. + + Raises: + RuntimeError: The XML documentation file is still missing after the build, which means GenerateDocumentationFile is not enabled on the + framework project. + """ + + subprocess.run( + ['dotnet', 'build', str(documented_project), '--configuration', build_configuration], + cwd=repository_root, + check=True, + ) + + if not framework_xml_documentation_path.is_file(): + raise RuntimeError( + f'The XML documentation file was not found at "{framework_xml_documentation_path}" after building the project - ' + 'GenerateDocumentationFile must be enabled on the framework\'s .csproj' + ) + + +def generate_markdown_into(output_directory: pathlib.Path) -> None: + """Shells out to xmldoc2md, the chosen C# XML documentation to Markdown generator, writing its output into the given temporary directory. + + Args: + output_directory: The temporary directory xmldoc2md should write its generated Markdown files into. + """ + + subprocess.run( + [ + 'dotnet', + 'tool', + 'run', + 'xmldoc2md', + '--', + str(framework_assembly_path), + '--output', + str(output_directory), + '--structure', + 'tree', + '--platform', + 'github-pages', + ], + cwd=repository_root, + check=True, + ) + + +def page_title(markdown_content: str, fallback: str) -> str: + """Extracts the H1 heading xmldoc2md put at the top of a generated page, so the navigation can show a type's real name (for example + "CliCommandLineArguments") instead of the all-lowercase file name xmldoc2md derived it from. + + Args: + markdown_content: The full text of one page xmldoc2md generated. + fallback: The label to use if the page has no H1 heading, which should not happen for a page xmldoc2md generated. + + Returns: + The heading text, or "fallback" if the page has no H1 heading. + """ + + heading_match = re.search(r'^# (.+)$', markdown_content, re.MULTILINE) + return heading_match.group(1) if heading_match else fallback + + +def namespace_path(markdown_content: str) -> tuple[str, ...] | None: + """Extracts the dotted namespace xmldoc2md put on every type's page (for example "Namespace: CliNetCore.Application") and splits it into the + real, properly cased path segments the navigation should group that type under, instead of the all-lowercase folder names "--structure tree" + derived them from. The root index page - the only page "--structure tree" writes without a "Namespace:" line - is handled by its caller + instead, so it is not this function's concern. + + Args: + markdown_content: The full text of one page xmldoc2md generated. + + Returns: + The namespace's segments, most general first, or "None" if the page has no "Namespace:" line. + """ + + namespace_match = re.search(r'^Namespace: (.+)$', markdown_content, re.MULTILINE) + return tuple(namespace_match.group(1).split('.')) if namespace_match else None + + +def inject_generated_pages(output_directory: pathlib.Path) -> None: + """Copies every Markdown file xmldoc2md produced into the MkDocs build under "api/", using mkdocs_gen_files.open() so that none of it is ever + written to the real "docs" directory on disk, and writes an "api/SUMMARY.md" alongside them that lists the same files as a literate nav (see + docs/developer-manual/tooling/documentation-website.md) - this is what gets the "api" section into the site's navigation without a ".pages" file. + + Args: + output_directory: The temporary directory xmldoc2md wrote its generated Markdown files into. + """ + + navigation = mkdocs_gen_files.Nav() + + for markdown_file in sorted(output_directory.rglob('*.md')): + relative_path = markdown_file.relative_to(output_directory) + markdown_content = markdown_file.read_text(encoding='utf-8') + + destination_path = pathlib.PurePosixPath('api') / relative_path + with mkdocs_gen_files.open(destination_path, 'w', encoding='utf-8') as destination_file: + destination_file.write(markdown_content) + + path_parts = relative_path.with_suffix('').parts + if path_parts == ('index',): + # The root index page: it has no "Namespace:" line for "namespace_path()" to read, and its H1 (the assembly's own name, "CLI.NET + # Core") would be a confusing navigation label sitting alongside the type names - "Index" says what the page actually is. + navigation[('Index',)] = relative_path.as_posix() + continue + + navigation[(*(namespace_path(markdown_content) or path_parts[:-1]), page_title(markdown_content, fallback=path_parts[-1]))] = ( + relative_path.as_posix() + ) + + with mkdocs_gen_files.open(pathlib.PurePosixPath('api') / 'SUMMARY.md', 'w', encoding='utf-8') as navigation_file: + navigation_file.writelines(navigation.build_literate_nav()) + + +ensure_framework_is_built() +with tempfile.TemporaryDirectory(prefix='clinetcore-api-docs-') as temporary_directory_name: + temporary_output_directory = pathlib.Path(temporary_directory_name) + generate_markdown_into(temporary_output_directory) + inject_generated_pages(temporary_output_directory) diff --git a/docs/build/inject-api-reference-link.py b/docs/build/inject-api-reference-link.py new file mode 100644 index 0000000..095db8f --- /dev/null +++ b/docs/build/inject-api-reference-link.py @@ -0,0 +1,47 @@ +"""MkDocs "hooks" module (see properdocs.yml) that appends an "API Reference" entry to the ordered list on the documentation site's own overview +page ("docs/README.md"). + +"docs/api" is never committed - it only exists once "generate-api-docs.py" has run as part of a build (see +docs/developer-manual/tooling/documentation-website.md) - so a hand-written link to it in "docs/README.md" would 404 on GitHub, which only ever +shows the committed source. This hook instead appends the link at build time, after whichever ordered list item on the overview page is the last +one that starts with a link, using that item's own number plus one, so the numbering stays correct regardless of how many items the list already +has. +""" + +import pathlib +import re +from typing import Any + +# Matches one top-level ordered list item that starts with a Markdown link, capturing its number so the appended item can continue the same +# sequence; "re.MULTILINE" anchors "^" to the start of each line rather than the start of the whole page. +ordered_list_item_pattern = re.compile(r'^(\d+)\. \[.*$', re.MULTILINE) + + +def on_page_markdown(markdown: str, page: Any, config: Any, **kwargs: Any) -> str: + """Appends the "API Reference" list item to "docs/README.md"'s ordered list, immediately after its last link-led item. + + Args: + markdown: The page's raw Markdown source, before it is converted to HTML. + page: The ProperDocs page "markdown" was read for. Only the site's root overview page ("docs/README.md", "file.src_uri" equal to + "README.md") is modified; every other page is returned unchanged. + config: The ProperDocs configuration for the current build; unused, but required by the hook's signature. + kwargs: Additional arguments the "on_page_markdown" event may be called with; unused, but required by the hook's signature. + + Returns: + "markdown" with the "API Reference" item appended after the overview page's last link-led ordered list item, or unchanged for every + other page, or if the overview page has no such list item to append after. + """ + + if pathlib.PurePosixPath(page.file.src_uri) != pathlib.PurePosixPath('README.md'): + return markdown + + list_item_matches = list(ordered_list_item_pattern.finditer(markdown)) + if not list_item_matches: + return markdown + + last_list_item_match = list_item_matches[-1] + next_item_number = int(last_list_item_match.group(1)) + 1 + insertion_point = last_list_item_match.end() + api_reference_item = f'\n{next_item_number}. [API Reference](api/index.md) — generated from the C# XML documentation comments at build time.' + + return markdown[:insertion_point] + api_reference_item + markdown[insertion_point:] diff --git a/docs/build/rewrite-repo-links.py b/docs/build/rewrite-repo-links.py new file mode 100644 index 0000000..a9586b9 --- /dev/null +++ b/docs/build/rewrite-repo-links.py @@ -0,0 +1,97 @@ +"""MkDocs "hooks" module (see properdocs.yml) that rewrites Markdown links pointing outside "docs_dir" into absolute GitHub URLs. + +Every hand-written article under "docs/" is free to link to files elsewhere in the repository - source code, "README.md", "CLAUDE.md", the +".github/workflows/" directory, and so on (see docs/developer-manual/tooling/documentation-website.md) - because those links already render +correctly on GitHub, which resolves a relative link against the file's real position in the repository. The built site has no such files: +"docs_dir" is the only part of the repository ProperDocs ever copies into "site/", so a relative link that climbs out of "docs/" would 404 there +even though the exact same link works on GitHub. This hook rewrites every such link, at build time, into an absolute "repo_url" link to the same +file on GitHub instead - a link that already resolves inside "docs_dir" is left untouched, since ProperDocs already resolves and rewrites those +correctly on its own. +""" + +import pathlib +import re +import urllib.parse +from typing import Any + +# The branch the built site is always deployed from (see docs/developer-manual/tooling/continuous-integration.md - the docs site is only ever +# built and published on a push to "main"), so a rewritten link always points at the same revision the live site itself was built from. +branch_name = 'main' + +# The repository root, resolved relative to this script rather than the current working directory, the same way generate-api-docs.py does it; this +# script lives inside "docs_dir"'s "build" subdirectory (see properdocs.yml's "exclude_docs"), two levels below the repository root. +repository_root: pathlib.Path = pathlib.Path(__file__).resolve().parent.parent.parent + +# Matches one Markdown link or image, capturing its "[label]" (including the leading "!" of an image), its raw target, and its optional +# "title" so both can be put back around a rewritten target unchanged. +markdown_link_pattern = re.compile(r'(!?\[[^\]]*\])\(([^)\s]+)(\s+"[^"]*")?\)') + + +def on_page_markdown(markdown: str, page: Any, config: Any, **kwargs: Any) -> str: + """Rewrites every Markdown link in "markdown" that resolves outside "docs_dir" into an absolute GitHub URL. + + Args: + markdown: The page's raw Markdown source, before it is converted to HTML. + page: The ProperDocs page "markdown" was read for. Its "file.src_uri" identifies the generated "api/" section (see + docs/developer-manual/tooling/documentation-website.md), which is skipped, since its links only ever point at other generated "api/" + pages, never outside "docs_dir". + config: The ProperDocs configuration for the current build, whose "docs_dir" bounds which resolved links count as "outside" and whose + "repo_url" is the GitHub repository a rewritten link points into. + kwargs: Additional arguments the "on_page_markdown" event may be called with; unused, but required by the hook's signature. + + Returns: + "markdown" with every outside-pointing link rewritten to an absolute GitHub URL, or unchanged for a page in the generated "api/" section. + """ + + if pathlib.PurePosixPath(page.file.src_uri).parts[0] == 'api': + return markdown + + source_directory = pathlib.Path(page.file.abs_src_path).parent + docs_directory = pathlib.Path(config['docs_dir']) + repo_url = config['repo_url'] + + def rewrite(match: re.Match[str]) -> str: + label, target, title = match.group(1), match.group(2), match.group(3) or '' + rewritten_target = rewrite_target(target, source_directory, docs_directory, repo_url) + return f'{label}({rewritten_target}{title})' + + return markdown_link_pattern.sub(rewrite, markdown) + + +def rewrite_target(target: str, source_directory: pathlib.Path, docs_directory: pathlib.Path, repo_url: str) -> str: + """Rewrites a single link target into an absolute GitHub URL, if and only if it resolves outside "docs_directory". + + Args: + target: One Markdown link's raw target, exactly as written - for example "../../CLAUDE.md#golden-rules". + source_directory: The directory of the page the link was found on, which "target" is resolved against. + docs_directory: The site's "docs_dir", the boundary between an internal link (left untouched) and one this hook rewrites. + repo_url: The GitHub repository a rewritten link points into. + + Returns: + "target" unchanged if it is not a relative filesystem path, already resolves inside "docs_directory", or resolves outside the repository + entirely (a broken link this hook cannot make sense of); otherwise, the equivalent absolute GitHub URL, with the original fragment (if + any) preserved. + """ + + path_part, fragment_separator, fragment = target.partition('#') + if not path_part or re.match(r'^[a-zA-Z][a-zA-Z0-9+.-]*:', path_part): + # Empty (a pure "#fragment" link) or already absolute (an URL scheme like "https:" or "mailto:") - nothing to rewrite. + return target + + resolved_path = (source_directory / urllib.parse.unquote(path_part)).resolve() + + try: + resolved_path.relative_to(docs_directory) + return target + except ValueError: + pass + + try: + relative_to_repository_root = resolved_path.relative_to(repository_root) + except ValueError: + # Outside the repository entirely - a broken link, which is not this hook's concern to fix. + return target + + reference = 'tree' if resolved_path.is_dir() else 'blob' + github_url = f'{repo_url}/{reference}/{branch_name}/{urllib.parse.quote(relative_to_repository_root.as_posix())}' + return f'{github_url}{fragment_separator}{fragment}' if fragment_separator else github_url diff --git a/docs/developer-manual/README.md b/docs/developer-manual/README.md index 905ee72..396ad9e 100644 --- a/docs/developer-manual/README.md +++ b/docs/developer-manual/README.md @@ -2,21 +2,22 @@ This manual is for people working on the CLI.NET Core codebase itself: where things live, how the code is styled, and how the project is built, linted, and formatted. -1. [Architecture](architecture/README.md) - 1. [Overview](architecture/overview.md) -2. [Tooling](tooling/README.md) +1. [Architecture](architecture.md) +2. [Logo Design](logo-design.md) +3. [Tooling](tooling/README.md) 1. [Developer Setup](tooling/developer-setup.md) 2. [Visual Studio Code Integration](tooling/vscode-integration.md) 3. [Spell Checking (CSpell)](tooling/spell-checking-cspell.md) - 4. [MarkdownLint](tooling/linting-markdownlint.md) - 5. [dprint](tooling/formatting-dprint.md) - 6. [Continuous Integration](tooling/continuous-integration.md) - 7. [Testing and Code Coverage](tooling/testing-and-code-coverage.md) -3. [Conventions](conventions/README.md) + 4. [Markdown Linting (MarkdownLint)](tooling/linting-markdownlint.md) + 5. [Code Formatting (dprint)](tooling/code-formatting-dprint.md) + 6. [Documentation Website](tooling/documentation-website.md) + 7. [Continuous Integration](tooling/continuous-integration.md) + 8. [Testing and Code Coverage](tooling/testing-and-code-coverage.md) +4. [Conventions](conventions/README.md) 1. [C# Style](conventions/csharp-style.md) 2. [Markdown Style](conventions/markdown-style.md) 3. [Commit Messages](conventions/commit-messages.md) 4. [File Naming Conventions](conventions/file-naming-conventions.md) 5. [Dependency Management](conventions/dependency-management.md) 6. [Testing](conventions/testing.md) -4. [Contributing](contributing.md) +5. [Contributing](contribution-guide.md) diff --git a/docs/developer-manual/architecture.md b/docs/developer-manual/architecture.md new file mode 100644 index 0000000..af69db0 --- /dev/null +++ b/docs/developer-manual/architecture.md @@ -0,0 +1,48 @@ +# Architecture + +This article covers how the repository is laid out and how the .NET projects inside it are configured. + +## Repository Layout + +The repository is organized into a few top-level directories: + +- [`source/`](../../source/) — The source code of the CLI.NET Core framework itself, and the sample app that demonstrates it. +- [`tests/`](../../tests/) — Unit and integration tests, plus the configuration for the linters and the code formatter (see [Tooling](tooling/README.md)). +- [`docs/`](../README.md) — This documentation, including the [Documentation Website](tooling/documentation-website.md)'s build scripts (`docs/build/`) and the one theme template it overrides (`docs/assets/templates/`), both wired in via [`properdocs.yml`](../../properdocs.yml). +- [`design/`](../../design/) — Logo and brand assets; see the [Logo Design](logo-design.md) guide that governs them. + +Top-level files round out the repository: [`README.md`](../../README.md) is the project's front door, [`CHANGELOG.md`](../../CHANGELOG.md) records what changed in each version, [`CONTRIBUTORS.md`](../../CONTRIBUTORS.md) lists everyone who has contributed, and [`LICENSE`](../../LICENSE) is the full text of the license (see [Contributing](contribution-guide.md) for how these are kept up to date). [`CONTRIBUTING.md`](../../CONTRIBUTING.md), [`CODE_OF_CONDUCT.md`](../../CODE_OF_CONDUCT.md), and [`SECURITY.md`](../../SECURITY.md) round out the community-facing files GitHub recognizes by name. [`global.json`](../../global.json) pins the exact .NET SDK version the repository builds with, and [`.config/dotnet-tools.json`](../../.config/dotnet-tools.json) is the local tool manifest that pins ReportGenerator, the coverage-report tool (see [Testing and Code Coverage](tooling/testing-and-code-coverage.md)) — both follow the same exact-version-only rule as everything else (see [Dependency Management](conventions/dependency-management.md)). + +## Solution and Projects + +[`source/CLI.NET Core.slnx`](../../source/CLI.NET%20Core.slnx) is the solution file, in the newer XML-based `.slnx` format rather than the classic `.sln` format. It groups three projects: + +- **`clinet-core`** ([`CLI.NET Core.csproj`](../../source/clinet-core/CLI.NET%20Core.csproj)) — The framework itself, packed and published as the `CliNetCore` NuGet package. +- **`sample-app`** ([`CLI.NET Core Sample App.csproj`](../../source/sample-app/CLI.NET%20Core%20Sample%20App.csproj)) — A runnable sample application that references `clinet-core` via a project reference and demonstrates how the framework is used. +- **`unit-tests`** ([`CLI.NET Core Unit Tests.csproj`](../../tests/unit-tests/CLI.NET%20Core%20Unit%20Tests.csproj)) — The xUnit test project that verifies `clinet-core`'s behavior, living under [`tests/`](../../tests/) rather than `source/` (see [Testing and Code Coverage](tooling/testing-and-code-coverage.md)). + +All three projects target `net10.0`, and all three enable `` and ``. `clinet-core` additionally sets ``, so that the XML documentation comments in the source (see [C# Style](conventions/csharp-style.md)) ship alongside the compiled assembly and are available to consumers of the NuGet package through their editor's tooltips, and grants the test project access to its `internal` members via `InternalsVisibleTo`, so internal types can be unit tested directly (see [Testing](conventions/testing.md)). + +`clinet-core` depends on the `Microsoft.Extensions.Hosting` NuGet package — the same generic-host infrastructure ASP.NET Core itself builds on. This dependency is what lets CLI.NET Core mirror the ASP.NET Core hosting model for command-line applications, as described in the [root README](../../README.md). + +## Build Output + +Two `Directory.Build.props` files redirect every project's `bin`/`obj` output out of its own folder and into the repository root's `build//` instead — `build/clinet-core/`, `build/sample-app/`, and `build/unit-tests/` — rather than a single one at the repository root, so that root stays uncluttered: + +- [`source/Directory.Build.props`](../../source/Directory.Build.props) covers `clinet-core` and `sample-app`, using `$([System.IO.Path]::GetFileName($(MSBuildProjectDirectory)))` to read each project's own folder name at evaluation time, so the one file covers both projects without listing them individually. +- [`tests/unit-tests/Directory.Build.props`](../../tests/unit-tests/Directory.Build.props) covers `unit-tests` alone, so it hard-codes `build/unit-tests/` instead — there is only the one project under `tests/`, so the folder-name lookup above would be needless indirection here. + +The documentation website builds into `build/documentation-website/` alongside them, via `site_dir` in [`properdocs.yml`](../../properdocs.yml) (see [Documentation Website](tooling/documentation-website.md)). The entire `build/` directory is generated and covered by [`.gitignore`](../../.gitignore) — nothing under it is ever committed. + +## Versioning and Licensing + +The NuGet package version is set independently in each `.csproj`'s `` property. [`CHANGELOG.md`](../../CHANGELOG.md) is the human-readable history of what each version changed; it is not generated from Git history, so it needs to be updated by hand (see [Contributing](contribution-guide.md)). + +The project is licensed under LGPL-3.0 (see [`LICENSE`](../../LICENSE)). Every `.csproj` mirrors this in its NuGet metadata. + +## Related + +- Coding conventions for the C# source: [C# Style](conventions/csharp-style.md). +- Linters, the code formatter, and editor setup: [Tooling](tooling/README.md). +- Running the unit tests and measuring code coverage: [Testing and Code Coverage](tooling/testing-and-code-coverage.md). +- How to propose and submit changes: [Contributing](contribution-guide.md). diff --git a/docs/developer-manual/architecture/README.md b/docs/developer-manual/architecture/README.md deleted file mode 100644 index d1b59f4..0000000 --- a/docs/developer-manual/architecture/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Architecture - -This section covers how the repository and the code inside it are put together: - -1. [Overview](overview.md) diff --git a/docs/developer-manual/architecture/overview.md b/docs/developer-manual/architecture/overview.md deleted file mode 100644 index e885ba3..0000000 --- a/docs/developer-manual/architecture/overview.md +++ /dev/null @@ -1,39 +0,0 @@ -# Overview - -This article covers how the repository is laid out and how the .NET projects inside it are configured. - -## Repository Layout - -The repository is organized into a few top-level directories: - -- [`source/`](../../../source/) — The source code of the CLI.NET Core framework itself, and the sample app that demonstrates it. -- [`tests/`](../../../tests/) — Unit and integration tests, plus the configuration for the linters and the code formatter (see [Tooling](../tooling/README.md)). -- [`docs/`](../../README.md) — This documentation. -- [`design/`](../../../design/) — Logo and brand assets, and the [design guide](../../../design/DESIGN.md) that governs them. - -Top-level files round out the repository: [`README.md`](../../../README.md) is the project's front door, [`CHANGELOG.md`](../../../CHANGELOG.md) records what changed in each version, [`CONTRIBUTORS.md`](../../../CONTRIBUTORS.md) lists everyone who has contributed, and [`LICENSE`](../../../LICENSE) is the full text of the license (see [Contributing](../contributing.md) for how these are kept up to date). [`CONTRIBUTING.md`](../../../CONTRIBUTING.md), [`CODE_OF_CONDUCT.md`](../../../CODE_OF_CONDUCT.md), and [`SECURITY.md`](../../../SECURITY.md) round out the community-facing files GitHub recognizes by name. [`global.json`](../../../global.json) pins the exact .NET SDK version the repository builds with, and [`.config/dotnet-tools.json`](../../../.config/dotnet-tools.json) is the local tool manifest that pins ReportGenerator, the coverage-report tool (see [Testing and Code Coverage](../tooling/testing-and-code-coverage.md)) — both follow the same exact-version-only rule as everything else (see [Dependency Management](../conventions/dependency-management.md)). - -## Solution and Projects - -[`source/CLI.NET Core.slnx`](../../../source/CLI.NET%20Core.slnx) is the solution file, in the newer XML-based `.slnx` format rather than the classic `.sln` format. It groups three projects: - -- **`clinet-core`** ([`CLI.NET Core.csproj`](../../../source/clinet-core/CLI.NET%20Core.csproj)) — The framework itself, packed and published as the `CliNetCore` NuGet package. -- **`sample-app`** ([`CLI.NET Core Sample App.csproj`](../../../source/sample-app/CLI.NET%20Core%20Sample%20App.csproj)) — A runnable sample application that references `clinet-core` via a project reference and demonstrates how the framework is used. -- **`unit-tests`** ([`CLI.NET Core Unit Tests.csproj`](../../../tests/unit-tests/CLI.NET%20Core%20Unit%20Tests.csproj)) — The xUnit test project that verifies `clinet-core`'s behavior, living under [`tests/`](../../../tests/) rather than `source/` (see [Testing and Code Coverage](../tooling/testing-and-code-coverage.md)). - -All three projects target `net10.0`, and all three enable `` and ``. `clinet-core` additionally sets ``, so that the XML documentation comments in the source (see [C# Style](../conventions/csharp-style.md)) ship alongside the compiled assembly and are available to consumers of the NuGet package through their editor's tooltips, and grants the test project access to its `internal` members via `InternalsVisibleTo`, so internal types can be unit tested directly (see [Testing](../conventions/testing.md)). - -`clinet-core` depends on the `Microsoft.Extensions.Hosting` NuGet package — the same generic-host infrastructure ASP.NET Core itself builds on. This dependency is what lets CLI.NET Core mirror the ASP.NET Core hosting model for command-line applications, as described in the [root README](../../../README.md). - -## Versioning and Licensing - -The NuGet package version is set independently in each `.csproj`'s `` property. [`CHANGELOG.md`](../../../CHANGELOG.md) is the human-readable history of what each version changed; it is not generated from Git history, so it needs to be updated by hand (see [Contributing](../contributing.md)). - -The project is licensed under LGPL-3.0 (see [`LICENSE`](../../../LICENSE)). Every `.csproj` mirrors this in its NuGet metadata. - -## Related - -- Coding conventions for the C# source: [C# Style](../conventions/csharp-style.md). -- Linters, the code formatter, and editor setup: [Tooling](../tooling/README.md). -- Running the unit tests and measuring code coverage: [Testing and Code Coverage](../tooling/testing-and-code-coverage.md). -- How to propose and submit changes: [Contributing](../contributing.md). diff --git a/docs/developer-manual/contributing.md b/docs/developer-manual/contribution-guide.md similarity index 96% rename from docs/developer-manual/contributing.md rename to docs/developer-manual/contribution-guide.md index 6993785..c9b3e58 100644 --- a/docs/developer-manual/contributing.md +++ b/docs/developer-manual/contribution-guide.md @@ -6,6 +6,6 @@ Participation is governed by [`CODE_OF_CONDUCT.md`](../../CODE_OF_CONDUCT.md), a ## Related -- Where the code you are changing likely lives: [Architecture](architecture/overview.md). +- Where the code you are changing likely lives: [Architecture](architecture.md). - The tools that check your change before it merges: [Tooling](tooling/README.md). - The conventions a change is expected to follow: [Conventions](conventions/README.md). diff --git a/docs/developer-manual/conventions/README.md b/docs/developer-manual/conventions/README.md index 87de613..6d9774c 100644 --- a/docs/developer-manual/conventions/README.md +++ b/docs/developer-manual/conventions/README.md @@ -1,6 +1,6 @@ # Conventions -This article summarizes the coding and styling conventions for the project: +This section summarizes the coding and styling conventions for the project: 1. [C# Style](csharp-style.md) 2. [Markdown Style](markdown-style.md) diff --git a/docs/developer-manual/conventions/commit-messages.md b/docs/developer-manual/conventions/commit-messages.md index 5bddfbb..d75f362 100644 --- a/docs/developer-manual/conventions/commit-messages.md +++ b/docs/developer-manual/conventions/commit-messages.md @@ -49,4 +49,5 @@ Co-Authored-By: Claude ## Related - The punctuation and title-case rules these messages follow: [Markdown Style](markdown-style.md). -- The full policy on AI-assisted work: the "Use of AI" section of the [root README](../../../README.md), and [Contributing](../contributing.md). +- The full policy on AI-assisted work: the "Use of AI" section of the [root README](../../../README.md), and [Contributing](../contribution-guide.md). +- The golden rule an AI assistant follows for staging and committing: [CLAUDE.md](../../../CLAUDE.md#golden-rules). diff --git a/docs/developer-manual/conventions/csharp-style.md b/docs/developer-manual/conventions/csharp-style.md index 0727ff7..7b26ef3 100644 --- a/docs/developer-manual/conventions/csharp-style.md +++ b/docs/developer-manual/conventions/csharp-style.md @@ -1,10 +1,10 @@ # C# Style -This article covers the conventions for C# code in [`source/`](../../../source/). It is about style and structure on top of the language itself — for the repository layout the code lives in, see [Architecture](../architecture/overview.md). +This article covers the conventions for C# code in [`source/`](../../../source/). It is about style and structure on top of the language itself — for the repository layout the code lives in, see [Architecture](../architecture.md). ## File Layout -Every file starts with a file-scoped namespace declaration (`namespace CliNetCore.Application;`), not a namespace block. Beyond the usings that [`ImplicitUsings`](../architecture/overview.md) already brings in, additional `using` directives are wrapped in a `#region Using Directives` block above the namespace declaration. +Every file starts with a file-scoped namespace declaration (`namespace CliNetCore.Application;`), not a namespace block. Beyond the usings that [`ImplicitUsings`](../architecture.md) already brings in, additional `using` directives are wrapped in a `#region Using Directives` block above the namespace declaration. Every type groups its members into `#region` blocks, regardless of file size — even a type with a single constructor and a single property uses regions. There is no size threshold below which regions are skipped. @@ -37,7 +37,7 @@ public interface ITest ## Documentation Comments -Every member has an XML documentation comment — public and internal ones because `GenerateDocumentationFile` is enabled in [the project files](../architecture/overview.md), so those ship in the compiled output and reach a consumer's editor tooltips, but `private` members are documented exactly the same way, for the same reason every other convention on this page exists: someone reading the source, not just the compiled output, needs it. A `` explains not just what a member does, but why it exists and how it relates to the rest of the design — the reasoning belongs next to the code, not only in a commit message. Parameters and return values are documented with `` and ``. When a member implements an interface member or overrides a base member and has nothing to add beyond what is already documented there, use `` instead of repeating the text. +Every member has an XML documentation comment — public and internal ones because `GenerateDocumentationFile` is enabled in [the project files](../architecture.md), so those ship in the compiled output and reach a consumer's editor tooltips, but `private` members are documented exactly the same way, for the same reason every other convention on this page exists: someone reading the source, not just the compiled output, needs it. A `` explains not just what a member does, but why it exists and how it relates to the rest of the design — the reasoning belongs next to the code, not only in a commit message. Parameters and return values are documented with `` and ``. When a member implements an interface member or overrides a base member and has nothing to add beyond what is already documented there, use `` instead of repeating the text. The C# compiler never expands `` into real text in the generated XML documentation file — it is left in verbatim — so [`clinet-core`'s `.csproj`](../../../source/clinet-core/CLI.NET%20Core.csproj) references [SauceControl.InheritDoc](https://github.com/saucecontrol/InheritDoc), which rewrites every `` with the real, inherited text as a build step (see [Documentation Website](../tooling/documentation-website.md)); without it, both a consumer's editor tooltip and the generated `docs/api` reference would show nothing for these members. Prose inside documentation comments follows the same punctuation rule as the rest of the documentation: periods, not semicolons (see [Markdown Style](markdown-style.md)). @@ -71,14 +71,14 @@ Do not write a comment that only restates what the code already says. If removin ## Nullability and Implicit Usings -Every project enables `enable` and `enable` (see [Architecture](../architecture/overview.md)). Write code that is genuinely null-safe rather than silencing the analyzer with `!`; a nullable parameter or return type should mean that `null` is a real, handled case. +Every project enables `enable` and `enable` (see [Architecture](../architecture.md)). Write code that is genuinely null-safe rather than silencing the analyzer with `!`; a nullable parameter or return type should mean that `null` is a real, handled case. ## Formatting -C# is **not** formatted by [dprint](../tooling/formatting-dprint.md) — dprint has no C# plugin in this project, so `.csproj` and `.slnx` files are formatted by it (they are XML), but `.cs` files are not. Indentation and basic whitespace rules for C# come from [`.editorconfig`](../../../.editorconfig) (4 spaces, no tabs), which most editors, including Visual Studio Code, apply automatically. Beyond what `.editorconfig` enforces, matching the conventions on this page during review is the only guard C# has today. +C# is **not** formatted by [dprint](../tooling/code-formatting-dprint.md) — dprint has no C# plugin in this project, so `.csproj` and `.slnx` files are formatted by it (they are XML), but `.cs` files are not. Indentation and basic whitespace rules for C# come from [`.editorconfig`](../../../.editorconfig) (4 spaces, no tabs), which most editors, including Visual Studio Code, apply automatically. Beyond what `.editorconfig` enforces, matching the conventions on this page during review is the only guard C# has today. ## Related -- Where these files live in the repository: [Architecture](../architecture/overview.md). +- Where these files live in the repository: [Architecture](../architecture.md). - The punctuation rule for prose versus plain comments also governs [Markdown Style](markdown-style.md). -- Formatting for the file types dprint does own: [dprint](../tooling/formatting-dprint.md). +- Formatting for the file types dprint does own: [dprint](../tooling/code-formatting-dprint.md). diff --git a/docs/developer-manual/conventions/dependency-management.md b/docs/developer-manual/conventions/dependency-management.md index 794239b..ef9673d 100644 --- a/docs/developer-manual/conventions/dependency-management.md +++ b/docs/developer-manual/conventions/dependency-management.md @@ -10,11 +10,23 @@ Every dependency is pinned to an exact version — never a floating range (`*`), ``` -not `Version="10.*"` or `Version="[10.0.0,)"`. The same rule applies outside of NuGet: the dprint plugins in [`dprint.json`](../../../dprint.json) are pinned to an exact release URL, [Continuous Integration](../tooling/continuous-integration.md) installs `dprint`, `cspell`, and `markdownlint-cli2` at exact, explicit versions, and [`global.json`](../../../global.json) pins the exact .NET SDK version (with `"rollForward": "disable"`, so a newer SDK on a machine or runner is never silently substituted) that every `dotnet` command in this repository, including CI, builds with. An upgrade is always a deliberate, visible change to a version string, never something that happens silently on the next restore. +not `Version="10.*"` or `Version="[10.0.0,)"`. The same rule applies outside of NuGet: the dprint plugins in [`dprint.json`](../../../dprint.json) are pinned to an exact release URL, `dprint`, `cspell`, and `markdownlint-cli2` are pinned to exact versions in [`package.json`](../../../package.json), the Python packages that build the documentation website are pinned exactly in [`requirements.txt`](../../../requirements.txt), and [`global.json`](../../../global.json) pins the exact .NET SDK version (with `"rollForward": "disable"`, so a newer SDK on a machine or runner is never silently substituted) that every `dotnet` command in this repository, including CI, builds with. An upgrade is always a deliberate, visible change to a version string, never something that happens silently on the next restore. ## Local .NET Tools -[`.config/dotnet-tools.json`](../../../.config/dotnet-tools.json) is a `dotnet` local tool manifest, restored with `dotnet tool restore`. It exists for tools that are invoked as part of the .NET build rather than through Node.js — currently just ReportGenerator, used to turn raw code-coverage data into a human-readable report (see [Testing and Code Coverage](../tooling/testing-and-code-coverage.md)). Like every other dependency, its version is pinned exactly; upgrading it is `dotnet tool update dotnet-reportgenerator-globaltool --version `, followed by committing the resulting change to the manifest. +[`.config/dotnet-tools.json`](../../../.config/dotnet-tools.json) is a `dotnet` local tool manifest, restored with `dotnet tool restore`. It exists for tools that are invoked as part of the .NET build rather than through Node.js or Python — ReportGenerator, used to turn raw code-coverage data into a human-readable report (see [Testing and Code Coverage](../tooling/testing-and-code-coverage.md)), and xmldoc2md, used to generate the `docs/api` C# API reference (see [Documentation Website](../tooling/documentation-website.md)). Like every other dependency, each tool's version is pinned exactly; upgrading one is `dotnet tool update --version `, followed by committing the resulting change to the manifest. + +## Node.js Dependencies (`package.json`) + +[`package.json`](../../../package.json) at the repository root declares `dprint`, `cspell`, and `markdownlint-cli2` as `devDependencies`, each pinned to an exact version (no `^` or `~` range prefix). [`package-lock.json`](../../../package-lock.json), generated by `npm install` and committed alongside it, locks the exact resolved version of every direct and transitive dependency, the same role `packages.lock.json` plays for NuGet below. `npm ci` (not `npm install`) is what both [Continuous Integration](../tooling/continuous-integration.md) and [Developer Setup](../tooling/developer-setup.md) use to restore it — it fails instead of silently updating `package-lock.json` if the lock file would no longer match `package.json`. + +When a `devDependencies` version changes, run `npm install` again to regenerate `package-lock.json` and commit it alongside the `package.json` change; the two must move together, exactly like a `PackageReference` change and its `packages.lock.json`. + +## Python Dependencies (`requirements.txt`) + +[`requirements.txt`](../../../requirements.txt) at the repository root pins the exact versions of ProperDocs, the Material theme, and the plugins that build the [documentation website](../tooling/documentation-website.md), installed with `python -m pip install --requirement requirements.txt`. Unlike the NuGet and NPM dependencies above, there is no lock file recording transitive dependency versions — this project has no equivalent of `pip-tools` or a similar lock-generating workflow in place, so only the direct dependencies listed in `requirements.txt` are pinned exactly. + +Run that `pip install` inside a virtual environment, never against the system or user Python installation — see [Developer Setup](../tooling/developer-setup.md) for the exact commands. Without one, `pip` either installs `properdocs` and its plugins globally (invisible to anyone else on the machine and to any other project's Python dependencies) or refuses outright with an externally-managed-environment error on distributions that lock down the system interpreter. ## Lock Files @@ -30,5 +42,6 @@ When a `PackageReference` version changes, run `dotnet restore` again to regener ## Related -- Where the `.csproj` files that set this live: [Architecture](../architecture/overview.md). +- Where the `.csproj` files that set this live: [Architecture](../architecture.md). - The pinned tool versions used outside of NuGet: [Continuous Integration](../tooling/continuous-integration.md). +- Running `npm ci` and `python -m pip install` locally: [Developer Setup](../tooling/developer-setup.md). diff --git a/docs/developer-manual/conventions/file-naming-conventions.md b/docs/developer-manual/conventions/file-naming-conventions.md index 5e504f6..b8b4aed 100644 --- a/docs/developer-manual/conventions/file-naming-conventions.md +++ b/docs/developer-manual/conventions/file-naming-conventions.md @@ -4,7 +4,7 @@ This article covers how files and directories are named throughout the repositor ## Kebab-Case by Default -Files and directories are named in **kebab-case** — lowercase words separated by hyphens — for example `command-line-parser`, `sample-app`, `developer-manual`, `formatting-dprint.md`. This applies repository-wide: source directories at the top level, the `docs/` tree, and any Markdown article. +Files and directories are named in **kebab-case** — lowercase words separated by hyphens — for example `command-line-parser`, `sample-app`, `developer-manual`, `code-formatting-dprint.md`. This applies repository-wide: source directories at the top level, the `docs/` tree, and any Markdown article. ## Well-Known Exceptions @@ -17,9 +17,9 @@ Files with a conventional, tool- or ecosystem-mandated name keep that name inste Inside a C# project's own source tree, directories and files switch to **PascalCase** — for example `source/clinet-core/Application/CliApplication.cs`. This follows the standard .NET convention of naming a file after the single type it contains, and grouping related types into a PascalCase namespace folder. The project's own top-level folder (`clinet-core`, `sample-app`) still follows the repository's kebab-case rule — the switch to PascalCase only happens once you are inside the project, looking at its namespaces and types. -The project and solution files themselves (`CLI.NET Core.csproj`, `CLI.NET Core Sample App.csproj`, `CLI.NET Core.slnx`) are named after the product, spaces included, rather than either convention — see [Architecture](../architecture/overview.md) for where they live. +The project and solution files themselves (`CLI.NET Core.csproj`, `CLI.NET Core Sample App.csproj`, `CLI.NET Core.slnx`) are named after the product, spaces included, rather than either convention — see [Architecture](../architecture.md) for where they live. ## Related -- Where these directories and projects sit in the repository: [Architecture](../architecture/overview.md). +- Where these directories and projects sit in the repository: [Architecture](../architecture.md). - The naming rules that apply once you are inside a C# file: [C# Style](csharp-style.md). diff --git a/docs/developer-manual/conventions/testing.md b/docs/developer-manual/conventions/testing.md index 45da2ce..eae545b 100644 --- a/docs/developer-manual/conventions/testing.md +++ b/docs/developer-manual/conventions/testing.md @@ -4,13 +4,13 @@ This article covers the conventions for the unit tests in [`tests/unit-tests`](. ## Every New Feature Is Unit Tested -From this point forward, a change that implements a new feature is not done until it has unit tests covering it, in the same pull request. This applies to human and AI-assisted contributions alike (see [Contributing](../contributing.md)). A bug fix should also add a test that reproduces the bug and would fail without the fix. Documentation-only changes, formatting, and pure refactors that do not change behavior are exempt, but a refactor that changes behavior along the way is not. +From this point forward, a change that implements a new feature is not done until it has unit tests covering it, in the same pull request. This applies to human and AI-assisted contributions alike (see [Contributing](../contribution-guide.md)). A bug fix should also add a test that reproduces the bug and would fail without the fix. Documentation-only changes, formatting, and pure refactors that do not change behavior are exempt, but a refactor that changes behavior along the way is not. Not every line needs to be covered for its own sake: a placeholder implementation that has no reachable failure path yet (for example a `catch` block nothing can currently throw into) does not need a contrived test written just to touch it. Test the behavior that exists, not the branches a future change will eventually make reachable. ## One Test Class per Source Type -Each type in `source/clinet-core` that has behavior worth verifying gets one test class, named `Tests`, in the mirroring namespace and folder: `CliNetCore.Application.CliApplication` is tested by `CliNetCore.UnitTests.Application.CliApplicationTests` in `tests/unit-tests/Application/CliApplicationTests.cs`. This mirrors how `source/clinet-core` itself is organized (see [Architecture](../architecture/overview.md)) so that a source file's tests are always easy to find. +Each type in `source/clinet-core` that has behavior worth verifying gets one test class, named `Tests`, in the mirroring namespace and folder: `CliNetCore.Application.CliApplication` is tested by `CliNetCore.UnitTests.Application.CliApplicationTests` in `tests/unit-tests/Application/CliApplicationTests.cs`. This mirrors how `source/clinet-core` itself is organized (see [Architecture](../architecture.md)) so that a source file's tests are always easy to find. ## Internal Types Are Tested Directly @@ -42,5 +42,5 @@ xUnit has no fixture mechanism (`IClassFixture`, `ICollectionFixture`) sui - How to run the tests and generate a coverage report: [Testing and Code Coverage](../tooling/testing-and-code-coverage.md). - The general C# conventions these tests also follow: [C# Style](csharp-style.md). -- Where the test project sits in the repository: [Architecture](../architecture/overview.md). -- The pull request requirement this article's first section states: [Contributing](../contributing.md). +- Where the test project sits in the repository: [Architecture](../architecture.md). +- The pull request requirement this article's first section states: [Contributing](../contribution-guide.md). diff --git a/design/DESIGN.md b/docs/developer-manual/logo-design.md similarity index 86% rename from design/DESIGN.md rename to docs/developer-manual/logo-design.md index 0c41dc7..3d3c76f 100644 --- a/design/DESIGN.md +++ b/docs/developer-manual/logo-design.md @@ -1,6 +1,6 @@ # CLI.NET Core — Brand & Logo Guide -This document describes the CLI.NET Core visual identity and how to use the logo assets in this repository. All assets live in this folder. +This document describes the CLI.NET Core visual identity and how to use the logo assets in this repository. All assets live in [`design/`](../../design/). ## Concept @@ -57,15 +57,15 @@ Do not re-draw the mark by hand; use the vector files below. ### Wordmark Lockups - `wordmark-light.svg` / `-dark.svg` — compact bar (mark + text). -- `readme-light.svg` / `-dark.svg` — tall horizontal lockup for READMEs. +- `readme-header-light.svg` / `-dark.svg` — tall horizontal lockup for READMEs. - `wordmark-bars.html` — responsive, fill-width HTML version of the bar (`.clinet-core-bar`, add `.clinet-core-bar--dark` on dark surfaces). ## Using the Logo on GitHub -GitHub sanitizes SVG `` effects when rendered via ``, so use the **baked PNGs** for the glass look, and let GitHub pick light/dark: +GitHub sanitizes SVG `` effects when rendered via ``, so use the **baked PNGs** for the glass look, and let GitHub pick light/dark. The vector masters (`readme-header-dark.svg` / `-light.svg`) stay in [`design/`](../../design/), but the baked PNGs themselves live in [`docs/assets/images/`](../assets/images/) — the only copy, referenced by both the root [`README.md`](../../README.md) and [`docs/README.md`](../README.md): ```markdown -![CLI.NET Core Logo](design/readme-header-dark.png#gh-dark-mode-only) ![CLI.NET Core Logo](design/readme-header-light.png#gh-light-mode-only) +![CLI.NET Core Logo](docs/assets/images/header-logo-dark.png#gh-dark-mode-only) ![CLI.NET Core Logo](docs/assets/images/header-logo-light.png#gh-light-mode-only) ``` ## Dos/Don'ts diff --git a/docs/developer-manual/tooling/README.md b/docs/developer-manual/tooling/README.md index a26e739..a9605bc 100644 --- a/docs/developer-manual/tooling/README.md +++ b/docs/developer-manual/tooling/README.md @@ -5,9 +5,10 @@ This section gives an overview of the tools used for development, including code 1. [Developer Setup](developer-setup.md) 2. [Visual Studio Code Integration](vscode-integration.md) 3. [Spell Checking (CSpell)](spell-checking-cspell.md) -4. [MarkdownLint](linting-markdownlint.md) -5. [dprint](formatting-dprint.md) -6. [Continuous Integration](continuous-integration.md) -7. [Testing and Code Coverage](testing-and-code-coverage.md) +4. [Markdown Linting (MarkdownLint)](linting-markdownlint.md) +5. [Code Formatting (dprint)](code-formatting-dprint.md) +6. [Documentation Website](documentation-website.md) +7. [Continuous Integration](continuous-integration.md) +8. [Testing and Code Coverage](testing-and-code-coverage.md) -The linters and the code formatter are not installed as project dependencies (there is no Node.js package manifest in this repository) — they are run directly from the command line, either installed globally or invoked on demand through `npx`. [Continuous Integration](continuous-integration.md) installs pinned versions of all three explicitly; running them locally with the same versions keeps results consistent with what CI reports. The test project and its tools are the exception: they are restored the normal .NET way, as `PackageReference`s and a local tool manifest (see [Testing and Code Coverage](testing-and-code-coverage.md)). +The linters and the code formatter are pinned as `devDependencies` in [`package.json`](../../../package.json) at the repository root and locked in [`package-lock.json`](../../../package-lock.json), restored with `npm ci` and run through `npx` — dprint additionally needs a persistent global install for its Visual Studio Code extension (see [Developer Setup](developer-setup.md)). [Continuous Integration](continuous-integration.md) runs the same `npm ci` and `npx` invocations; running them locally keeps results consistent with what CI reports. The test project and its tools follow the equivalent .NET pattern instead, as `PackageReference`s and a local tool manifest (see [Testing and Code Coverage](testing-and-code-coverage.md)). diff --git a/docs/developer-manual/tooling/formatting-dprint.md b/docs/developer-manual/tooling/code-formatting-dprint.md similarity index 67% rename from docs/developer-manual/tooling/formatting-dprint.md rename to docs/developer-manual/tooling/code-formatting-dprint.md index 0ff83ea..d15a196 100644 --- a/docs/developer-manual/tooling/formatting-dprint.md +++ b/docs/developer-manual/tooling/code-formatting-dprint.md @@ -1,4 +1,4 @@ -# Formatting (dprint) +# Code Formatting (dprint) This article covers code formatting, which is owned entirely by [dprint](https://dprint.dev). @@ -6,6 +6,8 @@ Configuration lives in [`dprint.json`](../../../dprint.json). dprint formats Mar ## Running It +dprint is pinned in [`package.json`](../../../package.json) and locked in [`package-lock.json`](../../../package-lock.json) like the other linters (see [Dependency Management](../conventions/dependency-management.md)), and `npm ci` installs it into `node_modules` — but the Visual Studio Code extension requires a separate, persistent **global** install regardless (see [Developer Setup](developer-setup.md)), so `dprint` is also available directly on the command line without `npx`: + ```shell dprint fmt ``` @@ -16,6 +18,8 @@ formats every file dprint is configured to handle, in place. To check formatting dprint check "**/*" ``` +If dprint is not installed globally, `npx dprint fmt` / `npx dprint check "**/*"` resolve to the same pinned version through `node_modules/.bin`. + Visual Studio Code is also configured to run dprint on save (see [Visual Studio Code Integration](vscode-integration.md)). ## dprint Owns Formatting — Linters Must Not diff --git a/docs/developer-manual/tooling/continuous-integration.md b/docs/developer-manual/tooling/continuous-integration.md index 797a380..3c5f531 100644 --- a/docs/developer-manual/tooling/continuous-integration.md +++ b/docs/developer-manual/tooling/continuous-integration.md @@ -1,18 +1,22 @@ # Continuous Integration -This article covers the two GitHub Actions workflows in [`.github/workflows/`](../../../.github/workflows/): [`linters.yml`](../../../.github/workflows/linters.yml) and [`tests.yml`](../../../.github/workflows/tests.yml). Both run on every push, to any branch, and are independent of each other — either can fail, be re-run, or be extended without touching the other, and a commit or pull request shows them as two separate checks. +This article covers the three GitHub Actions workflows in [`.github/workflows/`](../../../.github/workflows/): [`linters.yml`](../../../.github/workflows/linters.yml), [`tests.yml`](../../../.github/workflows/tests.yml), and [`docs.yml`](../../../.github/workflows/docs.yml). They are independent of each other — any one can fail, be re-run, or be extended without touching the others, and a commit or pull request shows them as separate checks. ## Linters Workflow -A single job, on the latest Ubuntu runner, checks out the repository, installs Node.js 24, installs pinned versions of the three tools this project relies on for formatting and linting, and then runs each of them in turn: +Runs on every push, to any branch. A single job, on the latest Ubuntu runner, checks out the repository, installs Node.js 24, then `npm ci` to install the exact versions of the three tools this project relies on for formatting and linting (pinned in [`package.json`](../../../package.json) and locked in [`package-lock.json`](../../../package-lock.json)), and runs each of them in turn through `npx`: -- `dprint check "**/*"` — verifies that every file dprint covers is already formatted (see [dprint](formatting-dprint.md)); unlike `dprint fmt`, it never modifies a file. -- `cspell lint --config tests/linters/.cspell.json --no-progress "**/*"` — spell-checks the whole repository (see [Spell Checking (CSpell)](spell-checking-cspell.md)). -- `markdownlint-cli2 --config tests/linters/.markdownlint.yml "**/*.md" "#**/bin/**" "#**/obj/**"` — lints every Markdown file, excluding build output (see [MarkdownLint](linting-markdownlint.md)). +- `npx dprint check "**/*"` — verifies that every file dprint covers is already formatted (see [dprint](code-formatting-dprint.md)); unlike `dprint fmt`, it never modifies a file. +- `npx cspell lint --config tests/linters/.cspell.json --no-progress "**/*"` — spell-checks the whole repository (see [Spell Checking (CSpell)](spell-checking-cspell.md)). +- `npx markdownlint-cli2 --config tests/linters/.markdownlint.yml "**/*.md" "#**/build/**" "#**/node_modules/**"` — lints every Markdown file, excluding build output (see [MarkdownLint](linting-markdownlint.md)). The spell-checking and Markdown-linting steps run even if an earlier step failed (`if: ${{ !cancelled() }}`), so a single failing tool does not hide findings from the others — the workflow always reports everything it can in one run. -The tool versions this workflow installs (`dprint`, `cspell`, `markdownlint-cli2`) are pinned exactly. When running these tools locally (see the "Running It" section of each tool's article), use the same pinned versions so that a local pass reliably predicts a green CI run. +`npm ci` fails instead of silently updating `package-lock.json` if it no longer matches `package.json` (see [Dependency Management](../conventions/dependency-management.md)). When running these tools locally (see the "Running It" section of each tool's article), the same `npm ci` plus `npx` gives a local run the exact versions CI uses, so a local pass reliably predicts a green CI run. + +## Docs Workflow + +Runs only on pushes to `main` — unlike the other two workflows, it deploys to GitHub Pages, so it only ever needs to run against what is actually published. A build job checks out the repository, sets up .NET and restores its local tools (for `xmldoc2md`), sets up Python and installs the pinned dependencies from [`requirements.txt`](../../../requirements.txt), and runs `properdocs build`, which regenerates the `docs/api` section from the framework's current XML documentation comments (see [Documentation Website](documentation-website.md)). A separate deploy job then publishes the built site to GitHub Pages through the `github-pages` environment. ## Tests Workflow @@ -22,6 +26,7 @@ There is currently no enforced minimum coverage percentage that fails the build. ## Related -- [dprint](formatting-dprint.md), [Spell Checking (CSpell)](spell-checking-cspell.md), and [MarkdownLint](linting-markdownlint.md) — the three tools the linters workflow runs. +- [dprint](code-formatting-dprint.md), [Spell Checking (CSpell)](spell-checking-cspell.md), and [MarkdownLint](linting-markdownlint.md) — the three tools the linters workflow runs. +- [Documentation Website](documentation-website.md) — what the docs workflow builds and deploys, in more detail. - [Testing and Code Coverage](testing-and-code-coverage.md) — the commands the tests workflow runs, in more detail. - [Dependency Management](../conventions/dependency-management.md) — how the pinned SDK, tool, and package versions these workflows use are kept exact. diff --git a/docs/developer-manual/tooling/developer-setup.md b/docs/developer-manual/tooling/developer-setup.md index 92cbb82..b8a6ae5 100644 --- a/docs/developer-manual/tooling/developer-setup.md +++ b/docs/developer-manual/tooling/developer-setup.md @@ -16,23 +16,50 @@ This currently installs [ReportGenerator](https://github.com/danielpalme/ReportG ## Node.js and NPM -[dprint](formatting-dprint.md), [CSpell](spell-checking-cspell.md), and [MarkdownLint](linting-markdownlint.md) are not installed as project dependencies (there is no Node.js package manifest in this repository) — install [Node.js](https://nodejs.org) (which bundles NPM), then: +[dprint](code-formatting-dprint.md), [CSpell](spell-checking-cspell.md), and [MarkdownLint](linting-markdownlint.md) are pinned as `devDependencies` in [`package.json`](../../../package.json) and locked in [`package-lock.json`](../../../package-lock.json). Install [Node.js](https://nodejs.org) (which bundles NPM), then, from the repository root: -- Install dprint globally, pinned to the version [Continuous Integration](continuous-integration.md) uses: +```shell +npm ci +``` + +installs the exact versions locked in `package-lock.json` into `node_modules` (never `npm install`, which would happily update the lock file — see [Dependency Management](../conventions/dependency-management.md)). NPM prints a notice that `dprint`'s install scripts are not yet covered by the `allowScripts` field, since `dprint` uses a `postinstall` script to fetch its platform-specific binary. `npm approve-scripts dprint` reviews and approves it, but writes the approval to the shared, checked-in `package.json` — approving it for every other developer too, not just you. + +To approve `dprint`'s install scripts for yourself only, without touching `package.json`, put an `allow-scripts` line in an `.npmrc` (`--allow-scripts` on the command line or as an environment variable is rejected outright for project-scoped installs): + +```ini +allow-scripts=dprint +``` + +Either the user-level `~/.npmrc` (applies to every project on the machine, never touches this repository at all) or a project-level `.npmrc` at the repository root works. A project-level `.npmrc` is covered by [`.gitignore`](../../../.gitignore), to avoid accidentally committing a personal, unreviewed policy for other developers. + +CSpell and MarkdownLint-cli2 are then available through `npx`: - ```shell - npm install --global dprint@0.55.1 - ``` +```shell +npx cspell lint --config tests/linters/.cspell.json --no-progress "**/*" +npx markdownlint-cli2 --config tests/linters/.markdownlint.yml "**/*.md" "#**/build/**" "#**/node_modules/**" +``` - A persistent install is required here, not just for the `dprint check "**/*"` and `dprint fmt` commands, but because the Visual Studio Code dprint extension has no formatting engine of its own — it calls the `dprint` executable on the machine directly (see [Visual Studio Code Integration](vscode-integration.md)). -- CSpell and MarkdownLint-cli2 do not need a persistent install — run them on demand through `npx`, pinned to the versions CI uses: +`npx` resolves these straight to the pinned version in `node_modules/.bin`, so a local pass reliably predicts a green CI run; their Visual Studio Code extensions bundle their own engines, so no further install is needed for editor integration. + +dprint is different: `npm ci` also installs it into `node_modules` (so `npx dprint check "**/*"` / `npx dprint fmt` work), but that alone is **not enough** for the editor — the Visual Studio Code dprint extension has no formatting engine of its own and requires `dprint` installed **globally, on the PATH**, regardless of any local, project-level install (see [Visual Studio Code Integration](vscode-integration.md)). Install it separately, pinned to the same version as `package.json`: + +```shell +npm install --global dprint@0.55.1 +``` + +## Python and ProperDocs + +The [documentation website](documentation-website.md) is built with [ProperDocs](https://properdocs.org/), themed with [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/). Install [Python](https://www.python.org/downloads/) (which bundles `pip`), then create a virtual environment and install the pinned dependencies from [`requirements.txt`](../../../requirements.txt) into it, rather than into the global or user Python installation (see [Dependency Management](../conventions/dependency-management.md)): + +```shell +python3 -m venv .venv +source .venv/bin/activate +python -m pip install --requirement requirements.txt +``` - ```shell - npx --yes cspell@10.0.1 lint --config tests/linters/.cspell.json --no-progress "**/*" - npx --yes markdownlint-cli2@0.23.0 --config tests/linters/.markdownlint.yml "**/*.md" "#**/bin/**" "#**/obj/**" - ``` +On Windows, activate with `.venv\Scripts\activate` instead of the `source` line above. `.venv` is already excluded in [`.gitignore`](../../../.gitignore), so it is never committed. - `npx` downloads and caches the pinned version the first time it runs, so a local pass reliably predicts a green CI run. Their Visual Studio Code extensions bundle their own engines, so no global install is needed for editor integration either. +This installs ProperDocs itself, the Material theme, and the two plugins that generate the `docs/api` C# API reference and the site navigation, all pinned to the exact versions in `requirements.txt`, isolated to this project. Activate the virtual environment (`source .venv/bin/activate`) in every new shell before running `properdocs` or the other commands in [Documentation Website](documentation-website.md) — deactivate it with `deactivate` when done. ## Visual Studio Code @@ -43,6 +70,7 @@ One of those, [Workspace Config Plus](https://marketplace.visualstudio.com/items ## Related - [Visual Studio Code Integration](vscode-integration.md) — the shared/local settings mechanism Workspace Config Plus enables. +- [Documentation Website](documentation-website.md) — running ProperDocs locally and how the `docs/api` reference is generated. - [Continuous Integration](continuous-integration.md) — the pinned tool versions to match locally. - [Dependency Management](../conventions/dependency-management.md) — how the SDK, local tool, and NuGet package versions are kept exact. - [Testing and Code Coverage](testing-and-code-coverage.md) — running the tests and collecting coverage once the SDK is installed. diff --git a/docs/developer-manual/tooling/documentation-website.md b/docs/developer-manual/tooling/documentation-website.md new file mode 100644 index 0000000..d7bcdca --- /dev/null +++ b/docs/developer-manual/tooling/documentation-website.md @@ -0,0 +1,134 @@ +# Documentation Website + +This article covers the [ProperDocs](https://properdocs.org/) site, themed with [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/), that publishes everything under [`docs/`](../../../docs), including the C# API reference that is generated from source at build time. + +## Why ProperDocs, Not MkDocs + +This site was originally built on [MkDocs](https://www.mkdocs.org/), but MkDocs itself has gone unmaintained. [ProperDocs](https://properdocs.org/) is a fork by MkDocs' own last active maintainer, positioned as an exact drop-in continuation of the MkDocs 1.x line rather than a break from it: it reads the same configuration format, and it registers both `mkdocs.*` and `properdocs.*` plugin and theme entry points, so [mkdocs-material](https://squidfunk.github.io/mkdocs-material/), [mkdocs-gen-files](https://github.com/oprypin/mkdocs-gen-files), and [mkdocs-literate-nav](https://oprypin.github.io/mkdocs-literate-nav/) — all still published under their original MkDocs-branded package names — work with it unmodified. Only the site-generator engine itself changed; the theme and the plugin are untouched. + +## Configuration + +[`properdocs.yml`](../../../properdocs.yml) lives at the repository root and points `docs_dir` at the existing [`docs/`](../../../docs) tree, so every hand-written article in [User Manual](../../user-manual/README.md) and this Developer Manual is part of the site without having been moved or renamed. `site_dir` is set to `build/documentation-website`, alongside the redirected `bin`/`obj` output of the three `.NET` projects (see [Architecture](../architecture.md)), rather than ProperDocs' own default of `site/`. Three plugins are enabled: + +- **search** — the built-in full-text search, enabled by default. +- **gen-files** ([mkdocs-gen-files](https://github.com/oprypin/mkdocs-gen-files)) — runs [`docs/build/generate-api-docs.py`](../../build/generate-api-docs.py) at the start of every build to produce the `api` section (see below). +- **literate-nav** ([mkdocs-literate-nav](https://oprypin.github.io/mkdocs-literate-nav/)) — lets the `api` entry in `nav` (see below) point at the `api/` directory instead of a single file, so its own navigation comes from a generated `api/SUMMARY.md` rather than being hand-listed. + +`markdown_extensions` in [`properdocs.yml`](../../../properdocs.yml) adds [mdx_truly_sane_lists](https://github.com/radude/mdx_truly_sane_lists), configured with `nested_indent: 3`. Without it, Python-Markdown (the engine underneath ProperDocs) only recognizes a nested list item when it is indented by a fixed 4 spaces, regardless of the parent marker's width — the various `README.md` files' tables of contents nest ordered sub-lists under `1.`-style markers using the natural, minimal 3-space indent, which Python-Markdown's default renders as a second top-level list instead. `nested_indent: 3` makes the required indent relative to the parent marker's width instead, so those tables of contents nest correctly without reformatting any Markdown file. + +## Light and Dark Mode + +`theme.palette` in [`properdocs.yml`](../../../properdocs.yml) lists two [mkdocs-material palettes](https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/#color-palette-toggle), one for `scheme: default` (light) and one for `scheme: slate` (dark), each tied to a `media: "(prefers-color-scheme: ...)"` query. A visitor's browser reports which scheme their OS is set to, and mkdocs-material's built-in script reads that to select the matching palette on load — so the site opens in light or dark mode automatically, with no configuration needed on the visitor's part. Each palette also carries a `toggle` (a sun icon on the light palette, a moon icon on the dark one) that lets a visitor override the OS choice for the rest of the session; mkdocs-material stores that override in the browser and remembers it on the next visit. + +### Making a Light/Dark Image Pair Work on Both GitHub and This Site + +[`docs/README.md`](../../README.md) and the root [`README.md`](../../../README.md) both show the [`CLI.NET Core` logo](../logo-design.md) as a pair of images — one for light mode, one for dark — using GitHub's own convention for this: an `` whose `src` ends in the literal suffix `#gh-dark-mode-only` or `#gh-light-mode-only`, which GitHub's renderer detects and uses to hide the non-matching image. Since `docs/README.md` is also a page on this site, that same pair needs to switch with mkdocs-material's own light/dark scheme too. mkdocs-material has its own, near-identical built-in convention for this — `#only-dark` / `#only-light` — but it is a **different** suffix, and since the trick on both sides is a literal string match on the end of the `src` attribute, one `` cannot satisfy two different literal suffixes at once. Rather than write out each image twice with a different suffix per platform (which would show two copies of each image on whichever platform does not recognize the other's suffix), [`docs/assets/styles/gh-mode-only-images.css`](../../assets/styles/gh-mode-only-images.css) — wired in via `extra_css` in [`properdocs.yml`](../../../properdocs.yml) — teaches mkdocs-material to also recognize GitHub's own suffixes, the same way mkdocs-material's own CSS recognizes its `#only-dark` / `#only-light` suffixes: an attribute selector matching `img[src$="#gh-dark-mode-only"]` (and the light equivalent), scoped to the `data-md-color-scheme` attribute mkdocs-material's light/dark toggle sets on the page. This keeps every Markdown image tag identical on both platforms — no `#only-dark` / `#only-light` suffix is used anywhere in this repository, only GitHub's. + +### The Site Header Logo + +`theme.logo` in [`properdocs.yml`](../../../properdocs.yml) sets the small badge mkdocs-material renders in the site header and in the mobile navigation drawer, to `assets/images/logo-light.png`. Unlike the light/dark image pair above, `theme.logo` is mkdocs-material's single, built-in header logo setting — it takes exactly one file, with no light/dark option of its own, so the value here is only the fallback. [`docs/assets/styles/site-logo.css`](../../assets/styles/site-logo.css) — also wired in via `extra_css` in [`properdocs.yml`](../../../properdocs.yml) — swaps in `assets/images/logo-dark.png` for the dark palette, using the same `data-md-color-scheme="slate"` attribute selector as [`gh-mode-only-images.css`](../../assets/styles/gh-mode-only-images.css) above, but applied to the CSS `content` property instead of `display`: since `theme.logo` renders as one ``, not a pair, there is nothing to hide, only a single rendered image to replace. `logo-light.png` and `logo-dark.png` are the 64×64 primary glass badge from the [design guide](../logo-design.md) (`badge-glass-light.svg` / `-dark.svg`), baked to PNG the same way the light/dark image pair above is. The design guide's flat badge exists for cases where filter effects get stripped or the badge renders too small to read them (its favicon use, above, is exactly that case) — neither applies here, since this is a plain PNG `` on this site's own header, not an SVG sanitized by GitHub, and 64×64 is large enough for the glass effect to read. + +### Static Assets + +Images and other static files the documentation itself needs (as opposed to files a plugin generates, like `docs/api`) live under [`docs/assets/`](../../assets) — for example [`docs/assets/images/`](../../assets/images) holds the baked PNGs of the logo used above, and [`docs/assets/styles/`](../../assets/styles) holds the CSS files mentioned above. Anything under `docs_dir` that is not excluded via `exclude_docs` is copied into the built site as-is, at the same relative path, so `docs/assets/images/header-logo-dark.png` becomes `assets/images/header-logo-dark.png` on the built site — which is also why `docs/README.md`'s copy of the logo pair, and `properdocs.yml`'s `theme.logo`, both use that same `assets/images/...` path rather than reaching up into [`design/`](../../../design), which is not published. The [design guide](../logo-design.md) keeps the vector masters that these PNGs were baked from; only the baked PNGs themselves live under `docs/assets/images/`. + +Two subdirectories of `docs/assets/` are excluded from the built site itself, via `exclude_docs` in [`properdocs.yml`](../../../properdocs.yml): + +- [`docs/build/`](../../build) — the four Python scripts described below (`generate-api-docs.py`, `copy-favicon-to-site-root.py`, `rewrite-repo-links.py`, `inject-api-reference-link.py`), which run at build time rather than being published as pages or static files. +- [`docs/assets/templates/`](../../assets/templates) — the one theme template described below (`main.html`), read by mkdocs-material itself rather than being published. + +## Links Outside `docs/` + +An article under `docs/` is free to link to a file anywhere else in the repository — source code, [`README.md`](../../../README.md), [`CLAUDE.md`](../../../CLAUDE.md), a workflow under [`.github/workflows/`](../../../.github/workflows/), and so on — using an ordinary relative Markdown link, the same as it would link to another article. That link renders correctly on GitHub, which resolves it against the file's real position in the repository, but the built site has no such files: `docs_dir` is the only part of the repository ProperDocs ever copies into `site_dir`, so the exact same link would 404 there. + +[`docs/build/rewrite-repo-links.py`](../../build/rewrite-repo-links.py) closes that gap. Wired in via `hooks:` in [`properdocs.yml`](../../../properdocs.yml) alongside `copy-favicon-to-site-root.py`, it defines an `on_page_markdown` function — a plain [MkDocs `hooks`](https://www.mkdocs.org/user-guide/configuration/#hooks) event, like `on_post_build` above, so no plugin package is needed for it either — that runs on every page's raw Markdown before it is converted to HTML. For every link on the page, it resolves the link's target against the page's own file on disk and checks whether the result still falls inside `docs_dir`: + +- **Inside `docs_dir`** — left untouched. ProperDocs already resolves and rewrites this kind of link correctly on its own. +- **Outside `docs_dir`, inside the repository** — rewritten into an absolute link at `repo_url`, pointing at the `main` branch (the one the site itself is always built and deployed from, see [Continuous Integration](continuous-integration.md)) — `tree` for a link to a directory, `blob` for a link to a file, matching GitHub's own URL scheme. A fragment on the original link (for example `CLAUDE.md#golden-rules`) is preserved on the rewritten one. +- **Outside the repository entirely** — left untouched. A link like this is already broken on GitHub, and rewriting it further is not this hook's concern. + +The generated [`api`](#the-api-reference-docsapi) section is skipped entirely: none of its cross-linking ever points outside `docs_dir`, and its pages exist only in memory during the build (see below), not at a real path this hook could resolve links against. + +## Navigation Is One Hand-Maintained `nav` List, Plus One Generated Section + +The site navigation is the single `nav` list in [`properdocs.yml`](../../../properdocs.yml) — there is no `.pages` file anywhere under `docs/`, and no plugin builds the *hand-written* part of the site's navigation from the directory tree. Adding, removing, moving, or reordering an article means editing that one list; nothing else in the repository has to change. + +The one exception is the `api` entry, `API Reference: api/`. Its file list is not known until a build actually runs xmldoc2md, so it cannot be hand-listed the way the rest of the site is. Instead, that entry's trailing slash tells **literate-nav** to look for a nav file inside `api/` — and [`generate-api-docs.py`](../../build/generate-api-docs.py) writes that nav file itself (see below), the same way it writes the pages it lists. No `.pages` file is involved: one Python script controls both the content and the navigation of the one section that cannot be hand-listed, and every other section stays exactly as hand-listed as before. + +## The API Reference (`docs/api`) + +The `api` section is generated entirely at build time and is **never committed** — there is no `docs/api` directory in the repository, and none is expected to appear; no `.gitignore` entry exists for it because nothing ever writes it to disk in the first place. Every `properdocs build` or `properdocs serve` run regenerates it from whatever the C# XML documentation comments currently say. + +[`docs/build/generate-api-docs.py`](../../build/generate-api-docs.py) does this in three steps: + +1. **Builds the project.** It runs `dotnet build` on the sample app (which transitively builds `clinet-core`), so that the framework's XML documentation file exists. The sample app, not `clinet-core` directly, is what gets built and read: a class library's own build output does not include its NuGet dependencies, and the next step needs every dependency of the framework assembly to be loadable, which only the sample app's build output provides. If `GenerateDocumentationFile` were ever disabled on [`clinet-core`'s `.csproj`](../../../source/clinet-core/CLI.NET%20Core.csproj), this step fails loudly rather than silently producing an empty API reference. +2. **Generates Markdown.** It shells out to [xmldoc2md](https://github.com/charlesdevandiere/xmldoc2md), pinned in [`.config/dotnet-tools.json`](../../../.config/dotnet-tools.json) like the other [local .NET tools](../conventions/dependency-management.md#local-net-tools), writing its output to a temporary directory — never into `docs/`. +3. **Injects the pages and their navigation.** It reads every Markdown file xmldoc2md wrote and re-emits it through `mkdocs_gen_files.open()`, which hands the content straight to the site generator's in-memory build. While doing so, it also records each page in an [`mkdocs_gen_files.Nav`](https://oprypin.github.io/mkdocs-gen-files/extras.html), and once every page has been injected, writes that `Nav`'s `build_literate_nav()` output to `api/SUMMARY.md` — the file **literate-nav** looks for by default. The temporary directory is discarded once the build has consumed it. + +Every page's entry in that `Nav` is built from the page's own content, not from the all-lowercase file and folder names `--structure tree` derived from the C# namespaces and type names — xmldoc2md has no option to change those (there is no such flag; `dotnet tool run xmldoc2md -- --help` was checked), and short of forking it there is no way to make it emit anything else: + +- A type's page is grouped under its real, properly cased namespace segments (for example `CliNetCore` and `Application`, not `clinetcore` and `application`), parsed from the `Namespace: CliNetCore.Application` line xmldoc2md puts on every such page. +- A type's own navigation label is its real name (for example `CliCommandLineArguments`), not the all-lowercase file name xmldoc2md derived it from, parsed from the page's H1 heading. +- The one page with neither a `Namespace:` line nor a fitting H1 to reuse — the root index page xmldoc2md writes for the whole assembly, whose H1 is the assembly's own name (`CLI.NET Core`) — is labeled `Index` instead, since reusing that H1 as a navigation label next to the type names it links to would be confusing. + +`api/SUMMARY.md` keeps the plugin's default name rather than being renamed to `README.md` to match every other section's overview page: `nav_file` is a global plugin setting, not one scoped per directory, and it makes literate-nav check for that filename at `docs_dir`'s own root before it even reads `nav` in [`properdocs.yml`](../../../properdocs.yml) — since a hand-written `docs/README.md` already exists there, renaming `nav_file` to `README.md` makes literate-nav try to parse that hand-written prose page as a literate nav list instead of using the hand-maintained `nav` list at all. + +The script itself lives inside `docs_dir`'s `build/` subdirectory (see [Static Assets](#static-assets)), so it is excluded via `exclude_docs` in [`properdocs.yml`](../../../properdocs.yml) — without that, the site generator would treat it like any other non-Markdown file under `docs/` and copy it verbatim into the built site. + +### Why xmldoc2md + +xmldoc2md and [DefaultDocumentation](https://github.com/Doraku/DefaultDocumentation) were both tried against this codebase before choosing. DefaultDocumentation is more configurable, but its defaults document `private` members and name one file per member with literal parentheses and commas in the filename (for example `CliNetCore.Application.CliApplicationBuilder.CreateBuilder(string[]).md`) — both unsuitable for a public API reference and for clean, predictable URLs, and both would need explicit configuration to fix. xmldoc2md's defaults already do the right thing for this project: it documents `protected` and `public` members only, produces one clean file per type organized into a namespace tree (`--structure tree`), and has a `--platform github-pages` preset that shapes links and front matter for exactly this kind of static site. It needed no extra configuration beyond the two flags used above. + +### Why Members With `` Need Another Package + +A member documented with `` (see [C# Style](../conventions/csharp-style.md)) — for example every property `CliApplicationBuilder` implements from `IHostApplicationBuilder` — showed up in `docs/api` with no description at all, just its signature. The cause is not xmldoc2md: the C# compiler never expands `` into real text in the generated XML documentation file, it is left in verbatim, so any tool that reads that file — xmldoc2md, or a consumer's editor tooltip for the compiled package — sees an empty member. [`clinet-core`'s `.csproj`](../../../source/clinet-core/CLI.NET%20Core.csproj) references [SauceControl.InheritDoc](https://github.com/saucecontrol/InheritDoc), pinned like every other dependency (see [Dependency Management](../conventions/dependency-management.md)), which rewrites `` with the real, inherited text as a build step — for every non-Debug build, which is why step 1 above builds in `Release` rather than the default `Debug` configuration. + +## Linking to the API Reference from the Overview Page + +[`docs/README.md`](../../README.md) — the site's own overview page — lists the [Developer Manual](../README.md) and [User Manual](../../user-manual/README.md) as an ordered list, and that list also needs an entry pointing at `docs/api`. A hand-written link to it cannot work: `docs/api` is generated entirely at build time (see above) and never committed, so a relative link to it in `docs/README.md`'s source would 404 when GitHub renders that file directly, the same problem [Links Outside `docs/`](#links-outside-docs) solves for links leaving `docs_dir` entirely — except here there is no repository file to redirect to instead, since `docs/api` does not exist outside a build. + +[`docs/build/inject-api-reference-link.py`](../../build/inject-api-reference-link.py) closes this gap the same way [`rewrite-repo-links.py`](../../build/rewrite-repo-links.py) does, with another `on_page_markdown` hook wired in via `hooks:` in [`properdocs.yml`](../../../properdocs.yml). Scoped to the site's root overview page only (`page.file.src_uri` equal to `README.md`), it finds the last ordered list item that starts with a Markdown link, reads that item's own number, and appends a new `[API Reference](api/index.md)` item numbered one higher — so `docs/README.md`'s source never lists `docs/api` at all, and the list still numbers correctly regardless of how many hand-written items precede it. `api/index.md` is a safe target because [`generate-api-docs.py`](../../build/generate-api-docs.py) always writes it (it is the root page xmldoc2md generates for the whole assembly, labeled `Index` in the nav — see above), and because it resolves inside `docs_dir`, so `rewrite-repo-links.py` leaves it untouched regardless of which of the two hooks runs first. + +## No Cross-Linking to `docs/api` Yet + +Type names mentioned in [User Manual](../../user-manual/README.md) or this Developer Manual's prose and code blocks stay plain text or plain code — they do not link to their `docs/api` page. Adding that (for example with [mkdocs-autorefs](https://github.com/mkdocstrings/autorefs)) is deliberately out of scope for now. The overview page's own `API Reference` list entry (see above) is the one exception, since it links to the section as a whole rather than to an individual type. + +## Favicon + +The favicon is baked from the [design guide](../logo-design.md)'s flat badge (`badge-flat-dark.svg` / `-light.svg`, chosen over the glass badge because a favicon is rendered too small for the glass effect to read) into three files under [`docs/assets/favicon/`](../../assets/favicon): + +- `favicon-light.svg` and `favicon-dark.svg` — the two SVGs themselves, used as described below. +- `favicon.ico` — a multi-resolution (16×16, 32×32, 48×48) fallback baked from the light SVG, for browsers that ignore the two files above. + +A favicon has no equivalent of the `#gh-dark-mode-only` / `#gh-light-mode-only` trick described above, because it is not an `` in the page — it is one or more `` elements in the page's ``, and which one a browser uses is up to the browser, not to this site's CSS. `theme.favicon: assets/favicon/favicon.ico` in [`properdocs.yml`](../../../properdocs.yml) is mkdocs-material's single, built-in favicon setting, and is only ever a single file — it has no light/dark option. The light/dark pair is added on top of it by [`docs/assets/templates/main.html`](../../assets/templates/main.html), a `theme.custom_dir` template that extends mkdocs-material's own `main.html` and fills only its `extrahead` block — the block Material's own documentation reserves for exactly this kind of addition, so this override tracks Material's own head markup instead of duplicating it — with two extra `` elements, one per SVG. Browser support for `media` on a favicon `` is inconsistent, so `favicon.ico` stays in place as the fallback for browsers that ignore both. `theme.custom_dir` is the one path in [`properdocs.yml`](../../../properdocs.yml) that is relative to the repository root rather than to `docs_dir`, which is why `assets/templates/` still needs its own `exclude_docs` entry (see [Static Assets](#static-assets)) even though it sits under `docs/assets/` alongside every other static asset. + +Browsers and crawlers also request `/favicon.ico` at the site's root directly, regardless of any `` element on the page. Since `docs/assets/favicon/` keeps the favicon grouped with the rest of the [static assets](#static-assets) rather than loose at `docs_dir`'s own root, it builds to `assets/favicon/favicon.ico`, not `favicon.ico`, on the site — [`docs/build/copy-favicon-to-site-root.py`](../../build/copy-favicon-to-site-root.py) closes that gap. It is a plain MkDocs [`hooks`](https://www.mkdocs.org/user-guide/configuration/#hooks) module (a feature of MkDocs core itself, not a plugin, so no plugin package or entry point is needed for it), wired in via `hooks:` in [`properdocs.yml`](../../../properdocs.yml), defining a single `on_post_build` function that runs after every build and copies the already-built `assets/favicon/favicon.ico` to the site's own root. Like [`generate-api-docs.py`](../../build/generate-api-docs.py), it lives inside `docs_dir`'s `build/` subdirectory and is excluded via `exclude_docs` so the site generator does not also copy the script itself into the built site as a static asset. + +## Running It Locally + +Install the pinned Python dependencies from [`requirements.txt`](../../../requirements.txt) (see [Developer Setup](developer-setup.md)): + +```shell +python -m pip install --requirement requirements.txt +``` + +Then, from the repository root: + +```shell +properdocs serve +``` + +serves the site at `http://127.0.0.1:8000` with live reload for anything under `docs/` (including `docs/build/generate-api-docs.py`) and `properdocs.yml`. To produce the static site once, into `build/documentation-website/` (already covered by [`.gitignore`](../../../.gitignore) as build output, alongside the `.NET` projects' own redirected build output — see [Architecture](../architecture.md)): + +```shell +properdocs build +``` + +Both commands regenerate `docs/api` from scratch every time, as described above — there is nothing to clean up afterwards. + +## Related + +- [Developer Setup](developer-setup.md) — installing Python and the pinned ProperDocs dependencies. +- [Dependency Management](../conventions/dependency-management.md) — how `requirements.txt` and the local .NET tool pin their exact versions. +- [Continuous Integration](continuous-integration.md) — the workflow that builds this site and deploys it to GitHub Pages on every push to `main`. +- [Design Guide](../logo-design.md) — the vector masters the logo and favicon are baked from, and how to use them on GitHub. diff --git a/docs/developer-manual/tooling/linting-markdownlint.md b/docs/developer-manual/tooling/linting-markdownlint.md index ee7f63a..4ca52fe 100644 --- a/docs/developer-manual/tooling/linting-markdownlint.md +++ b/docs/developer-manual/tooling/linting-markdownlint.md @@ -1,4 +1,4 @@ -# MarkdownLint +# Markdown Linting (MarkdownLint) This article covers Markdown linting. @@ -10,11 +10,13 @@ The config keeps the default rules but disables line-length (prose is not hard-w ## Running It +After [installing the pinned Node.js dependencies](developer-setup.md) with `npm ci`: + ```shell -npx --yes markdownlint-cli2@0.23.0 --config tests/linters/.markdownlint.yml "**/*.md" "#**/bin/**" "#**/obj/**" +npx markdownlint-cli2 --config tests/linters/.markdownlint.yml "**/*.md" "#**/build/**" "#**/node_modules/**" "#**/.venv/**" ``` -The version matches the one [Continuous Integration](continuous-integration.md) installs. The trailing `#`-prefixed globs are `markdownlint-cli2`'s negated-glob syntax (it has no `--ignore` flag) and exclude build output. +`npx` resolves this to the exact version pinned in [`package.json`](../../../package.json) and locked in [`package-lock.json`](../../../package-lock.json), the same version [Continuous Integration](continuous-integration.md) installs. The trailing `#`-prefixed globs are `markdownlint-cli2`'s negated-glob syntax (it has no `--ignore` flag) and exclude build output, `node_modules`, and the Python `.venv` used to build the documentation website — unlike dprint and CSpell, `markdownlint-cli2` does not ignore these by default. ## Writing Docs That Pass diff --git a/docs/developer-manual/tooling/spell-checking-cspell.md b/docs/developer-manual/tooling/spell-checking-cspell.md index 6e79a2f..153847f 100644 --- a/docs/developer-manual/tooling/spell-checking-cspell.md +++ b/docs/developer-manual/tooling/spell-checking-cspell.md @@ -14,11 +14,13 @@ Build output (`bin` and `obj`), SVG files, and OS metadata files (`.DS_Store`, ` ## Running It and Handling Findings +After [installing the pinned Node.js dependencies](developer-setup.md) with `npm ci`: + ```shell -npx --yes cspell@10.0.1 lint --config tests/linters/.cspell.json --no-progress "**/*" +npx cspell lint --config tests/linters/.cspell.json --no-progress "**/*" ``` -The version matches the one [Continuous Integration](continuous-integration.md) installs, so a local run agrees with what CI reports. When a legitimate technical term or proper noun is flagged, add it to the `words` list in [`.cspell.json`](../../../tests/linters/.cspell.json) rather than rewording the text. +`npx` resolves this to the exact version pinned in [`package.json`](../../../package.json) and locked in [`package-lock.json`](../../../package-lock.json), the same version [Continuous Integration](continuous-integration.md) installs, so a local run agrees with what CI reports. When a legitimate technical term or proper noun is flagged, add it to the `words` list in [`.cspell.json`](../../../tests/linters/.cspell.json) rather than rewording the text. ## Related diff --git a/docs/developer-manual/tooling/testing-and-code-coverage.md b/docs/developer-manual/tooling/testing-and-code-coverage.md index d57eece..a21f94e 100644 --- a/docs/developer-manual/tooling/testing-and-code-coverage.md +++ b/docs/developer-manual/tooling/testing-and-code-coverage.md @@ -44,4 +44,4 @@ Open `coverage-report/index.html` to browse coverage by assembly, class, and lin - The conventions the tests themselves follow: [Testing](../conventions/testing.md). - How dependencies, including this local `dotnet` tool, are pinned: [Dependency Management](../conventions/dependency-management.md). - The workflow that runs this in CI: [Continuous Integration](continuous-integration.md). -- Where the test project sits in the repository: [Architecture](../architecture/overview.md). +- Where the test project sits in the repository: [Architecture](../architecture.md). diff --git a/docs/developer-manual/tooling/vscode-integration.md b/docs/developer-manual/tooling/vscode-integration.md index 8e19f81..12f835d 100644 --- a/docs/developer-manual/tooling/vscode-integration.md +++ b/docs/developer-manual/tooling/vscode-integration.md @@ -4,23 +4,31 @@ This article covers the Visual Studio Code setup under [`.vscode/`](../../../.vs ## Shared and Local Configuration -The workspace configuration is split with the [Workspace Config Plus](https://marketplace.visualstudio.com/items?itemName=Swellaby.workspace-config-plus) extension (`Swellaby.workspace-config-plus`, recommended in [`extensions.json`](../../../.vscode/extensions.json)). For `settings` (and, if they are ever added, `tasks` and `launch`), the extension reads two source files and merges them into the file Visual Studio Code actually loads: +The workspace configuration is split with the [Workspace Config Plus](https://marketplace.visualstudio.com/items?itemName=Swellaby.workspace-config-plus) extension (`Swellaby.workspace-config-plus`, recommended in [`extensions.json`](../../../.vscode/extensions.json)). For `settings`, `tasks`, and `launch`, the extension reads two source files and merges them into the file Visual Studio Code actually loads: -- `settings.shared.json` — checked into Git, applies to every developer. -- `settings.local.json` — per-developer, ignored by Git (see [`.gitignore`](../../../.gitignore)). -- `settings.json` — **generated** by the extension from the two files above. +- `settings.shared.json`, `tasks.shared.json`, `launch.shared.json` — checked into Git, apply to every developer. +- `settings.local.json`, `tasks.local.json`, `launch.local.json` — per-developer, ignored by Git (see [`.gitignore`](../../../.gitignore)). +- `settings.json`, `tasks.json`, `launch.json` — **generated** by the extension from the two files above. -The `settings.local.json` layer wins over `settings.shared.json`, so each developer can override or extend the shared config without touching it. +The local layer wins over the shared layer, so each developer can override or extend the shared config without touching it. ### Rules -- **Never edit the generated `settings.json`.** It is produced by the extension and overwritten on every change; any manual edit is lost. It is also Git-ignored — only `extensions.json` and the `*.shared.json` files are committed. -- **Change `settings.shared.json` only for things useful to every developer.** A shared change is committed and affects the whole team, so it must be genuinely common (a project-wide setting, a lint integration). -- **Everything personal goes in `settings.local.json`.** Anything specific to your machine or preference — icon themes, file-nesting patterns, other personal editor tweaks — belongs in the local layer, never in the shared one. +- **Never edit a generated file (`settings.json`, `tasks.json`, `launch.json`).** Each is produced by the extension and overwritten on every change; any manual edit is lost. They are also Git-ignored — only `extensions.json` and the `*.shared.json` files are committed. +- **Change a `*.shared.json` file only for things useful to every developer.** A shared change is committed and affects the whole team, so it must be genuinely common (a project-wide setting, a build or debug configuration, a lint integration). +- **Everything personal goes in the matching `*.local.json` file.** Anything specific to your machine or preference — icon themes, file-nesting patterns, other personal editor tweaks — belongs in the local layer, never in the shared one. ## Settings -[`settings.shared.json`](../../../.vscode/settings.shared.json) sets a 150-character editor ruler to match [dprint's line width](formatting-dprint.md), maps a few file names to the correct language for syntax highlighting, hides build output and OS clutter from the explorer and file watcher, protects the `main` and `development` branches from direct commits, imports the [CSpell](spell-checking-cspell.md) configuration and raises its diagnostic severity to error, points [MarkdownLint](linting-markdownlint.md) at the project configuration, enables format-on-save with [dprint](formatting-dprint.md), and points `dotnet.defaultSolution` at [`CLI.NET Core.slnx`](../../../source/CLI.NET%20Core.slnx) so a C# extension (OmniSharp or C# Dev Kit) does not have to guess which solution to load — without it, the solution's non-standard location (in `source/`, not the repository root) and its cross-directory reference to the unit test project under `tests/` (see [Architecture](../architecture/overview.md)) can lead the extension to miss part of the solution and report a file as not belonging to any project. +[`settings.shared.json`](../../../.vscode/settings.shared.json) sets a 150-character editor ruler to match [dprint's line width](code-formatting-dprint.md), maps a few file names to the correct language for syntax highlighting, hides build output and OS clutter from the explorer and file watcher, protects the `main` and `development` branches from direct commits, imports the [CSpell](spell-checking-cspell.md) configuration and raises its diagnostic severity to error, points [MarkdownLint](linting-markdownlint.md) at the project configuration, enables format-on-save with [dprint](code-formatting-dprint.md), and points `dotnet.defaultSolution` at [`CLI.NET Core.slnx`](../../../source/CLI.NET%20Core.slnx) so a C# extension (OmniSharp or C# Dev Kit) does not have to guess which solution to load — without it, the solution's non-standard location (in `source/`, not the repository root) and its cross-directory reference to the unit test project under `tests/` (see [Architecture](../architecture.md)) can lead the extension to miss part of the solution and report a file as not belonging to any project. + +## Tasks + +[`tasks.shared.json`](../../../.vscode/tasks.shared.json) exposes the same commands the [Golden Rules](../../../CLAUDE.md#golden-rules) and [Continuous Integration](continuous-integration.md) run, as Visual Studio Code tasks: installing the Node.js, .NET tool, and Python dependencies (individually and combined as "Install Dependencies"), running `dprint` ("Format"), `cspell` ("Lint Spelling"), `markdownlint-cli2` ("Lint Markdown", combined with the two linters as the default "Lint" task), building the sample app ("Build Sample App", the `preLaunchTask` of the "Debug Sample App" launch configuration below), running the unit tests ("Test"), and building the ProperDocs documentation website ("Build Docs"). + +## Launch Configuration + +[`launch.shared.json`](../../../.vscode/launch.shared.json) defines "Debug Sample App", a `coreclr` launch configuration (from the `ms-dotnettools.csharp` extension) that runs the "Build Sample App" task first and then attaches the debugger to the built `sample-app` executable, with its working directory set to `source/sample-app` so relative paths in the sample app behave the same as running it from the command line. ## Extensions @@ -30,5 +38,5 @@ The CSpell and MarkdownLint extensions bundle their own engines, so the version ## Related -- The tools these settings configure: [Spell Checking (CSpell)](spell-checking-cspell.md), [MarkdownLint](linting-markdownlint.md), [dprint](formatting-dprint.md). +- The tools these settings configure: [Spell Checking (CSpell)](spell-checking-cspell.md), [MarkdownLint](linting-markdownlint.md), [dprint](code-formatting-dprint.md). - The pinned versions these extensions approximate: [Continuous Integration](continuous-integration.md). diff --git a/docs/user-manual/README.md b/docs/user-manual/README.md index 009fc1b..2c0aa04 100644 --- a/docs/user-manual/README.md +++ b/docs/user-manual/README.md @@ -2,8 +2,6 @@ This manual is for people building a command line application with CLI.NET Core, as opposed to people working on CLI.NET Core itself (for that, see the [Developer Manual](../developer-manual/README.md)). -CLI.NET Core is a .NET framework for command line applications, built in the style of ASP.NET Core: it lets you define commands and their arguments much the same way you would define actions and parameters for a Web API. The package is distributed on NuGet as `CliNetCore` (see [Architecture](../developer-manual/architecture/overview.md) for how the project and its packages are laid out). - -This part of the documentation is still growing alongside the framework's public API — the [root README](../../README.md) is the best starting point for anything not yet covered here. +CLI.NET Core is a .NET framework for command line applications, built in the style of ASP.NET Core: it lets you define commands and their arguments much the same way you would define actions and parameters for a Web API. The package is distributed on NuGet as `CliNetCore` (see [Architecture](../developer-manual/architecture.md) for how the project and its packages are laid out). 1. [Building Applications](building-applications.md) diff --git a/docs/user-manual/building-applications.md b/docs/user-manual/building-applications.md index 07b0228..5445610 100644 --- a/docs/user-manual/building-applications.md +++ b/docs/user-manual/building-applications.md @@ -102,5 +102,5 @@ CLI.NET Core does not yet have a command model: there is no way to define indivi ## Related -- How the framework's projects and packages are laid out: [Architecture](../developer-manual/architecture/overview.md). +- How the framework's projects and packages are laid out: [Architecture](../developer-manual/architecture.md). - Back to the [User Manual](README.md). diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8528a99 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2759 @@ +{ + "name": "command-line-parser", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "command-line-parser", + "devDependencies": { + "cspell": "10.0.1", + "dprint": "0.55.1", + "markdownlint-cli2": "0.23.0" + } + }, + "node_modules/@cspell/cspell-bundled-dicts": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-10.0.1.tgz", + "integrity": "sha512-WvkSDNX4Uyyj/ZgbPO6L38iFNMfK1EqsH1FteRiI2qLz6QZMXRFrIt12OqiWIplzZDDaVpBH9FCJOPJll0fjCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/dict-ada": "^4.1.1", + "@cspell/dict-al": "^1.1.1", + "@cspell/dict-aws": "^4.0.17", + "@cspell/dict-bash": "^4.2.2", + "@cspell/dict-companies": "^3.2.11", + "@cspell/dict-cpp": "^7.0.2", + "@cspell/dict-cryptocurrencies": "^5.0.5", + "@cspell/dict-csharp": "^4.0.8", + "@cspell/dict-css": "^4.1.1", + "@cspell/dict-dart": "^2.3.2", + "@cspell/dict-data-science": "^2.0.13", + "@cspell/dict-django": "^4.1.6", + "@cspell/dict-docker": "^1.1.17", + "@cspell/dict-dotnet": "^5.0.13", + "@cspell/dict-elixir": "^4.0.8", + "@cspell/dict-en_us": "^4.4.33", + "@cspell/dict-en-common-misspellings": "^2.1.12", + "@cspell/dict-en-gb-mit": "^3.1.22", + "@cspell/dict-filetypes": "^3.0.18", + "@cspell/dict-flutter": "^1.1.1", + "@cspell/dict-fonts": "^4.0.6", + "@cspell/dict-fsharp": "^1.1.1", + "@cspell/dict-fullstack": "^3.2.9", + "@cspell/dict-gaming-terms": "^1.1.2", + "@cspell/dict-git": "^3.1.0", + "@cspell/dict-golang": "^6.0.26", + "@cspell/dict-google": "^1.0.9", + "@cspell/dict-haskell": "^4.0.6", + "@cspell/dict-html": "^4.0.15", + "@cspell/dict-html-symbol-entities": "^4.0.5", + "@cspell/dict-java": "^5.0.12", + "@cspell/dict-julia": "^1.1.1", + "@cspell/dict-k8s": "^1.0.12", + "@cspell/dict-kotlin": "^1.1.1", + "@cspell/dict-latex": "^5.1.0", + "@cspell/dict-lorem-ipsum": "^4.0.5", + "@cspell/dict-lua": "^4.0.8", + "@cspell/dict-makefile": "^1.0.5", + "@cspell/dict-markdown": "^2.0.16", + "@cspell/dict-monkeyc": "^1.0.12", + "@cspell/dict-node": "^5.0.9", + "@cspell/dict-npm": "^5.2.38", + "@cspell/dict-php": "^4.1.1", + "@cspell/dict-powershell": "^5.0.15", + "@cspell/dict-public-licenses": "^2.0.16", + "@cspell/dict-python": "^4.2.26", + "@cspell/dict-r": "^2.1.1", + "@cspell/dict-ruby": "^5.1.1", + "@cspell/dict-rust": "^4.1.2", + "@cspell/dict-scala": "^5.0.9", + "@cspell/dict-shell": "^1.1.2", + "@cspell/dict-software-terms": "^5.2.2", + "@cspell/dict-sql": "^2.2.1", + "@cspell/dict-svelte": "^1.0.7", + "@cspell/dict-swift": "^2.0.6", + "@cspell/dict-terraform": "^1.1.3", + "@cspell/dict-typescript": "^3.2.3", + "@cspell/dict-vue": "^3.0.5", + "@cspell/dict-zig": "^1.0.0" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/cspell-json-reporter": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-10.0.1.tgz", + "integrity": "sha512-/nes1RGILec3WCBcoMOd0byNTBtnJuPaVz/+ZzqYkLtY7x58VMcBG5kyP6hPyN8cIwjRADE/SR43gwdXuqk/FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-types": "10.0.1" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/cspell-performance-monitor": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-performance-monitor/-/cspell-performance-monitor-10.0.1.tgz", + "integrity": "sha512-9tVcHXwRnbazUv4WSG0h3MqV4+LgmLNgSALAQUflPPW0EMxTf7C4Dmv9cgxJyCEQrdnVKCr58nPPaahhz9LJUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/cspell-pipe": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-10.0.1.tgz", + "integrity": "sha512-HPeXMD9AZ3V/qPkvQaPcak+C7cJ2z7JTHN8smd6J8L2aThLRky2cHc2OyeaHPSHB7WA47b4z2n5u5nawZhv5VQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/cspell-resolver": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-10.0.1.tgz", + "integrity": "sha512-PIzkZHD1fGUQx1XteK2d1iQ0Mzq/maYcoB4jkvAiiR6WqP3MWYNKFdI9z+R5pOq5KgMfW+5Ig1q0oSR6h8irlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-directory": "^5.0.0" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/cspell-service-bus": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-10.0.1.tgz", + "integrity": "sha512-y6NcIGP2IdXaBL4PVH8vxsr7K27wzz3Ech87UtUtrDSXAiVEOvXgAIknEOUVp59rTlUE8Rn4IRURC6f/hgMyfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/cspell-types": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-10.0.1.tgz", + "integrity": "sha512-kLgLShnWADDVreKC63pBrWkcvxgZzFIfO34Jhx/SWfuOIA3cD8AXT+HjyuLfoGJ7mUb58hv2kUziKzEy4INb1w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/cspell-worker": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-worker/-/cspell-worker-10.0.1.tgz", + "integrity": "sha512-L2bJerfuYOls2wEknm8FmynLtj/G7O4UqX9I/HznRggEW6i2yZIxagDetpVDNowpyavNHJ3SJtUFiyMiZc16Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cspell-lib": "10.0.1" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/dict-ada": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.1.1.tgz", + "integrity": "sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-al": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-al/-/dict-al-1.1.1.tgz", + "integrity": "sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-aws": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.17.tgz", + "integrity": "sha512-ORcblTWcdlGjIbWrgKF+8CNEBQiLVKdUOFoTn0KPNkAYnFcdPP0muT4892h7H4Xafh3j72wqB4/loQ6Nti9E/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-bash": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.2.3.tgz", + "integrity": "sha512-ljUZoKHbDqw5Sx0qpL2qTUlmkmr+vhZH/sCNrNaBZKTbdgiswErSnIF1jRbGmEitJNxHRHWsuZyVgnTGfVO1Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/dict-shell": "1.2.0" + } + }, + "node_modules/@cspell/dict-companies": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.2.12.tgz", + "integrity": "sha512-mjiz/N3zWOCsz5VfwMUydSl7uW0OU9H2PnbCNc3RV44Vj6Q59CSp6EYGSGZQxrXU1gpsuZUrwr6QCjNjFOOg5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-cpp": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-7.0.2.tgz", + "integrity": "sha512-dfbeERiVNeqmo/npivdR6rDiBCqZi3QtjH2Z0HFcXwpdj6i97dX1xaKyK2GUsO/p4u1TOv63Dmj5Vm48haDpuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-cryptocurrencies": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.5.tgz", + "integrity": "sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-csharp": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.8.tgz", + "integrity": "sha512-qmk45pKFHSxckl5mSlbHxmDitSsGMlk/XzFgt7emeTJWLNSTUK//MbYAkBNRtfzB4uD7pAFiKgpKgtJrTMRnrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-css": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.1.2.tgz", + "integrity": "sha512-+ylGoKdwZ2sVOCOnU2Eq5wDZx+RaVX3HoKyNHGGsFvhSw6IidQ6tH/mAPKBDofViHJoWCPNlklE0lTr6MDG3QA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-dart": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.3.2.tgz", + "integrity": "sha512-sUiLW56t9gfZcu8iR/5EUg+KYyRD83Cjl3yjDEA2ApVuJvK1HhX+vn4e4k4YfjpUQMag8XO2AaRhARE09+/rqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-data-science": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.16.tgz", + "integrity": "sha512-M72mxv5asuAnORurz4iXRJ+Tw9XBq6eu7D2Ne7biP0Z1RciKGNxXWu9JycA/KlVvK1hAlKj/fANlXhuEWpXKFg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-django": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.6.tgz", + "integrity": "sha512-SdbSFDGy9ulETqNz15oWv2+kpWLlk8DJYd573xhIkeRdcXOjskRuxjSZPKfW7O3NxN/KEf3gm3IevVOiNuFS+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-docker": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.17.tgz", + "integrity": "sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-dotnet": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.13.tgz", + "integrity": "sha512-xPp7jMnFpOri7tzmqmm/dXMolXz1t2bhNqxYkOyMqXhvs08oc7BFs+EsbDY0X7hqiISgeFZGNqn0dOCr+ncPYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-elixir": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.8.tgz", + "integrity": "sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-en_us": { + "version": "4.4.36", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.36.tgz", + "integrity": "sha512-2yOhI/+7d1DbfvMljGW4jw8pLqDEsVmnvUXBOCFXtLU2BWgQkrqOJDCNseYjEiEbTp0OtdrWEWWPFSP1TNugQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-en-common-misspellings": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.13.tgz", + "integrity": "sha512-00rpydUxKNWY2xxrSx+h46aNWLvbkJdd57SsnEFt24fbs1fROhXZ6XSQu+gQz/zNuiCvFi4Ro3ej9DLbEdWQmQ==", + "dev": true, + "license": "CC BY-SA 4.0" + }, + "node_modules/@cspell/dict-en-gb-mit": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.25.tgz", + "integrity": "sha512-zGODptk24CMrXi49ieG2SUm94CKxEsVF0dYNF+1ZYH0MSsQDZ/PKDlrrbvtBqSupKdPSj0Z9sjOmMNfHHW9ZSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-filetypes": { + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.18.tgz", + "integrity": "sha512-yU7RKD/x1IWmDLzWeiItMwgV+6bUcU/af23uS0+uGiFUbsY1qWV/D4rxlAAO6Z7no3J2z8aZOkYIOvUrJq0Rcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-flutter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-flutter/-/dict-flutter-1.1.1.tgz", + "integrity": "sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-fonts": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-4.0.6.tgz", + "integrity": "sha512-aR/0csY01dNb0A1tw/UmN9rKgHruUxsYsvXu6YlSBJFu60s26SKr/k1o4LavpHTQ+lznlYMqAvuxGkE4Flliqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-fsharp": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.1.1.tgz", + "integrity": "sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-fullstack": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.9.tgz", + "integrity": "sha512-diZX+usW5aZ4/b2T0QM/H/Wl9aNMbdODa1Jq0ReBr/jazmNeWjd+PyqeVgzd1joEaHY+SAnjrf/i9CwKd2ZtWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-gaming-terms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.1.2.tgz", + "integrity": "sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-git": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.1.0.tgz", + "integrity": "sha512-KEt9zGkxqGy2q1nwH4CbyqTSv5nadpn8BAlDnzlRcnL0Xb3LX9xTgSGShKvzb0bw35lHoYyLWN2ZKAqbC4pgGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-golang": { + "version": "6.0.26", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.26.tgz", + "integrity": "sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-google": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-google/-/dict-google-1.0.9.tgz", + "integrity": "sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-haskell": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.6.tgz", + "integrity": "sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-html": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.15.tgz", + "integrity": "sha512-GJYnYKoD9fmo2OI0aySEGZOjThnx3upSUvV7mmqUu8oG+mGgzqm82P/f7OqsuvTaInZZwZbo+PwJQd/yHcyFIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-html-symbol-entities": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.5.tgz", + "integrity": "sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-java": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-5.0.12.tgz", + "integrity": "sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-julia": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-julia/-/dict-julia-1.1.1.tgz", + "integrity": "sha512-WylJR9TQ2cgwd5BWEOfdO3zvDB+L7kYFm0I9u0s9jKHWQ6yKmfKeMjU9oXxTBxIufhCXm92SKwwVNAC7gjv+yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-k8s": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.13.tgz", + "integrity": "sha512-ELGkS13k7K/NEfVimBSrxVTfqXvOF/Kvxj4I62YxRm8bvHbfoXgrGaOx28lPiNRz+dmu+yYtvuXbnURKtYbC6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-kotlin": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-kotlin/-/dict-kotlin-1.1.1.tgz", + "integrity": "sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-latex": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-5.1.0.tgz", + "integrity": "sha512-qxT4guhysyBt0gzoliXYEBYinkAdEtR2M7goRaUH0a7ltCsoqqAeEV8aXYRIdZGcV77gYSobvu3jJL038tlPAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-lorem-ipsum": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-4.0.5.tgz", + "integrity": "sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-lua": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.8.tgz", + "integrity": "sha512-N4PkgNDMu9JVsRu7JBS/3E/dvfItRgk9w5ga2dKq+JupP2Y3lojNaAVFhXISh4Y0a6qXDn2clA6nvnavQ/jjLA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-makefile": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.5.tgz", + "integrity": "sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-markdown": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.17.tgz", + "integrity": "sha512-H8bAxih6U8NOnSPL7R8My+tqjaB4tmnJTjERuz4zYqmf+cH+5xshX3UVgKlwWFcyjsYfv/zEDuRdMctQv1q6HQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@cspell/dict-css": "^4.1.2", + "@cspell/dict-html": "^4.0.15", + "@cspell/dict-html-symbol-entities": "^4.0.5", + "@cspell/dict-typescript": "^3.2.3" + } + }, + "node_modules/@cspell/dict-monkeyc": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.12.tgz", + "integrity": "sha512-MN7Vs11TdP5mbdNFQP5x2Ac8zOBm97ARg6zM5Sb53YQt/eMvXOMvrep7+/+8NJXs0jkp70bBzjqU4APcqBFNAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-node": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.9.tgz", + "integrity": "sha512-hO+ga+uYZ/WA4OtiMEyKt5rDUlUyu3nXMf8KVEeqq2msYvAPdldKBGH7lGONg6R/rPhv53Rb+0Y1SLdoK1+7wQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-npm": { + "version": "5.2.43", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.43.tgz", + "integrity": "sha512-H2gYwtu59dNO9662Uq0usfuhyNd7lZJE1C61a/UXcpRyWWSrTo2Bz+vwGYp1bXZ1LmjXadqvwJ8ArFlGdiadNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-php": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.1.1.tgz", + "integrity": "sha512-EXelI+4AftmdIGtA8HL8kr4WlUE11OqCSVlnIgZekmTkEGSZdYnkFdiJ5IANSALtlQ1mghKjz+OFqVs6yowgWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-powershell": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.15.tgz", + "integrity": "sha512-l4S5PAcvCFcVDMJShrYD0X6Huv9dcsQPlsVsBGbH38wvuN7gS7+GxZFAjTNxDmTY1wrNi1cCatSg6Pu2BW4rgg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-public-licenses": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.16.tgz", + "integrity": "sha512-EQRrPvEOmwhwWezV+W7LjXbIBjiy6y/shrET6Qcpnk3XANTzfvWflf9PnJ5kId/oKWvihFy0za0AV1JHd03pSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-python": { + "version": "4.2.29", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.29.tgz", + "integrity": "sha512-OnEt1a35iuQzc2Ize1qU/43ZyF10urRKAm+mlTz++vnAgDLBHpKfWakpSK50nyL5/1WvyQ8BaMjb52MBLEpTeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/dict-data-science": "^2.0.16" + } + }, + "node_modules/@cspell/dict-r": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.1.1.tgz", + "integrity": "sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-ruby": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.1.1.tgz", + "integrity": "sha512-LHrp84oEV6q1ZxPPyj4z+FdKyq1XAKYPtmGptrd+uwHbrF/Ns5+fy6gtSi7pS+uc0zk3JdO9w/tPK+8N1/7WUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-rust": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.1.2.tgz", + "integrity": "sha512-O1FHrumYcO+HZti3dHfBPUdnDFkI+nbYK3pxYmiM1sr+G0ebOd6qchmswS0Wsc6ZdEVNiPYJY/gZQR6jfW3uOg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-scala": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.9.tgz", + "integrity": "sha512-AjVcVAELgllybr1zk93CJ5wSUNu/Zb5kIubymR/GAYkMyBdYFCZ3Zbwn4Zz8GJlFFAbazABGOu0JPVbeY59vGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-shell": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-shell/-/dict-shell-1.2.0.tgz", + "integrity": "sha512-PVctvT22lJ49niMiakO8xieY7ELCAzjSqhejWR7bAMb5AZ9F4WDEs+XdGMnoVHWeXq7K5rcepLPmEJb+37zzIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-software-terms": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.2.4.tgz", + "integrity": "sha512-z6y/TGH3QNf5wB4pVvN/P3GfFEW/Whf6QAekNsIn06VKl95dnamfpkPWqV8rEtCixQFaKalb5+y9hRQXH3XQ1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-sql": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.2.1.tgz", + "integrity": "sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-svelte": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.7.tgz", + "integrity": "sha512-hGZsGqP0WdzKkdpeVLBivRuSNzOTvN036EBmpOwxH+FTY2DuUH7ecW+cSaMwOgmq5JFSdTcbTNFlNC8HN8lhaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-swift": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.6.tgz", + "integrity": "sha512-PnpNbrIbex2aqU1kMgwEKvCzgbkHtj3dlFLPMqW1vSniop7YxaDTtvTUO4zA++ugYAEL+UK8vYrBwDPTjjvSnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-terraform": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.1.4.tgz", + "integrity": "sha512-Ere42ilvMFvQA4GlcN0OKlruMPR6EsvaB+iTHzj2xc+NJGRK64V7yApUcWrOrSgTiM/vhWXPIsK3OMfiAiNdmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-typescript": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.2.3.tgz", + "integrity": "sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-vue": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.5.tgz", + "integrity": "sha512-Mqutb8jbM+kIcywuPQCCaK5qQHTdaByoEO2J9LKFy3sqAdiBogNkrplqUK0HyyRFgCfbJUgjz3N85iCMcWH0JA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-zig": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-zig/-/dict-zig-1.0.0.tgz", + "integrity": "sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dynamic-import": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-10.0.1.tgz", + "integrity": "sha512-mP1gdq00aIcH8HxNMqnH11X6BKxLcneDtFgl/ecjIKnaGKwi44m8AndP5Kr4ODaYdl8UUw9O3dJh7KaQXnLHZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/url": "10.0.1", + "import-meta-resolve": "^4.2.0" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/filetypes": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-10.0.1.tgz", + "integrity": "sha512-Z5S35giU5IW49fBBq6BksUbE8PC4IYPfaKuwl5Nl9jkf/OkAKiBmCowKX45NzRUQInwK/GSqqIUifrNeI6LdLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/rpc": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/rpc/-/rpc-10.0.1.tgz", + "integrity": "sha512-axSRKv3zEAmBm66iD/FV/MPmE4/Yf7c3PZiwTW894Yd3iEhtn3KPKeTrqQ2/tDrhB1Z2qTsap/Hue0MK4o5WXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/strong-weak-map": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-10.0.1.tgz", + "integrity": "sha512-lenN1DVyPi8nJLSMSJJ670ddTjyiruLueuSZO1qLcxBqUhgxDt/mALu9N/1m6WdOVcg6m/5cLiZVg2KOo2UzRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@cspell/url": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@cspell/url/-/url-10.0.1.tgz", + "integrity": "sha512-abYYgI29wJhWIfWTYrYuzRYDcHQUQ1N5ylnhxYn1NJnIQMqUWGLbDmt12JABtZ+R6h6UNatQrS7rhP86etvJyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/@dprint/android-arm64": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/android-arm64/-/android-arm64-0.55.1.tgz", + "integrity": "sha512-H1UQIcBJEo1dA8AVrkPnw5AYMMGHNe4w0lq9rEDSY3Lds0VUKACu2MK3JLNARpjljh3WKeO109J26Wpq8PTuGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@dprint/android-x64": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/android-x64/-/android-x64-0.55.1.tgz", + "integrity": "sha512-8dPiGpEo4S4cQhnLaoHtNm4HiqR9DIPr/81z3gWb5qTmJp8bKj+3DO4mAmmldMRBicmpXCss0xudniNTxnWOMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@dprint/darwin-arm64": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/darwin-arm64/-/darwin-arm64-0.55.1.tgz", + "integrity": "sha512-c65u8f63R/etCKP0CZ/RkbMCRC99wgZDt/vhwjY2QdO0k23dMKXKEV6+ruUEwV2toh+QIRT02dRL7BtxjA0V7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@dprint/darwin-x64": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/darwin-x64/-/darwin-x64-0.55.1.tgz", + "integrity": "sha512-AsET9+4rMk7ZFEMq3HYl/hYNBMBrR/juiSzclEU0oUCf3koxFbVKL8Srad8Vhyv8jEnOaMn78TCnahmHhp1SCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@dprint/linux-arm64-glibc": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/linux-arm64-glibc/-/linux-arm64-glibc-0.55.1.tgz", + "integrity": "sha512-vt7w+aL2MjtD3RRfnUTDuK/b7xg1/feBe3WoV0S2rGNmPEJI+K7wwBXkOl/I77V9w3PP2zvN/aRY5dMOZrkbSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-arm64-musl": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/linux-arm64-musl/-/linux-arm64-musl-0.55.1.tgz", + "integrity": "sha512-7shUIOvJcTn4IYEPlb/Up4KLt5kbDuCTdc3DN68i88XT+nRBFQatD7Qok+6Ysra34KJMxF88QVHiimjH1ABy4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-loong64-glibc": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/linux-loong64-glibc/-/linux-loong64-glibc-0.55.1.tgz", + "integrity": "sha512-G2mXiOGpkeC3LP8huSnImxU7hMvRY6rHIW+yBuw1vDDm5r6+H9CVhcXr3TMOk3poZ7IlWwQnDmP0/RuGQUEpYw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-loong64-musl": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/linux-loong64-musl/-/linux-loong64-musl-0.55.1.tgz", + "integrity": "sha512-uO2r4QPYawDfQUyATr0opqfMkG2hVpz3yScRKs5ve9PWzqx2f5kJaaLuLVoXwSbWqDkozMGPcldAVSpOwGJ24Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-ppc64-glibc": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/linux-ppc64-glibc/-/linux-ppc64-glibc-0.55.1.tgz", + "integrity": "sha512-NVR+BN4kTQd/vJ91YOGSkBotAM2f1hFD+HD5zq5Oc1djxjl1U2Zvc667UWKamxaXdv/ssZxkLuj0lbvbx9E3/A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-ppc64-musl": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/linux-ppc64-musl/-/linux-ppc64-musl-0.55.1.tgz", + "integrity": "sha512-34Db83XGoKbwmvIY52sokywIMCmmIZM1vzw4ILlOkKugG51TltBOzLCssT9Rkfxz/TjR9dgwtBHEQJZjftuQ6Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-riscv64-glibc": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/linux-riscv64-glibc/-/linux-riscv64-glibc-0.55.1.tgz", + "integrity": "sha512-MVeYH48GhR3QkfhMhhYbXl9x+iO2sK1nNpr2ByJ4IKogHIlvlFff0eeukqAZZAod+rhAzJnLMBGgIDbIUHWK1g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-x64-glibc": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/linux-x64-glibc/-/linux-x64-glibc-0.55.1.tgz", + "integrity": "sha512-ILHgXOIIXeZDjt51egaC4kmu744UuNfaauUM1El+dG9QGan4Vv4f0RRDDbQMKN95zTpacECuNKEhuUtS8euNWA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/linux-x64-musl": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/linux-x64-musl/-/linux-x64-musl-0.55.1.tgz", + "integrity": "sha512-SwWxWd313VkeH63fR4IlQCDfJNJ+mSw50BidP0PQInCNbUHYOIeWTcyzAaVa8GQdLvmhiNcJ0pmTDOL0F98QIQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@dprint/win32-arm64": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/win32-arm64/-/win32-arm64-0.55.1.tgz", + "integrity": "sha512-9jiZWZ2AkaaXIkaOpOoiP6v0XxuBFp1Hyi3kUMUAY4NVCmvlSEDQhr+UZgnDhozYygDbAV13iNMLERDeoUa0nA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@dprint/win32-x64": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/@dprint/win32-x64/-/win32-x64-0.55.1.tgz", + "integrity": "sha512-+RmeJL8zzlEWTcrvyFN51rz0S98tV43Socs7xeVGE2S/EH5dG2fG2/fBe6RB3kuyZSfGqByApYeicIdshYIAgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/katex": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.2.tgz", + "integrity": "sha512-2bxTP2yUH7AJj/VAXfcA+4IcWGdQ87HwBANLt5XxGTeomo8yG0y95N1um9i5StvhT/Bl0/2cARA5v1PpPXUxUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/comment-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-5.0.0.tgz", + "integrity": "sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cspell": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-10.0.1.tgz", + "integrity": "sha512-Gg6w/flT3fKfl3la62hfTnhtNnDQ+9mU7kUhVqw/axl/Ms4oENw0oJMkWFIoj4f6nL/SDPz7KcPXd2XbkKFNmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-json-reporter": "10.0.1", + "@cspell/cspell-performance-monitor": "10.0.1", + "@cspell/cspell-pipe": "10.0.1", + "@cspell/cspell-types": "10.0.1", + "@cspell/cspell-worker": "10.0.1", + "@cspell/dynamic-import": "10.0.1", + "@cspell/url": "10.0.1", + "ansi-regex": "^6.2.2", + "chalk": "^5.6.2", + "chalk-template": "^1.1.2", + "commander": "^14.0.3", + "cspell-config-lib": "10.0.1", + "cspell-dictionary": "10.0.1", + "cspell-gitignore": "10.0.1", + "cspell-glob": "10.0.1", + "cspell-io": "10.0.1", + "cspell-lib": "10.0.1", + "fast-json-stable-stringify": "^2.1.0", + "flatted": "^3.4.2", + "semver": "^7.8.1", + "tinyglobby": "^0.2.16" + }, + "bin": { + "cspell": "bin.mjs", + "cspell-esm": "bin.mjs" + }, + "engines": { + "node": ">=22.18.0" + }, + "funding": { + "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" + } + }, + "node_modules/cspell-config-lib": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-10.0.1.tgz", + "integrity": "sha512-hMpo/0j6k7pbiqrLDOLJKD2IGP9XwhjKf2miiM6p84Xeo4nyuFZaxxDCQ68R851HSYFrrdltgpoipMbj1h2Tnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-types": "10.0.1", + "comment-json": "^5.0.0", + "smol-toml": "^1.6.1", + "yaml": "^2.9.0" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/cspell-dictionary": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-10.0.1.tgz", + "integrity": "sha512-3cZ659vgsZWkzGQJR/sNqGDVt/OnvTSieLKI76V++4t1bHJfochb9ZrrwsuMsb1VPGiyqClUP1/O6WrefF/FVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-performance-monitor": "10.0.1", + "@cspell/cspell-pipe": "10.0.1", + "@cspell/cspell-types": "10.0.1", + "cspell-trie-lib": "10.0.1", + "fast-equals": "^6.0.0" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/cspell-gitignore": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-10.0.1.tgz", + "integrity": "sha512-wN23U61Mx6qPJN3CesOmBU9vnbJ0jQm/ylK0iaVui3CcnO7Zzl5qLu5mPHUzGQGm8yso6qjyxqo16Ho7LpZGOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/url": "10.0.1", + "cspell-glob": "10.0.1", + "cspell-io": "10.0.1" + }, + "bin": { + "cspell-gitignore": "bin.mjs" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/cspell-glob": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-10.0.1.tgz", + "integrity": "sha512-7bII9J3aSSpZDwhx7w+zfQXbMxHZQ3be0ilUp5bHrsjz6o07v/NqOHMGcwKdPn1sw2dxDz9sv057xE5pqXnSdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/url": "10.0.1", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/cspell-grammar": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-10.0.1.tgz", + "integrity": "sha512-xC9AFYmaI9wsO//a7S5tdDGKGJVD5UEEsTg+Up2fi7lPfXIryisYmV6tePNL1SEg0idYss4ja8LUZ3Mib09BjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "10.0.1", + "@cspell/cspell-types": "10.0.1" + }, + "bin": { + "cspell-grammar": "bin.mjs" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/cspell-io": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-10.0.1.tgz", + "integrity": "sha512-8C2ka07faxflnaqEBO3pektS21XViE/SEHT7F5ZD1ou7FyMR5u3xawTBJSczClfsxLt/WYeztBYrpmGAjmjksw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-service-bus": "10.0.1", + "@cspell/url": "10.0.1" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/cspell-lib": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-10.0.1.tgz", + "integrity": "sha512-RpsIPiLzc4/YMW8BMRKpyJ81x439qjYWcqgdKeXnMkbKM88J9PexzutfFf/4v97v96KzfNitEzMpbI0uj8OeUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-bundled-dicts": "10.0.1", + "@cspell/cspell-performance-monitor": "10.0.1", + "@cspell/cspell-pipe": "10.0.1", + "@cspell/cspell-resolver": "10.0.1", + "@cspell/cspell-types": "10.0.1", + "@cspell/dynamic-import": "10.0.1", + "@cspell/filetypes": "10.0.1", + "@cspell/rpc": "10.0.1", + "@cspell/strong-weak-map": "10.0.1", + "@cspell/url": "10.0.1", + "cspell-config-lib": "10.0.1", + "cspell-dictionary": "10.0.1", + "cspell-glob": "10.0.1", + "cspell-grammar": "10.0.1", + "cspell-io": "10.0.1", + "cspell-trie-lib": "10.0.1", + "env-paths": "^4.0.0", + "gensequence": "^8.0.8", + "import-fresh": "^4.0.0", + "resolve-from": "^5.0.0", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-uri": "^3.1.0", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=22.18.0" + } + }, + "node_modules/cspell-trie-lib": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-10.0.1.tgz", + "integrity": "sha512-BFvhalSkRQFjKrZ//FKK7fRGrZFpifnxB5AwCkzsIsBZqicsfafcQ1xP21qpb0QqyV/IomjNgviG+tRJs+0rMw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.18.0" + }, + "peerDependencies": { + "@cspell/cspell-types": "10.0.1" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dprint": { + "version": "0.55.1", + "resolved": "https://registry.npmjs.org/dprint/-/dprint-0.55.1.tgz", + "integrity": "sha512-tgUCT9gAM7veMvLX5NmRoYVLnKGKrIYRXpNpJDJj5U7/YIIJef5rLJhPZaJXwB7ad2Vo0Kv//nQM1xkrn5j8kQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "dprint": "bin.cjs" + }, + "optionalDependencies": { + "@dprint/android-arm64": "0.55.1", + "@dprint/android-x64": "0.55.1", + "@dprint/darwin-arm64": "0.55.1", + "@dprint/darwin-x64": "0.55.1", + "@dprint/linux-arm64-glibc": "0.55.1", + "@dprint/linux-arm64-musl": "0.55.1", + "@dprint/linux-loong64-glibc": "0.55.1", + "@dprint/linux-loong64-musl": "0.55.1", + "@dprint/linux-ppc64-glibc": "0.55.1", + "@dprint/linux-ppc64-musl": "0.55.1", + "@dprint/linux-riscv64-glibc": "0.55.1", + "@dprint/linux-x64-glibc": "0.55.1", + "@dprint/linux-x64-musl": "0.55.1", + "@dprint/win32-arm64": "0.55.1", + "@dprint/win32-x64": "0.55.1" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-4.0.0.tgz", + "integrity": "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-safe-filename": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-equals": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-6.0.2.tgz", + "integrity": "sha512-sAjhj9ZhOxYCGiNMnZLaucOqf5ZeFnHNoKoAZiD9thhJ0N8RP85qJK759/97C/3L7NzzmGVB5uiX9AUpySZmUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/gensequence": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-8.0.8.tgz", + "integrity": "sha512-omMVniXEXpdx/vKxGnPRoO2394Otlze28TyxECbFVyoSpZ9H3EO7lemjcB12OpQJzRW4e5tt/dL1rOxry6aMHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-directory": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-5.0.0.tgz", + "integrity": "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "6.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.0.tgz", + "integrity": "sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.5", + "is-path-inside": "^4.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-4.0.0.tgz", + "integrity": "sha512-Fpi660c7VPDM3fPKYovStd9IP1CPOikf6v/dGxJJMmHPcwYQIMJ4W7kO1avBYEpMqkCh+Dx3Ln6H7VYqgztLjw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.15" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ini": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", + "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-safe-filename": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-safe-filename/-/is-safe-filename-0.1.1.tgz", + "integrity": "sha512-4SrR7AdnY11LHfDKTZY1u6Ga3RuxZdl3YKWWShO5iyuG5h8QS4GD2tOb04peBJ5I7pXbR+CGBNEhTcwK+FzN3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.0.tgz", + "integrity": "sha512-YeLUMlvR4Ou1B119LIaM0r65JvbOBooJDc9yEu0dClb/uSC5P4FrLU8OCCz/HXWvtPoIrR0dRzABTjo1sTN9Bw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.mjs" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/katex": { + "version": "0.16.47", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz", + "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/linkify-it": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/markdown-it": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz", + "integrity": "sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.1", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdownlint": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.41.0.tgz", + "integrity": "sha512-xMUI3ChBuRuxuLF4ENvCZyS8z/+Jly1coUcZwErKLIB3sDj7ojpaTBa1e9YVPhSN4jGEIjYGQCldbTJS/hqS+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark": "4.0.2", + "micromark-core-commonmark": "2.0.3", + "micromark-extension-directive": "4.0.0", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.1", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.2", + "string-width": "8.2.1" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/markdownlint-cli2": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.23.0.tgz", + "integrity": "sha512-1nmgQmU/ZTMRVwYCDs7i1HI3zfBISnT2NNRv+9V01oOLZbAtqL+a7tldpPhBWBVBten3FqhMCGV6EUh9McqutQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "globby": "16.2.0", + "js-yaml": "5.2.0", + "jsonc-parser": "3.3.1", + "jsonpointer": "5.0.1", + "markdown-it": "14.2.0", + "markdownlint": "0.41.0", + "markdownlint-cli2-formatter-default": "0.0.6", + "micromatch": "4.0.8", + "smol-toml": "1.7.0" + }, + "bin": { + "markdownlint-cli2": "markdownlint-cli2-bin.mjs" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/markdownlint-cli2-formatter-default": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.6.tgz", + "integrity": "sha512-VVDGKsq9sgzu378swJ0fcHfSicUnMxnL8gnLm/Q4J/xsNJ4e5bA6lvAz7PCzIl0/No0lHyaWdqVD2jotxOSFMQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + }, + "peerDependencies": { + "markdownlint-cli2": ">=0.0.4" + } + }, + "node_modules/markdownlint-cli2/node_modules/smol-toml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", + "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/mdurl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", + "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smol-toml": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.1.tgz", + "integrity": "sha512-PPlsspAZ4jbMBu5DMFhfUGDQLu/vrL4SyBROVS37x8ynnVmFIs1VPBz1Co8Xks3TvpIaZXmU85y4DrQ+UyVFoQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/string-width": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz", + "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..185f5f4 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "command-line-parser", + "private": true, + "devDependencies": { + "cspell": "10.0.1", + "dprint": "0.55.1", + "markdownlint-cli2": "0.23.0" + } +} diff --git a/properdocs.yml b/properdocs.yml new file mode 100644 index 0000000..0045503 --- /dev/null +++ b/properdocs.yml @@ -0,0 +1,132 @@ +# ProperDocs configuration for the CLI.NET Core documentation website (see docs/developer-manual/tooling/documentation-website.md); "docs_dir" points at +# the existing hand-written documentation tree instead of a dedicated "site" source folder, so this is the only configuration file the site needs +site_name: CLI.NET Core Documentation +site_description: A .NET command line application framework in the style of ASP.NET Core. +repo_url: https://github.com/lecode-official/command-line-parser +repo_name: lecode-official/command-line-parser +docs_dir: docs + +# Builds the site into "build/documentation-website/" instead of the default "site/", so the built website sits alongside the ".NET projects'" +# redirected build output under "build/" (see Architecture and Documentation Website); read dynamically off "config['site_dir']" by +# "copy-favicon-to-site-root.py" below, so nothing else needs to change to honor this +site_dir: build/documentation-website + +# All four "build/*.py" scripts live inside "docs_dir" itself, and "assets/templates/" holds a theme template rather than a documentation page +# (see docs/developer-manual/tooling/documentation-website.md), so all five are excluded here - otherwise ProperDocs would copy them into the +# built site as static assets, the same as it would any other file under "docs_dir" +exclude_docs: | + build/generate-api-docs.py + build/copy-favicon-to-site-root.py + build/rewrite-repo-links.py + build/inject-api-reference-link.py + assets/templates/ + +# All three are plain MkDocs "hooks" modules (not plugins - none needs an entry point, just an event function) (see +# docs/developer-manual/tooling/documentation-website.md): "copy-favicon-to-site-root.py" runs "on_post_build" to copy the built favicon to the +# site root, "rewrite-repo-links.py" runs "on_page_markdown" to rewrite links pointing outside "docs_dir" into absolute GitHub URLs, and +# "inject-api-reference-link.py" runs "on_page_markdown" to append the "API Reference" entry to the overview page's list, since "docs/api" does +# not exist to link to until "generate-api-docs.py" (wired in via "gen-files" below) has run +hooks: + - docs/build/copy-favicon-to-site-root.py + - docs/build/rewrite-repo-links.py + - docs/build/inject-api-reference-link.py + +theme: + name: material + + # "theme.favicon" writes a single "" from this one file, which is the fallback for every browser that does not honor + # "assets/templates/main.html"'s light/dark "" pair below, and also what "copy-favicon-to-site-root.py" copies to the site root for + # browsers and crawlers that request "/favicon.ico" directly (see docs/developer-manual/tooling/documentation-website.md) + favicon: assets/favicon/favicon.ico + + # "docs/assets/templates/main.html" (excluded from the built site above, like the two scripts) adds a light/dark favicon pair on top of the + # "favicon" fallback above (see docs/developer-manual/tooling/documentation-website.md); "custom_dir" itself is relative to this file, not to + # "docs_dir", unlike every other path on this page + custom_dir: docs/assets/templates + + # "theme.logo" is mkdocs-material's single, built-in header logo setting, and only ever a single file - it has no light/dark option, so this + # points at the light variant as the fallback; "assets/styles/site-logo.css" below swaps in the dark variant for the dark palette (see + # docs/developer-manual/tooling/documentation-website.md) + logo: assets/images/logo-light.png + + # Two palettes, each tied to a "prefers-color-scheme" media query, so the site opens in whichever scheme matches the visitor's OS setting instead + # of always defaulting to light; the "toggle" on each lets a visitor override that for the rest of the session + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to light mode + +# "gh-mode-only-images.css" teaches mkdocs-material to also honor the "#gh-dark-mode-only" / "#gh-light-mode-only" URL suffixes GitHub itself +# recognizes on an "" element, so the same Markdown image pair switches with both GitHub's and mkdocs-material's color scheme (see +# docs/developer-manual/tooling/documentation-website.md); paths here are relative to "docs_dir", like "theme.favicon" above. "site-logo.css" +# swaps "theme.logo" itself to its dark variant for the dark palette, the same color-scheme switch applied to a single "" instead of a pair +extra_css: + - assets/styles/gh-mode-only-images.css + - assets/styles/site-logo.css + +markdown_extensions: + - pymdownx.highlight + - pymdownx.superfences + + # Without this, Python-Markdown only recognizes a nested list item (as used by the table of contents in the various "README.md" files) when it is + # indented by a fixed 4 spaces, regardless of the width of the parent marker - a 3-character ordered marker like "1. " followed by only 3 spaces + # of indent (the natural, minimal alignment) renders as a second top-level list instead of nesting. "nested_indent: 3" makes indentation relative + # to the parent marker's width instead, so 3-space indent nests correctly under "1. " markers without reformatting any Markdown file + - mdx_truly_sane_lists: + nested_indent: 3 + +plugins: + - search + + # Generates the "api" section at build time from the C# XML documentation comments; nothing it produces is ever written to "docs/api" on disk + # (see docs/developer-manual/tooling/documentation-website.md) + - gen-files: + scripts: + - docs/build/generate-api-docs.py + + # Lets the "api" section's nav entry below (the "api/" line) point at a directory instead of a file; "generate-api-docs.py" writes that + # directory's "SUMMARY.md" itself (see docs/developer-manual/tooling/documentation-website.md), so the "api" section's nav is generated exactly + # like its pages are, without a ".pages" file anywhere. "nav_file" is left at its default, "SUMMARY.md", rather than renamed to "README.md" to + # match every other section's overview-page name: this plugin option is global, not per-directory, and it also makes literate-nav look for + # that file at "docs_dir"'s own root before it even reads "nav" below - since a hand-written "docs/README.md" already exists there, renaming + # this to "README.md" makes the plugin try to parse that hand-written prose page as a literate nav list instead of using the "nav" list below + - literate-nav + +# The one hand-maintained navigation list for the site, instead of a ".pages" file per directory (see +# docs/developer-manual/tooling/documentation-website.md); adding, removing, or reordering an article means editing this list, except the "api" +# entry below, whose own nav comes from the generated "api/README.md" (literate-nav) instead of being hand-listed here +nav: + - Overview: README.md + - User Manual: + - Overview: user-manual/README.md + - Building Applications: user-manual/building-applications.md + - Developer Manual: + - Overview: developer-manual/README.md + - Architecture: developer-manual/architecture.md + - Tooling: + - Overview: developer-manual/tooling/README.md + - Developer Setup: developer-manual/tooling/developer-setup.md + - Visual Studio Code Integration: developer-manual/tooling/vscode-integration.md + - Spell Checking (CSpell): developer-manual/tooling/spell-checking-cspell.md + - Markdown Linting (MarkdownLint): developer-manual/tooling/linting-markdownlint.md + - Code Formatting (dprint): developer-manual/tooling/code-formatting-dprint.md + - Documentation Website: developer-manual/tooling/documentation-website.md + - Continuous Integration: developer-manual/tooling/continuous-integration.md + - Testing and Code Coverage: developer-manual/tooling/testing-and-code-coverage.md + - Conventions: + - Overview: developer-manual/conventions/README.md + - C# Style: developer-manual/conventions/csharp-style.md + - Markdown Style: developer-manual/conventions/markdown-style.md + - Commit Messages: developer-manual/conventions/commit-messages.md + - File Naming Conventions: developer-manual/conventions/file-naming-conventions.md + - Dependency Management: developer-manual/conventions/dependency-management.md + - Testing: developer-manual/conventions/testing.md + - Contributing: developer-manual/contribution-guide.md + - API Reference: api/ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0c444a2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +# Python dependencies for building the documentation site with ProperDocs (see docs/developer-manual/tooling/documentation-website.md). +# Every version is pinned exactly, matching this project's policy of never using version ranges (see Dependency Management) - install with: +# python -m pip install --requirement requirements.txt + +properdocs==1.6.7 +mkdocs-material==9.7.7 +mkdocs-gen-files==0.6.1 +mkdocs-literate-nav==0.6.3 +mdx-truly-sane-lists==1.3 diff --git a/source/Directory.Build.props b/source/Directory.Build.props new file mode 100644 index 0000000..35a96ca --- /dev/null +++ b/source/Directory.Build.props @@ -0,0 +1,14 @@ + + + + $([System.IO.Path]::GetFileName('$(MSBuildProjectDirectory)')) + $(MSBuildThisFileDirectory)../build/$(ProjectFolderName)/bin/ + $(MSBuildThisFileDirectory)../build/$(ProjectFolderName)/obj/ + + diff --git a/source/clinet-core/CLI.NET Core.csproj b/source/clinet-core/CLI.NET Core.csproj index 7be1e6b..9c518ec 100644 --- a/source/clinet-core/CLI.NET Core.csproj +++ b/source/clinet-core/CLI.NET Core.csproj @@ -33,6 +33,15 @@ + + + diff --git a/source/clinet-core/packages.lock.json b/source/clinet-core/packages.lock.json index 2105cb9..ef2c967 100644 --- a/source/clinet-core/packages.lock.json +++ b/source/clinet-core/packages.lock.json @@ -32,6 +32,12 @@ "Microsoft.Extensions.Options": "10.0.0" } }, + "SauceControl.InheritDoc": { + "type": "Direct", + "requested": "[2.0.2, )", + "resolved": "2.0.2", + "contentHash": "QGassHKg6wfLZStucfxW4lsfVf2KOxHSEtrd0whP9R6PJSSSua3WPcOXxyVzRVqfmXV8dLQcp9oCnMYiJNhFaQ==" + }, "Microsoft.Extensions.Configuration": { "type": "Transitive", "resolved": "10.0.0", diff --git a/tests/linters/.cspell.json b/tests/linters/.cspell.json index 3831331..1fab277 100644 --- a/tests/linters/.cspell.json +++ b/tests/linters/.cspell.json @@ -31,8 +31,7 @@ "**/*.svg", // Build output and caches, which contain binaries and generated code that should not be spell-checked - "**/bin/", - "**/obj/", + "**/build/", ], // A list of dictionaries that should be added beyond the American English dictionary @@ -81,30 +80,45 @@ // A list of words that are not in the included default dictionary for American English "words": [ "Anson", + "autorefs", "clinet", + "clinetcore", "codezombiech", "Containerfile", + "coreclr", "docz", "dotnettools", + "extrahead", "globaltool", "Inkscape", + "lecode", "LINQ", "markdowntable", + "mkdocs", "Neumann", "parameterless", + "properdocs", + "publiccode", + "pycache", + "pymdownx", "reportgenerator", "reporttypes", + "rglob", "slnx", + "superfences", "Swellaby", "sysexits", "Takumi", "targetdir", "unreviewed", + "venv", "vsicons", "vsocde", "wayou", "wordmark", "wordmarks", + "xmldoc2markdown", + "xmldoc2md", "xunit", "yzhang", ], diff --git a/tests/unit-tests/Directory.Build.props b/tests/unit-tests/Directory.Build.props new file mode 100644 index 0000000..6c273c9 --- /dev/null +++ b/tests/unit-tests/Directory.Build.props @@ -0,0 +1,12 @@ + + + + $(MSBuildThisFileDirectory)../../build/unit-tests/bin/ + $(MSBuildThisFileDirectory)../../build/unit-tests/obj/ + +