From 30aab25167a72294969042ce6d799c323f8e2524 Mon Sep 17 00:00:00 2001 From: Seth Falco Date: Sat, 13 Jan 2024 15:25:58 +0000 Subject: [PATCH] feat: add explicit option for unquoted attribute values PR-URL: https://github.com/isaacs/sax-js/pull/268 Credit: @SethFalco Close: #268 Reviewed-by: @isaacs --- README.md | 3 ++ lib/sax.js | 10 ++++- test/unquoted-attribute-values.js | 71 +++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/unquoted-attribute-values.js diff --git a/README.md b/README.md index afcd3f3d..c974b0f7 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,9 @@ Settings supported: * `strictEntities` - Boolean. If true, only parse [predefined XML entities](http://www.w3.org/TR/REC-xml/#sec-predefined-ent) (`&`, `'`, `>`, `<`, and `"`) +* `unquotedAttributeValues` - Boolean. If true, then unquoted + attribute values are allowed. Defaults to `false` when `strict` + is true, `true` otherwise. ## Methods diff --git a/lib/sax.js b/lib/sax.js index c4288cc2..4e029766 100644 --- a/lib/sax.js +++ b/lib/sax.js @@ -71,6 +71,12 @@ parser.ns = Object.create(rootNS) } + // disallow unquoted attribute values if not otherwise configured + // and strict mode is true + if (parser.opt.unquotedAttributeValues === undefined) { + parser.opt.unquotedAttributeValues = !strict; + } + // mostly just for error reporting parser.trackPosition = parser.opt.position !== false if (parser.trackPosition) { @@ -1379,7 +1385,9 @@ parser.q = c parser.state = S.ATTRIB_VALUE_QUOTED } else { - strictFail(parser, 'Unquoted attribute value') + if (!parser.opt.unquotedAttributeValues) { + error(parser, 'Unquoted attribute value') + } parser.state = S.ATTRIB_VALUE_UNQUOTED parser.attribValue = c } diff --git a/test/unquoted-attribute-values.js b/test/unquoted-attribute-values.js new file mode 100644 index 00000000..42e3f7c7 --- /dev/null +++ b/test/unquoted-attribute-values.js @@ -0,0 +1,71 @@ +// strict: true +require(__dirname).test({ + xml: '', + expect: [ + ['opentagstart', { name: 'svg', attributes: {} }], + ['error', 'Unquoted attribute value\nLine: 0\nColumn: 12\nChar: 2'], + ['attribute', { name: 'width', value: '20px' }], + ['error', 'Unquoted attribute value\nLine: 0\nColumn: 24\nChar: 2'], + ['attribute', { name: 'height', value: '20px' }], + ['opentag', { name: 'svg', attributes: { + width: '20px', + height: '20px' + }, isSelfClosing: true }], + ['closetag', 'svg'], + ], + strict: true +}) + +// strict: false +require(__dirname).test({ + xml: '', + expect: [ + ['opentagstart', { name: 'SVG', attributes: {} }], + ['attribute', { name: 'WIDTH', value: '20px' }], + ['attribute', { name: 'HEIGHT', value: '20px' }], + ['opentag', { name: 'SVG', attributes: { + WIDTH: '20px', + HEIGHT: '20px' + }, isSelfClosing: true }], + ['closetag', 'SVG'], + ], + strict: false +}) + +// strict: true, opt: { unquotedAttributeValues: true } +require(__dirname).test({ + xml: '', + expect: [ + ['opentagstart', { name: 'svg', attributes: {} }], + ['attribute', { name: 'width', value: '20px' }], + ['attribute', { name: 'height', value: '20px' }], + ['opentag', { name: 'svg', attributes: { + width: '20px', + height: '20px' + }, isSelfClosing: true }], + ['closetag', 'svg'], + ], + strict: true, + opt: { + unquotedAttributeValues: true + } +}) + +// strict: false, opt: { unquotedAttributeValues: true } +require(__dirname).test({ + xml: '', + expect: [ + ['opentagstart', { name: 'SVG', attributes: {} }], + ['attribute', { name: 'WIDTH', value: '20px' }], + ['attribute', { name: 'HEIGHT', value: '20px' }], + ['opentag', { name: 'SVG', attributes: { + WIDTH: '20px', + HEIGHT: '20px' + }, isSelfClosing: true }], + ['closetag', 'SVG'], + ], + strict: false, + opt: { + unquotedAttributeValues: true + } +})