Skip to content

Commit

Permalink
refactor: vendor stable and optimize
Browse files Browse the repository at this point in the history
  • Loading branch information
msand committed Oct 21, 2019
1 parent 2c89462 commit 091ca82
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 11 deletions.
5 changes: 0 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@
},
"dependencies": {
"css-select": "^2.0.2",
"css-tree": "^1.0.0-alpha.36",
"stable": "^0.1.8"
"css-tree": "^1.0.0-alpha.36"
},
"devDependencies": {
"@react-native-community/bob": "^0.7.0",
Expand Down
84 changes: 80 additions & 4 deletions src/css.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import csstree, {
SelectorList,
} from 'css-tree';
import cssSelect, { Adapter, Options, Predicate, Query } from 'css-select';
import stable from 'stable';

/*
* Style element inlining experiment based on SVGO
Expand Down Expand Up @@ -396,7 +395,7 @@ function specificity(selector: Selector): Specificity {
function compareSpecificity(
aSpecificity: Specificity,
bSpecificity: Specificity,
) {
): number {
for (let i = 0; i < 4; i += 1) {
if (aSpecificity[i] < bSpecificity[i]) {
return -1;
Expand All @@ -417,20 +416,97 @@ function compareSpecificity(
function bySelectorSpecificity(
selectorA: FlatSelector,
selectorB: FlatSelector,
) {
): number {
const aSpecificity = specificity(selectorA.item.data as Selector),
bSpecificity = specificity(selectorB.item.data as Selector);
return compareSpecificity(aSpecificity, bSpecificity);
}

// Run a single pass with the given chunk size.
function pass<T>(
arr: T[],
comp: (a: T, b: T) => number,
chk: number,
result: T[],
) {
// Step size / double chunk size.
const dbl = chk * 2;
// Bounds of the left and right chunks.
let l, r, e;
// Iterators over the left and right chunk.
let li, ri;

// Iterate over pairs of chunks.
let i = 0;
const len = arr.length;
for (l = 0; l < len; l += dbl) {
r = l + chk;
e = r + chk;
if (r > len) {
r = len;
}
if (e > len) {
e = len;
}

// Iterate both chunks in parallel.
li = l;
ri = r;
while (true) {
// Compare the chunks.
if (li < r && ri < e) {
// This works for a regular `sort()` compatible comparator,
// but also for a simple comparator like: `a > b`
if (comp(arr[li], arr[ri]) <= 0) {
result[i++] = arr[li++];
} else {
result[i++] = arr[ri++];
}
}
// Nothing to compare, just flush what's left.
else if (li < r) {
result[i++] = arr[li++];
} else if (ri < e) {
result[i++] = arr[ri++];
}
// Both iterators are at the chunk ends.
else {
break;
}
}
}
}

// Execute the sort using the input array and a second buffer as work space.
// Returns one of those two, containing the final result.
function exec<T>(arr: T[], len: number, comp: (a: T, b: T) => number) {
// Rather than dividing input, simply iterate chunks of 1, 2, 4, 8, etc.
// Chunks are the size of the left or right hand in merge sort.
// Stop when the left-hand covers all of the array.
let buffer = new Array(len);
for (let chk = 1; chk < len; chk *= 2) {
pass<T>(arr, comp, chk, buffer);
const tmp = arr;
arr = buffer;
buffer = tmp;
}

return arr;
}

/**
* Sort selectors stably by their specificity.
*
* @param {Array} selectors to be sorted
* @return {Array} Stable sorted selectors
*/
function sortSelectors(selectors: FlatSelectorList) {
return stable(selectors, bySelectorSpecificity);
// Short-circuit when there's nothing to sort.
const len = selectors.length;
if (len <= 1) {
return selectors;
}
return exec(selectors.slice(), len, bySelectorSpecificity);
}

const declarationParseProps = {
Expand Down

0 comments on commit 091ca82

Please sign in to comment.