📦 deps(thirdparty): update snapshots
This commit is contained in:
+201
@@ -0,0 +1,201 @@
|
||||
---
|
||||
name: hugo-to-markdown
|
||||
description: Convert Hugo documentation sites and Hugo-managed content into standard Markdown. Use when Agent needs to inspect a local Hugo repository, read hugo.toml or config files, content/, archetypes/, layouts/_shortcodes/, layouts/_markup/, and related docs content, then produce Markdown...
|
||||
risk: unknown
|
||||
source: https://github.com/chaunsin/agent-skills/tree/master/skills/hugo-to-markdown
|
||||
source_repo: chaunsin/agent-skills
|
||||
source_type: community
|
||||
date_added: 2026-07-01
|
||||
license: Apache-2.0
|
||||
license_source: https://github.com/chaunsin/agent-skills/blob/master/LICENSE
|
||||
---
|
||||
|
||||
# Hugo To Markdown
|
||||
## When to Use
|
||||
|
||||
Use this skill when you need convert Hugo documentation sites and Hugo-managed content into standard Markdown. Use when Agent needs to inspect a local Hugo repository, read hugo.toml or config files, content/, archetypes/, layouts/_shortcodes/, layouts/_markup/, and related docs content, then produce Markdown...
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
Use this skill when Markdown output must be derived from the local Hugo site, not guessed from generic Hugo knowledge. The conversion rules are the combination of Hugo's official behavior and the repository's own configuration, shortcode templates, render hooks, archetypes, and content conventions.
|
||||
|
||||
The target output is standard Markdown:
|
||||
|
||||
- Keep plain Markdown and YAML front matter.
|
||||
- Replace or materialize Hugo-only constructs.
|
||||
- Preserve meaning when exact rendering is not safely reproducible.
|
||||
- Prefer explicit Markdown text over live Hugo template syntax.
|
||||
- Distinguish literal Hugo syntax examples from active Hugo features before rewriting anything.
|
||||
|
||||
## Official Basis
|
||||
|
||||
Treat the repository's own Hugo configuration and templates as the primary ruleset. For any site under conversion, inspect these rule sources in the user's provided site root:
|
||||
|
||||
- `hugo.toml` (or `hugo.yaml`, `hugo.yml`, `hugo.json`, or `config/*`)
|
||||
- `archetypes/*`
|
||||
- `data/*`
|
||||
- `layouts/_shortcodes/*` or `layouts/shortcodes/*`
|
||||
- `layouts/_markup/*`
|
||||
- `content/**`
|
||||
|
||||
Also read any local docs that define shortcode, front matter, bundle, resource, and render-hook behavior.
|
||||
|
||||
Do not assume built-in Hugo defaults if the repository overrides them locally.
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Inventory the site before converting files
|
||||
|
||||
Always inspect the site-level rules first.
|
||||
|
||||
```bash
|
||||
python3 scripts/inventory_hugo_rules.py --site-root /path/to/hugo-site
|
||||
```
|
||||
|
||||
Example invocation for the user's site:
|
||||
|
||||
```bash
|
||||
python3 skills/hugo-to-markdown/scripts/inventory_hugo_rules.py \
|
||||
--site-root /path/to/your-hugo-site
|
||||
```
|
||||
|
||||
This inventory step is mandatory for batch work. It identifies:
|
||||
|
||||
- active config files
|
||||
- module mounts and content roots
|
||||
- custom shortcodes
|
||||
- custom render hooks
|
||||
- front matter keys seen in content
|
||||
- shortcode usage across content files
|
||||
|
||||
### 2. Convert with repository rules, not generic heuristics
|
||||
|
||||
Read `references/conversion-workflow.md` before changing files. Then:
|
||||
|
||||
1. Resolve the real content root from `hugo.toml`, `config.*`, and module mounts.
|
||||
2. Read archetypes to understand expected front matter shape.
|
||||
3. Read the front matter configuration to understand date aliases, fallback order, filename-derived dates, and other inferred metadata.
|
||||
4. Read site data sources in `data/` when shortcodes or partials pull structured content from them.
|
||||
5. Read custom shortcode templates in `layouts/_shortcodes/` or `layouts/shortcodes/`.
|
||||
6. Classify each encountered shortcode as embedded, custom, or inline, then check whether it uses named or positional arguments, block syntax, or self-closing syntax.
|
||||
7. Read render hooks in `layouts/_markup/`.
|
||||
8. Check whether the repo already defines Markdown- or JSON-facing export templates and partials; if it does, use those as evidence for how the site itself downgrades Hugo constructs.
|
||||
9. Follow `include`-style shortcodes into referenced content files when the docs site composes content from shared fragments.
|
||||
10. Convert one file or one coherent section at a time.
|
||||
|
||||
### 3. Preserve semantics during conversion
|
||||
|
||||
Use these rules by default:
|
||||
|
||||
- Keep YAML front matter unless the user explicitly asks for front-matter-free Markdown.
|
||||
- Preserve core fields such as `title`, `description`, `date`, `draft`, `aliases`, `slug`, `url`, `weight`, and nested `params` when they still carry meaning.
|
||||
- Preserve `publishDate`, `lastmod`, `expiryDate`, and page resource metadata when they still affect meaning or downstream routing.
|
||||
- Normalize reserved Hugo front matter keys to their canonical names when the repo mixes casing, for example `Title` to `title`, `Description` to `description`, and `LinkTitle` to `linkTitle`.
|
||||
- Account for Hugo front matter aliases and tokens before deciding a field is unused. The official Hugo docs recognize aliases such as `pubdate`, `published`, `modified`, and `unpublishdate`, plus tokens such as `:default`, `:filename`, `:fileModTime`, and `:git`.
|
||||
- Convert Hugo internal links to normal Markdown links with resolved destinations.
|
||||
- Replace Hugo shortcodes with plain Markdown, HTML, or explicit notes only after reading the local shortcode implementation.
|
||||
- Preserve or materialize shortcode arguments according to the shortcode's real calling convention. Do not assume every shortcode is named-argument, self-closing, or block-capable.
|
||||
- Materialize dynamically generated lists and tables when the shortcode renders content from sections or data files.
|
||||
- Leave literal Hugo examples unchanged when the document is documenting Hugo syntax rather than invoking it. This applies both inside fenced code blocks and to escaped forms such as `{{</* foo */>}}` or `{{%/* foo */%}}` that appear in prose, tables, or notation examples.
|
||||
- Preserve block attribute semantics such as `{.class #id}` and code-fence attributes when the destination Markdown flavor supports them. If not, downgrade explicitly instead of silently dropping them.
|
||||
|
||||
### 4. Apply Hugo-specific body rules carefully
|
||||
|
||||
Many Hugo documentation sites use complex local behaviors. Be alert for these common patterns:
|
||||
|
||||
- `hugo.toml` mounts `content/en` to the logical `content` root, so link and include resolution must use Hugo logical paths instead of preserving `/en/` blindly.
|
||||
- The docs basis depends on Hugo front matter configuration for date resolution, aliases, and filename-derived metadata; read `configuration/front-matter.md` and `[frontmatter]` in `hugo.toml` before normalizing dates or slugs.
|
||||
- `include` renders another page through `RenderShortcodes`; follow the referenced content file and inline the resulting Markdown.
|
||||
- `quick-reference`, `render-list-of-pages-in-section`, and `render-table-of-pages-in-section` generate navigation content from sections; replace them with materialized Markdown lists or tables.
|
||||
- `glossary-term`, `glossary`, `get-page-desc`, `module-mounts-note`, `new-in`, and `deprecated-in` expand to prose or badges; convert them into explicit Markdown text or callouts.
|
||||
- `code-toggle` may read config snippets and data-backed examples; preserve the underlying code sample, not the UI toggle.
|
||||
- `datatable`, `per-lang-config-keys`, `root-configuration-keys`, `syntax-highlighting-styles`, `chroma-lexers`, `newtemplatesystem`, and `hl` are also local shortcodes; inspect their implementations before deciding whether to materialize, flatten, or downgrade.
|
||||
- if the repo has data-backed or example-extraction shortcodes such as `features-table`, `optional-features-table`, `clients-example`, or `jupyter-example`, inspect the referenced `data/` files, local example sources, and Markdown-export partials before deciding whether to materialize or downgrade.
|
||||
- glossary links can use the special Markdown destination `(g)`; resolve these to stable glossary links instead of leaving the placeholder.
|
||||
- `img` and `imgproc` are presentation helpers around page, global, or remote resources; preserve the underlying image reference and caption semantics.
|
||||
- `eturl` emits links to embedded template sources; convert to a normal Markdown link if the destination is known, otherwise preserve as a textual note.
|
||||
- the local link render hook resolves destinations in this order: content page, page resource, section resource when the page is not a leaf bundle, then global resource. It also validates fragments and glossary shorthand.
|
||||
- blockquote and code-block render hooks add alert, file-label, summary, and detail semantics; preserve these semantics in Markdown or explicit notes.
|
||||
- embedded `ref` and `relref` are obsolete for Markdown in modern Hugo docs and can interact poorly with the custom link render hook; resolve the final destination instead of preserving the shortcode.
|
||||
- the local docs use Markdown attributes and code-fence options that can change rendered output. Keep these semantics when the destination flavor supports them.
|
||||
|
||||
Read `references/shortcodes-and-render-hooks.md` before converting any file that contains Hugo syntax.
|
||||
|
||||
### 5. Validate the output
|
||||
|
||||
After conversion, scan the generated Markdown for leftover Hugo-only syntax.
|
||||
|
||||
```bash
|
||||
python3 skills/hugo-to-markdown/scripts/check_standard_markdown.py \
|
||||
--root /path/to/output
|
||||
```
|
||||
|
||||
If the validator reports active Hugo syntax outside code fences, either:
|
||||
|
||||
- resolve it fully, or
|
||||
- replace it with a safe textual explanation
|
||||
|
||||
Do not silently ship unresolved `{{< ... >}}`, `{{% ... %}}`, or Go template expressions.
|
||||
|
||||
### 6. Downgrade explicitly when full materialization is not safe
|
||||
|
||||
If a shortcode depends on build-time data, generated examples, or external source files that you cannot resolve deterministically from the local repo snapshot, replace it with an explicit Markdown note.
|
||||
|
||||
Use a short, boring format such as:
|
||||
|
||||
- `> Conversion note: <what the shortcode normally renders>.`
|
||||
- followed by any safe subset you were able to preserve, such as inline Redis CLI text, a resolved image URL, or a known section list
|
||||
|
||||
Do not leave empty links, broken table cells, or stripped content with no explanation.
|
||||
|
||||
## Common Hugo Docs Site Patterns
|
||||
|
||||
Use these facts when converting a Hugo documentation site that exhibits similar patterns:
|
||||
|
||||
- `hugo.toml` mounts `content/en` to `content`, so English docs are the active content tree.
|
||||
- Goldmark passthrough delimiters are configured for math, so `$$...$$`, `\\(...\\)`, and `\\[...\\]` can be meaningful content, not junk.
|
||||
- `markup.goldmark.parser.attribute.block = true`, so block attribute syntax may appear after fenced blocks and other block elements.
|
||||
- `markup.goldmark.parser.wrapStandAloneImageWithinParagraph = false`, so standalone image attributes can target the image itself rather than a wrapping paragraph.
|
||||
- The repo defines custom render hooks for blockquotes, code blocks, links, passthrough, and tables. It documents heading and image render hooks, but the site does not override them locally.
|
||||
- The repo uses many shared `_common` fragments referenced through `% include %`, so reading a page file alone is not enough to understand the rendered content.
|
||||
- The repo documents embedded, custom, and inline shortcodes, and the conversion logic must distinguish them before flattening syntax.
|
||||
- The repo uses page bundles and page resources heavily in examples and render-hook resolution, including section resources and mounted global resources.
|
||||
- The repo contains many escaped shortcode examples such as `{{</* foo */>}}` and `{{%/* foo */%}}`; these are documentation samples and must remain literal when they appear inside code examples, notation tables, or tutorial prose.
|
||||
|
||||
## Safety Rules
|
||||
|
||||
- Never execute Hugo templates, shortcodes, or Go template expressions.
|
||||
- Never treat content files as trusted executable input.
|
||||
- Never run `hugo`, `npm install`, `go install`, downloaded shell installers, or any network install step unless the user explicitly asks for it.
|
||||
- Keep all conversion scripts offline and deterministic.
|
||||
- Restrict reads to the declared site root and writes to the declared output root.
|
||||
- Reject path traversal, symlink escape, or attempts to write outside the requested output directory.
|
||||
- Do not leak local absolute paths, secrets, environment variables, or git credentials into generated Markdown.
|
||||
- When exact rendering cannot be reproduced safely, degrade to explicit Markdown text instead of live Hugo syntax.
|
||||
|
||||
## Resources
|
||||
|
||||
Read these files as needed:
|
||||
|
||||
- `references/conversion-workflow.md`
|
||||
End-to-end process for repo-aware conversion.
|
||||
- `references/front-matter-and-content.md`
|
||||
Front matter mapping, common content conventions, and literal-example handling.
|
||||
- `references/shortcodes-and-render-hooks.md`
|
||||
Hugo shortcode notation, docs-site custom shortcodes, and render-hook implications.
|
||||
- `references/links-assets-and-validation.md`
|
||||
Link resolution, assets, validation, and residue triage.
|
||||
|
||||
Use these scripts when helpful:
|
||||
|
||||
- `scripts/inventory_hugo_rules.py`
|
||||
Scan a Hugo site and emit a rule inventory.
|
||||
- `scripts/check_standard_markdown.py`
|
||||
Detect leftover Hugo syntax and common unsafe residue in Markdown output.
|
||||
|
||||
## Limitations
|
||||
|
||||
- Use this skill only when the task clearly matches its upstream source and local project context.
|
||||
- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
|
||||
- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Hugo To Markdown"
|
||||
short_description: "Convert Hugo docs into standard Markdown safely."
|
||||
default_prompt: "Use $hugo-to-markdown to convert the Hugo docs under a local site into standard Markdown files."
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
# Conversion Workflow
|
||||
|
||||
## Purpose
|
||||
|
||||
Use this workflow when converting a Hugo documentation site into standard Markdown that no longer depends on Hugo runtime features.
|
||||
|
||||
## Step 1: Locate the real rule sources
|
||||
|
||||
Read these in order:
|
||||
|
||||
1. `hugo.toml`, `hugo.yaml`, `hugo.yml`, `hugo.json`, or `config/*`
|
||||
2. `archetypes/*`
|
||||
3. `data/*`
|
||||
4. official or local docs that define shortcode, front matter, bundle, resource, and render-hook behavior
|
||||
5. `layouts/_shortcodes/*` or `layouts/shortcodes/*`
|
||||
6. `layouts/_markup/*`
|
||||
7. Markdown- or JSON-facing export templates and partials such as `layouts/_default/*.md`, `layouts/_default/*.json`, or `layouts/partials/markdown-*.html`
|
||||
8. `content/*`
|
||||
|
||||
## Step 2: Build a site inventory
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 skills/hugo-to-markdown/scripts/inventory_hugo_rules.py \
|
||||
--site-root /path/to/your-hugo-site
|
||||
```
|
||||
|
||||
Inspect the output for:
|
||||
|
||||
- content root and module mounts
|
||||
- active shortcode names
|
||||
- render hook names
|
||||
- frequently used shortcodes
|
||||
- front matter keys
|
||||
- front matter alias or token usage that changes visible dates or slugs
|
||||
- whether shortcode usage clusters around content graph expansion, section listings, data-backed tables, or external example extraction
|
||||
|
||||
Use the inventory to batch files by complexity:
|
||||
|
||||
- plain Markdown only
|
||||
- front matter only
|
||||
- literal Hugo documentation examples
|
||||
- pages with Markdown attributes or code-fence options that must be preserved
|
||||
- built-in shortcode usage
|
||||
- content-graph shortcodes such as `include`, `embed-md`, `glossary-term`, and `table-children`
|
||||
- custom shortcode usage
|
||||
- data-backed shortcode usage
|
||||
- render-hook-sensitive links and assets
|
||||
|
||||
## Step 3: Convert one slice at a time
|
||||
|
||||
Preferred order:
|
||||
|
||||
1. plain pages
|
||||
2. pages with only front matter normalization
|
||||
3. pages that mostly document Hugo syntax and contain literal shortcode examples
|
||||
4. pages using shared includes
|
||||
5. pages using custom shortcodes
|
||||
6. pages whose content is partially generated from sections or data files
|
||||
7. pages whose content depends on generated code examples or external local sources
|
||||
|
||||
This keeps regressions local and makes validation easier.
|
||||
|
||||
## Step 4: Materialize dynamic content
|
||||
|
||||
If a shortcode generates prose, lists, tables, or badges, replace it with the resulting Markdown.
|
||||
|
||||
Examples from the Hugo docs site:
|
||||
|
||||
- `include` pulls another content file and renders its shortcodes
|
||||
- `quick-reference` expands section content
|
||||
- `render-list-of-pages-in-section` builds a list from a section
|
||||
- `render-table-of-pages-in-section` builds a table from section pages
|
||||
- `glossary` materializes glossary content
|
||||
|
||||
Do not keep these as live Hugo shortcodes in the final standard Markdown.
|
||||
|
||||
When evaluating a shortcode, classify it first:
|
||||
|
||||
1. Static wrapper around inner Markdown or a simple asset
|
||||
2. Content graph expansion into other pages or sections
|
||||
3. Data-backed expansion using `data/*`
|
||||
4. Generated example extraction from files outside the current page
|
||||
|
||||
This classification determines whether you can materialize the output directly, need recursive page resolution, need data-file reads, or must degrade to an explicit note.
|
||||
|
||||
Also determine whether the shortcode is:
|
||||
|
||||
- embedded, custom, or inline
|
||||
- block, self-closing, or dual-form
|
||||
- named-argument, positional-argument, or dual-mode
|
||||
|
||||
These choices affect how you parse the call and how much of the surrounding Markdown Hugo would have rendered.
|
||||
|
||||
## Step 5: Keep literal Hugo examples literal
|
||||
|
||||
The docs site frequently documents Hugo syntax itself. Distinguish:
|
||||
|
||||
- live shortcode calls that affect rendering
|
||||
- escaped shortcode examples intended for readers
|
||||
|
||||
Common literal-example pattern:
|
||||
|
||||
```text
|
||||
{{</* shortcode arg=value */>}}
|
||||
```
|
||||
|
||||
When the construct is inside a fenced code block or otherwise clearly documentation, preserve it literally.
|
||||
|
||||
Also preserve escaped forms such as these when they appear in prose or tables:
|
||||
|
||||
```text
|
||||
{{%/* foo */%}}
|
||||
{{</* foo */>}}
|
||||
```
|
||||
|
||||
Do not strip them just because they match a loose shortcode regex.
|
||||
|
||||
## Step 6: Normalize front matter before building derived content
|
||||
|
||||
Before using front matter to populate generated tables or lists:
|
||||
|
||||
- map reserved keys case-insensitively, for example `Title` to `title`
|
||||
- treat `linkTitle` and `LinkTitle` as the same logical field
|
||||
- account for aliases such as `publishdate` or `modified`
|
||||
- account for front matter tokens such as `:filename` and `:fileModTime` when deciding whether metadata is derived
|
||||
- preserve unknown custom keys as-is
|
||||
|
||||
This prevents empty links and missing descriptions when a repo mixes Hugo key casing conventions.
|
||||
|
||||
## Step 7: Validate aggressively
|
||||
|
||||
After each batch:
|
||||
|
||||
```bash
|
||||
python3 skills/hugo-to-markdown/scripts/check_standard_markdown.py \
|
||||
--root /path/to/output
|
||||
```
|
||||
|
||||
Treat validator hits as unresolved work unless they are deliberate examples inside code fences.
|
||||
|
||||
If you intentionally downgraded a shortcode to an explanatory note, that note should remain in the output and the original shortcode should not.
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
# Front Matter And Content Rules
|
||||
|
||||
## Front Matter Policy
|
||||
|
||||
Default to YAML front matter in the output unless the user explicitly asks to strip metadata.
|
||||
|
||||
Preserve fields when they still carry meaning in the destination:
|
||||
|
||||
- `title`
|
||||
- `linkTitle`
|
||||
- `description`
|
||||
- `date`
|
||||
- `publishDate`
|
||||
- `lastmod`
|
||||
- `expiryDate`
|
||||
- `draft`
|
||||
- `aliases`
|
||||
- `slug`
|
||||
- `url`
|
||||
- `weight`
|
||||
- `categories`
|
||||
- `keywords`
|
||||
- `params`
|
||||
- `menus` or menu-like data
|
||||
|
||||
Do not invent fields that were not present.
|
||||
|
||||
If the repository mixes casing for reserved Hugo fields, normalize to the documented canonical form in the output:
|
||||
|
||||
- `Title` -> `title`
|
||||
- `Description` -> `description`
|
||||
- `LinkTitle` -> `linkTitle`
|
||||
|
||||
Apply this normalization before using front matter to build lists, tables, or link labels. Preserve unknown custom keys with their original spelling unless the user asks for a schema rewrite.
|
||||
|
||||
## Effective Value Rules
|
||||
|
||||
Do not treat front matter as a flat key-value copy problem. In Hugo, some visible values are inferred from configuration, aliases, filenames, or Git metadata.
|
||||
|
||||
For the source site, check any site-specific front matter documentation and the local `[frontmatter]` config before deciding which value is authoritative.
|
||||
|
||||
Important alias rules:
|
||||
|
||||
- `publishDate` can come from `publishdate`, `pubdate`, or `published`
|
||||
- `lastmod` can come from `lastmod` or `modified`
|
||||
- `expiryDate` can come from `expirydate` or `unpublishdate`
|
||||
|
||||
Important token rules:
|
||||
|
||||
- `:default` means Hugo falls back through the documented default date sequence
|
||||
- `:filename` can derive `date` and sometimes `slug` from a date-prefixed filename
|
||||
- `:fileModTime` can supply a date from the file modification time
|
||||
- `:git` can supply a date from Git history when enabled
|
||||
|
||||
Conversion guidance:
|
||||
|
||||
- Preserve explicit source fields when they are already concrete and meaningful.
|
||||
- If the repository relies on alias or token resolution, do not mistakenly drop related metadata just because the canonical key is absent.
|
||||
- If you can deterministically resolve a derived date or slug from the local snapshot and the user wants flattened output, materialize it explicitly.
|
||||
- If deterministic resolution would require Git state, build execution, or missing metadata, keep the source field plus a short conversion note instead of guessing.
|
||||
|
||||
## Resources Metadata
|
||||
|
||||
Front matter may also describe page resources, not just page metadata.
|
||||
|
||||
Preserve `resources` metadata when it affects:
|
||||
|
||||
- image or file labels
|
||||
- resource lookup by `Name`
|
||||
- titles used in generated link text
|
||||
- custom `params`
|
||||
- wildcard-driven assignments
|
||||
|
||||
In Hugo's page resource rules, matching order matters and `name` and `title` can use the `:counter` placeholder. Do not simplify these structures away unless the destination explicitly does not need them.
|
||||
|
||||
## Archetype Signal
|
||||
|
||||
Read archetypes before normalizing front matter. In a typical Hugo docs site:
|
||||
|
||||
- `archetypes/default.md` establishes the common fields
|
||||
- `archetypes/functions.md` adds nested `params.functions_and_methods`
|
||||
- `archetypes/methods.md` follows the same method metadata pattern
|
||||
- `archetypes/glossary.md` and `archetypes/news.md` introduce content-type-specific fields
|
||||
|
||||
When an archetype or local content convention introduces nested fields, preserve the shape unless the user asks for a simplified schema.
|
||||
|
||||
## Content Composition Rules
|
||||
|
||||
The page file is not always the full source of truth. Also check:
|
||||
|
||||
- shared `_common` content fragments
|
||||
- shortcode-generated prose
|
||||
- data-backed shortcode inputs from `data/*`
|
||||
- generated example source files referenced by local shortcodes
|
||||
- render-hook-driven link behavior
|
||||
- page bundle resources and section resources
|
||||
- page resource metadata from the page's own front matter
|
||||
- front matter configuration that changes how dates, slugs, and publish status are derived
|
||||
|
||||
## Literal Examples Versus Live Syntax
|
||||
|
||||
Preserve literal Hugo examples when they are:
|
||||
|
||||
- inside fenced code blocks
|
||||
- clearly escaped with comment markers such as `/* ... */`
|
||||
- part of tutorial prose explaining Hugo syntax
|
||||
|
||||
Important escaped forms to preserve:
|
||||
|
||||
- `{{</* foo */>}}`
|
||||
- `{{%/* foo */%}}`
|
||||
- notation examples that compare `%` and `<` shortcode forms in tables or inline code
|
||||
|
||||
Do not treat those as active shortcode invocations merely because they are outside fenced code blocks.
|
||||
|
||||
Convert live syntax when it changes the rendered page.
|
||||
|
||||
## Explicit Downgrade Policy
|
||||
|
||||
When a local shortcode cannot be materialized safely:
|
||||
|
||||
- remove the live Hugo syntax
|
||||
- replace it with a short Markdown explanation
|
||||
- keep any safe subset of content that is already visible in the page source
|
||||
|
||||
Examples:
|
||||
|
||||
- preserve inline Redis CLI text even if a multi-language tabset cannot be rebuilt
|
||||
- preserve a resolved image URL even if the surrounding presentation wrapper is custom
|
||||
- preserve section purpose and emit a note if a build-time data table cannot be reconstructed
|
||||
|
||||
## Markdown Features To Preserve
|
||||
|
||||
Keep these when possible:
|
||||
|
||||
- fenced code blocks
|
||||
- tables
|
||||
- blockquotes
|
||||
- definition lists if the destination Markdown flavor supports them
|
||||
- math passthrough delimiters when the destination supports math
|
||||
- block attributes only when the destination flavor supports them
|
||||
- heading attributes such as `## Heading {#id .class}`
|
||||
- code fence attributes and highlighting options when the destination flavor supports them
|
||||
|
||||
If the destination does not support a feature, downgrade explicitly instead of dropping content.
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
# Links, Assets, And Validation
|
||||
|
||||
## Link Resolution
|
||||
|
||||
Treat link resolution as repository-specific behavior.
|
||||
|
||||
For a typical Hugo docs site:
|
||||
|
||||
- internal links may be plain Markdown destinations
|
||||
- `ref` and `relref` appear in docs examples and sometimes in live content
|
||||
- the custom link render hook resolves pages, page resources, section resources, and global resources
|
||||
- broken-link behavior is controlled by config and local render-hook logic
|
||||
- glossary shorthand may appear as a Markdown destination exactly equal to `(g)`
|
||||
- fragments may be validated against target headings rather than passed through blindly
|
||||
|
||||
### Resolution Order
|
||||
|
||||
For a custom `render-link.html`, the typical resolution order is:
|
||||
|
||||
1. content page
|
||||
2. page resource from the current page bundle
|
||||
3. section resource from the current section when the page is not a leaf bundle
|
||||
4. global resource from `assets`
|
||||
|
||||
Implications for conversion:
|
||||
|
||||
- do not assume every relative link targets a page
|
||||
- do not treat page bundle files and section bundle files as interchangeable
|
||||
- do not preserve `/en/` filesystem paths when the site mounts `content/en` to logical `content`
|
||||
- preserve query strings and fragments when they still resolve after flattening
|
||||
- if fragment validity cannot be confirmed from the local snapshot, keep the fragment but add a note only when there is real ambiguity
|
||||
|
||||
When converting:
|
||||
|
||||
- resolve internal destinations to ordinary Markdown links
|
||||
- keep remote links as normal external links
|
||||
- preserve fragments when they still point to stable headings
|
||||
- avoid copying unresolved Hugo link functions into the output
|
||||
- if a generated table or list uses front matter fields for labels, resolve those fields case-insensitively before emitting the final Markdown
|
||||
|
||||
## Assets
|
||||
|
||||
Check all of these before rewriting image or file references:
|
||||
|
||||
- page bundle resources
|
||||
- section resources
|
||||
- mounted assets
|
||||
- static files
|
||||
|
||||
For Hugo repositories, also distinguish:
|
||||
|
||||
- leaf bundles versus branch bundles
|
||||
- page resources of type `page` versus image, data, document, or video resources
|
||||
- resource metadata defined in front matter under `resources`
|
||||
|
||||
For sites with custom link render hooks, the hook may explicitly rely on assets and mounted resources. Read `hugo.toml` and `layouts/_markup/render-link.html` before changing asset paths.
|
||||
|
||||
### Bundle-Aware Rules
|
||||
|
||||
Use the official page bundle and page resource docs as conversion constraints:
|
||||
|
||||
- files next to `index.md` in a leaf bundle can be page resources and may not be rendered as standalone pages
|
||||
- files under a branch bundle can be descendant content pages or non-page resources depending on placement
|
||||
- section resource lookup is invalid for leaf bundles in the local render-link logic
|
||||
- resource `Name`, `Title`, and `params` can come from front matter metadata rather than filename alone
|
||||
|
||||
If a shortcode or render hook references a resource, check whether the destination depends on bundle type before flattening the path.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 skills/hugo-to-markdown/scripts/check_standard_markdown.py \
|
||||
--root /path/to/output
|
||||
```
|
||||
|
||||
Review hits for:
|
||||
|
||||
- active `{{< ... >}}` or `{{% ... %}}`
|
||||
- active Go template expressions such as `{{ if ... }}` or `{{ .Page ... }}`
|
||||
- Hugo-specific link helpers left in prose
|
||||
- leaked local absolute paths
|
||||
- executable HTML or script residue that should have been downgraded
|
||||
- empty Markdown links or table cells caused by front matter key mismatches
|
||||
- missing downgrade notes where content was stripped but not materialized
|
||||
|
||||
## Residue Triage
|
||||
|
||||
If the validator reports a construct:
|
||||
|
||||
1. Check whether it is inside a fenced code block.
|
||||
2. Check whether it is an escaped literal example such as `{{</* foo */>}}` or `{{%/* foo */%}}` in prose or a notation table.
|
||||
3. If it is a literal example, keep it.
|
||||
4. If it is active Hugo syntax, resolve or rewrite it.
|
||||
5. If it cannot be resolved safely, replace it with explicit Markdown text explaining the original behavior.
|
||||
|
||||
## Downgrade Review
|
||||
|
||||
After conversion, manually inspect each explanatory note you introduced:
|
||||
|
||||
- verify that the original shortcode syntax is gone
|
||||
- verify that the note still tells the reader what was omitted
|
||||
- verify that any safe subset, such as inline code, resolved links, or image URLs, was preserved
|
||||
|
||||
The goal is standard Markdown with explicit loss reporting, not silent truncation.
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
# Shortcodes And Render Hooks
|
||||
|
||||
## Shortcode Notation
|
||||
|
||||
Hugo has two shortcode notations:
|
||||
|
||||
- `{{< ... >}}`
|
||||
- `{{% ... %}}`
|
||||
|
||||
Use Hugo's rule, documented in the official shortcode pages:
|
||||
|
||||
- `%` notation is rendered before Markdown
|
||||
- `<` notation is rendered after Markdown
|
||||
|
||||
For conversion, do not preserve this live syntax in the final standard Markdown unless the document is explicitly teaching Hugo syntax.
|
||||
|
||||
## Shortcode Calling Rules
|
||||
|
||||
Before rewriting a shortcode, determine all of these:
|
||||
|
||||
1. embedded, custom, or inline
|
||||
2. opening/closing block form, self-closing form, or both
|
||||
3. named arguments, positional arguments, or both
|
||||
4. whether mixed named and positional arguments are forbidden
|
||||
5. whether the shortcode must be called with `%` notation or `<` notation
|
||||
|
||||
These are not cosmetic details. They can change visible output, table-of-contents behavior, and whether inner Markdown is rendered at all.
|
||||
|
||||
Important Hugo rules from the official shortcode docs:
|
||||
|
||||
- inline shortcodes are a separate feature and are disabled unless explicitly enabled
|
||||
- some shortcodes require body content, some forbid it, and some support both forms
|
||||
- named arguments are case-sensitive
|
||||
- named and positional arguments cannot be mixed within one shortcode call
|
||||
- multiline arguments and raw string literals are valid shortcode syntax
|
||||
- nested shortcodes are allowed except for inline shortcodes
|
||||
|
||||
## First Classify The Shortcode
|
||||
|
||||
Before rewriting any shortcode, classify it into one of these groups:
|
||||
|
||||
1. Literal documentation example
|
||||
2. Static wrapper around local content or assets
|
||||
3. Content-graph expander
|
||||
4. Data-backed renderer
|
||||
5. External example extractor
|
||||
|
||||
This classification should drive the conversion strategy:
|
||||
|
||||
- Literal documentation example: preserve literally
|
||||
- Static wrapper: replace with normal Markdown or HTML
|
||||
- Content-graph expander: recursively resolve local pages or sections
|
||||
- Data-backed renderer: read the referenced `data/*` or local metadata source
|
||||
- External example extractor: inspect the referenced local example files, or downgrade with an explicit note if deterministic reconstruction is not possible
|
||||
|
||||
## Docs-Site Custom Shortcodes
|
||||
|
||||
A typical Hugo docs site may define custom shortcodes such as these in `layouts/_shortcodes/`:
|
||||
|
||||
- `code-toggle`
|
||||
- `datatable`
|
||||
- `deprecated-in`
|
||||
- `eturl`
|
||||
- `get-page-desc`
|
||||
- `glossary`
|
||||
- `glossary-term`
|
||||
- `hl`
|
||||
- `img`
|
||||
- `imgproc`
|
||||
- `include`
|
||||
- `module-mounts-note`
|
||||
- `new-in`
|
||||
- `newtemplatesystem`
|
||||
- `per-lang-config-keys`
|
||||
- `quick-reference`
|
||||
- `render-list-of-pages-in-section`
|
||||
- `render-table-of-pages-in-section`
|
||||
- `root-configuration-keys`
|
||||
- `syntax-highlighting-styles`
|
||||
|
||||
Read the local template file before deciding the replacement.
|
||||
|
||||
If the repository already contains Markdown-export partials or AI-facing templates, use them as evidence for how the site itself flattens these constructs. Do not copy them blindly without understanding which shortcodes they intentionally expand and which they intentionally leave alone.
|
||||
|
||||
## High-Impact Local Rules
|
||||
|
||||
### Logical content paths
|
||||
|
||||
Some Hugo docs sites mount a language subdirectory such as `content/en` to the logical `content` root. Resolve links and `include` targets against logical paths rather than filesystem paths that retain the language prefix.
|
||||
|
||||
### `include`
|
||||
|
||||
`include` renders another content page through `RenderShortcodes`. This means:
|
||||
|
||||
- the included file may contain more shortcodes
|
||||
- the included file is part of the final visible content
|
||||
- conversion must recursively resolve the referenced file
|
||||
- the included file should contribute body content, not duplicate front matter
|
||||
|
||||
### `quick-reference`
|
||||
|
||||
This shortcode renders sections and child pages dynamically. Replace it with a materialized Markdown structure.
|
||||
|
||||
### `render-list-of-pages-in-section`
|
||||
|
||||
This shortcode builds lists from a section path. Replace it with normal Markdown lists and descriptions.
|
||||
|
||||
### `render-table-of-pages-in-section`
|
||||
|
||||
This shortcode builds tables from a section path and filters. Replace it with a standard Markdown table if practical, otherwise a clear list.
|
||||
|
||||
### `glossary-term` and `glossary`
|
||||
|
||||
These inject glossary links or glossary content. Preserve the resulting prose or links, not the shortcode syntax.
|
||||
|
||||
### `new-in` and `deprecated-in`
|
||||
|
||||
Convert these into plain Markdown callouts or inline labels such as:
|
||||
|
||||
- `New in Hugo 0.144.0.`
|
||||
- `Deprecated in Hugo 0.144.0.`
|
||||
|
||||
### `code-toggle`
|
||||
|
||||
Preserve the underlying example content as fenced code, not the UI toggle mechanism.
|
||||
|
||||
Common parameter patterns to watch for:
|
||||
|
||||
- `file=hugo` or similar parameters indicate repository-style config examples
|
||||
- `fm=true` means the emitted example includes front matter semantics
|
||||
- `config=` and `dataKey=` style usage can pull data-backed snippets, so read local data files before flattening the shortcode
|
||||
|
||||
If the required data source is not locally obvious or requires remarshal logic that you cannot reproduce safely, replace the shortcode with:
|
||||
|
||||
- the visible inline sample when one exists, or
|
||||
- a short note explaining that the repository builds multiple code variants from data at render time
|
||||
|
||||
### `datatable`
|
||||
|
||||
This shortcode renders a table from `hugo.Data.docs`. Materialize the table from local data when the selected package, list, and field set are clear.
|
||||
|
||||
### `per-lang-config-keys` and `root-configuration-keys`
|
||||
|
||||
These shortcodes summarize configuration metadata. Treat them as data-backed expanders rather than simple badges or links.
|
||||
|
||||
### `syntax-highlighting-styles` and `chroma-lexers`
|
||||
|
||||
These shortcodes materialize large generated lists from local data or template logic. Prefer explicit Markdown tables or lists when practical, otherwise downgrade with a clear note describing the omitted generated gallery.
|
||||
|
||||
### `newtemplatesystem` and `hl`
|
||||
|
||||
These are local presentation helpers. Inspect whether they emit prose, badges, or inline highlighted code before deciding the downgrade format.
|
||||
|
||||
## Embedded Shortcodes
|
||||
|
||||
The official Hugo docs also document embedded shortcodes. Even when the source site mostly uses custom shortcodes, treat embedded shortcodes as first-class conversion cases because other Hugo repositories often rely on them directly.
|
||||
|
||||
High-value embedded shortcode guidance:
|
||||
|
||||
- `details`: convert to a Markdown callout or HTML `<details>` block while preserving the summary text and body content
|
||||
- `figure`: preserve the image destination, alt text, caption, title, and attribution semantics; plain Markdown image plus surrounding caption text is usually safer than keeping shortcode syntax
|
||||
- `highlight`: convert to fenced code when the rendered result is a code sample; preserve inline highlighting as inline code or HTML only when necessary
|
||||
- `param`: resolve to the referenced site parameter if it is locally knowable, otherwise replace with a conversion note
|
||||
- `qr`: preserve the encoded text and add a note or image link only if the generated asset is locally resolvable
|
||||
- `ref` and `relref`: replace with the final resolved Markdown destination, not the shortcode itself
|
||||
- `youtube`, `vimeo`, `instagram`, and `x`: convert to stable normal links or embeds only if the destination is explicit and safe
|
||||
|
||||
If a repository overrides an embedded shortcode in `layouts/_shortcodes`, treat the local override as authoritative.
|
||||
|
||||
## Inline Shortcodes
|
||||
|
||||
Inline shortcodes are rare but important because they can define executable template logic inside content.
|
||||
|
||||
Conversion rules:
|
||||
|
||||
- If the page is documenting inline shortcode syntax, preserve the example literally.
|
||||
- If the page is actually using an inline shortcode and the rendered text is locally obvious, preserve the rendered text rather than the template body.
|
||||
- If the rendered value depends on runtime state such as `now`, environment variables, or build context, replace it with an explicit note instead of guessing.
|
||||
|
||||
### `glossary-term` and glossary links
|
||||
|
||||
Some Hugo docs sites use glossary shortcuts in two forms:
|
||||
|
||||
- `glossary-term` shortcode usage
|
||||
- Markdown links whose destination is exactly `(g)`
|
||||
|
||||
Both should become explicit Markdown links or explicit glossary labels in the output.
|
||||
|
||||
### `ref` and `relref`
|
||||
|
||||
When these appear as live shortcode calls rather than literal documentation examples:
|
||||
|
||||
- resolve them to the final destination
|
||||
- preserve query strings and fragments when they are valid
|
||||
- do not emit `ref` or `relref` literally in the final Markdown
|
||||
|
||||
In many modern Hugo docs sites, Markdown pages generally prefer render-hook-based destination resolution instead of these shortcodes.
|
||||
|
||||
### Content-graph expanders in other Hugo sites
|
||||
|
||||
Other Hugo repos may use shortcodes with similar graph-expansion behavior under different names, for example:
|
||||
|
||||
- `embed-md`
|
||||
- `table-children`
|
||||
- `command-group`
|
||||
|
||||
Treat these as repository-specific features. Read the local shortcode or partial implementation before deciding whether it expands sibling pages, child sections, or data files.
|
||||
|
||||
### Data-backed and example-extraction shortcodes in other Hugo sites
|
||||
|
||||
In docs repos such as Redis or Rclone sites, expect shortcodes like:
|
||||
|
||||
- `features-table`
|
||||
- `optional-features-table`
|
||||
- `clients-example`
|
||||
- `jupyter-example`
|
||||
|
||||
These often depend on:
|
||||
|
||||
- `data/*`
|
||||
- generated metadata files
|
||||
- local example source trees
|
||||
- Markdown-export partials
|
||||
|
||||
Materialize them only when the local dependency chain is clear and deterministic. Otherwise, downgrade to a `Conversion note:` block and keep any safe inline content.
|
||||
|
||||
## Literal Example Guardrails
|
||||
|
||||
Preserve escaped shortcode examples even outside fenced code blocks when they are part of:
|
||||
|
||||
- notation comparison tables
|
||||
- syntax tutorials
|
||||
- inline prose demonstrating how to call a shortcode
|
||||
|
||||
Examples:
|
||||
|
||||
```text
|
||||
{{%/* foo */%}}
|
||||
{{</* foo */>}}
|
||||
```
|
||||
|
||||
Do not strip these with a generic shortcode remover.
|
||||
|
||||
## Render Hooks
|
||||
|
||||
The site under conversion may define render hooks for:
|
||||
|
||||
- blockquotes
|
||||
- code blocks
|
||||
- links
|
||||
- passthrough
|
||||
- tables
|
||||
|
||||
Important implications:
|
||||
|
||||
- a Markdown link may resolve against pages, page resources, section resources, or global resources
|
||||
- content may depend on local validation logic for broken links
|
||||
- rendered HTML may differ from generic CommonMark defaults
|
||||
- blockquotes can carry alert semantics such as note, tip, important, warning, and caution
|
||||
- code blocks can carry file labels, copy flags, details wrappers, summaries, trim behavior, and language remapping
|
||||
- passthrough hooks can make math delimiters meaningful content rather than raw noise
|
||||
- Markdown attributes can surface in render hook context and therefore must not be stripped blindly
|
||||
|
||||
For sites with similar render-hook patterns:
|
||||
|
||||
- the local repository overrides render hooks for blockquotes, code blocks, links, passthrough, and tables
|
||||
- it documents heading and image render hooks, but does not override them locally in `layouts/_markup/`
|
||||
- the local link hook handles glossary shorthand `(g)`, validates fragments, and checks section resources only when the current page is not a leaf bundle
|
||||
- the local code block hook can add file labels, copy buttons, details wrappers, summaries, trim behavior, and language remapping based on code fence attributes
|
||||
|
||||
For link-heavy pages, read the local `render-link.html` and the official render-hook docs before rewriting links.
|
||||
|
||||
## Safe Fallback Format
|
||||
|
||||
If a shortcode remains unresolved after inspection, replace it with a short explicit note in the final Markdown, for example:
|
||||
|
||||
```text
|
||||
> Conversion note: `clients-example` normally renders multi-language tabs. This sample keeps only the inline Redis CLI content.
|
||||
```
|
||||
|
||||
This is preferable to shipping live Hugo syntax or silently dropping meaning.
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ACTIVE_PATTERNS = {
|
||||
"hugo_shortcode": re.compile(r"\{\{(?:<|%)"),
|
||||
"go_template_action": re.compile(r"\{\{\s*(?:\.|if\b|with\b|range\b|end\b|partial\b|site\b|warnf\b|errorf\b)"),
|
||||
"local_absolute_path": re.compile(r"(?:/Users/|/home/|/private/var/folders/)"),
|
||||
"script_tag": re.compile(r"<script\b", re.IGNORECASE),
|
||||
}
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Detect leftover Hugo syntax and unsafe residue in Markdown output.",
|
||||
)
|
||||
parser.add_argument("--root", required=True, help="Directory containing Markdown output")
|
||||
parser.add_argument("--json", action="store_true", help="Emit JSON instead of text")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def markdown_files(root: Path):
|
||||
return sorted(
|
||||
p for p in root.rglob("*") if p.is_file() and p.suffix.lower() in {".md", ".markdown", ".mdown"}
|
||||
)
|
||||
|
||||
|
||||
def scan_file(path: Path):
|
||||
findings = []
|
||||
in_fence = False
|
||||
for lineno, line in enumerate(path.read_text(encoding="utf-8", errors="replace").splitlines(), start=1):
|
||||
stripped = line.lstrip()
|
||||
if stripped.startswith("```") or stripped.startswith("~~~"):
|
||||
in_fence = not in_fence
|
||||
continue
|
||||
if in_fence:
|
||||
continue
|
||||
for rule, pattern in ACTIVE_PATTERNS.items():
|
||||
if pattern.search(line):
|
||||
findings.append({"line": lineno, "rule": rule, "text": line.strip()})
|
||||
return findings
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
root = Path(args.root).expanduser().resolve()
|
||||
if not root.is_dir():
|
||||
raise SystemExit(f"Root does not exist or is not a directory: {root}")
|
||||
|
||||
report = []
|
||||
for path in markdown_files(root):
|
||||
findings = scan_file(path)
|
||||
if findings:
|
||||
report.append({"path": str(path), "findings": findings})
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(report, indent=2, ensure_ascii=False))
|
||||
else:
|
||||
if not report:
|
||||
print("No active Hugo residue found outside fenced code blocks.")
|
||||
for item in report:
|
||||
print(item["path"])
|
||||
for finding in item["findings"]:
|
||||
print(f" L{finding['line']}: {finding['rule']}: {finding['text']}")
|
||||
|
||||
raise SystemExit(1 if report else 0)
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from collections import Counter, defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import tomllib
|
||||
except ModuleNotFoundError: # pragma: no cover
|
||||
tomllib = None
|
||||
|
||||
|
||||
SHORTCODE_RE = re.compile(
|
||||
r"\{\{[<%](?:/\*)?\s*(?!/)([A-Za-z0-9][A-Za-z0-9_/-]*)",
|
||||
re.MULTILINE,
|
||||
)
|
||||
YAML_KEY_RE = re.compile(r"^([A-Za-z0-9_-]+):", re.MULTILINE)
|
||||
TOML_KEY_RE = re.compile(r"^([A-Za-z0-9_.-]+)\s*=", re.MULTILINE)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Inventory Hugo config, shortcodes, render hooks, and content patterns.",
|
||||
)
|
||||
parser.add_argument("--site-root", required=True, help="Path to the Hugo site root")
|
||||
parser.add_argument("--output", help="Write JSON output to this file")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def ensure_dir(path: Path) -> Path:
|
||||
resolved = path.expanduser().resolve()
|
||||
if not resolved.is_dir():
|
||||
raise SystemExit(f"Site root does not exist or is not a directory: {resolved}")
|
||||
return resolved
|
||||
|
||||
|
||||
def read_text(path: Path) -> str:
|
||||
return path.read_text(encoding="utf-8", errors="replace")
|
||||
|
||||
|
||||
def relpath(path: Path, root: Path) -> str:
|
||||
return str(path.resolve().relative_to(root))
|
||||
|
||||
|
||||
def find_config_files(root: Path):
|
||||
direct = [
|
||||
root / "hugo.toml",
|
||||
root / "hugo.yaml",
|
||||
root / "hugo.yml",
|
||||
root / "hugo.json",
|
||||
root / "config.toml",
|
||||
root / "config.yaml",
|
||||
root / "config.yml",
|
||||
root / "config.json",
|
||||
]
|
||||
found = [p for p in direct if p.is_file()]
|
||||
config_dir = root / "config"
|
||||
if config_dir.is_dir():
|
||||
found.extend(sorted(p for p in config_dir.rglob("*") if p.is_file()))
|
||||
return found
|
||||
|
||||
|
||||
def load_config_summary(path: Path):
|
||||
suffix = path.suffix.lower()
|
||||
if suffix == ".toml" and tomllib is not None:
|
||||
data = tomllib.loads(read_text(path))
|
||||
elif suffix == ".json":
|
||||
data = json.loads(read_text(path))
|
||||
else:
|
||||
return {
|
||||
"path": str(path),
|
||||
"format": suffix.lstrip("."),
|
||||
"parsed": False,
|
||||
}
|
||||
|
||||
module_mounts = data.get("module", {}).get("mounts", [])
|
||||
markup = data.get("markup", {})
|
||||
goldmark = markup.get("goldmark", {})
|
||||
passthrough = (
|
||||
goldmark.get("extensions", {})
|
||||
.get("passthrough", {})
|
||||
.get("delimiters", {})
|
||||
)
|
||||
parser_attribute = goldmark.get("parser", {}).get("attribute", {})
|
||||
render_hook_params = data.get("params", {}).get("render_hooks", {}).get("link", {})
|
||||
|
||||
return {
|
||||
"path": str(path),
|
||||
"format": suffix.lstrip("."),
|
||||
"parsed": True,
|
||||
"module_mounts": module_mounts,
|
||||
"goldmark_passthrough_delimiters": passthrough,
|
||||
"goldmark_block_attributes": parser_attribute.get("block"),
|
||||
"link_render_error_level": render_hook_params.get("errorLevel"),
|
||||
}
|
||||
|
||||
|
||||
def extract_frontmatter_keys(text: str):
|
||||
if text.startswith("---\n"):
|
||||
end = text.find("\n---", 4)
|
||||
if end != -1:
|
||||
return sorted(set(YAML_KEY_RE.findall(text[4:end])))
|
||||
if text.startswith("+++\n"):
|
||||
end = text.find("\n+++", 4)
|
||||
if end != -1:
|
||||
return sorted({k.split(".")[0] for k in TOML_KEY_RE.findall(text[4:end])})
|
||||
return []
|
||||
|
||||
|
||||
def content_files(root: Path):
|
||||
content_root = root / "content"
|
||||
if not content_root.is_dir():
|
||||
return []
|
||||
exts = {".md", ".markdown", ".mdown", ".gotmpl"}
|
||||
return sorted(p for p in content_root.rglob("*") if p.is_file() and p.suffix.lower() in exts)
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
site_root = ensure_dir(Path(args.site_root))
|
||||
|
||||
shortcode_files = sorted((site_root / "layouts" / "_shortcodes").glob("*"))
|
||||
render_hook_files = sorted((site_root / "layouts" / "_markup").glob("render-*"))
|
||||
|
||||
shortcode_usage = Counter()
|
||||
shortcode_locations = defaultdict(list)
|
||||
frontmatter_keys = Counter()
|
||||
content_entries = []
|
||||
|
||||
for path in content_files(site_root):
|
||||
text = read_text(path)
|
||||
keys = extract_frontmatter_keys(text)
|
||||
for key in keys:
|
||||
frontmatter_keys[key] += 1
|
||||
|
||||
names = SHORTCODE_RE.findall(text)
|
||||
unique_names = sorted(set(names))
|
||||
for name in names:
|
||||
shortcode_usage[name] += 1
|
||||
for name in unique_names:
|
||||
shortcode_locations[name].append(relpath(path, site_root))
|
||||
|
||||
content_entries.append(
|
||||
{
|
||||
"path": relpath(path, site_root),
|
||||
"frontmatter_keys": keys,
|
||||
"shortcodes": unique_names,
|
||||
}
|
||||
)
|
||||
|
||||
result = {
|
||||
"site_root": str(site_root),
|
||||
"config_files": [load_config_summary(path) for path in find_config_files(site_root)],
|
||||
"shortcode_templates": [
|
||||
{
|
||||
"name": path.stem,
|
||||
"path": relpath(path, site_root),
|
||||
}
|
||||
for path in shortcode_files
|
||||
if path.is_file()
|
||||
],
|
||||
"render_hooks": [
|
||||
{
|
||||
"name": path.stem,
|
||||
"path": relpath(path, site_root),
|
||||
}
|
||||
for path in render_hook_files
|
||||
if path.is_file()
|
||||
],
|
||||
"frontmatter_key_frequency": dict(frontmatter_keys.most_common()),
|
||||
"shortcode_usage_frequency": dict(shortcode_usage.most_common()),
|
||||
"shortcode_locations": dict(sorted(shortcode_locations.items())),
|
||||
"content_files": content_entries,
|
||||
}
|
||||
|
||||
payload = json.dumps(result, indent=2, sort_keys=True)
|
||||
if args.output:
|
||||
output = Path(args.output)
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(payload + "\n", encoding="utf-8")
|
||||
else:
|
||||
print(payload)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user