diff --git a/lib/build-pages/page-data-vars-catch.test.js b/lib/build-pages/page-data-vars-catch.test.js new file mode 100644 index 0000000..5ea1840 --- /dev/null +++ b/lib/build-pages/page-data-vars-catch.test.js @@ -0,0 +1,72 @@ +import { test } from 'node:test' +import assert from 'node:assert' +import { mkdtemp, writeFile, rm } from 'node:fs/promises' +import { join } from 'node:path' +import { tmpdir } from 'node:os' +import { PageData } from './page-data.js' + +const fakeLayout = { + name: 'default', + render: async (/** @type {any} */ { children }) => String(children), + layoutStylePath: null, + layoutClientPath: null, +} + +test.describe('PageData.vars catch block wrapping', () => { + test('wraps error from spread merge with page path and preserves cause', async () => { + const dir = await mkdtemp(join(tmpdir(), 'domstack-pagedata-test-')) + const mdFile = join(dir, 'test.md') + + try { + await writeFile(mdFile, '# Test\n\nContent.') + + const pd = new PageData({ + pageInfo: /** @type {any} */ ({ + path: 'blog/post', + outputName: 'index.html', + type: 'md', + pageFile: { filepath: mdFile }, + }), + globalVars: { layout: 'default' }, + globalStyle: undefined, + globalClient: undefined, + defaultStyle: null, + defaultClient: null, + builderOptions: /** @type {any} */ ({}), + }) + + await pd.init({ layouts: { default: fakeLayout } }) + + // After init, replace pageVars with a proxy that throws on property access + // to trigger the catch block in the vars getter. + const originalError = new Error('getter exploded') + pd.pageVars = new Proxy({}, { + ownKeys: () => ['exploding-key'], + getOwnPropertyDescriptor: () => ({ enumerable: true, configurable: true, writable: true, value: undefined }), + get (_target, key) { + if (typeof key === 'symbol') return undefined + throw originalError + }, + }) + + assert.throws( + () => pd.vars, + (err) => { + assert.ok(err instanceof Error, 'throws an Error') + assert.ok( + err.message.includes('blog/post'), + `message should include page path, got: "${err.message}"` + ) + assert.ok( + err.message.includes('getter exploded'), + `message should include original error message, got: "${err.message}"` + ) + assert.strictEqual(err.cause, originalError, 'err.cause should be the original error') + return true + } + ) + } finally { + await rm(dir, { recursive: true, force: true }) + } + }) +}) diff --git a/lib/build-pages/page-data.js b/lib/build-pages/page-data.js index 0cafe0f..83c869d 100644 --- a/lib/build-pages/page-data.js +++ b/lib/build-pages/page-data.js @@ -137,14 +137,21 @@ export class PageData { * @return {T} globalVars, pageVars, and buildVars merged together */ get vars () { - if (!this.#initialized) throw new Error('Initialize PageData before accessing vars') - const { globalVars, globalDataVars, pageVars, builderVars } = this - // @ts-ignore - return { - ...globalVars, - ...globalDataVars, - ...pageVars, - ...builderVars, + if (!this.#initialized) throw new Error(`Initialize PageData before accessing vars for page "${this.pageInfo?.path ?? ''}"`) + try { + const { globalVars, globalDataVars, pageVars, builderVars } = this + // @ts-ignore + return { + ...globalVars, + ...globalDataVars, + ...pageVars, + ...builderVars, + } + } catch (err) { + throw new Error( + `Failed to resolve vars for page "${this.pageInfo?.path ?? ''}": ${err instanceof Error ? err.message : String(err)}`, + { cause: err } + ) } } diff --git a/lib/build-pages/page-data.test.js b/lib/build-pages/page-data.test.js new file mode 100644 index 0000000..82123f7 --- /dev/null +++ b/lib/build-pages/page-data.test.js @@ -0,0 +1,53 @@ +import { test } from 'node:test' +import assert from 'node:assert' +import { PageData } from './page-data.js' + +test.describe('PageData.vars', () => { + test('throws with page path before initialization', () => { + const pd = new PageData({ + pageInfo: /** @type {any} */ ({ path: 'blog/test-post' }), + globalVars: {}, + globalStyle: undefined, + globalClient: undefined, + defaultStyle: null, + defaultClient: null, + builderOptions: /** @type {any} */ ({}), + }) + + assert.throws( + () => pd.vars, + (err) => { + assert.ok(err instanceof Error, 'throws an Error') + assert.ok( + err.message.includes('blog/test-post'), + `error message should include the page path, got: "${err.message}"` + ) + return true + } + ) + }) + + test('error message includes unknown page fallback when pageInfo has no path', () => { + const pd = new PageData({ + pageInfo: /** @type {any} */ ({}), + globalVars: {}, + globalStyle: undefined, + globalClient: undefined, + defaultStyle: null, + defaultClient: null, + builderOptions: /** @type {any} */ ({}), + }) + + assert.throws( + () => pd.vars, + (err) => { + assert.ok(err instanceof Error, 'throws an Error') + assert.ok( + err.message.includes(''), + `error message should include fallback text, got: "${err.message}"` + ) + return true + } + ) + }) +})