Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Create GitHub Release

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
TAG_NAME: ${{ github.ref_name }}
run: |
gh release create "$TAG_NAME" \
--repo "$GITHUB_REPOSITORY" \
--title "$TAG_NAME" \
--generate-notes
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.1-rc.2] - 2026-09-06

### Added

- Release workflow migration to Bun-based publish automation
- Repository metadata aligned for the htnabe fork
- Documentation updates for the current GitHub Actions naming and release flow
- GitHub Actions workflow to automatically create a GitHub Release with generated notes when a `v*` tag is pushed

### Changed

- Updated the package publishing configuration for the maintained fork
- Simplified the GitHub environment declaration to the standard Actions string form

### Fixed

- Prevented a Markdown table from absorbing an immediately following Hugo shortcode closing tag (e.g. `{{</ table >}}`) as an extra table row by inserting the required blank line before it

## [0.0.1-rc.1] - 2026-08-19

### Added
Expand Down
124 changes: 64 additions & 60 deletions bun.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions docs/development/publishing.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

This repository publishes to npm through the manually triggered workflow in [.github/workflows/publish.yml](../../.github/workflows/publish.yml).

GitHub Releases are created separately and automatically by [.github/workflows/release.yml](../../.github/workflows/release.yml), which runs whenever a tag matching `v*` is pushed. It creates a GitHub Release for that tag with auto-generated release notes (`gh release create --generate-notes`). This workflow does not publish to npm — it only records the release on GitHub. Push the version tag (e.g. `git tag v0.0.1-rc.2 && git push origin v0.0.1-rc.2`) after the corresponding npm publish has completed, matching the tag to the version that was published.

## Release flow

The project uses a manual, main-branch release flow:
Expand Down Expand Up @@ -52,3 +54,4 @@ No `NPM_TOKEN` secret is required for the current configuration.
- Publishing is manual and intended to be triggered from `main`, not from ordinary tag pushes.
- The repository keeps the release logic in CI rather than in ad hoc local commands.
- Before a release, ensure the package version and changelog metadata are in sync with the intended feature or fix set.
- After npm publish succeeds, push a matching `vX.Y.Z` tag to trigger the GitHub Release workflow.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@htnabe/prettier-plugin-hugo-post",
"version": "0.0.1-rc.1",
"version": "0.0.1-rc.2",
"description": "A Prettier plugin for formatting Hugo content files with YAML, TOML, or JSON front matter, Markdown content, and Go template syntax.",
"main": "dist/index.mjs",
"types": "dist/index.d.mts",
Expand Down Expand Up @@ -42,12 +42,12 @@
"prettier-plugin-toml": "^2.0.6"
},
"devDependencies": {
"@types/node": "^26.2.0",
"@types/node": "^26.4.1",
"@typescript/native-preview": "^7.0.0-dev.20260707.2",
"@vitest/coverage-v8": "^4.1.11",
"jsdom": "^30.0.1",
"lefthook": "^2.1.10",
"oxlint": "^1.79.0",
"lefthook": "^2.1.12",
"oxlint": "^1.81.0",
"oxlint-tsgolint": "^7.0.2001",
"prettier": "^3.9.6",
"ts-node": "^10.9.2",
Expand Down
33 changes: 31 additions & 2 deletions src/features/printers/printers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,24 +325,53 @@ function ensureProperBlockSpacing(content: string): string {
const line = lines[i];
const prevLine = i > 0 ? lines[i - 1] : '';
const trimmedLine = line.trim();
const trimmedPrevLine = prevLine.trim();

// Check if this line is an end control that should be standalone
const isEndControl = trimmedLine.match(/^\{\{\s*end\s*\}\}$/);
const prevIsListItem = prevLine.trim().match(/^[-*+]\s/);
const prevIsListItem = trimmedPrevLine.match(/^[-*+]\s/);

// If previous line is a list item and current line is {{ end }},
// add blank line to prevent markdown formatter from indenting the {{ end }}
if (isEndControl && prevIsListItem) {
result.push('');
result.push(line);
} else {
continue;
}

// GFM tables are terminated by a blank line. If a non-table line follows a
// table row directly (no blank line in between), Prettier's markdown
// formatter absorbs that line into the table as an extra single-cell row.
// Insert a blank line to keep the following line (e.g. a Hugo shortcode
// closing tag) outside of the table.
const prevIsTableRow = isMarkdownTableRow(trimmedPrevLine);
const currentIsTableRow = isMarkdownTableRow(trimmedLine);
if (prevIsTableRow && trimmedLine !== '' && !currentIsTableRow) {
result.push('');
result.push(line);
continue;
}

result.push(line);
}

return result.join('\n');
}

/**
* Determine whether a trimmed line looks like a Markdown (GFM) table row.
* This is intentionally conservative to avoid treating Go-template pipelines
* (e.g. `{{ .Title | upper }}`) or YAML block scalars (`key: |`) as tables.
*/
function isMarkdownTableRow(trimmedLine: string): boolean {
if (trimmedLine === '' || trimmedLine.startsWith('{{')) return false;
// Most GFM table rows either start with '|' (Prettier's output) or contain
// at least one pipe with surrounding spaces: "a | b".
if (!(trimmedLine.startsWith('|') || /\s\|\s/.test(trimmedLine))) return false;
const pipeCount = trimmedLine.split('|').length - 1;
return pipeCount >= 2;
}

/**
* Format a template expression with proper spacing and structure
*/
Expand Down
27 changes: 27 additions & 0 deletions tests/suites/basic-functionality.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,33 @@ More content`,
'More content',
],
},
{
name: 'prevents a closing shortcode from being absorbed into a preceding table',
input: `{{< table "table" >}}
| lang/libs | Year | Use case |
|---|---|---|
| Hugo | 3.5 | Hobby |
{{</table>}}`,
shouldContain: ['{{< table "table" >}}', '{{< /table >}}'],
// The closing shortcode must not be turned into an extra table row.
shouldNotContain: ['| {{< /table >}} |', '| {{</ table >}} |'],
},
{
name: 'does not treat Go template pipelines as table rows',
input: `# {{ .Title | upper }}

Some text with {{ .Params.author | default "Anonymous" }}.
Next line right after pipeline.`,
shouldContain: [
'{{ .Title | upper }}',
'{{ .Params.author | default "Anonymous" }}',
'Next line right after pipeline.',
],
// No spurious blank line should be inserted after the pipeline lines.
shouldNotContain: [
'{{ .Params.author | default "Anonymous" }}.\n\nNext line right after pipeline.',
],
},
]);
});

Expand Down
Loading