From fbe130835de5a609cb04a1f37031f7bedb4fbd90 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sat, 10 Dec 2016 11:07:11 -0500 Subject: [PATCH] =?UTF-8?q?add=20support=20for=20declared=20namespaces=20?= =?UTF-8?q?=E2=80=93=20fixes=20#147?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/generate/index.js | 11 +++++++++- src/utils/namespaces.js | 8 +++++++ src/validate/html/index.js | 18 ++++++++++++++++ src/validate/index.js | 12 ++++++----- src/validate/js/index.js | 8 ++++++- src/validate/js/propValidators/index.js | 4 +++- src/validate/js/propValidators/namespace.js | 5 +++++ .../Rect.html | 7 +++++++ .../_config.js | 3 ++- .../main.html | 1 + .../Rect.html | 7 +++++++ .../_config.js | 21 +++++++++++++++++++ .../main.html | 11 ++++++++++ .../input.html | 7 +++++++ .../warnings.json | 1 + .../input.html} | 0 .../warnings.json | 8 +++++++ 17 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 src/utils/namespaces.js create mode 100644 src/validate/js/propValidators/namespace.js create mode 100644 test/generator/svg-child-component-declared-namespace-shorthand/Rect.html rename test/generator/{svg-child-component => svg-child-component-declared-namespace-shorthand}/_config.js (97%) rename test/generator/{svg-child-component => svg-child-component-declared-namespace-shorthand}/main.html (99%) create mode 100644 test/generator/svg-child-component-declared-namespace/Rect.html create mode 100644 test/generator/svg-child-component-declared-namespace/_config.js create mode 100644 test/generator/svg-child-component-declared-namespace/main.html create mode 100644 test/validator/svg-child-component-declared-namespace/input.html create mode 100644 test/validator/svg-child-component-declared-namespace/warnings.json rename test/{generator/svg-child-component/Rect.html => validator/svg-child-component-undeclared-namespace/input.html} (100%) create mode 100644 test/validator/svg-child-component-undeclared-namespace/warnings.json diff --git a/src/generate/index.js b/src/generate/index.js index cb0368548a62..b54b5217a000 100644 --- a/src/generate/index.js +++ b/src/generate/index.js @@ -4,6 +4,7 @@ import deindent from '../utils/deindent.js'; import isReference from '../utils/isReference.js'; import counter from './utils/counter.js'; import flattenReference from '../utils/flattenReference.js'; +import namespaces from '../utils/namespaces.js'; import getIntro from './utils/getIntro.js'; import getOutro from './utils/getOutro.js'; import visitors from './visitors/index.js'; @@ -277,9 +278,17 @@ export default function generate ( parsed, source, options, names ) { }); } + let namespace = null; + if ( templateProperties.namespace ) { + const ns = templateProperties.namespace.value; + namespace = namespaces[ ns ] || ns; + + // TODO remove the namespace property from the generated code, it's unused past this point + } + generator.push({ name: 'renderMainFragment', - namespace: null, + namespace, target: 'target', elementDepth: 0, localElementDepth: 0, diff --git a/src/utils/namespaces.js b/src/utils/namespaces.js new file mode 100644 index 000000000000..2ab0b116f058 --- /dev/null +++ b/src/utils/namespaces.js @@ -0,0 +1,8 @@ +export const html = 'http://www.w3.org/1999/xhtml'; +export const mathml = 'http://www.w3.org/1998/Math/MathML'; +export const svg = 'http://www.w3.org/2000/svg'; +export const xlink = 'http://www.w3.org/1999/xlink'; +export const xml = 'http://www.w3.org/XML/1998/namespace'; +export const xmlns = 'http://www.w3.org/2000/xmlns'; + +export default { html, mathml, svg, xlink, xml, xmlns }; diff --git a/src/validate/html/index.js b/src/validate/html/index.js index d7d7378fbd75..a7feafc42bf0 100644 --- a/src/validate/html/index.js +++ b/src/validate/html/index.js @@ -1,13 +1,31 @@ +import * as namespaces from '../../utils/namespaces.js'; + +const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|switch|symbol|text|textPath|title|tref|tspan|unknown|use|view|vkern)$/; + export default function validateHtml ( validator, html ) { + let elementDepth = 0; + function visit ( node ) { if ( node.type === 'EachBlock' ) { if ( !~validator.names.indexOf( node.context ) ) validator.names.push( node.context ); if ( node.index && !~validator.names.indexOf( node.index ) ) validator.names.push( node.index ); } + if ( node.type === 'Element' ) { + if ( elementDepth === 0 && validator.namespace !== namespaces.svg && svg.test( node.name ) ) { + validator.warn( `<${node.name}> is an SVG element – did you forget to add { namespace: 'svg' } ?`, node.start ); + } + + elementDepth += 1; + } + if ( node.children ) { node.children.forEach( visit ); } + + if ( node.type === 'Element' ) { + elementDepth -= 1; + } } html.children.forEach( visit ); diff --git a/src/validate/index.js b/src/validate/index.js index 89811421e5ed..47f440cd4ae8 100644 --- a/src/validate/index.js +++ b/src/validate/index.js @@ -40,17 +40,19 @@ export default function validate ( parsed, source, options ) { templateProperties: {}, - names: [] - }; + names: [], - if ( parsed.html ) { - validateHtml( validator, parsed.html ); - } + namespace: null + }; if ( parsed.js ) { validateJs( validator, parsed.js ); } + if ( parsed.html ) { + validateHtml( validator, parsed.html ); + } + return { names: validator.names }; diff --git a/src/validate/js/index.js b/src/validate/js/index.js index 026a304309be..60e34e111b32 100644 --- a/src/validate/js/index.js +++ b/src/validate/js/index.js @@ -2,6 +2,7 @@ import propValidators from './propValidators/index.js'; import FuzzySet from './utils/FuzzySet.js'; import checkForDupes from './utils/checkForDupes.js'; import checkForComputedKeys from './utils/checkForComputedKeys.js'; +import namespaces from '../../utils/namespaces.js'; const validPropList = Object.keys( propValidators ); @@ -29,7 +30,7 @@ export default function validateJs ( validator, js ) { checkForDupes( validator, validator.defaultExport.declaration.properties ); validator.defaultExport.declaration.properties.forEach( prop => { - validator.templateProperties[ prop.key.value ] = prop; + validator.templateProperties[ prop.key.name ] = prop; }); validator.defaultExport.declaration.properties.forEach( prop => { @@ -48,5 +49,10 @@ export default function validateJs ( validator, js ) { } } }); + + if ( validator.templateProperties.namespace ) { + const ns = validator.templateProperties.namespace.value.value; + validator.namespace = namespaces[ ns ] || ns; + } } } diff --git a/src/validate/js/propValidators/index.js b/src/validate/js/propValidators/index.js index dcbf004fffda..655df8d3cc43 100644 --- a/src/validate/js/propValidators/index.js +++ b/src/validate/js/propValidators/index.js @@ -6,6 +6,7 @@ import helpers from './helpers.js'; import methods from './methods.js'; import components from './components.js'; import events from './events.js'; +import namespace from './namespace.js'; export default { data, @@ -15,5 +16,6 @@ export default { helpers, methods, components, - events + events, + namespace }; diff --git a/src/validate/js/propValidators/namespace.js b/src/validate/js/propValidators/namespace.js new file mode 100644 index 000000000000..ca664261ef60 --- /dev/null +++ b/src/validate/js/propValidators/namespace.js @@ -0,0 +1,5 @@ +export default function namespace ( validator, prop ) { + if ( prop.value.type !== 'Literal' || typeof prop.value.value !== 'string' ) { + validator.error( `The 'namespace' property must be a string literal representing a valid namespace`, prop.start ); + } +} diff --git a/test/generator/svg-child-component-declared-namespace-shorthand/Rect.html b/test/generator/svg-child-component-declared-namespace-shorthand/Rect.html new file mode 100644 index 000000000000..c158d7fdf57e --- /dev/null +++ b/test/generator/svg-child-component-declared-namespace-shorthand/Rect.html @@ -0,0 +1,7 @@ + + + diff --git a/test/generator/svg-child-component/_config.js b/test/generator/svg-child-component-declared-namespace-shorthand/_config.js similarity index 97% rename from test/generator/svg-child-component/_config.js rename to test/generator/svg-child-component-declared-namespace-shorthand/_config.js index 977b02407d4c..2944111fd9e1 100644 --- a/test/generator/svg-child-component/_config.js +++ b/test/generator/svg-child-component-declared-namespace-shorthand/_config.js @@ -1,12 +1,13 @@ export default { - skip: true, data: { x: 0, y: 0, width: 100, height: 100 }, + html: ``, + test ( assert, component, target ) { const svg = target.querySelector( 'svg' ); const rect = target.querySelector( 'rect' ); diff --git a/test/generator/svg-child-component/main.html b/test/generator/svg-child-component-declared-namespace-shorthand/main.html similarity index 99% rename from test/generator/svg-child-component/main.html rename to test/generator/svg-child-component-declared-namespace-shorthand/main.html index 226687a3d1d4..7bef9787b0f1 100644 --- a/test/generator/svg-child-component/main.html +++ b/test/generator/svg-child-component-declared-namespace-shorthand/main.html @@ -1,6 +1,7 @@ + diff --git a/test/generator/svg-child-component-declared-namespace/_config.js b/test/generator/svg-child-component-declared-namespace/_config.js new file mode 100644 index 000000000000..2944111fd9e1 --- /dev/null +++ b/test/generator/svg-child-component-declared-namespace/_config.js @@ -0,0 +1,21 @@ +export default { + data: { + x: 0, + y: 0, + width: 100, + height: 100 + }, + + html: ``, + + test ( assert, component, target ) { + const svg = target.querySelector( 'svg' ); + const rect = target.querySelector( 'rect' ); + + assert.equal( svg.namespaceURI, 'http://www.w3.org/2000/svg' ); + assert.equal( rect.namespaceURI, 'http://www.w3.org/2000/svg' ); + + component.set({ width: 150, height: 50 }); + assert.equal( target.innerHTML, `` ); + } +}; diff --git a/test/generator/svg-child-component-declared-namespace/main.html b/test/generator/svg-child-component-declared-namespace/main.html new file mode 100644 index 000000000000..7bef9787b0f1 --- /dev/null +++ b/test/generator/svg-child-component-declared-namespace/main.html @@ -0,0 +1,11 @@ + + + + + diff --git a/test/validator/svg-child-component-declared-namespace/input.html b/test/validator/svg-child-component-declared-namespace/input.html new file mode 100644 index 000000000000..c158d7fdf57e --- /dev/null +++ b/test/validator/svg-child-component-declared-namespace/input.html @@ -0,0 +1,7 @@ + + + diff --git a/test/validator/svg-child-component-declared-namespace/warnings.json b/test/validator/svg-child-component-declared-namespace/warnings.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/test/validator/svg-child-component-declared-namespace/warnings.json @@ -0,0 +1 @@ +[] diff --git a/test/generator/svg-child-component/Rect.html b/test/validator/svg-child-component-undeclared-namespace/input.html similarity index 100% rename from test/generator/svg-child-component/Rect.html rename to test/validator/svg-child-component-undeclared-namespace/input.html diff --git a/test/validator/svg-child-component-undeclared-namespace/warnings.json b/test/validator/svg-child-component-undeclared-namespace/warnings.json new file mode 100644 index 000000000000..6faf58a4570a --- /dev/null +++ b/test/validator/svg-child-component-undeclared-namespace/warnings.json @@ -0,0 +1,8 @@ +[{ + "message": " is an SVG element – did you forget to add { namespace: 'svg' } ?", + "loc": { + "line": 1, + "column": 0 + }, + "pos": 0 +}]