mirror of
https://github.com/gorhill/uBlock.git
synced 2026-03-11 09:04:36 +00:00
[mv3] Re-work specific cosmetic filtering-related content scripts
To minimize delay to First Contentful Paint. The idea is to avoid complex data structures such as Map in order to speed up first content script execution. Use stringified arrays where practical and instanciate those arrays from their stringified representation only when there are actual cosmetic filters to apply. Related commit: https://github.com/gorhill/uBlock/commit/6039ef2b6d
This commit is contained in:
parent
6039ef2b6d
commit
0aa0d81caf
9 changed files with 169 additions and 312 deletions
|
|
@ -30,42 +30,47 @@ self.proceduralImports = undefined;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const selectors = [];
|
const isolatedAPI = self.isolatedAPI;
|
||||||
const exceptions = [];
|
const selectors = new Set();
|
||||||
|
const exceptions = new Set();
|
||||||
|
|
||||||
const lookupHostname = (hostname, details, out) => {
|
const lookupHostname = (hostname, details) => {
|
||||||
let seqi = details.hostnamesMap.get(hostname);
|
const listref = isolatedAPI.binarySearch(details.hostnames, hostname);
|
||||||
if ( seqi === undefined ) { return; }
|
if ( listref === -1 ) { return; }
|
||||||
const { argsList, argsSeqs } = details;
|
if ( Array.isArray(details.selectorLists) === false ) {
|
||||||
for (;;) {
|
details.selectorLists = details.selectorLists.split(';');
|
||||||
const argi = argsSeqs[seqi++];
|
details.selectorListRefs = JSON.parse(`[${details.selectorListRefs}]`);
|
||||||
const done = argi > 0;
|
}
|
||||||
out.push(...JSON.parse(argsList[done ? argi : -argi]));
|
const ilist = details.selectorListRefs[listref];
|
||||||
if ( done ) { break; }
|
const list = JSON.parse(`[${details.selectorLists[ilist]}]`);
|
||||||
|
for ( const iselector of list ) {
|
||||||
|
if ( iselector >= 0 ) {
|
||||||
|
selectors.add(details.selectors[iselector]);
|
||||||
|
} else {
|
||||||
|
exceptions.add(details.selectors[~iselector]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const lookupAll = hostname => {
|
const lookupAll = hostname => {
|
||||||
for ( const details of proceduralImports ) {
|
for ( const details of proceduralImports ) {
|
||||||
lookupHostname(hostname, details, selectors);
|
lookupHostname(hostname, details);
|
||||||
const matches = [];
|
|
||||||
lookupHostname(`~${hostname}`, details, matches);
|
|
||||||
if ( matches.length === 0 ) { continue; }
|
|
||||||
exceptions.push(...matches.map(a => JSON.stringify(a)));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.isolatedAPI.forEachHostname(lookupAll, {
|
isolatedAPI.forEachHostname(lookupAll, {
|
||||||
hasEntities: proceduralImports.some(a => a.hasEntities)
|
hasEntities: proceduralImports.some(a => a.hasEntities)
|
||||||
});
|
});
|
||||||
|
|
||||||
proceduralImports.length = 0;
|
proceduralImports.length = 0;
|
||||||
|
|
||||||
if ( selectors.length === 0 ) { return; }
|
for ( const selector of exceptions ) {
|
||||||
|
selectors.delete(selector);
|
||||||
|
}
|
||||||
|
|
||||||
const exceptedSelectors = exceptions.length !== 0
|
if ( selectors.size === 0 ) { return; }
|
||||||
? selectors.filter(a => exceptions.includes(JSON.stringify(a)) === false)
|
|
||||||
: selectors;
|
const exceptedSelectors = Array.from(selectors).map(a => JSON.parse(a));
|
||||||
if ( exceptedSelectors.length === 0 ) { return; }
|
|
||||||
|
|
||||||
const declaratives = exceptedSelectors.filter(a => a.cssable);
|
const declaratives = exceptedSelectors.filter(a => a.cssable);
|
||||||
if ( declaratives.length !== 0 ) {
|
if ( declaratives.length !== 0 ) {
|
||||||
|
|
|
||||||
|
|
@ -30,42 +30,48 @@ self.specificImports = undefined;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const selectors = [];
|
const isolatedAPI = self.isolatedAPI;
|
||||||
const exceptions = [];
|
const selectors = new Set();
|
||||||
|
const exceptions = new Set();
|
||||||
|
|
||||||
const lookupHostname = (hostname, details, out) => {
|
const lookupHostname = (hostname, details) => {
|
||||||
let seqi = details.hostnamesMap.get(hostname);
|
const listref = isolatedAPI.binarySearch(details.hostnames, hostname);
|
||||||
if ( seqi === undefined ) { return; }
|
if ( listref === -1 ) { return; }
|
||||||
const { argsList, argsSeqs } = details;
|
if ( Array.isArray(details.selectorLists) === false ) {
|
||||||
for (;;) {
|
details.selectorLists = details.selectorLists.split(';');
|
||||||
const argi = argsSeqs[seqi++];
|
details.selectorListRefs = JSON.parse(`[${details.selectorListRefs}]`);
|
||||||
const done = argi > 0;
|
}
|
||||||
out.push(...argsList[done ? argi : -argi].split('\n'));
|
const ilist = details.selectorListRefs[listref];
|
||||||
if ( done ) { break; }
|
const list = JSON.parse(`[${details.selectorLists[ilist]}]`);
|
||||||
|
for ( const iselector of list ) {
|
||||||
|
if ( iselector >= 0 ) {
|
||||||
|
selectors.add(details.selectors[iselector]);
|
||||||
|
} else {
|
||||||
|
exceptions.add(details.selectors[~iselector]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const lookupAll = hostname => {
|
const lookupAll = hostname => {
|
||||||
for ( const details of specificImports ) {
|
for ( const details of specificImports ) {
|
||||||
lookupHostname(hostname, details, selectors);
|
lookupHostname(hostname, details);
|
||||||
lookupHostname(`~${hostname}`, details, exceptions);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.isolatedAPI.forEachHostname(lookupAll, {
|
isolatedAPI.forEachHostname(lookupAll, {
|
||||||
hasEntities: specificImports.some(a => a.hasEntities)
|
hasEntities: specificImports.some(a => a.hasEntities)
|
||||||
});
|
});
|
||||||
|
|
||||||
specificImports.length = 0;
|
specificImports.length = 0;
|
||||||
|
|
||||||
if ( selectors.length === 0 ) { return; }
|
for ( const selector of exceptions ) {
|
||||||
|
selectors.delete(selector);
|
||||||
|
}
|
||||||
|
|
||||||
const exceptedSelectors = exceptions.length !== 0
|
if ( selectors.size === 0 ) { return; }
|
||||||
? selectors.filter(a => exceptions.includes(a) === false)
|
|
||||||
: selectors;
|
|
||||||
if ( exceptedSelectors.length === 0 ) { return; }
|
|
||||||
|
|
||||||
self.cssAPI.insert(`${exceptedSelectors.join(',')}{display:none!important;}`);
|
const css = `${Array.from(selectors).join(',\n')}{display:none!important;}`;
|
||||||
|
self.cssAPI.insert(css);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,28 @@
|
||||||
if ( r !== undefined ) { return r; }
|
if ( r !== undefined ) { return r; }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
isolatedAPI.binarySearch = (sorted, target) => {
|
||||||
|
let l = 0, i = 0, d = 0;
|
||||||
|
let r = sorted.length;
|
||||||
|
let candidate;
|
||||||
|
while ( l < r ) {
|
||||||
|
i = l + r >>> 1;
|
||||||
|
candidate = sorted[i];
|
||||||
|
d = target.length - candidate.length;
|
||||||
|
if ( d === 0 ) {
|
||||||
|
if ( target === candidate ) { return i; }
|
||||||
|
d = target < candidate ? -1 : 1;
|
||||||
|
}
|
||||||
|
if ( d < 0 ) {
|
||||||
|
r = i;
|
||||||
|
} else {
|
||||||
|
l = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
})(self.isolatedAPI);
|
})(self.isolatedAPI);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
||||||
|
|
@ -90,11 +90,6 @@ const jsonSetMapReplacer = (k, v) => {
|
||||||
return v;
|
return v;
|
||||||
};
|
};
|
||||||
|
|
||||||
const uidint32 = (s) => {
|
|
||||||
const h = createHash('sha256').update(s).digest('hex').slice(0,8);
|
|
||||||
return parseInt(h,16) & 0x7FFFFFFF;
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const consoleLog = console.log;
|
const consoleLog = console.log;
|
||||||
|
|
@ -812,84 +807,6 @@ const globalHighlyGenericExceptionSet = new Set();
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// This merges selectors which are used by the same hostnames
|
|
||||||
|
|
||||||
function groupSelectorsByHostnames(mapin) {
|
|
||||||
if ( mapin === undefined ) { return []; }
|
|
||||||
const merged = new Map();
|
|
||||||
for ( const [ selector, details ] of mapin ) {
|
|
||||||
if ( details.rejected ) { continue; }
|
|
||||||
const json = JSON.stringify(details);
|
|
||||||
let entries = merged.get(json);
|
|
||||||
if ( entries === undefined ) {
|
|
||||||
entries = new Set();
|
|
||||||
merged.set(json, entries);
|
|
||||||
}
|
|
||||||
entries.add(selector);
|
|
||||||
}
|
|
||||||
const out = [];
|
|
||||||
for ( const [ json, entries ] of merged ) {
|
|
||||||
const details = JSON.parse(json);
|
|
||||||
details.selectors = Array.from(entries).sort();
|
|
||||||
out.push(details);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This merges hostnames which have the same set of selectors.
|
|
||||||
//
|
|
||||||
// Also, we sort the hostnames to increase likelihood that selector with
|
|
||||||
// same hostnames will end up in same generated scriptlet.
|
|
||||||
|
|
||||||
function groupHostnamesBySelectors(arrayin) {
|
|
||||||
const contentMap = new Map();
|
|
||||||
for ( const entry of arrayin ) {
|
|
||||||
const id = uidint32(JSON.stringify(entry.selectors));
|
|
||||||
let details = contentMap.get(id);
|
|
||||||
if ( details === undefined ) {
|
|
||||||
details = { a: entry.selectors };
|
|
||||||
contentMap.set(id, details);
|
|
||||||
}
|
|
||||||
if ( entry.matches !== undefined ) {
|
|
||||||
if ( details.y === undefined ) {
|
|
||||||
details.y = new Set();
|
|
||||||
}
|
|
||||||
for ( const hn of entry.matches ) {
|
|
||||||
details.y.add(hn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( entry.excludeMatches !== undefined ) {
|
|
||||||
if ( details.n === undefined ) {
|
|
||||||
details.n = new Set();
|
|
||||||
}
|
|
||||||
for ( const hn of entry.excludeMatches ) {
|
|
||||||
details.n.add(hn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const out = Array.from(contentMap).map(a => [
|
|
||||||
a[0], {
|
|
||||||
a: a[1].a,
|
|
||||||
y: a[1].y ? Array.from(a[1].y) : undefined,
|
|
||||||
n: a[1].n ? Array.from(a[1].n) : undefined,
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scriptletHostnameToIdMap = (hostnames, id, map) => {
|
|
||||||
for ( const hn of hostnames ) {
|
|
||||||
const existing = map.get(hn);
|
|
||||||
if ( existing === undefined ) {
|
|
||||||
map.set(hn, id);
|
|
||||||
} else if ( Array.isArray(existing) ) {
|
|
||||||
existing.push(id);
|
|
||||||
} else {
|
|
||||||
map.set(hn, [ existing, id ]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const scriptletJsonReplacer = (k, v) => {
|
const scriptletJsonReplacer = (k, v) => {
|
||||||
if ( k === 'n' ) {
|
if ( k === 'n' ) {
|
||||||
if ( v === undefined || v.size === 0 ) { return; }
|
if ( v === undefined || v.size === 0 ) { return; }
|
||||||
|
|
@ -904,175 +821,86 @@ const scriptletJsonReplacer = (k, v) => {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
function argsMap2List(argsMap, hostnamesMap) {
|
async function processCosmeticFilters(assetDetails, realm, mapin) {
|
||||||
const argsList = [ '' ];
|
|
||||||
const indexMap = new Map();
|
|
||||||
for ( const [ id, details ] of argsMap ) {
|
|
||||||
indexMap.set(id, argsList.length);
|
|
||||||
argsList.push(details);
|
|
||||||
}
|
|
||||||
const argsSeqs = [ 0 ];
|
|
||||||
const argsSeqsIndices = new Map();
|
|
||||||
for ( const [ hn, ids ] of hostnamesMap ) {
|
|
||||||
const seqKey = JSON.stringify(ids);
|
|
||||||
if ( argsSeqsIndices.has(seqKey) ) {
|
|
||||||
hostnamesMap.set(hn, argsSeqsIndices.get(seqKey));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const seqIndex = argsSeqs.length;
|
|
||||||
argsSeqsIndices.set(seqKey, seqIndex);
|
|
||||||
hostnamesMap.set(hn, seqIndex);
|
|
||||||
if ( typeof ids === 'number' ) {
|
|
||||||
argsSeqs.push(indexMap.get(ids));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for ( let i = 0; i < ids.length; i++ ) {
|
|
||||||
argsSeqs.push(-indexMap.get(ids[i]));
|
|
||||||
}
|
|
||||||
argsSeqs[argsSeqs.length-1] = -argsSeqs[argsSeqs.length-1];
|
|
||||||
}
|
|
||||||
return { argsList, argsSeqs };
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
async function processCosmeticFilters(assetDetails, mapin) {
|
|
||||||
if ( mapin === undefined ) { return 0; }
|
if ( mapin === undefined ) { return 0; }
|
||||||
if ( mapin.size === 0 ) { return 0; }
|
if ( mapin.size === 0 ) { return 0; }
|
||||||
|
|
||||||
const domainBasedEntries = groupHostnamesBySelectors(
|
// Collate all distinct selectors
|
||||||
groupSelectorsByHostnames(mapin)
|
const allSelectors = new Map();
|
||||||
);
|
const allHostnames = new Map();
|
||||||
// We do not want more than n CSS files per subscription, so we will
|
let hasEntities = false;
|
||||||
// group multiple unrelated selectors in the same file, and distinct
|
for ( const [ selector, details ] of mapin ) {
|
||||||
// css declarations will be injected programmatically according to the
|
if ( details.rejected ) { continue; }
|
||||||
// hostname of the current document.
|
if ( allSelectors.has(selector) === false ) {
|
||||||
//
|
allSelectors.set(selector, allSelectors.size);
|
||||||
|
}
|
||||||
|
const iSelector = allSelectors.get(selector);
|
||||||
|
if ( details.matches ) {
|
||||||
|
for ( const hn of details.matches ) {
|
||||||
|
if ( allHostnames.has(hn) === false ) {
|
||||||
|
allHostnames.set(hn, new Set());
|
||||||
|
}
|
||||||
|
allHostnames.get(hn).add(iSelector);
|
||||||
|
hasEntities ||= hn.endsWith('.*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( details.excludeMatches ) {
|
||||||
|
for ( const hn of details.excludeMatches ) {
|
||||||
|
if ( allHostnames.has(hn) === false ) {
|
||||||
|
allHostnames.set(hn, new Set());
|
||||||
|
}
|
||||||
|
allHostnames.get(hn).add(~iSelector);
|
||||||
|
hasEntities ||= hn.endsWith('.*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const allSelectorLists = new Map();
|
||||||
|
for ( const [ hn, selectorSet ] of allHostnames ) {
|
||||||
|
const list = JSON.stringify(Array.from(selectorSet).sort()).slice(1, -1);
|
||||||
|
if ( allSelectorLists.has(list) === false ) {
|
||||||
|
allSelectorLists.set(list, allSelectorLists.size);
|
||||||
|
}
|
||||||
|
allHostnames.set(hn, allSelectorLists.get(list));
|
||||||
|
}
|
||||||
|
|
||||||
// The cosmetic filters will be injected programmatically as content
|
// The cosmetic filters will be injected programmatically as content
|
||||||
// script and the decisions to activate the cosmetic filters will be
|
// script and the decisions to activate the cosmetic filters will be
|
||||||
// done at injection time according to the document's hostname.
|
// done at injection time according to the document's hostname.
|
||||||
const generatedFiles = [];
|
|
||||||
|
|
||||||
const argsMap = domainBasedEntries.map(entry => [
|
|
||||||
entry[0],
|
|
||||||
entry[1].a ? entry[1].a.join('\n') : undefined,
|
|
||||||
]);
|
|
||||||
const hostnamesMap = new Map();
|
|
||||||
let hasEntities = false;
|
|
||||||
for ( const [ id, details ] of domainBasedEntries ) {
|
|
||||||
if ( details.y ) {
|
|
||||||
scriptletHostnameToIdMap(details.y, id, hostnamesMap);
|
|
||||||
hasEntities ||= details.y.some(a => a.endsWith('.*'));
|
|
||||||
}
|
|
||||||
if ( details.n ) {
|
|
||||||
scriptletHostnameToIdMap(details.n.map(a => `~${a}`), id, hostnamesMap);
|
|
||||||
hasEntities ||= details.n.some(a => a.endsWith('.*'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap);
|
|
||||||
|
|
||||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
const originalScriptletMap = await loadAllSourceScriptlets();
|
||||||
let patchedScriptlet = originalScriptletMap.get('css-specific').replace(
|
let patchedScriptlet = originalScriptletMap.get(`css-${realm}`).replace(
|
||||||
'$rulesetId$',
|
'$rulesetId$',
|
||||||
assetDetails.id
|
assetDetails.id
|
||||||
);
|
);
|
||||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||||
/\bself\.\$argsList\$/,
|
/\bself\.\$selectors\$/,
|
||||||
`${JSON.stringify(argsList, scriptletJsonReplacer)}`
|
`/* ${allSelectors.size} */ ${JSON.stringify(Array.from(allSelectors.keys()))}`
|
||||||
);
|
);
|
||||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||||
/\bself\.\$argsSeqs\$/,
|
/\bself\.\$selectorLists\$/,
|
||||||
`${JSON.stringify(argsSeqs, scriptletJsonReplacer)}`
|
`/* ${allSelectorLists.size} */ ${JSON.stringify(Array.from(allSelectorLists.keys()).join(';'))}`
|
||||||
);
|
);
|
||||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
const sortedHostnames = Array.from(allHostnames.keys()).toSorted((a, b) => {
|
||||||
/\bself\.\$hostnamesMap\$/,
|
const d = a.length - b.length;
|
||||||
`${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}`
|
if ( d !== 0 ) { return d; }
|
||||||
);
|
return a < b ? -1 : 1;
|
||||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
|
||||||
'self.$hasEntities$',
|
|
||||||
JSON.stringify(hasEntities)
|
|
||||||
);
|
|
||||||
writeFile(`${scriptletDir}/specific/${assetDetails.id}.js`, patchedScriptlet);
|
|
||||||
generatedFiles.push(`${assetDetails.id}`);
|
|
||||||
|
|
||||||
if ( generatedFiles.length !== 0 ) {
|
|
||||||
log(`CSS-specific: ${mapin.size} distinct filters`);
|
|
||||||
log(`\tCombined into ${hostnamesMap.size} distinct hostnames`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hostnamesMap.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
async function processProceduralCosmeticFilters(assetDetails, mapin) {
|
|
||||||
if ( mapin === undefined ) { return 0; }
|
|
||||||
if ( mapin.size === 0 ) { return 0; }
|
|
||||||
|
|
||||||
const procedurals = new Map();
|
|
||||||
mapin.forEach((details, jsonSelector) => {
|
|
||||||
procedurals.set(jsonSelector, details);
|
|
||||||
});
|
});
|
||||||
if ( procedurals.size === 0 ) { return 0; }
|
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||||
|
/\bself\.\$selectorListRefs\$/,
|
||||||
const contentArray = groupHostnamesBySelectors(
|
`/* ${sortedHostnames.length} */ "${JSON.stringify(sortedHostnames.map(a => allHostnames.get(a))).slice(1, -1)}"`
|
||||||
groupSelectorsByHostnames(procedurals)
|
|
||||||
);
|
|
||||||
|
|
||||||
const argsMap = contentArray.map(entry => [
|
|
||||||
entry[0],
|
|
||||||
entry[1].a,
|
|
||||||
]);
|
|
||||||
const hostnamesMap = new Map();
|
|
||||||
let hasEntities = false;
|
|
||||||
for ( const [ id, details ] of contentArray ) {
|
|
||||||
if ( details.y ) {
|
|
||||||
scriptletHostnameToIdMap(details.y, id, hostnamesMap);
|
|
||||||
hasEntities ||= details.y.some(a => a.endsWith('.*'));
|
|
||||||
}
|
|
||||||
if ( details.n ) {
|
|
||||||
scriptletHostnameToIdMap(details.n.map(a => `~${a}`), id, hostnamesMap);
|
|
||||||
hasEntities ||= details.n.some(a => a.endsWith('.*'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap);
|
|
||||||
const argsListAfter = [];
|
|
||||||
for ( const a of argsList ) {
|
|
||||||
const aAfter = [];
|
|
||||||
for ( let b of a ) {
|
|
||||||
aAfter.push(JSON.parse(b));
|
|
||||||
}
|
|
||||||
argsListAfter.push(JSON.stringify(aAfter));
|
|
||||||
}
|
|
||||||
const originalScriptletMap = await loadAllSourceScriptlets();
|
|
||||||
let patchedScriptlet = originalScriptletMap.get('css-procedural').replace(
|
|
||||||
'$rulesetId$',
|
|
||||||
assetDetails.id
|
|
||||||
);
|
);
|
||||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||||
/\bself\.\$argsList\$/,
|
/\bself\.\$hostnames\$/,
|
||||||
`${JSON.stringify(argsListAfter, scriptletJsonReplacer)}`
|
`/* ${sortedHostnames.length} */ ${JSON.stringify(sortedHostnames)}`
|
||||||
);
|
|
||||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
|
||||||
/\bself\.\$argsSeqs\$/,
|
|
||||||
`${JSON.stringify(argsSeqs, scriptletJsonReplacer)}`
|
|
||||||
);
|
|
||||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
|
||||||
/\bself\.\$hostnamesMap\$/,
|
|
||||||
`${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}`
|
|
||||||
);
|
);
|
||||||
patchedScriptlet = safeReplace(patchedScriptlet,
|
patchedScriptlet = safeReplace(patchedScriptlet,
|
||||||
'self.$hasEntities$',
|
'self.$hasEntities$',
|
||||||
JSON.stringify(hasEntities)
|
JSON.stringify(hasEntities)
|
||||||
);
|
);
|
||||||
writeFile(`${scriptletDir}/procedural/${assetDetails.id}.js`, patchedScriptlet);
|
writeFile(`${scriptletDir}/${realm}/${assetDetails.id}.js`, patchedScriptlet);
|
||||||
|
|
||||||
if ( contentArray.length !== 0 ) {
|
log(`CSS-${realm}: ${allSelectors.size} distinct filters for ${allHostnames.size} distinct hostnames`);
|
||||||
log(`Procedural-related distinct filters: ${procedurals.size} distinct combined selectors`);
|
|
||||||
log(`\tCombined into ${hostnamesMap.size} distinct hostnames`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hostnamesMap.size;
|
return sortedHostnames.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
@ -1213,10 +1041,13 @@ async function rulesetFromURLs(assetDetails) {
|
||||||
);
|
);
|
||||||
const specificCosmeticStats = await processCosmeticFilters(
|
const specificCosmeticStats = await processCosmeticFilters(
|
||||||
assetDetails,
|
assetDetails,
|
||||||
|
'specific',
|
||||||
declarativeCosmetic
|
declarativeCosmetic
|
||||||
);
|
);
|
||||||
const proceduralStats = await processProceduralCosmeticFilters(
|
|
||||||
|
const proceduralStats = await processCosmeticFilters(
|
||||||
assetDetails,
|
assetDetails,
|
||||||
|
'procedural',
|
||||||
proceduralCosmetic
|
proceduralCosmetic
|
||||||
);
|
);
|
||||||
const scriptletStats = await processScriptletFilters(
|
const scriptletStats = await processScriptletFilters(
|
||||||
|
|
|
||||||
|
|
@ -173,40 +173,30 @@ export async function commit(rulesetId, path, writeFn) {
|
||||||
return a[0] < b[0] ? -1 : 1;
|
return a[0] < b[0] ? -1 : 1;
|
||||||
}).map(a => ([ a[0], JSON.stringify(Array.from(a[1]).map(a => JSON.parse(a))).slice(1,-1)]));
|
}).map(a => ([ a[0], JSON.stringify(Array.from(a[1]).map(a => JSON.parse(a))).slice(1,-1)]));
|
||||||
let content = safeReplace(scriptletTemplate, /\$rulesetId\$/, rulesetId, 0);
|
let content = safeReplace(scriptletTemplate, /\$rulesetId\$/, rulesetId, 0);
|
||||||
if ( worldDetails.hasEntities ) {
|
content = safeReplace(content, 'self.$hasEntities$', 'true');
|
||||||
content = safeReplace(content,
|
content = safeReplace(content, 'self.$hasAncestors$', 'true');
|
||||||
'const $hasEntities$ = false;',
|
|
||||||
'const $hasEntities$ = true;'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if ( worldDetails.hasAncestors ) {
|
|
||||||
content = safeReplace(content,
|
|
||||||
'const $hasAncestors$ = false;',
|
|
||||||
'const $hasAncestors$ = true;'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
content = safeReplace(content,
|
content = safeReplace(content,
|
||||||
'const $scriptletHostnames$ = [];',
|
'self.$scriptletHostnames$',
|
||||||
`const $scriptletHostnames$ = /* ${hostnames.length} */ ${JSON.stringify(hostnames.map(a => a[0]))};`
|
`/* ${hostnames.length} */ ${JSON.stringify(hostnames.map(a => a[0]))}`
|
||||||
);
|
);
|
||||||
content = safeReplace(content,
|
content = safeReplace(content,
|
||||||
'const $scriptletArglistRefs$ = [];',
|
'self.$scriptletArglistRefs$',
|
||||||
`const $scriptletArglistRefs$ = /* ${hostnames.length} */ ${JSON.stringify(hostnames.map(a => a[1]).join(';'))};`
|
`/* ${hostnames.length} */ ${JSON.stringify(hostnames.map(a => a[1]).join(';'))}`
|
||||||
);
|
);
|
||||||
content = safeReplace(content,
|
content = safeReplace(content,
|
||||||
'const $scriptletArglists$ = [];',
|
'self.$scriptletArglists$',
|
||||||
`const $scriptletArglists$ = /* ${arglists.size} */ ${JSON.stringify(Array.from(arglists.keys()).join(';'))};`
|
`/* ${arglists.size} */ ${JSON.stringify(Array.from(arglists.keys()).join(';'))}`
|
||||||
);
|
);
|
||||||
content = safeReplace(content,
|
content = safeReplace(content,
|
||||||
'const $scriptletArgs$ = [];',
|
'self.$scriptletArgs$',
|
||||||
`const $scriptletArgs$ = /* ${args.size} */ ${JSON.stringify(Array.from(args.keys()).join('\n'))};`
|
`/* ${args.size} */ ${JSON.stringify(Array.from(args.keys()))}`
|
||||||
);
|
);
|
||||||
content = safeReplace(content,
|
content = safeReplace(content,
|
||||||
'const $scriptletFunctions$ = [];',
|
'self.$scriptletFunctions$',
|
||||||
`const $scriptletFunctions$ = /* ${scriptletFunctions.size} */\n[${Array.from(scriptletFunctions.keys()).join(',')}];`
|
`/* ${scriptletFunctions.size} */\n[${Array.from(scriptletFunctions.keys()).join(',')}]`
|
||||||
);
|
);
|
||||||
content = safeReplace(content,
|
content = safeReplace(content,
|
||||||
'function $scriptletCode$(){} // eslint-disable-line',
|
'self.$scriptletCode$',
|
||||||
Array.from(allFunctions.values()).join('\n\n')
|
Array.from(allFunctions.values()).join('\n\n')
|
||||||
);
|
);
|
||||||
writeFn(`${path}/${world.toLowerCase()}/${rulesetId}.js`, content);
|
writeFn(`${path}/${world.toLowerCase()}/${rulesetId}.js`, content);
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,15 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const argsList = self.$argsList$;
|
|
||||||
const argsSeqs = self.$argsSeqs$;
|
const selectors = self.$selectors$;
|
||||||
const hostnamesMap = new Map(self.$hostnamesMap$);
|
const selectorLists = self.$selectorLists$;
|
||||||
|
const selectorListRefs = self.$selectorListRefs$;
|
||||||
|
const hostnames = self.$hostnames$;
|
||||||
const hasEntities = self.$hasEntities$;
|
const hasEntities = self.$hasEntities$;
|
||||||
|
|
||||||
self.proceduralImports = self.proceduralImports || [];
|
self.proceduralImports = self.proceduralImports || [];
|
||||||
self.proceduralImports.push({ argsList, argsSeqs, hostnamesMap, hasEntities });
|
self.proceduralImports.push({ selectors, selectorLists, selectorListRefs, hostnames, hasEntities });
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,14 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const argsList = self.$argsList$;
|
const selectors = self.$selectors$;
|
||||||
const argsSeqs = self.$argsSeqs$;
|
const selectorLists = self.$selectorLists$;
|
||||||
const hostnamesMap = new Map(self.$hostnamesMap$);
|
const selectorListRefs = self.$selectorListRefs$;
|
||||||
|
const hostnames = self.$hostnames$;
|
||||||
const hasEntities = self.$hasEntities$;
|
const hasEntities = self.$hasEntities$;
|
||||||
|
|
||||||
self.specificImports = self.specificImports || [];
|
self.specificImports = self.specificImports || [];
|
||||||
self.specificImports.push({ argsList, argsSeqs, hostnamesMap, hasEntities });
|
self.specificImports.push({ selectors, selectorLists, selectorListRefs, hostnames, hasEntities });
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,24 +30,24 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
function $scriptletCode$(){} // eslint-disable-line
|
self.$scriptletCode$
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const scriptletGlobals = {}; // eslint-disable-line
|
const scriptletGlobals = {}; // eslint-disable-line
|
||||||
|
|
||||||
const $scriptletFunctions$ = [];
|
const $scriptletFunctions$ = self.$scriptletFunctions$;
|
||||||
|
|
||||||
const $scriptletArgs$ = [];
|
const $scriptletArgs$ = self.$scriptletArgs$;
|
||||||
|
|
||||||
const $scriptletArglists$ = [];
|
const $scriptletArglists$ = self.$scriptletArglists$;
|
||||||
|
|
||||||
const $scriptletArglistRefs$ = [];
|
const $scriptletArglistRefs$ = self.$scriptletArglistRefs$;
|
||||||
|
|
||||||
const $scriptletHostnames$ = [];
|
const $scriptletHostnames$ = self.$scriptletHostnames$;
|
||||||
|
|
||||||
const $hasEntities$ = false;
|
const $hasEntities$ = self.$hasEntities$;
|
||||||
const $hasAncestors$ = false;
|
const $hasAncestors$ = self.$hasAncestors$;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
@ -150,7 +150,7 @@ const todo = new Set();
|
||||||
// Execute scriplets
|
// Execute scriplets
|
||||||
{
|
{
|
||||||
const arglists = $scriptletArglists$.split(';');
|
const arglists = $scriptletArglists$.split(';');
|
||||||
const args = $scriptletArgs$.split('\n');
|
const args = $scriptletArgs$;
|
||||||
for ( const ref of todo ) {
|
for ( const ref of todo ) {
|
||||||
if ( ref < 0 ) { continue; }
|
if ( ref < 0 ) { continue; }
|
||||||
if ( todo.has(~ref) ) { continue; }
|
if ( todo.has(~ref) ) { continue; }
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7eab39d94e48ba6410aad20780d8be1e28b70e27
|
Subproject commit 9e03b41c86e9afc79a782f1cefcc5770d53a0c67
|
||||||
Loading…
Reference in a new issue