Skip to content

Commit

Permalink
Merge pull request #607 from sveltejs/gh-583
Browse files Browse the repository at this point in the history
add cascade option, to prevent components inheriting styles
  • Loading branch information
Rich-Harris authored Jun 1, 2017
2 parents b5b484b + 7b99d47 commit 6636ea3
Show file tree
Hide file tree
Showing 18 changed files with 203 additions and 23 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"glob": "^7.1.1",
"jsdom": "^9.9.1",
"locate-character": "^2.0.0",
"magic-string": "^0.19.0",
"magic-string": "^0.21.1",
"mocha": "^3.2.0",
"node-resolve": "^1.3.3",
"nyc": "^10.0.0",
Expand Down
6 changes: 5 additions & 1 deletion src/generators/Generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ export default class Generator {
transitions: Set<string>;
importedComponents: Map<string, string>;

code: MagicString;

bindingGroups: string[];
expectedProperties: Set<string>;
cascade: boolean;
css: string;
cssId: string;
usesRefs: boolean;
Expand Down Expand Up @@ -59,7 +62,8 @@ export default class Generator {
this.expectedProperties = new Set();

this.code = new MagicString( source );
this.css = parsed.css ? processCss( parsed, this.code ) : null;
this.cascade = options.cascade !== false; // TODO remove this option in v2
this.css = parsed.css ? processCss( parsed, this.code, this.cascade ) : null;
this.cssId = parsed.css ? `svelte-${parsed.hash}` : '';
this.usesRefs = false;

Expand Down
2 changes: 1 addition & 1 deletion src/generators/dom/visitors/Element/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default function visitElement ( generator: DomGenerator, block: Block, st
block.mount( name, state.parentNode );

// add CSS encapsulation attribute
if ( generator.cssId && state.isTopLevel ) {
if ( generator.cssId && ( !generator.cascade || state.isTopLevel ) ) {
block.builders.create.addLine( `${generator.helper( 'setAttribute' )}( ${name}, '${generator.cssId}', '' );` );
}

Expand Down
1 change: 1 addition & 0 deletions src/generators/server-side-rendering/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class SsrGenerator extends Generator {
super( parsed, source, name, options );
this.bindings = [];
this.renderCode = '';
this.elementDepth = 0;
}

append ( code: string ) {
Expand Down
2 changes: 1 addition & 1 deletion src/generators/server-side-rendering/visitors/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default function visitElement ( generator: SsrGenerator, block: Block, no
}
});

if ( generator.cssId && !generator.elementDepth ) {
if ( generator.cssId && ( !generator.cascade || generator.elementDepth === 0 ) ) {
openingTag += ` ${generator.cssId}`;
}

Expand Down
76 changes: 59 additions & 17 deletions src/generators/shared/processCss.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import MagicString from 'magic-string';
import { Parsed, Node } from '../../interfaces';

const commentsPattern = /\/\*[\s\S]*?\*\//g;

export default function processCss ( parsed: Parsed, code ) {
export default function processCss ( parsed: Parsed, code: MagicString, cascade: boolean ) {
const css = parsed.css.content.styles;
const offset = parsed.css.content.start;

Expand All @@ -14,9 +15,13 @@ export default function processCss ( parsed: Parsed, code ) {
if ( node.type === 'Atrule' && node.name.toLowerCase() === 'keyframes' ) {
node.expression.children.forEach( ( expression: Node ) => {
if ( expression.type === 'Identifier' ) {
const newName = `svelte-${parsed.hash}-${expression.name}`;
code.overwrite( expression.start, expression.end, newName );
keyframes.set( expression.name, newName );
if ( expression.name.startsWith( '-global-' ) ) {
code.remove( expression.start, expression.start + 8 );
} else {
const newName = `svelte-${parsed.hash}-${expression.name}`;
code.overwrite( expression.start, expression.end, newName );
keyframes.set( expression.name, newName );
}
}
});
} else if ( node.children ) {
Expand All @@ -30,26 +35,63 @@ export default function processCss ( parsed: Parsed, code ) {

function transform ( rule: Node ) {
rule.selector.children.forEach( ( selector: Node ) => {
const start = selector.start - offset;
const end = selector.end - offset;
if ( cascade ) {
// TODO disable cascading (without :global(...)) in v2
const start = selector.start - offset;
const end = selector.end - offset;

const selectorString = css.slice( start, end );
const selectorString = css.slice( start, end );

const firstToken = selector.children[0];
const firstToken = selector.children[0];

let transformed;
let transformed;

if ( firstToken.type === 'TypeSelector' ) {
const insert = firstToken.end - offset;
const head = css.slice( start, insert );
const tail = css.slice( insert, end );
if ( firstToken.type === 'TypeSelector' ) {
const insert = firstToken.end - offset;
const head = css.slice( start, insert );
const tail = css.slice( insert, end );

transformed = `${head}${attr}${tail}, ${attr} ${selectorString}`;
} else {
transformed = `${attr}${selectorString}, ${attr} ${selectorString}`;
transformed = `${head}${attr}${tail}, ${attr} ${selectorString}`;
} else {
transformed = `${attr}${selectorString}, ${attr} ${selectorString}`;
}

code.overwrite( selector.start, selector.end, transformed );
}

code.overwrite( start + offset, end + offset, transformed );
else {
let shouldTransform = true;
let c = selector.start;

selector.children.forEach( ( child: Node ) => {
if ( child.type === 'WhiteSpace' || child.type === 'Combinator' ) {
code.appendLeft( c, attr );
shouldTransform = true;
return;
}

if ( !shouldTransform ) return;

if ( child.type === 'PseudoClassSelector' ) {
// `:global(xyz)` > xyz
if ( child.name === 'global' ) {
const first = child.children[0];
const last = child.children[child.children.length - 1];
code.remove( child.start, first.start ).remove( last.end, child.end );
} else {
code.prependRight( c, attr );
}

shouldTransform = false;
}

c = child.end;
});

if ( shouldTransform ) {
code.appendLeft( c, attr );
}
}
});

rule.block.children.forEach( ( block: Node ) => {
Expand Down
1 change: 1 addition & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface CompileOptions {

dev?: boolean;
shared?: boolean | string;
cascade?: boolean;

onerror?: (error: Error) => void
onwarn?: (warning: Warning) => void
Expand Down
14 changes: 12 additions & 2 deletions test/css/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import assert from 'assert';
import * as fs from 'fs';
import { svelte, exists } from '../helpers.js';
import { svelte } from '../helpers.js';

function tryRequire ( file ) {
try {
return require( file ).default;
} catch ( err ) {
if ( err.code !== 'MODULE_NOT_FOUND' ) throw err;
return null;
}
}

describe( 'css', () => {
fs.readdirSync( 'test/css/samples' ).forEach( dir => {
Expand All @@ -14,9 +23,10 @@ describe( 'css', () => {
}

( solo ? it.only : it )( dir, () => {
const config = tryRequire( `./samples/${dir}/_config.js` ) || {};
const input = fs.readFileSync( `test/css/samples/${dir}/input.html`, 'utf-8' ).replace( /\s+$/, '' );

const actual = svelte.compile( input ).css;
const actual = svelte.compile( input, config ).css;
fs.writeFileSync( `test/css/samples/${dir}/_actual.css`, actual );
const expected = fs.readFileSync( `test/css/samples/${dir}/expected.css`, 'utf-8' );

Expand Down
13 changes: 13 additions & 0 deletions test/css/samples/cascade-false-global-keyframes/expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

@keyframes why {
0% { color: red; }
100% { color: blue; }
}

[svelte-2486527405].animated, [svelte-2486527405] .animated {
animation: why 2s;
}

[svelte-2486527405].also-animated, [svelte-2486527405] .also-animated {
animation: not-defined-here 2s;
}
17 changes: 17 additions & 0 deletions test/css/samples/cascade-false-global-keyframes/input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div class='animated'>animated</div>
<div class='also-animated'>also animated</div>

<style>
@keyframes -global-why {
0% { color: red; }
100% { color: blue; }
}

.animated {
animation: why 2s;
}

.also-animated {
animation: not-defined-here 2s;
}
</style>
3 changes: 3 additions & 0 deletions test/css/samples/cascade-false-global/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
cascade: false
};
12 changes: 12 additions & 0 deletions test/css/samples/cascade-false-global/expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

div {
color: red;
}

div.foo {
color: blue;
}

.foo {
font-weight: bold;
}
16 changes: 16 additions & 0 deletions test/css/samples/cascade-false-global/input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div>red</div>
<div class='foo'>bold/blue</div>

<style>
:global(div) {
color: red;
}

:global(div.foo) {
color: blue;
}

:global(.foo) {
font-weight: bold;
}
</style>
13 changes: 13 additions & 0 deletions test/css/samples/cascade-false-keyframes/expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

@keyframes svelte-776829126-why {
0% { color: red; }
100% { color: blue; }
}

[svelte-776829126].animated, [svelte-776829126] .animated {
animation: svelte-776829126-why 2s;
}

[svelte-776829126].also-animated, [svelte-776829126] .also-animated {
animation: not-defined-here 2s;
}
17 changes: 17 additions & 0 deletions test/css/samples/cascade-false-keyframes/input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div class='animated'>animated</div>
<div class='also-animated'>also animated</div>

<style>
@keyframes why {
0% { color: red; }
100% { color: blue; }
}

.animated {
animation: why 2s;
}

.also-animated {
animation: not-defined-here 2s;
}
</style>
3 changes: 3 additions & 0 deletions test/css/samples/cascade-false/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
cascade: false
};
12 changes: 12 additions & 0 deletions test/css/samples/cascade-false/expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

div[svelte-4161687011] {
color: red;
}

div.foo[svelte-4161687011] {
color: blue;
}

.foo[svelte-4161687011] {
font-weight: bold;
}
16 changes: 16 additions & 0 deletions test/css/samples/cascade-false/input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div>red</div>
<div class='foo'>bold/blue</div>

<style>
div {
color: red;
}

div.foo {
color: blue;
}

.foo {
font-weight: bold;
}
</style>

0 comments on commit 6636ea3

Please sign in to comment.