support ref:foo as a CSS selector (#693)

pull/738/head
Rich Harris 8 years ago
parent a7876c737b
commit 82559c3775

@ -51,7 +51,7 @@ export default class Selector {
}); });
} }
transform(code: MagicString, attr: string) { transform(code: MagicString, attr: string, id: string) {
function encapsulateBlock(block: Block) { function encapsulateBlock(block: Block) {
let i = block.selectors.length; let i = block.selectors.length;
while (i--) { while (i--) {
@ -64,7 +64,19 @@ export default class Selector {
code.appendLeft(selector.end, attr); code.appendLeft(selector.end, attr);
} }
return; break;
}
i = block.selectors.length;
while (i--) {
const selector = block.selectors[i];
if (selector.type === 'RefSelector') {
code.overwrite(selector.start, selector.end, `[svelte-ref-${selector.name}]`, {
contentOnly: true,
storeName: false
});
}
} }
} }
@ -154,6 +166,14 @@ function selectorAppliesTo(blocks: Block[], node: Node, stack: Node[]): boolean
if (node.name !== selector.name && selector.name !== '*') return false; if (node.name !== selector.name && selector.name !== '*') return false;
} }
else if (selector.type === 'RefSelector') {
if (node.attributes.some((attr: Node) => attr.type === 'Ref' && attr.name === selector.name)) {
node._cssRefAttribute = selector.name;
return true;
}
return;
}
else { else {
// bail. TODO figure out what these could be // bail. TODO figure out what these could be
return true; return true;

@ -87,6 +87,12 @@ export default function visitElement(
block.builders.hydrate.addLine( block.builders.hydrate.addLine(
`@encapsulateStyles( ${name} );` `@encapsulateStyles( ${name} );`
); );
if (node._cssRefAttribute) {
block.builders.hydrate.addLine(
`@setAttribute( ${name}, 'svelte-ref-${node._cssRefAttribute}', '' );`
)
}
} }
function visitAttributesAndAddProps() { function visitAttributesAndAddProps() {

@ -58,6 +58,10 @@ export default function visitElement(
if (node._needsCssAttribute) { if (node._needsCssAttribute) {
openingTag += ` ${generator.stylesheet.id}`; openingTag += ` ${generator.stylesheet.id}`;
if (node._cssRefAttribute) {
openingTag += ` svelte-ref-${node._cssRefAttribute}`;
}
} }
openingTag += '>'; openingTag += '>';

@ -1,5 +1,5 @@
import parse from 'css-tree/lib/parser/index.js'; import parse from 'css-tree/lib/parser/index.js';
import walk from 'css-tree/lib/utils/walk.js'; import { walk } from 'estree-walker';
import { Parser } from '../index'; import { Parser } from '../index';
import { Node } from '../../interfaces'; import { Node } from '../../interfaces';
@ -23,12 +23,33 @@ export default function readStyle(parser: Parser, start: number, attributes: Nod
} }
} }
ast = JSON.parse(JSON.stringify(ast));
// tidy up AST // tidy up AST
walk.all(ast, (node: Node) => { walk(ast, {
if (node.loc) { enter: (node: Node) => {
node.start = node.loc.start.offset; // replace `ref:a` nodes
node.end = node.loc.end.offset; if (node.type === 'Selector') {
delete node.loc; for (let i = 0; i < node.children.length; i += 1) {
const a = node.children[i];
const b = node.children[i + 1];
if (isRefSelector(a, b)) {
node.children.splice(i, 2, {
type: 'RefSelector',
start: a.loc.start.offset,
end: b.loc.end.offset,
name: b.name
});
}
}
}
if (node.loc) {
node.start = node.loc.start.offset;
node.end = node.loc.end.offset;
delete node.loc;
}
} }
}); });
@ -39,7 +60,7 @@ export default function readStyle(parser: Parser, start: number, attributes: Nod
start, start,
end, end,
attributes, attributes,
children: JSON.parse(JSON.stringify(ast.children)), children: ast.children,
content: { content: {
start: contentStart, start: contentStart,
end: contentEnd, end: contentEnd,
@ -47,3 +68,13 @@ export default function readStyle(parser: Parser, start: number, attributes: Nod
}, },
}; };
} }
function isRefSelector(a: Node, b: Node) {
if (!b) return false;
return (
a.type === 'TypeSelector' &&
a.name === 'ref' &&
b.type === 'PseudoClassSelector'
);
}

@ -84,7 +84,7 @@ describe("css", () => {
// dom // dom
assert.equal( assert.equal(
normalizeHtml(window, html).replace(/svelte-\d+/g, 'svelte-xyz'), normalizeHtml(window, html.replace(/svelte-\d+/g, 'svelte-xyz')),
normalizeHtml(window, expected.html) normalizeHtml(window, expected.html)
); );
@ -92,7 +92,7 @@ describe("css", () => {
const component = eval(`(function () { ${ssr.code}; return SvelteComponent; }())`); const component = eval(`(function () { ${ssr.code}; return SvelteComponent; }())`);
assert.equal( assert.equal(
normalizeHtml(window, component.render(config.data)).replace(/svelte-\d+/g, 'svelte-xyz'), normalizeHtml(window, component.render(config.data).replace(/svelte-\d+/g, 'svelte-xyz')),
normalizeHtml(window, expected.html) normalizeHtml(window, expected.html)
); );
}); });

@ -0,0 +1,23 @@
export default {
cascade: false,
data: {
active: true
},
warnings: [{
message: 'Unused CSS selector',
loc: {
column: 1,
line: 12
},
pos: 174,
frame: `
10: }
11:
12: ref:button.inactive {
^
13: color: green;
14: }`
}]
};

@ -0,0 +1 @@
[svelte-ref-button].active[svelte-xyz]{color:red}

@ -0,0 +1 @@
<button class="active" svelte-ref-button="" svelte-xyz="">deactivate</button>

@ -0,0 +1,15 @@
{{#if active}}
<button ref:button class='active'>deactivate</button>
{{else}}
<button ref:button>activate</button>
{{/if}}
<style>
ref:button.active {
color: red;
}
ref:button.inactive {
color: green;
}
</style>

@ -0,0 +1,19 @@
export default {
cascade: false,
warnings: [{
message: 'Unused CSS selector',
loc: {
column: 1,
line: 14
},
pos: 120,
frame: `
12: }
13:
14: ref:d {
^
15: color: blue;
16: }`
}]
};

@ -0,0 +1 @@
[svelte-ref-a][svelte-xyz]{color:red}[svelte-ref-b][svelte-xyz]{color:green}

@ -0,0 +1,3 @@
<div svelte-xyz='' svelte-ref-a=''></div>
<div svelte-xyz='' svelte-ref-b=''></div>
<div></div>

@ -0,0 +1,17 @@
<div ref:a></div>
<div ref:b></div>
<div ref:c></div>
<style>
ref:a {
color: red;
}
ref:b {
color: green;
}
ref:d {
color: blue;
}
</style>

@ -0,0 +1,7 @@
<div ref:foo/>
<style>
ref:foo {
color: red;
}
</style>

@ -0,0 +1,96 @@
{
"hash": 1104014177,
"html": {
"start": 0,
"end": 14,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 14,
"type": "Element",
"name": "div",
"attributes": [
{
"start": 5,
"end": 12,
"type": "Ref",
"name": "foo"
}
],
"children": []
},
{
"start": 14,
"end": 16,
"type": "Text",
"data": "\n\n"
}
]
},
"css": {
"start": 16,
"end": 60,
"attributes": [],
"children": [
{
"type": "Rule",
"selector": {
"type": "SelectorList",
"children": [
{
"type": "Selector",
"children": [
{
"type": "RefSelector",
"start": 25,
"end": 32,
"name": "foo"
}
],
"start": 25,
"end": 32
}
],
"start": 25,
"end": 32
},
"block": {
"type": "Block",
"children": [
{
"type": "Declaration",
"important": false,
"property": "color",
"value": {
"type": "Value",
"children": [
{
"type": "Identifier",
"name": "red",
"start": 44,
"end": 47
}
],
"start": 43,
"end": 47
},
"start": 37,
"end": 47
}
],
"start": 33,
"end": 51
},
"start": 25,
"end": 51
}
],
"content": {
"start": 23,
"end": 52,
"styles": "\n\tref:foo {\n\t\tcolor: red;\n\t}\n"
}
},
"js": null
}

@ -1976,9 +1976,9 @@ magic-string@^0.19.0, magic-string@~0.19.0:
dependencies: dependencies:
vlq "^0.2.1" vlq "^0.2.1"
magic-string@^0.22.1: magic-string@^0.22.3:
version "0.22.1" version "0.22.3"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.1.tgz#a1bda64dfd4ae6c63797a45a67ee473b1f8d0e0f" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.3.tgz#047989d99bfc7cbdefba1604adc8912551cd7ef1"
dependencies: dependencies:
vlq "^0.2.1" vlq "^0.2.1"

Loading…
Cancel
Save