single-file-core 1.5.57 → 1.5.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/helper.js +109 -8
- package/core/index.js +7 -0
- package/core/lib/processor-helper-common.js +8 -28
- package/core/lib/processor-helper-inline.js +5 -0
- package/core/lib/processor-helper.js +4 -1
- package/core/util.js +9 -15
- package/package.json +2 -2
package/core/helper.js
CHANGED
|
@@ -77,6 +77,7 @@ const EMPTY_RESOURCE = "data:,";
|
|
|
77
77
|
const DEFAULT_REPLACED_CHARACTERS = ["~", "+", "?", "%", "*", ":", "|", "\"", "<", ">", "\\\\", "\x00-\x1f", "\x7F"];
|
|
78
78
|
const DEFAULT_REPLACEMENT_CHARACTER = "_";
|
|
79
79
|
const DEFAULT_REPLACEMENT_CHARACTERS = ["~", "+", "?", "%", "*", ":", "|", """, "<", ">", "\"];
|
|
80
|
+
const NESTING_TRACK_ID_ATTRIBUTE_NAME = "data-sf-nesting-track-id";
|
|
80
81
|
const addEventListener = (type, listener, options) => globalThis.addEventListener(type, listener, options);
|
|
81
82
|
// eslint-disable-next-line no-unused-vars
|
|
82
83
|
const dispatchEvent = event => { try { globalThis.dispatchEvent(event); } catch (error) { /* ignored */ } };
|
|
@@ -87,6 +88,7 @@ const Blob = globalThis.Blob;
|
|
|
87
88
|
const CustomEvent = globalThis.CustomEvent;
|
|
88
89
|
const MutationObserver = globalThis.MutationObserver;
|
|
89
90
|
const URL = globalThis.URL;
|
|
91
|
+
const DOMParser = globalThis.DOMParser;
|
|
90
92
|
|
|
91
93
|
export {
|
|
92
94
|
initUserScriptHandler,
|
|
@@ -103,6 +105,8 @@ export {
|
|
|
103
105
|
getContentSize,
|
|
104
106
|
digest,
|
|
105
107
|
getValidFilename,
|
|
108
|
+
parseDocContent,
|
|
109
|
+
fixInvalidNesting,
|
|
106
110
|
ON_BEFORE_CAPTURE_EVENT_NAME,
|
|
107
111
|
ON_AFTER_CAPTURE_EVENT_NAME,
|
|
108
112
|
WIN_ID_ATTRIBUTE_NAME,
|
|
@@ -130,7 +134,8 @@ export {
|
|
|
130
134
|
INFOBAR_TAGNAME,
|
|
131
135
|
WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
132
136
|
MESSAGE_PREFIX,
|
|
133
|
-
NO_SCRIPT_PROPERTY_NAME
|
|
137
|
+
NO_SCRIPT_PROPERTY_NAME,
|
|
138
|
+
NESTING_TRACK_ID_ATTRIBUTE_NAME
|
|
134
139
|
};
|
|
135
140
|
|
|
136
141
|
function initUserScriptHandler() {
|
|
@@ -205,13 +210,7 @@ function preProcessDoc(doc, win, options) {
|
|
|
205
210
|
const invalidElements = new Map();
|
|
206
211
|
let elementsInfo;
|
|
207
212
|
if (win && doc.documentElement) {
|
|
208
|
-
doc
|
|
209
|
-
const placeHolderElement = doc.createElement("template");
|
|
210
|
-
placeHolderElement.setAttribute(INVALID_ELEMENT_ATTRIBUTE_NAME, "");
|
|
211
|
-
placeHolderElement.content.appendChild(element.cloneNode(true));
|
|
212
|
-
invalidElements.set(element, placeHolderElement);
|
|
213
|
-
element.replaceWith(placeHolderElement);
|
|
214
|
-
});
|
|
213
|
+
markInvalidNesting(doc);
|
|
215
214
|
elementsInfo = getElementsInfo(win, doc, doc.documentElement, options);
|
|
216
215
|
if (options.moveStylesInHead) {
|
|
217
216
|
doc.querySelectorAll("body style, body ~ style").forEach(element => {
|
|
@@ -260,6 +259,91 @@ function preProcessDoc(doc, win, options) {
|
|
|
260
259
|
};
|
|
261
260
|
}
|
|
262
261
|
|
|
262
|
+
function markInvalidNesting(doc) {
|
|
263
|
+
addTrackIds(doc.body);
|
|
264
|
+
const verificationDoc = parseDocContent(serialize(doc));
|
|
265
|
+
const markedMap = buildTrackIdMap(doc.body);
|
|
266
|
+
const normalizedMap = buildTrackIdMap(verificationDoc.body);
|
|
267
|
+
const trackIds = new Set();
|
|
268
|
+
Object.keys(markedMap).forEach(id => {
|
|
269
|
+
if (id in normalizedMap) {
|
|
270
|
+
const markedParent = markedMap[id].parentElement?.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME) || null;
|
|
271
|
+
const normalizedParent = normalizedMap[id]?.parentElement?.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME) || null;
|
|
272
|
+
if (markedParent !== normalizedParent) {
|
|
273
|
+
let current = markedMap[id];
|
|
274
|
+
while (current && current !== doc.body) {
|
|
275
|
+
const currentId = current.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
276
|
+
if (currentId) {
|
|
277
|
+
trackIds.add(currentId);
|
|
278
|
+
}
|
|
279
|
+
current = current.parentElement;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
cleanupTrackIds(doc.body, trackIds);
|
|
285
|
+
|
|
286
|
+
function addTrackIds(element, index = 0, parentTrackId = "") {
|
|
287
|
+
const trackId = parentTrackId ? `${parentTrackId}.${index + 1}` : `${index + 1}`;
|
|
288
|
+
element.setAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME, trackId);
|
|
289
|
+
Array.from(element.children).forEach((child, indexChild) => addTrackIds(child, indexChild, trackId));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function buildTrackIdMap(element) {
|
|
293
|
+
const trackIds = {};
|
|
294
|
+
traverse(element);
|
|
295
|
+
return trackIds;
|
|
296
|
+
|
|
297
|
+
function traverse(element) {
|
|
298
|
+
if (element.getAttribute) {
|
|
299
|
+
const id = element.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
300
|
+
if (id) {
|
|
301
|
+
trackIds[id] = element;
|
|
302
|
+
}
|
|
303
|
+
Array.from(element.children).forEach(traverse);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function cleanupTrackIds(element, toKeep) {
|
|
309
|
+
const id = element.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
310
|
+
if (id && !toKeep.has(id)) {
|
|
311
|
+
element.removeAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
312
|
+
}
|
|
313
|
+
Array.from(element.children).forEach(child => cleanupTrackIds(child, toKeep));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function fixInvalidNesting(document, NESTING_TRACK_ID_ATTRIBUTE_NAME, preventCleanup = false) {
|
|
318
|
+
const trackIds = {};
|
|
319
|
+
if (document.currentScript) {
|
|
320
|
+
document.currentScript.remove();
|
|
321
|
+
}
|
|
322
|
+
buildTrackIdMap(document.body);
|
|
323
|
+
Object.keys(trackIds).forEach(id => {
|
|
324
|
+
const element = trackIds[id];
|
|
325
|
+
const idParts = id.split(".");
|
|
326
|
+
if (idParts.length > 1) {
|
|
327
|
+
const parentId = idParts.slice(0, -1).join(".");
|
|
328
|
+
const expectedParent = trackIds[parentId];
|
|
329
|
+
if (expectedParent && element.parentElement !== expectedParent) {
|
|
330
|
+
expectedParent.appendChild(element);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
if (!preventCleanup) {
|
|
335
|
+
Object.keys(trackIds).forEach(id => trackIds[id].removeAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function buildTrackIdMap(element) {
|
|
339
|
+
const id = element.getAttribute(NESTING_TRACK_ID_ATTRIBUTE_NAME);
|
|
340
|
+
if (id) {
|
|
341
|
+
trackIds[id] = element;
|
|
342
|
+
}
|
|
343
|
+
Array.from(element.children).forEach(buildTrackIdMap);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
263
347
|
function getElementsInfo(win, doc, element, options, data = { usedFonts: new Map(), canvases: [], images: [], posters: [], videos: [], shadowRoots: [], markedElements: [] }, adoptedStyleSheetsCache = new Map(), ascendantHidden) {
|
|
264
348
|
if (element.childNodes) {
|
|
265
349
|
const elements = Array.from(element.childNodes).filter(node => (node instanceof win.HTMLElement) || (node instanceof win.SVGElement) || (node instanceof globalThis.HTMLElement) || (node instanceof globalThis.SVGElement));
|
|
@@ -746,4 +830,21 @@ function getValidFilename(filename, replacedCharacters = DEFAULT_REPLACED_CHARAC
|
|
|
746
830
|
.replace(/\.\//g, "." + replacementCharacter)
|
|
747
831
|
.replace(/\/\./g, "/" + replacementCharacter);
|
|
748
832
|
return filename;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function parseDocContent(content, baseURI) {
|
|
836
|
+
const doc = (new DOMParser()).parseFromString(content, "text/html");
|
|
837
|
+
if (!doc.head) {
|
|
838
|
+
doc.documentElement.insertBefore(doc.createElement("HEAD"), doc.body);
|
|
839
|
+
}
|
|
840
|
+
let baseElement = doc.querySelector("base");
|
|
841
|
+
if (!baseElement || !baseElement.getAttribute("href")) {
|
|
842
|
+
if (baseElement) {
|
|
843
|
+
baseElement.remove();
|
|
844
|
+
}
|
|
845
|
+
baseElement = doc.createElement("base");
|
|
846
|
+
baseElement.setAttribute("href", baseURI);
|
|
847
|
+
doc.head.insertBefore(baseElement, doc.head.firstChild);
|
|
848
|
+
}
|
|
849
|
+
return doc;
|
|
749
850
|
}
|
package/core/index.js
CHANGED
|
@@ -470,6 +470,7 @@ class Processor {
|
|
|
470
470
|
pageContent = content.data || "";
|
|
471
471
|
}
|
|
472
472
|
this.doc = util.parseDocContent(pageContent, this.baseURI);
|
|
473
|
+
util.fixInvalidNesting(this.doc);
|
|
473
474
|
if (this.options.saveRawPage) {
|
|
474
475
|
let charset;
|
|
475
476
|
this.doc.querySelectorAll("meta[charset]").forEach(element => {
|
|
@@ -582,6 +583,11 @@ class Processor {
|
|
|
582
583
|
if (this.options.displayStats) {
|
|
583
584
|
size = util.getContentSize(this.doc.documentElement.outerHTML);
|
|
584
585
|
}
|
|
586
|
+
if (this.doc.querySelector(`[${util.NESTING_TRACK_ID_ATTRIBUTE_NAME}]`)) {
|
|
587
|
+
const scriptElement = this.doc.createElement("script");
|
|
588
|
+
scriptElement.textContent = `(${util.getFixInvalidNestingSource()})(document, "${util.NESTING_TRACK_ID_ATTRIBUTE_NAME}");`;
|
|
589
|
+
this.doc.body.appendChild(scriptElement);
|
|
590
|
+
}
|
|
585
591
|
const content = util.serialize(this.doc, this.options.compressHTML);
|
|
586
592
|
if (this.options.displayStats) {
|
|
587
593
|
const contentSize = util.getContentSize(content);
|
|
@@ -896,6 +902,7 @@ class Processor {
|
|
|
896
902
|
this.doc.querySelectorAll("a[ping], area[ping]").forEach(element => element.removeAttribute("ping"));
|
|
897
903
|
this.doc.querySelectorAll("a[attributionsrc], img[attributionsrc], script[attributionsrc]").forEach(element => element.removeAttribute("attributionsrc"));
|
|
898
904
|
this.doc.querySelectorAll("link[rel=import][href]").forEach(element => element.remove());
|
|
905
|
+
this.doc.querySelectorAll("link[rel=compression-dictionary]").forEach(element => element.remove());
|
|
899
906
|
}
|
|
900
907
|
|
|
901
908
|
replaceInvalidElements() {
|
|
@@ -329,37 +329,17 @@ class ProcessorHelperCommon {
|
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
replacePseudoClassDefined(stylesheet) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
332
|
+
cssTree.walk(stylesheet, {
|
|
333
|
+
enter: function(node, item, list) {
|
|
334
|
+
if (node.type == "PseudoClassSelector" && node.name == "defined") {
|
|
335
|
+
if (item.prev == null || item.prev.data.type == "Combinator" || item.prev.data.type == "WhiteSpace") {
|
|
336
|
+
list.replace(item, cssTree.parse("*", { context: "selector" }).children.head);
|
|
337
|
+
} else {
|
|
338
|
+
list.remove(item);
|
|
339
339
|
}
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
|
-
}
|
|
343
|
-
if (removedSelectors.length) {
|
|
344
|
-
removedSelectors.forEach(({ parentSelector, selector }) => {
|
|
345
|
-
if (parentSelector.data.children.size == 0 || !selector.prev || selector.prev.data.type == "Combinator" || selector.prev.data.type == "WhiteSpace") {
|
|
346
|
-
parentSelector.data.children.replace(selector, cssTree.parse("*", { context: "selector" }).children.head);
|
|
347
|
-
} else {
|
|
348
|
-
parentSelector.data.children.remove(selector);
|
|
349
|
-
}
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function replacePseudoDefinedSelector(selector, parentSelector) {
|
|
354
|
-
if (selector.data.children) {
|
|
355
|
-
for (let childSelector = selector.data.children.head; childSelector; childSelector = childSelector.next) {
|
|
356
|
-
replacePseudoDefinedSelector(childSelector, selector);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
if (selector.data.type == "PseudoClassSelector" && selector.data.name == "defined") {
|
|
360
|
-
removedSelectors.push({ parentSelector, selector });
|
|
361
|
-
}
|
|
362
|
-
}
|
|
342
|
+
});
|
|
363
343
|
}
|
|
364
344
|
|
|
365
345
|
resolveStylesheetURLs(stylesheet, baseURI, workStylesheet) {
|
|
@@ -99,6 +99,11 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
99
99
|
this.removeSingleLineCssComments(stylesheet);
|
|
100
100
|
}
|
|
101
101
|
this.replacePseudoClassDefined(stylesheet);
|
|
102
|
+
options.inlineStylesheets.forEach((content, index) => {
|
|
103
|
+
if (content === element.textContent) {
|
|
104
|
+
options.inlineStylesheets.set(index, this.generateStylesheetContent(stylesheet, options));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
102
107
|
stylesheetInfo.stylesheet = stylesheet;
|
|
103
108
|
} else {
|
|
104
109
|
stylesheets.delete(element);
|
|
@@ -102,7 +102,10 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
102
102
|
linkElement.setAttribute("type", "text/css");
|
|
103
103
|
const name = "stylesheet_" + resources.stylesheets.size + ".css";
|
|
104
104
|
linkElement.setAttribute("href", name);
|
|
105
|
-
|
|
105
|
+
let content = options.inlineStylesheets.get(stylesheetRefIndex);
|
|
106
|
+
const stylesheet = cssTree.parse(content, { context: "stylesheet", parseCustomProperty: true });
|
|
107
|
+
this.replacePseudoClassDefined(stylesheet);
|
|
108
|
+
content = this.generateStylesheetContent(stylesheet, options);
|
|
106
109
|
resources.stylesheets.set(resources.stylesheets.size, { name, content });
|
|
107
110
|
linkElements.set(stylesheetRefIndex, linkElement);
|
|
108
111
|
});
|
package/core/util.js
CHANGED
|
@@ -116,20 +116,7 @@ function getInstance(utilOptions) {
|
|
|
116
116
|
return helper.getValidFilename(filename, replacedCharacters, replacementCharacter, replacementCharacters);
|
|
117
117
|
},
|
|
118
118
|
parseDocContent(content, baseURI) {
|
|
119
|
-
|
|
120
|
-
if (!doc.head) {
|
|
121
|
-
doc.documentElement.insertBefore(doc.createElement("HEAD"), doc.body);
|
|
122
|
-
}
|
|
123
|
-
let baseElement = doc.querySelector("base");
|
|
124
|
-
if (!baseElement || !baseElement.getAttribute("href")) {
|
|
125
|
-
if (baseElement) {
|
|
126
|
-
baseElement.remove();
|
|
127
|
-
}
|
|
128
|
-
baseElement = doc.createElement("base");
|
|
129
|
-
baseElement.setAttribute("href", baseURI);
|
|
130
|
-
doc.head.insertBefore(baseElement, doc.head.firstChild);
|
|
131
|
-
}
|
|
132
|
-
return doc;
|
|
119
|
+
return helper.parseDocContent(content, baseURI);
|
|
133
120
|
},
|
|
134
121
|
parseXMLContent(content) {
|
|
135
122
|
return (new DOMParser()).parseFromString(content, "text/xml");
|
|
@@ -142,6 +129,12 @@ function getInstance(utilOptions) {
|
|
|
142
129
|
return doc;
|
|
143
130
|
}
|
|
144
131
|
},
|
|
132
|
+
fixInvalidNesting(doc) {
|
|
133
|
+
helper.fixInvalidNesting(doc, helper.NESTING_TRACK_ID_ATTRIBUTE_NAME, true);
|
|
134
|
+
},
|
|
135
|
+
getFixInvalidNestingSource() {
|
|
136
|
+
return helper.fixInvalidNesting.toString().replace(/\s+/g, " ");
|
|
137
|
+
},
|
|
145
138
|
async digest(algo, text) {
|
|
146
139
|
return helper.digest(algo, text);
|
|
147
140
|
},
|
|
@@ -229,7 +222,8 @@ function getInstance(utilOptions) {
|
|
|
229
222
|
EMPTY_RESOURCE: helper.EMPTY_RESOURCE,
|
|
230
223
|
INFOBAR_TAGNAME: helper.INFOBAR_TAGNAME,
|
|
231
224
|
WAIT_FOR_USERSCRIPT_PROPERTY_NAME: helper.WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
232
|
-
NO_SCRIPT_PROPERTY_NAME: helper.NO_SCRIPT_PROPERTY_NAME
|
|
225
|
+
NO_SCRIPT_PROPERTY_NAME: helper.NO_SCRIPT_PROPERTY_NAME,
|
|
226
|
+
NESTING_TRACK_ID_ATTRIBUTE_NAME: helper.NESTING_TRACK_ID_ATTRIBUTE_NAME
|
|
233
227
|
};
|
|
234
228
|
|
|
235
229
|
async function getContent(resourceURL, options) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "single-file-core",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.59",
|
|
4
4
|
"description": "SingleFile Core",
|
|
5
5
|
"author": "Gildas Lormeau",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|
|
@@ -16,6 +16,6 @@
|
|
|
16
16
|
},
|
|
17
17
|
"homepage": "https://github.com/gildas-lormeau/single-file-core#readme",
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"eslint": "^9.
|
|
19
|
+
"eslint": "^9.39.1"
|
|
20
20
|
}
|
|
21
21
|
}
|