diff --git a/flow-libs/posthtml.js.flow b/flow-libs/posthtml.js.flow index c223570ce2d..6e4f38c8930 100644 --- a/flow-libs/posthtml.js.flow +++ b/flow-libs/posthtml.js.flow @@ -10,7 +10,7 @@ declare module 'posthtml' { declare type PostHTMLNode = { tag: string, attrs?: {[string]: string, ...}, - content?: Array, + content?: Array, location?: { start: {|line: number, column: number|}, end: {|line: number, column: number|}, diff --git a/packages/core/integration-tests/test/html.js b/packages/core/integration-tests/test/html.js index 2b4df44cc38..cb8c1d603e9 100644 --- a/packages/core/integration-tests/test/html.js +++ b/packages/core/integration-tests/test/html.js @@ -498,7 +498,7 @@ describe('html', function() { // minifySvg is false assert( html.includes( - 'SVG', + 'SVG', ), ); }); @@ -2322,8 +2322,20 @@ describe('html', function() { }); it('should work with bundle names that have colons in them', async function() { + if (process.platform === 'win32') { + return; + } + + // Windows paths cannot contain colons and will fail to git clone, so write the file here (in memory). + await overlayFS.mkdirp(path.join(__dirname, 'integration/url-colon')); + await overlayFS.writeFile( + path.join(__dirname, 'integration/url-colon/a:b:c.html'), + '

Test

', + ); + let b = await bundle( path.join(__dirname, 'integration/url-colon/relative.html'), + {inputFS: overlayFS}, ); assertBundles(b, [ @@ -2342,6 +2354,7 @@ describe('html', function() { b = await bundle( path.join(__dirname, 'integration/url-colon/absolute.html'), + {inputFS: overlayFS}, ); assertBundles(b, [ @@ -2358,4 +2371,19 @@ describe('html', function() { output = await outputFS.readFile(b.getBundles()[0].filePath, 'utf8'); assert(output.includes('/a:b:c.html')); }); + + it('should normalize case of SVG elements and attributes when minified', async function() { + let b = await bundle( + path.join(__dirname, 'integration/html-svg-case/index.html'), + { + mode: 'production', + }, + ); + + let output = await outputFS.readFile(b.getBundles()[0].filePath, 'utf8'); + assert(output.includes(', + inSVG = false, +) { + if (Array.isArray(node)) { + for (let i = 0; i < node.length; i++) { + // $FlowFixMe + node[i] = mapSVG(node[i], inSVG); + } + } else if (node && typeof node === 'object') { + let {tag, attrs} = node; + if (inSVG || tag === 'svg') { + if (SVG_TAG_NAMES[tag]) { + node.tag = SVG_TAG_NAMES[tag]; + } + + if (attrs) { + for (let key in attrs) { + if (SVG_ATTRS[key]) { + attrs[SVG_ATTRS[key]] = attrs[key]; + delete attrs[key]; + } + } + } + } + + if (node.content != null) { + mapSVG(node.content, inSVG || tag === 'svg'); + } + } + + return node; +} diff --git a/packages/optimizers/htmlnano/src/svgMappings.js b/packages/optimizers/htmlnano/src/svgMappings.js new file mode 100644 index 00000000000..216114ed4bf --- /dev/null +++ b/packages/optimizers/htmlnano/src/svgMappings.js @@ -0,0 +1,102 @@ +// @flow +// Based on parse5: https://github.com/inikulin/parse5/blob/252819607421a5741cf745bb60c404f023531b0d/packages/parse5/lib/common/foreign-content.js#L54 + +export const SVG_TAG_NAMES: {|[string]: string|} = { + altglyph: 'altGlyph', + altglyphdef: 'altGlyphDef', + altglyphitem: 'altGlyphItem', + animatecolor: 'animateColor', + animatemotion: 'animateMotion', + animatetransform: 'animateTransform', + clippath: 'clipPath', + feblend: 'feBlend', + fecolormatrix: 'feColorMatrix', + fecomponenttransfer: 'feComponentTransfer', + fecomposite: 'feComposite', + feconvolvematrix: 'feConvolveMatrix', + fediffuselighting: 'feDiffuseLighting', + fedisplacementmap: 'feDisplacementMap', + fedistantlight: 'feDistantLight', + feflood: 'feFlood', + fefunca: 'feFuncA', + fefuncb: 'feFuncB', + fefuncg: 'feFuncG', + fefuncr: 'feFuncR', + fegaussianblur: 'feGaussianBlur', + feimage: 'feImage', + femerge: 'feMerge', + femergenode: 'feMergeNode', + femorphology: 'feMorphology', + feoffset: 'feOffset', + fepointlight: 'fePointLight', + fespecularlighting: 'feSpecularLighting', + fespotlight: 'feSpotLight', + fetile: 'feTile', + feturbulence: 'feTurbulence', + foreignobject: 'foreignObject', + glyphref: 'glyphRef', + lineargradient: 'linearGradient', + radialgradient: 'radialGradient', + textpath: 'textPath', +}; + +export const SVG_ATTRS: {|[string]: string|} = { + attributename: 'attributeName', + attributetype: 'attributeType', + basefrequency: 'baseFrequency', + baseprofile: 'baseProfile', + calcmode: 'calcMode', + clippathunits: 'clipPathUnits', + diffuseconstant: 'diffuseConstant', + edgemode: 'edgeMode', + filterunits: 'filterUnits', + glyphref: 'glyphRef', + gradienttransform: 'gradientTransform', + gradientunits: 'gradientUnits', + kernelmatrix: 'kernelMatrix', + kernelunitlength: 'kernelUnitLength', + keypoints: 'keyPoints', + keysplines: 'keySplines', + keytimes: 'keyTimes', + lengthadjust: 'lengthAdjust', + limitingconeangle: 'limitingConeAngle', + markerheight: 'markerHeight', + markerunits: 'markerUnits', + markerwidth: 'markerWidth', + maskcontentunits: 'maskContentUnits', + maskunits: 'maskUnits', + numoctaves: 'numOctaves', + pathlength: 'pathLength', + patterncontentunits: 'patternContentUnits', + patterntransform: 'patternTransform', + patternunits: 'patternUnits', + pointsatx: 'pointsAtX', + pointsaty: 'pointsAtY', + pointsatz: 'pointsAtZ', + preservealpha: 'preserveAlpha', + preserveaspectratio: 'preserveAspectRatio', + primitiveunits: 'primitiveUnits', + refx: 'refX', + refy: 'refY', + repeatcount: 'repeatCount', + repeatdur: 'repeatDur', + requiredextensions: 'requiredExtensions', + requiredfeatures: 'requiredFeatures', + specularconstant: 'specularConstant', + specularexponent: 'specularExponent', + spreadmethod: 'spreadMethod', + startoffset: 'startOffset', + stddeviation: 'stdDeviation', + stitchtiles: 'stitchTiles', + surfacescale: 'surfaceScale', + systemlanguage: 'systemLanguage', + tablevalues: 'tableValues', + targetx: 'targetX', + targety: 'targetY', + textlength: 'textLength', + viewbox: 'viewBox', + viewtarget: 'viewTarget', + xchannelselector: 'xChannelSelector', + ychannelselector: 'yChannelSelector', + zoomandpan: 'zoomAndPan', +}; diff --git a/packages/transformers/html/src/HTMLTransformer.js b/packages/transformers/html/src/HTMLTransformer.js index 2bc99afe1a2..13b85e80dce 100644 --- a/packages/transformers/html/src/HTMLTransformer.js +++ b/packages/transformers/html/src/HTMLTransformer.js @@ -18,6 +18,7 @@ export default (new Transformer({ type: 'posthtml', version: '0.4.1', program: parse(await asset.getCode(), { + lowerCaseTags: true, lowerCaseAttributeNames: true, sourceLocations: true, }),