diff --git a/src/config.ts b/src/config.ts index ebfdcf9b..cc8c5162 100644 --- a/src/config.ts +++ b/src/config.ts @@ -66,6 +66,11 @@ interface ResolvedConfig extends BaseConfig { * new object. */ cache?: Cache; + + /** + * A callback for internal warnings or errors (for example, when parsing invalid abbreviation) + */ + warn?: (message: string, err?: Error) => void } export type Config = ResolvedConfig & { options: Options }; diff --git a/src/markup/snippets.ts b/src/markup/snippets.ts index d86d04f0..2f5e94a8 100644 --- a/src/markup/snippets.ts +++ b/src/markup/snippets.ts @@ -15,6 +15,7 @@ import type { Config } from '../config.js'; export default function resolveSnippets(abbr: Abbreviation, config: Config): Abbreviation { const stack: string[] = []; const reversed = config.options['output.reverseAttributes']; + const { warn } = config; const resolve = (child: AbbreviationNode): Abbreviation | null => { const snippet = child.name && config.snippets[child.name]; @@ -26,7 +27,15 @@ export default function resolveSnippets(abbr: Abbreviation, config: Config): Abb return null; } - const snippetAbbr = parse(snippet, config); + let snippetAbbr: Abbreviation; + try { + // User may add invlid snippet. In this case, silently bail out + snippetAbbr = parse(snippet, config); + } catch (err) { + warn?.(`Unable to parse "${snippet}" snippet`, err); + return null; + } + stack.push(snippet); walkResolve(snippetAbbr, resolve, config); stack.pop(); diff --git a/test/snippets.ts b/test/snippets.ts index 6b8626f7..d024361a 100644 --- a/test/snippets.ts +++ b/test/snippets.ts @@ -14,4 +14,14 @@ describe('Snippets', () => { it('XSL', () => { Object.keys(xsl).forEach(k => ok(markup(xsl[k]), k)); }); + + it('Invalid snippets', () => { + const snippets = { + invalid: 'invalid snippet', + valid: 'button' + } + + const result = expand('invalid+valid', { snippets }) + equal(result, '\n') + }); });