Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit a3ff5cf

Browse files
vsavkinrkirov
authored andcommitted
feat(css_shim): implement polyfill-unscoped-next-selector and polyfill-non-strict
1 parent 7a745d5 commit a3ff5cf

File tree

2 files changed

+99
-29
lines changed

2 files changed

+99
-29
lines changed

lib/core_dom/css_shim.dart

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,12 @@ String shimCssText(String css, String tag) =>
3737
* * `:host`
3838
* * `:host(.x)`
3939
*
40-
* When the shim is not powerful enough, you can fall back on the polyfill-next-selector
41-
* directive.
40+
* When the shim is not powerful enough, you can fall back on the polyfill-next-selector,
41+
* polyfill-unscoped-next-selector, and polyfill-non-strict directives.
4242
*
43-
* polyfill-next-selector {content: 'x > y'}
44-
* z {}
45-
*
46-
* Becomes:
47-
*
48-
* x[tag] > y[tag]
43+
* * `polyfill-next-selector {content: 'x > y'}` z {} becomes `x[tag] > y[tag] {}`
44+
* * `polyfill-unscoped-next-selector {content: 'x > y'} z {}` becomes `x > y {}`
45+
* * `polyfill-non-strict {} z {}` becomes `tag z {}`
4946
*
5047
* See http://www.polymer-project.org/docs/polymer/styling.html#at-polyfill
5148
*
@@ -54,17 +51,15 @@ String shimCssText(String css, String tag) =>
5451
*/
5552
class _CssShim {
5653
static final List SELECTOR_SPLITS = const [' ', '>', '+', '~'];
57-
static final RegExp POLYFILL_NEXT_SELECTOR_DIRECTIVE = new RegExp(
58-
r"polyfill-next-selector"
54+
55+
static final RegExp CONTENT = new RegExp(
5956
r"[^}]*"
6057
r"content\:[\s]*"
6158
r"'([^']*)'"
62-
r"[^}]*}"
63-
r"([^{]*)",
59+
r"[^}]*}",
6460
caseSensitive: false,
6561
multiLine: true
6662
);
67-
static final int NEXT_SELECTOR_CONTENT = 1;
6863

6964
static final String HOST_TOKEN = '-host-element';
7065
static final RegExp COLON_SELECTORS = new RegExp(r'(' + HOST_TOKEN + r')(\(.*\)){0,1}(.*)',
@@ -79,21 +74,29 @@ class _CssShim {
7974
static final RegExp COLON_HOST = new RegExp('($HOST_TOKEN$PAREN_SUFFIX',
8075
caseSensitive: false, multiLine: true);
8176

77+
static final String POLYFILL_NON_STRICT = "polyfill-non-strict";
78+
static final String POLYFILL_UNSCOPED_NEXT_SELECTOR = "polyfill-unscoped-next-selector";
79+
static final String POLYFILL_NEXT_SELECTOR = "polyfill-next-selector";
80+
81+
static final List<RegExp> COMBINATORS = [
82+
new RegExp(r'/shadow/', caseSensitive: false),
83+
new RegExp(r'/shadow-deep/', caseSensitive: false),
84+
new RegExp(r'::shadow', caseSensitive: false),
85+
new RegExp(r'/deep/', caseSensitive: false)
86+
];
87+
8288
final String tag;
8389
final String attr;
8490

8591
_CssShim(String tag)
8692
: tag = tag, attr = "[$tag]";
8793

8894
String shimCssText(String css) {
89-
final preprocessed = convertColonHost(applyPolyfillNextSelectorDirective(css));
95+
final preprocessed = convertColonHost(css);
9096
final rules = cssToRules(preprocessed);
9197
return scopeRules(rules);
9298
}
9399

94-
String applyPolyfillNextSelectorDirective(String css) =>
95-
css.replaceAllMapped(POLYFILL_NEXT_SELECTOR_DIRECTIVE, (m) => m[NEXT_SELECTOR_CONTENT]);
96-
97100
String convertColonHost(String css) {
98101
css = css.replaceAll(":host", HOST_TOKEN);
99102

@@ -120,35 +123,82 @@ class _CssShim {
120123
List<_Rule> cssToRules(String css) =>
121124
new _Parser(css).parse();
122125

123-
String scopeRules(List<_Rule> rules) =>
124-
rules.map(scopeRule).join("\n");
126+
String scopeRules(List<_Rule> rules) {
127+
final scopedRules = [];
128+
129+
var prevRule;
130+
rules.forEach((rule) {
131+
if (prevRule != null && prevRule.selectorText == POLYFILL_NON_STRICT) {
132+
scopedRules.add(scopeNonStrictMode(rule));
133+
134+
} else if (prevRule != null && prevRule.selectorText == POLYFILL_UNSCOPED_NEXT_SELECTOR) {
135+
final content = extractContent(prevRule);
136+
scopedRules.add(ruleToString(new _Rule(content, body: rule.body)));
137+
138+
} else if (prevRule != null && prevRule.selectorText == POLYFILL_NEXT_SELECTOR) {
139+
final content = extractContent(prevRule);
140+
scopedRules.add(scopeStrictMode(new _Rule(content, body: rule.body)));
141+
142+
} else if (rule.selectorText != POLYFILL_NON_STRICT &&
143+
rule.selectorText != POLYFILL_UNSCOPED_NEXT_SELECTOR &&
144+
rule.selectorText != POLYFILL_NEXT_SELECTOR) {
145+
scopedRules.add(scopeStrictMode(rule));
146+
}
147+
148+
prevRule = rule;
149+
});
150+
151+
return scopedRules.join("\n");
152+
}
153+
154+
String extractContent(_Rule rule) {
155+
return CONTENT.firstMatch(rule.body)[1];
156+
}
157+
158+
String ruleToString(_Rule rule) {
159+
return "${rule.selectorText} ${rule.body}";
160+
}
125161

126-
String scopeRule(_Rule rule) {
162+
String scopeStrictMode(_Rule rule) {
127163
if (rule.hasNestedRules) {
128164
final selector = rule.selectorText;
129165
final rules = scopeRules(rule.rules);
130166
return '$selector {\n$rules\n}';
131167
} else {
132-
final scopedSelector = scopeSelector(rule.selectorText);
168+
final scopedSelector = scopeSelector(rule.selectorText, strict: true);
133169
final scopedBody = cssText(rule);
134170
return "$scopedSelector $scopedBody";
135171
}
136172
}
137173

138-
String scopeSelector(String selector) {
139-
final parts = selector.split(",");
174+
String scopeNonStrictMode(_Rule rule) {
175+
final scopedBody = cssText(rule);
176+
final scopedSelector = scopeSelector(rule.selectorText, strict: false);
177+
return "${scopedSelector} $scopedBody";
178+
}
179+
180+
String scopeSelector(String selector, {bool strict}) {
181+
final parts = replaceCombinators(selector).split(",");
140182
final scopedParts = parts.fold([], (res, p) {
141-
res.add(scopeSimpleSelector(p.trim()));
183+
res.add(scopeSimpleSelector(p.trim(), strict: strict));
142184
return res;
143185
});
144186
return scopedParts.join(", ");
145187
}
146188

147-
String scopeSimpleSelector(String selector) {
189+
String replaceCombinators(String selector) {
190+
return COMBINATORS.fold(selector, (sel, combinator) {
191+
return sel.replaceAll(combinator, ' ');
192+
});
193+
}
194+
195+
String scopeSimpleSelector(String selector, {bool strict}) {
148196
if (selector.contains(HOST_TOKEN)) {
149197
return replaceColonSelectors(selector);
198+
} else if (strict) {
199+
return insertTagToEverySelectorPart(selector);
150200
} else {
151-
return insertTag(selector);
201+
return "$tag $selector";
152202
}
153203
}
154204

@@ -162,7 +212,7 @@ class _CssShim {
162212
});
163213
}
164214

165-
String insertTag(String selector) {
215+
String insertTagToEverySelectorPart(String selector) {
166216
selector = handleIsSelector(selector);
167217

168218
SELECTOR_SPLITS.forEach((split) {
@@ -258,7 +308,7 @@ class _Lexer {
258308
int start = index;
259309
advance();
260310
while (isSelector(peek)) advance();
261-
String string = input.substring(start, index);
311+
String string = input.substring(start, index).trim();
262312
return new _Token(string, "selector");
263313
}
264314

test/core_dom/css_shim_spec.dart

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,29 @@ main() {
7575
expect(s(':host(.x,.y) {}', "a")).toEqual('a.x, a.y {}');
7676
});
7777

78-
it("should insert directives", () {
78+
it("should support polyfill-next-selector", () {
7979
var css = s("polyfill-next-selector {content: 'x > y'} z {}", "a");
8080
expect(css).toEqual('x[a]>y[a] {}');
8181
});
82+
83+
it("should support polyfill-unscoped-next-selector", () {
84+
var css = s("polyfill-unscoped-next-selector {content: 'x > y'} z {}", "a");
85+
expect(css).toEqual('x > y {}');
86+
});
87+
88+
it("should support polyfill-non-strict-next-selector", () {
89+
var css = s("polyfill-non-strict {} one, two {}", "a");
90+
expect(css).toEqual('a one, a two {}');
91+
});
92+
93+
it("should handle ::shadow", () {
94+
var css = s("polyfill-non-strict {} x::shadow > y {}", "a");
95+
expect(css).toEqual('a x > y {}');
96+
});
97+
98+
it("should handle /deep/", () {
99+
var css = s("polyfill-non-strict {} x /deep/ y {}", "a");
100+
expect(css).toEqual('a x y {}');
101+
});
82102
});
83103
}

0 commit comments

Comments
 (0)