From d8f5866f1263cde27f2df767628ed3e833d0bd4a Mon Sep 17 00:00:00 2001 From: Andrew Seier Date: Sat, 9 Nov 2024 22:41:41 -0800 Subject: [PATCH] Improve template issue forensics. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The major changes can be boiled down into the following additions: * Print the registered custom element tag name in the error. * Print the approximate template line number in the error.† † Note that this isn’t the _file line number_, just a naive count of the number of newlines encountered so far in the given template. Closes #201. --- CHANGELOG.md | 5 +++++ test/test-initialization-errors.js | 4 ++-- test/test-template-engine.js | 15 +++++++++------ x-element.d.ts.map | 2 +- x-element.js | 13 +++++++++++-- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddaa1ce..855f04e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Template errors now include approximate line numbers from the offending + template. They also print the registered custom element tag name (#201). + ## [1.1.1] - 2024-11-09 ### Fixed diff --git a/test/test-initialization-errors.js b/test/test-initialization-errors.js index a97dce2..0d20954 100644 --- a/test/test-initialization-errors.js +++ b/test/test-initialization-errors.js @@ -106,7 +106,7 @@ it('errors are thrown in connectedCallback when template result fails to render' try { el.connectedCallback(); } catch (error) { - const expected = ' — Invalid template for "TestElement" at path "test-element-3"'; + const expected = 'Invalid template for "TestElement" / at path "test-element-3".'; message = error.message; passed = error.message.endsWith(expected); } @@ -142,7 +142,7 @@ it('errors are thrown in connectedCallback when template result fails to render try { el.connectedCallback(); } catch (error) { - const expected = ' — Invalid template for "TestElement" at path "test-element-4[id="testing"][class="foo bar"][boolean][variation="primary"]"'; + const expected = 'Invalid template for "TestElement" / at path "test-element-4[id="testing"][class="foo bar"][boolean][variation="primary"]".'; message = error.message; passed = error.message.endsWith(expected); } diff --git a/test/test-template-engine.js b/test/test-template-engine.js index 100dd52..7ce3767 100644 --- a/test/test-template-engine.js +++ b/test/test-template-engine.js @@ -846,6 +846,7 @@ describe('rendering errors', () => { container.remove(); }); + // Note that this is line “1” because it’s perfectly inlined. it('throws for unquoted attributes', () => { const templateResultReference = html`
Gotta double-quote those.
`; const container = document.createElement('div'); @@ -856,12 +857,14 @@ describe('rendering errors', () => { } catch (e) { error = e; } - assert(error?.message === `Found invalid template string "
{ - const templateResultReference = html`
Gotta double-quote those.
`; + const templateResultReference = html`\n
Gotta double-quote those.
`; const container = document.createElement('div'); document.body.append(container); let error; @@ -870,12 +873,12 @@ describe('rendering errors', () => { } catch (e) { error = e; } - assert(error?.message === `Found invalid template string "
{ - const templateResultReference = html`
Gotta double-quote those.
`; + const templateResultReference = html`\n\n\n
Gotta double-quote those.
`; const container = document.createElement('div'); document.body.append(container); let error; @@ -884,7 +887,7 @@ describe('rendering errors', () => { } catch (e) { error = e; } - assert(error?.message === `Found invalid template string "
{ } catch (e) { error = e; } - assert(error?.message === `Found invalid template string "
at path "${pathString}".`; throw new Error(message, { cause: error }); } } @@ -1385,7 +1387,14 @@ class TemplateEngine { string = string.slice(0, -3 - name.length) + `${TemplateEngine.#ATTRIBUTE_PREFIXES.property}${iii}="${name}`; state.index = 1; // Accounts for an expected quote character next. } else { - throw new Error(`Found invalid template string "${string}" at "${string.slice(state.index)}".`); + // We try and intuit what authors consider line “1”. + const handled = [...strings.slice(0, iii), string.slice(0, state.index)].join(''); + const lineCount = handled.startsWith('\n') + ? handled.split('\n').length - 1 + : handled.split('\n').length; + // We say “on or after” because interpolated JS could add lines we + // cannot know about or account for. + throw new Error(`Found invalid template on or after line ${lineCount} in substring \`${string}\`. Failed to parse \`${string.slice(state.index)}\`.`); } } } else {