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
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ const blogIndex: PageFunction<BlogVars> = async ({
return html`<div>
<p>I love ${favoriteCake}!!</p>
<ul>
${yearPages.map(yearPage => html`<li><a href="${`/${yearPage.pageInfo.path}/`}">${basename(yearPage.pageInfo.path)}</a></li>`)}
${yearPages.map(yearPage => html`<li><a href="${yearPage.pageInfo.url}">${basename(yearPage.pageInfo.path)}</a></li>`)}
</ul>
</div>`
}
Expand Down Expand Up @@ -930,8 +930,8 @@ const feedsTemplate: TemplateAsyncIterator<TemplateVars> = async function * ({
return {
date_published: page.vars['publishDate'],
title: page.vars['title'],
url: `${homePageUrl}/${page.pageInfo.path}/`,
id: `${homePageUrl}/${page.pageInfo.path}/#${page.vars['publishDate']}`,
url: `${homePageUrl}${page.pageInfo.url}`,
id: `${homePageUrl}${page.pageInfo.url}#${page.vars['publishDate']}`,
content_html: await page.renderInnerPage({ pages })
}
}, { concurrency: 4 })
Expand Down Expand Up @@ -1022,7 +1022,7 @@ const buildGlobalData: AsyncGlobalDataFunction<GlobalData> = async ({ pages }) =
<ul className="blog-index-list">
${blogPosts.map(p => html`
<li className="blog-entry h-entry">
<a className="blog-entry-link u-url u-uid p-name" href="/${p.pageInfo.path}/">
<a className="blog-entry-link u-url u-uid p-name" href={p.pageInfo.url}>
${p.vars?.title}
</a>
</li>
Expand Down Expand Up @@ -1164,6 +1164,7 @@ Pages and Layouts receive an object with the following parameters:
- `page`: An object of the page being rendered with the following parameters:
- `type`: The type of page (`md`, `html`, or `js`)
- `path`: The directory path for the page.
- `url`: The canonical URL path for the page (e.g. `/blog/my-post/` for index pages, `/blog/loose-page.html` for loose non-index pages). Combine with a `siteUrl` from `global.vars.ts` to build full URLs: `` `${vars.siteUrl}${page.url}` ``.
- `outputName`: The output name of the final file.
- `outputRelname`: The relative output name/path of the output file.
- `pageFile`: Raw `src` path details of the page file
Expand Down
17 changes: 17 additions & 0 deletions lib/build-pages/compute-page-url.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { fsPathToUrlPath } from './page-builders/fs-path-to-url.js'

/**
* Derive the canonical URL path for a page from its filesystem path and output name.
* Index pages get a trailing-slash URL; other outputs include the filename.
*
* @param {object} params
* @param {string} params.path - The page's directory path relative to src root
* @param {string} params.outputName - The output filename (e.g. 'index.html' or 'loose-md.html')
* @returns {string}
*/
export function computePageUrl ({ path, outputName }) {
if (outputName === 'index.html') {
return path ? fsPathToUrlPath(path) + '/' : '/'
}
return path ? fsPathToUrlPath(path) + '/' + outputName : '/' + outputName
}
28 changes: 12 additions & 16 deletions lib/build-pages/page-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import { resolveVars, resolvePostVars } from './resolve-vars.js'
import { pageBuilders } from './page-builders/index.js'
// @ts-expect-error
import pretty from 'pretty'

/**
Expand Down Expand Up @@ -152,13 +151,12 @@ export class PageData {
if (!this.#initialized) throw new Error(`Initialize PageData before accessing vars for page "${this.pageInfo?.path ?? '<unknown page>'}"`)
try {
const { globalVars, globalDataVars, pageVars, builderVars } = this
// @ts-ignore
return {
return /** @type {T} */ (/** @type {unknown} */ ({
...globalVars,
...globalDataVars,
...pageVars,
...builderVars,
}
}))
} catch (err) {
throw new Error(
`Failed to resolve vars for page "${this.pageInfo?.path ?? '<unknown page>'}": ${err instanceof Error ? err.message : String(err)}`,
Expand Down Expand Up @@ -268,17 +266,15 @@ export class PageData {
if (!layout) throw new Error('A layout is required to render')
const innerPage = await this.renderInnerPage({ pages })

return pretty(
await layout.render({
vars,
styles,
scripts,
page: pageInfo,
pages,
// @ts-expect-error - innerPage type varies by page builder but layout handles it
children: innerPage,
workers: this.workers
})
)
const rendered = await layout.render({
vars,
styles,
scripts,
page: pageInfo,
pages,
children: /** @type {U} */ (/** @type {unknown} */ (innerPage)),
workers: this.workers
})
return pretty(String(rendered))
}
}
19 changes: 19 additions & 0 deletions lib/build-pages/page-data.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { test } from 'node:test'
import assert from 'node:assert'
import { PageData } from './page-data.js'
import { computePageUrl } from './compute-page-url.js'

test.describe('PageData.vars', () => {
test('throws with page path before initialization', () => {
Expand Down Expand Up @@ -51,3 +52,21 @@ test.describe('PageData.vars', () => {
)
})
})

test.describe('computePageUrl', () => {
test('root index.html maps to /', () => {
assert.strictEqual(computePageUrl({ path: '', outputName: 'index.html' }), '/')
})

test('nested index.html gets a trailing-slash URL', () => {
assert.strictEqual(computePageUrl({ path: 'blog/post', outputName: 'index.html' }), '/blog/post/')
})

test('non-index output includes filename in URL', () => {
assert.strictEqual(computePageUrl({ path: 'md-page', outputName: 'loose-md.html' }), '/md-page/loose-md.html')
})

test('non-index file at root includes filename only', () => {
assert.strictEqual(computePageUrl({ path: '', outputName: 'robots.txt' }), '/robots.txt')
})
})
4 changes: 4 additions & 0 deletions lib/identify-pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { resolve, relative, join, basename } from 'path'
import { pageBuilders } from './build-pages/index.js'
import { DomStackDuplicatePageError } from './helpers/domstack-error.js'
import { nodeHasTS } from './helpers/has-ts.js'
import { computePageUrl } from './build-pages/compute-page-url.js'

const __dirname = import.meta.dirname

Expand Down Expand Up @@ -157,6 +158,7 @@ const shaper = ({
* @property {PageFileAsset | undefined} [pageVars] - The variables associated with the page. (Replace 'any' with the appropriate type if known.)
* @property {Object<string, PageFileAsset> | undefined} [workers] - Web worker files associated with this page.
* @property {string} path - The directory path for the page.
* @property {string} url - The canonical URL path for the page (e.g. `/blog/my-post/` or `/blog/loose-page.html`).
* @property {string} outputName - The name of the output file.
* @property {string} outputRelname - The relative name/path for the output file.
* @property {boolean} draft - If the page is marked as a draft or not. Draft pages are only included when buildDrafts is passed.
Expand Down Expand Up @@ -317,6 +319,7 @@ export async function identifyPages (src, opts = {}) {
pageVars,
workers: Object.keys(workerFiles).length > 0 ? { ...workerFiles } : undefined,
path: dir,
url: computePageUrl({ path: dir, outputName: 'index.html' }),
outputName: 'index.html',
outputRelname: join(dir, 'index.html'),
draft: opts?.buildDrafts ? /\.draft\.(html|md|js)$/.test(page.basename) : false,
Expand Down Expand Up @@ -344,6 +347,7 @@ export async function identifyPages (src, opts = {}) {
pageFile: fileInfo,
type: 'md',
path: dir,
url: computePageUrl({ path: dir, outputName }),
outputName,
outputRelname: join(dir, outputName),
draft: opts.buildDrafts ? isDraftFile : false,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@types/markdown-it": "^14.1.1",
"@types/markdown-it-footnote": "^3.0.4",
"@types/node": "^25.3.0",
"@types/pretty": "^2.0.3",
"@voxpelli/tsconfig": "^16.0.0",
"auto-changelog": "^2.4.0",
"cheerio": "^1.0.0-rc.10",
Expand Down