diff --git a/src/generators/shared/processCss.js b/src/generators/shared/processCss.js index 4f57e4db7016..bb0012e9903a 100644 --- a/src/generators/shared/processCss.js +++ b/src/generators/shared/processCss.js @@ -1,31 +1,24 @@ -import parse from 'css-tree/lib/parser/index.js'; -import walk from 'css-tree/lib/utils/walk.js'; - const commentsPattern = /\/\*[\s\S]*?\*\//g; export default function processCss ( parsed, code ) { const css = parsed.css.content.styles; const offset = parsed.css.content.start; - const ast = parse( css, { - positions: true - }); - const attr = `[svelte-${parsed.hash}]`; - walk.rules( ast, rule => { - rule.selector.children.each( selector => { - const start = selector.loc.start.offset; - const end = selector.loc.end.offset; + function transform ( rule ) { + rule.selector.children.forEach( selector => { + const start = selector.start - offset; + const end = selector.end - offset; const selectorString = css.slice( start, end ); - const firstToken = selector.children.head; + const firstToken = selector.children[0]; let transformed; - if ( firstToken.data.type === 'TypeSelector' ) { - const insert = firstToken.data.loc.end.offset; + if ( firstToken.type === 'TypeSelector' ) { + const insert = firstToken.end - offset; const head = css.slice( start, insert ); const tail = css.slice( insert, end ); @@ -36,7 +29,19 @@ export default function processCss ( parsed, code ) { code.overwrite( start + offset, end + offset, transformed ); }); - }); + } + + function walk ( node ) { + if ( node.type === 'Rule' ) { + transform( node ); + } else if ( node.children ) { + node.children.forEach( walk ); + } else if ( node.block ) { + walk( node.block ); + } + } + + parsed.css.children.forEach( walk ); // remove comments. TODO would be nice if this was exposed in css-tree let match; diff --git a/src/parse/read/style.js b/src/parse/read/style.js index 5da3cc2d4cc5..d367aba6271a 100644 --- a/src/parse/read/style.js +++ b/src/parse/read/style.js @@ -1,8 +1,35 @@ +import parse from 'css-tree/lib/parser/index.js'; +import walk from 'css-tree/lib/utils/walk.js'; + export default function readStyle ( parser, start, attributes ) { const contentStart = parser.index; const styles = parser.readUntil( /<\/style>/ ); const contentEnd = parser.index; + let ast; + + try { + ast = parse( styles, { + positions: true, + offset: contentStart + }); + } catch ( err ) { + if ( err.name === 'CssSyntaxError' ) { + parser.error( err.message, err.offset ); + } else { + throw err; + } + } + + // tidy up AST + walk.all( ast, node => { + if ( node.loc ) { + node.start = node.loc.start.offset; + node.end = node.loc.end.offset; + delete node.loc; + } + }); + parser.eat( '', true ); const end = parser.index; @@ -10,6 +37,7 @@ export default function readStyle ( parser, start, attributes ) { start, end, attributes, + children: JSON.parse( JSON.stringify( ast.children ) ), content: { start: contentStart, end: contentEnd, diff --git a/test/parse.js b/test/parse.js index 166a17609b3c..fa8cf85d8dc4 100644 --- a/test/parse.js +++ b/test/parse.js @@ -16,7 +16,7 @@ describe( 'parse', () => { const input = fs.readFileSync( `test/parser/${dir}/input.html`, 'utf-8' ).replace( /\s+$/, '' ); try { - const actual = svelte.parse( input ); + const actual = JSON.parse( JSON.stringify( svelte.parse( input ) ) ); const expected = require( `./parser/${dir}/output.json` ); assert.deepEqual( actual.html, expected.html ); diff --git a/test/parser/css/output.json b/test/parser/css/output.json index 1e78e04cf5fb..72c2a139db44 100644 --- a/test/parser/css/output.json +++ b/test/parser/css/output.json @@ -29,7 +29,61 @@ "start": 23, "end": 48, "styles": "\n\tdiv {\n\t\tcolor: red;\n\t}\n" - } + }, + "children": [ + { + "type": "Rule", + "start": 25, + "end": 47, + "selector": { + "type": "SelectorList", + "start": 25, + "end": 28, + "children": [ + { + "type": "Selector", + "start": 25, + "end": 28, + "children": [ + { + "type": "TypeSelector", + "start": 25, + "end": 28, + "name": "div" + } + ] + } + ] + }, + "block": { + "type": "Block", + "start": 29, + "end": 47, + "children": [ + { + "type": "Declaration", + "start": 33, + "end": 43, + "important": false, + "property": "color", + "value": { + "type": "Value", + "start": 39, + "end": 43, + "children": [ + { + "type": "Identifier", + "start": 40, + "end": 43, + "name": "red" + } + ] + } + } + ] + } + } + ] }, "js": null } diff --git a/test/parser/error-css/error.json b/test/parser/error-css/error.json new file mode 100644 index 000000000000..177f6d977f90 --- /dev/null +++ b/test/parser/error-css/error.json @@ -0,0 +1,8 @@ +{ + "message": "LeftCurlyBracket is expected", + "loc": { + "line": 2, + "column": 16 + }, + "pos": 24 +} diff --git a/test/parser/error-css/input.html b/test/parser/error-css/input.html new file mode 100644 index 000000000000..d073ec625845 --- /dev/null +++ b/test/parser/error-css/input.html @@ -0,0 +1,3 @@ + \ No newline at end of file