single-file-core 1.6.24 → 1.6.25

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 CHANGED
@@ -69,6 +69,7 @@ const DISABLED_NOSCRIPT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "disable
69
69
  const SELECTED_CONTENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "selected-content";
70
70
  const INVALID_ELEMENT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "invalid-element";
71
71
  const ASYNC_SCRIPT_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "async-script";
72
+ const CUSTOM_STYLESHEET_ATTRIBUTE_NAME = "data-" + SINGLE_FILE_PREFIX + "custom-stylesheet";
72
73
  const FLOW_ELEMENTS_SELECTOR = "*:not(base):not(link):not(meta):not(noscript):not(script):not(style):not(template):not(title)";
73
74
  const KEPT_TAG_NAMES = ["NOSCRIPT", "DISABLED-NOSCRIPT", "META", "LINK", "STYLE", "TITLE", "TEMPLATE", "SOURCE", "OBJECT", "SCRIPT", "HEAD", "BODY"];
74
75
  const INVOKABLE_ELEMENTS_SELECTOR = "[popover][id], dialog[id]";
@@ -173,6 +174,7 @@ export {
173
174
  WIN_ID_ATTRIBUTE_NAME,
174
175
  PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME,
175
176
  REMOVED_CONTENT_ATTRIBUTE_NAME,
177
+ CUSTOM_STYLESHEET_ATTRIBUTE_NAME,
176
178
  HIDDEN_CONTENT_ATTRIBUTE_NAME,
177
179
  HIDDEN_FRAME_ATTRIBUTE_NAME,
178
180
  IMAGE_ATTRIBUTE_NAME,
@@ -305,6 +307,9 @@ function preProcessDoc(doc, win, options) {
305
307
  element.textContent = "";
306
308
  });
307
309
  initDoc(doc);
310
+ if (options.customStylesheet) {
311
+ insertCustomStylesheet(doc, options.customStylesheet);
312
+ }
308
313
  if (doc.head) {
309
314
  doc.head.querySelectorAll(FLOW_ELEMENTS_SELECTOR).forEach(element => element.hidden = true);
310
315
  }
@@ -375,6 +380,16 @@ function preProcessDoc(doc, win, options) {
375
380
  };
376
381
  }
377
382
 
383
+ function insertCustomStylesheet(doc, content) {
384
+ const parentElement = doc.body || doc.documentElement;
385
+ if (parentElement) {
386
+ const styleElement = doc.createElement("style");
387
+ styleElement.setAttribute(CUSTOM_STYLESHEET_ATTRIBUTE_NAME, "");
388
+ styleElement.textContent = content;
389
+ parentElement.appendChild(styleElement);
390
+ }
391
+ }
392
+
378
393
  function markInvalidNesting(doc) {
379
394
  if (!doc.body) {
380
395
  return;
@@ -1093,6 +1108,7 @@ function testHiddenElement(element, computedStyle) {
1093
1108
 
1094
1109
  function postProcessDoc(doc, markedElements, invalidElements) {
1095
1110
  removeNestingMarkers(doc);
1111
+ doc.querySelectorAll("style[" + CUSTOM_STYLESHEET_ATTRIBUTE_NAME + "]").forEach(element => element.remove());
1096
1112
  doc.querySelectorAll("[" + DISABLED_NOSCRIPT_ATTRIBUTE_NAME + "]").forEach(element => {
1097
1113
  element.textContent = element.getAttribute(DISABLED_NOSCRIPT_ATTRIBUTE_NAME);
1098
1114
  element.removeAttribute(DISABLED_NOSCRIPT_ATTRIBUTE_NAME);
package/core/index.js CHANGED
@@ -661,6 +661,7 @@ class Processor {
661
661
  }
662
662
 
663
663
  preProcessPage() {
664
+ this.doc.querySelectorAll("style[" + util.CUSTOM_STYLESHEET_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(util.CUSTOM_STYLESHEET_ATTRIBUTE_NAME));
664
665
  this.doc.body.querySelectorAll(":not(svg) title, meta, link[href][rel*=\"icon\"]").forEach(element => {
665
666
  if ((this.options.win && element instanceof this.options.win.HTMLElement) || element instanceof globalThis.HTMLElement) {
666
667
  this.doc.head.appendChild(element);
@@ -331,13 +331,15 @@ function getProcessorHelperClass(utilInstance) {
331
331
  blockMixedContent: options.blockMixedContent,
332
332
  ...getStylesheetReferrer(resourceURL, options)
333
333
  });
334
- const name = "fonts/" + indexResource + extension;
334
+ const name = this.testEmptyResource(content) ? util.EMPTY_RESOURCE : "fonts/" + indexResource + extension;
335
335
  if (!isDataURL(resourceURL) && options.saveOriginalURLs) {
336
336
  urlNode.value = "-sf-url-original(" + JSON.stringify(originalResourceURL) + ") " + name;
337
337
  } else {
338
338
  urlNode.value = name;
339
339
  }
340
- resources.fonts.set(indexResource, { name, content, extension, contentType, url: resourceURL });
340
+ if (!this.testEmptyResource(content)) {
341
+ resources.fonts.set(indexResource, { name, content, extension, contentType, url: resourceURL });
342
+ }
341
343
  }
342
344
 
343
345
  groupDuplicateFonts(stylesheets, fonts) {
@@ -412,13 +414,15 @@ function getProcessorHelperClass(utilInstance) {
412
414
  if ((!testIgnoredPath(resourceURL) && testValidURL(resourceURL)) || testGeneratedDataURI(resourceURL, options)) {
413
415
  let { content, indexResource, contentType, extension } = await batchRequest.addURL(resourceURL,
414
416
  { asBinary: true, expectedType: "image", ...getStylesheetReferrer(resourceURL, options) });
415
- const name = "images/" + indexResource + extension;
417
+ const name = this.testEmptyResource(content) ? util.EMPTY_RESOURCE : "images/" + indexResource + extension;
416
418
  if (!isDataURL(resourceURL) && options.saveOriginalURLs) {
417
419
  urlNode.value = "-sf-url-original(" + JSON.stringify(originalResourceURL) + ") " + name;
418
420
  } else {
419
421
  urlNode.value = name;
420
422
  }
421
- resources.images.set(indexResource, { name, content, extension, contentType, url: resourceURL });
423
+ if (!this.testEmptyResource(content)) {
424
+ resources.images.set(indexResource, { name, content, extension, contentType, url: resourceURL });
425
+ }
422
426
  }
423
427
  } else {
424
428
  urlNode.value = util.EMPTY_RESOURCE;
@@ -505,8 +509,10 @@ function getProcessorHelperClass(utilInstance) {
505
509
 
506
510
  async processImageSrcset(resourceURL, srcsetValue, resources, batchRequest, resourceElement) {
507
511
  const { content, indexResource, extension, contentType } = await batchRequest.addURL(resourceURL, { asBinary: true, expectedType: "image", referrerPolicy: getReferrerPolicy(resourceElement) });
508
- const name = "images/" + indexResource + extension;
509
- resources.images.set(indexResource, { name, content, extension, contentType, url: resourceURL });
512
+ const name = this.testEmptyResource(content) ? util.EMPTY_RESOURCE : "images/" + indexResource + extension;
513
+ if (!this.testEmptyResource(content)) {
514
+ resources.images.set(indexResource, { name, content, extension, contentType, url: resourceURL });
515
+ }
510
516
  resourceElement.removeAttribute(CROSS_ORIGIN_ATTRIBUTE_NAME);
511
517
  return serializeSrcset([Object.assign({}, srcsetValue, { url: name })]);
512
518
  }
package/core/util.js CHANGED
@@ -70,6 +70,8 @@ const ZIP_FILENAME_OFFSET = 30;
70
70
  const USD_FILENAME_REGEXP = /\.usd[ac]?$/i;
71
71
  const CONTENT_TYPES_HTML = ["text/html", "application/xhtml+xml"];
72
72
  const EXPECTED_TYPES_MEDIA = ["font", "image", "video", "audio", "model"];
73
+ const EXPECTED_TYPE_IMAGE = "image";
74
+ const EXPECTED_TYPE_DOCUMENT = "document";
73
75
 
74
76
  const URL = globalThis.URL;
75
77
  const DOMParser = globalThis.DOMParser;
@@ -226,6 +228,7 @@ function getInstance(utilOptions) {
226
228
  PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME: helper.PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME,
227
229
  STYLESHEET_ATTRIBUTE_NAME: helper.STYLESHEET_ATTRIBUTE_NAME,
228
230
  LINK_STYLESHEET_ATTRIBUTE_NAME: helper.LINK_STYLESHEET_ATTRIBUTE_NAME,
231
+ CUSTOM_STYLESHEET_ATTRIBUTE_NAME: helper.CUSTOM_STYLESHEET_ATTRIBUTE_NAME,
229
232
  SELECTED_CONTENT_ATTRIBUTE_NAME: helper.SELECTED_CONTENT_ATTRIBUTE_NAME,
230
233
  INVALID_ELEMENT_ATTRIBUTE_NAME: helper.INVALID_ELEMENT_ATTRIBUTE_NAME,
231
234
  COMMENT_HEADER: helper.COMMENT_HEADER,
@@ -320,7 +323,7 @@ function getInstance(utilOptions) {
320
323
  charset = options.charset;
321
324
  }
322
325
  if (options.asBinary) {
323
- if (response.status >= 400 || (EXPECTED_TYPES_MEDIA.includes(options.expectedType) && CONTENT_TYPES_HTML.includes(contentType))) {
326
+ if ((response.status >= 400 && options.expectedType != EXPECTED_TYPE_IMAGE) || (EXPECTED_TYPES_MEDIA.includes(options.expectedType) && CONTENT_TYPES_HTML.includes(contentType))) {
324
327
  return getFetchResponse(resourceURL, options);
325
328
  }
326
329
  try {
@@ -337,7 +340,7 @@ function getInstance(utilOptions) {
337
340
  return getFetchResponse(resourceURL, options);
338
341
  }
339
342
  } else {
340
- if (response.status >= 400) {
343
+ if (response.status >= 400 && options.expectedType != EXPECTED_TYPE_DOCUMENT) {
341
344
  return getFailedFetchResponse(resourceURL, options);
342
345
  }
343
346
  if (options.validateTextContentType && contentType && !contentType.startsWith(PREFIX_CONTENT_TYPE_TEXT)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.6.24",
3
+ "version": "1.6.25",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",