Skip to content

Commit 998cc7e

Browse files
authored
Merge pull request #6 from asmcss/refactor-mixins
Refactored mixins
2 parents 5706191 + e569391 commit 998cc7e

12 files changed

Lines changed: 525 additions & 964 deletions

β€Ždist/assembler.cjs.jsβ€Ž

Lines changed: 131 additions & 243 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Ždist/assembler.es.jsβ€Ž

Lines changed: 131 additions & 243 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Ždist/assembler.jsβ€Ž

Lines changed: 131 additions & 243 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Ždist/assembler.min.jsβ€Ž

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žrollup.config.jsβ€Ž

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ const MAIN = 'src/index.ts';
88
const GLOBALS = {};
99
const EXTERNAL = Object.keys(GLOBALS);
1010

11+
const production = !process.env.ROLLUP_WATCH;
12+
1113
const resolver = importResolver(['.mjs', '.ts', '.js']);
1214
const typescript = ts({
1315
target: "es2015"
1416
});
1517

16-
export default [
18+
const CONFIG = [
1719
{
1820
input: MAIN,
1921
output: {
@@ -24,38 +26,46 @@ export default [
2426
},
2527
external: EXTERNAL,
2628
plugins: [resolver, typescript]
27-
},
28-
{
29-
input: MAIN,
30-
output: {
31-
name: NAME,
32-
file: pkg.browser.replace('.js', '.min.js'),
33-
format: 'umd',
34-
globals: GLOBALS
35-
},
36-
external: EXTERNAL,
37-
plugins: [resolver, typescript, terser()]
38-
},
39-
{
40-
input: MAIN,
41-
output: {
42-
name: NAME,
43-
file: pkg.main,
44-
format: 'cjs',
45-
globals: GLOBALS
29+
}
30+
];
31+
32+
if (production) {
33+
CONFIG.push(...[
34+
{
35+
input: MAIN,
36+
output: {
37+
name: NAME,
38+
file: pkg.browser.replace('.js', '.min.js'),
39+
format: 'umd',
40+
globals: GLOBALS
41+
},
42+
external: EXTERNAL,
43+
plugins: [resolver, typescript, terser()]
4644
},
47-
external: EXTERNAL,
48-
plugins: [resolver, typescript]
49-
},
50-
{
51-
input: MAIN,
52-
output: {
53-
name: NAME,
54-
file: pkg.module,
55-
format: 'es',
56-
globals: GLOBALS
45+
{
46+
input: MAIN,
47+
output: {
48+
name: NAME,
49+
file: pkg.main,
50+
format: 'cjs',
51+
globals: GLOBALS
52+
},
53+
external: EXTERNAL,
54+
plugins: [resolver, typescript]
5755
},
58-
external: EXTERNAL,
59-
plugins: [resolver, typescript]
60-
}
61-
];
56+
{
57+
input: MAIN,
58+
output: {
59+
name: NAME,
60+
file: pkg.module,
61+
format: 'es',
62+
globals: GLOBALS
63+
},
64+
external: EXTERNAL,
65+
plugins: [resolver, typescript]
66+
}
67+
]);
68+
}
69+
70+
71+
export default CONFIG;

β€Žsrc/StyleHandler.tsβ€Ž

Lines changed: 62 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
*/
1616

1717
import {ALIASES, DEFAULT_VALUES, PROPERTY_LIST, PROPERTY_VARIANTS, STATE_LIST, VALUE_WRAPPER} from "./list";
18-
import {HASH_VAR_PREFIX, PROPERTY_REGEX, HASH_CLASS_PREFIX} from "./helpers";
18+
import {HASH_VAR_PREFIX, PROPERTY_REGEX, HASH_CLASS_PREFIX, trim} from "./helpers";
1919
import {Root} from "./Root";
2020
import type {UserSettings} from "./helpers";
21+
import {resolveMixin} from "./mixins";
2122

2223
type PropertyInfo = {
2324
entry: string,
@@ -32,9 +33,20 @@ type PropertyInfo = {
3233
rank: number
3334
};
3435

36+
type AssemblerEntry = {
37+
n: string, //name
38+
p: string, //property
39+
e: string, //class|entry
40+
}
3541

3642
const VAR_REGEX = /@([a-zA-Z0-9\-_]+)/g;
3743
const REPLACE_REGEX = /\$(selector|body|class|value|property|state|variants|var)/g;
44+
const SPLIT_REGEX = /(?<!\\);/;
45+
const MIXIN_PREFIX = '^';
46+
47+
// do not match comma inside parenthesis
48+
// 2px, linear-gradient(blue, red), inline => [2px, linear-gradient(blue, red), inline]
49+
const COMMA_DELIMITED = /\s*,\s*(?![^(]*\))/gm;
3850

3951
export default class StyleHandler {
4052
readonly style: CSSStyleSheet;
@@ -61,30 +73,29 @@ export default class StyleHandler {
6173
return this.settings;
6274
}
6375

64-
handleStyleChange(element: HTMLElement, oldContent: string|null, content: string|null): void {
76+
handleStyleChange(element: HTMLElement, content: string|null, old: AssemblerEntry[]): AssemblerEntry[] {
6577

6678
if (content === null) {
67-
return this.handleStyleRemoved(element, oldContent);
79+
return this.handleStyleRemoved(element, old);
6880
}
6981

7082
const newEntries = this.getStyleEntries(content);
7183
const classList = element.hasAttribute('class') ? element.getAttribute('class').split(' ') : [];
84+
const assemblerEntries: AssemblerEntry[] = [];
7285

7386
// remove old entries
74-
if (oldContent !== null) {
75-
for (const {name, property, entry} of this.getStyleProperties(oldContent)) {
76-
if (!newEntries.has(name)) {
77-
const index = classList.indexOf(entry);
78-
if (index >= 0) {
79-
classList.splice(index, 1);
80-
}
81-
element.style.removeProperty(property);
87+
for (const {n:name, p:property, e:entry} of old) {
88+
if (!newEntries.has(name)) {
89+
const index = classList.indexOf(entry);
90+
if (index >= 0) {
91+
classList.splice(index, 1);
8292
}
93+
element.style.removeProperty(property);
8394
}
8495
}
8596

8697
for (const info of newEntries.values()) {
87-
const {entry, property, hash, value} = info;
98+
const {entry, property, hash, value, name} = info;
8899
const index = classList.indexOf(entry);
89100
if (index < 0) {
90101
classList.push(entry);
@@ -93,16 +104,19 @@ export default class StyleHandler {
93104
this.generateCSS(info);
94105
}
95106
element.style.setProperty(property, value);
107+
assemblerEntries.push({e:entry, n: name, p: property});
96108
}
97109

98110
element.setAttribute('class', classList.join(' '));
111+
112+
return assemblerEntries;
99113
}
100114

101-
handleStyleRemoved(element: HTMLElement, content: string): void {
115+
handleStyleRemoved(element: HTMLElement, old: AssemblerEntry[]): AssemblerEntry[] {
102116

103117
const classList = element.hasAttribute('class') ? element.getAttribute('class').split(' ') : [];
104118

105-
for (const {property, entry} of this.getStyleProperties(content)) {
119+
for (const {p:property, e:entry} of old) {
106120
const index = classList.indexOf(entry);
107121
if (index >= 0) {
108122
classList.splice(index, 1);
@@ -111,6 +125,8 @@ export default class StyleHandler {
111125
}
112126

113127
element.setAttribute('class', classList.join(' '));
128+
129+
return [];
114130
}
115131

116132
private extract(attr: string, value: string|string[]|null = null): PropertyInfo[] {
@@ -192,22 +208,15 @@ export default class StyleHandler {
192208
return result;
193209
}
194210

195-
private getStyleEntries(content: string, resolve: boolean = true): Map<string, PropertyInfo> {
211+
private getStyleEntries(content: string): Map<string, PropertyInfo> {
196212
const entries = new Map<string, PropertyInfo>();
197213

198-
for (let name of content.split(';')) {
199-
name = name.trim();
200-
if (name === '') {
201-
continue;
202-
}
203-
214+
for (let name of this.getResolvedProperties(content)) {
204215
let value = null;
205-
206216
const pos = name.indexOf(':');
207-
if (pos < 0) {
208-
name = name.trim();
209-
} else {
210-
value = resolve ? name.substr(pos + 1) : null;
217+
218+
if (pos >= 0) {
219+
value = name.substr(pos + 1);
211220
name = name.substr(0, pos).trim();
212221
}
213222

@@ -219,64 +228,42 @@ export default class StyleHandler {
219228
return entries;
220229
}
221230

222-
private * getStyleProperties(content: string): Iterable<{property: string, name: string, entry: string}> {
223-
const base = STATE_LIST.length;
224-
const MEDIA_LIST = this.breakpoints;
225-
226-
for (let attr of content.split(';')) {
227-
let value = null;
228-
const pos = attr.indexOf(':');
229-
if (pos < 0) {
230-
attr = attr.trim();
231-
} else {
232-
value = attr.substr(pos + 1);
233-
attr = attr.substr(0, pos).trim();
234-
}
235-
236-
const m = PROPERTY_REGEX.exec(attr)?.groups;
237-
238-
if (!m || !m.property) {
239-
continue;
240-
}
241-
242-
const media = MEDIA_LIST.indexOf(m.media || 'all');
243-
const state = STATE_LIST.indexOf(m.state || 'normal');
244-
245-
if (media < 0 || state < 0) {
231+
private getResolvedProperties(content: string, stack: string[] = []): string[] {
232+
const entries = [];
233+
for (let name of content.split(SPLIT_REGEX)) {
234+
name = name.trim();
235+
if (name === '') {
246236
continue;
247237
}
248-
249-
let properties: string|string[] = m.property;
250-
251-
if (ALIASES.hasOwnProperty(properties)) {
252-
properties = ALIASES[properties];
253-
if (typeof properties === 'function') {
254-
properties = (properties as (a: string|null) => string[])(value as string|null);
238+
// extract mixin
239+
if (name.startsWith(MIXIN_PREFIX)) {
240+
const pos = name.indexOf(':');
241+
let mixin, args;
242+
243+
if (pos < 0) {
244+
mixin = name.substr(1);
245+
args = [];
246+
} else {
247+
mixin = name.substr(1, pos - 1);
248+
args = name.substr(pos + 1).split(COMMA_DELIMITED).map(trim);
255249
}
256-
}
257-
258-
if (!Array.isArray(properties)) {
259-
properties = [properties];
260-
}
261-
262-
for (const property of properties) {
263-
const name = PROPERTY_LIST.indexOf(property);
264250

265-
if (name < 0) {
266-
continue;
251+
if (stack.indexOf(mixin) >= 0) {
252+
stack.push(mixin);
253+
throw new Error('Recursive mixin detected: ' + stack.join('->'));
267254
}
268255

269-
const scope = m.scope || '';
270-
const hash = (((name * base) + media) * base + state).toString(16) + (scope ? `-${scope}` : '');
271-
const internalProperty = (m.media ? m.media + '--' : '') + (scope ? scope + '__' : '') + property + (m.state ? '__' + m.state : '');
256+
stack.push(mixin);
257+
entries.push(...this.getResolvedProperties(resolveMixin(this.settings, mixin, args), stack))
258+
stack.pop();
272259

273-
yield {
274-
name: (m.media ? m.media + '|' : '') + (scope ? scope + '!' : '') + property + (m.state ? '.' + m.state : ''),
275-
property: HASH_VAR_PREFIX + internalProperty,
276-
entry: HASH_CLASS_PREFIX + '#' + hash,
277-
};
260+
continue;
278261
}
262+
263+
entries.push(name);
279264
}
265+
266+
return entries;
280267
}
281268

282269
private generateCSS(info: PropertyInfo): void {

β€Žsrc/helpers.tsβ€Ž

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export type UserSettings = {
2626
states: string[],
2727
scopes: string[],
2828
xStyleAttribute: string,
29-
xApplyAttribute: string,
3029
};
3130
type StyleType = string|{[key: string]: string};
3231
const regex = /([a-z0-9]|(?=[A-Z]))([A-Z])/g;
@@ -84,7 +83,6 @@ export function getUserSettings(dataset: {[key: string]: string}): UserSettings
8483
const xl = dataset.breakpointXl || ("1280px");
8584

8685
const xStyleAttribute = dataset.xStyleAttribute || "x-style";
87-
const xApplyAttribute = dataset.xApplyAttribute || "x-apply";
8886

8987
return {
9088
enabled,
@@ -98,7 +96,6 @@ export function getUserSettings(dataset: {[key: string]: string}): UserSettings
9896
breakpoints,
9997
media: {xs, sm, md, lg, xl},
10098
xStyleAttribute,
101-
xApplyAttribute,
10299
};
103100
}
104101

0 commit comments

Comments
 (0)