single-file-core 1.3.1 → 1.3.3

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/index.js CHANGED
@@ -336,9 +336,9 @@ class BatchRequest {
336
336
  this.duplicates = new Map();
337
337
  }
338
338
 
339
- addURL(resourceURL, { asBinary, expectedType, groupDuplicates, baseURI, blockMixedContent } = {}) {
339
+ addURL(resourceURL, { asBinary, expectedType, groupDuplicates, baseURI, blockMixedContent, contentType } = {}) {
340
340
  return new Promise((resolve, reject) => {
341
- const requestKey = JSON.stringify([resourceURL, asBinary, expectedType, baseURI, blockMixedContent]);
341
+ const requestKey = JSON.stringify([resourceURL, asBinary, expectedType, baseURI, blockMixedContent, contentType]);
342
342
  let resourceRequests = this.requests.get(requestKey);
343
343
  if (!resourceRequests) {
344
344
  resourceRequests = [];
@@ -365,7 +365,7 @@ class BatchRequest {
365
365
  const resourceURLs = [...this.requests.keys()];
366
366
  let indexResource = 0;
367
367
  return Promise.all(resourceURLs.map(async requestKey => {
368
- const [resourceURL, asBinary, expectedType, baseURI, blockMixedContent] = JSON.parse(requestKey);
368
+ const [resourceURL, asBinary, expectedType, baseURI, blockMixedContent, contentType] = JSON.parse(requestKey);
369
369
  const resourceRequests = this.requests.get(requestKey);
370
370
  try {
371
371
  const currentIndexResource = indexResource;
@@ -374,6 +374,7 @@ class BatchRequest {
374
374
  asBinary,
375
375
  inline: !options.compressContent,
376
376
  expectedType,
377
+ contentType,
377
378
  maxResourceSize: options.maxResourceSize,
378
379
  maxResourceSizeEnabled: options.maxResourceSizeEnabled,
379
380
  frameId: options.windowId,
@@ -1343,7 +1344,7 @@ class Processor {
1343
1344
  }
1344
1345
  if (testValidURL(resourceURL)) {
1345
1346
  element.removeAttribute("src");
1346
- await this.processorHelper.processScript(element, resourceURL, this.options, this.charset);
1347
+ await this.processorHelper.processScript(element, resourceURL, this.options, this.charset, this.batchRequest, this.resources);
1347
1348
  if (element.getAttribute("async") == "async" || element.getAttribute(util.ASYNC_SCRIPT_ATTRIBUTE_NAME) == "") {
1348
1349
  element.setAttribute("async", "");
1349
1350
  }
@@ -21,6 +21,8 @@
21
21
  * Source.
22
22
  */
23
23
 
24
+ /* global globalThis */
25
+
24
26
  import {
25
27
  normalizeFontFamily,
26
28
  getFontWeight
@@ -59,6 +61,8 @@ const FONT_STRETCHES = {
59
61
  "extra-expanded": "150%",
60
62
  "ultra-expanded": "200%"
61
63
  };
64
+ const Blob = globalThis.Blob;
65
+ const FileReader = globalThis.FileReader;
62
66
 
63
67
  let util, cssTree;
64
68
 
@@ -74,7 +78,8 @@ export {
74
78
  replaceOriginalURLs,
75
79
  testIgnoredPath,
76
80
  testValidPath,
77
- testValidURL
81
+ testValidURL,
82
+ toDataURI
78
83
  };
79
84
 
80
85
  function getProcessorHelperCommonClass(utilInstance, cssTreeInstance) {
@@ -575,12 +580,10 @@ function processFontDetails(fontsDetails, fontResources) {
575
580
  }
576
581
  }
577
582
 
578
- function getUpdatedResourceContent(resourceURL, content, options) {
583
+ function getUpdatedResourceContent(resourceURL, options) {
579
584
  if (options.rootDocument && options.updatedResources[resourceURL]) {
580
585
  options.updatedResources[resourceURL].retrieved = true;
581
586
  return options.updatedResources[resourceURL].content;
582
- } else {
583
- return content.data || "";
584
587
  }
585
588
  }
586
589
 
@@ -652,4 +655,13 @@ function getURL(urlFunction) {
652
655
 
653
656
  function getFontStretch(stretch) {
654
657
  return FONT_STRETCHES[stretch] || stretch;
658
+ }
659
+
660
+ function toDataURI(content, contentType, charset) {
661
+ return new Promise((resolve, reject) => {
662
+ const reader = new FileReader();
663
+ reader.onload = () => resolve(reader.result);
664
+ reader.onerror = () => reject(new Error(reader.error));
665
+ reader.readAsDataURL(new Blob([content], { type: (contentType || "") + (charset ? ";charset=" + charset : "") }));
666
+ });
655
667
  }
@@ -59,7 +59,8 @@ import {
59
59
  replaceOriginalURLs,
60
60
  testIgnoredPath,
61
61
  testValidPath,
62
- testValidURL
62
+ testValidURL,
63
+ toDataURI
63
64
  } from "./processor-helper-common.js";
64
65
 
65
66
  export {
@@ -152,7 +153,7 @@ function getProcessorHelperClass(utilInstance) {
152
153
  options.inline = true;
153
154
  const content = await this.getStylesheetContent(resourceURL, options);
154
155
  resourceURL = content.resourceURL;
155
- content.data = getUpdatedResourceContent(resourceURL, content, options);
156
+ content.data = getUpdatedResourceContent(resourceURL, options) || content.data;
156
157
  if (content.data && content.data.match(/^<!doctype /i)) {
157
158
  content.data = "";
158
159
  }
@@ -200,13 +201,11 @@ function getProcessorHelperClass(utilInstance) {
200
201
  return this.resolveLinkStylesheetURLs(resourceURL, baseURI, options, workStylesheet);
201
202
  }
202
203
  resourceURL = content.resourceURL;
203
- content.data = getUpdatedResourceContent(content.resourceURL, content, options);
204
+ content.data = getUpdatedResourceContent(content.resourceURL, options) || content.data;
204
205
  if (content.data && content.data.match(/^<!doctype /i)) {
205
206
  content.data = "";
206
207
  }
207
-
208
208
  content.data = content.data.replace(/:defined/gi, "*");
209
-
210
209
  let stylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
211
210
  const importFound = await this.resolveImportURLs(stylesheet, resourceURL, options, workStylesheet);
212
211
  if (importFound) {
@@ -305,9 +304,11 @@ function getProcessorHelperClass(utilInstance) {
305
304
  // ignored
306
305
  }
307
306
  if (testValidURL(resourceURL)) {
307
+ const declaredContentType = ["OBJECT", "EMBED"].includes(resourceElement.tagName.toUpperCase()) ? resourceElement.getAttribute("type") : "";
308
+ const groupDuplicates = options.groupDuplicateImages && resourceElement.tagName.toUpperCase() == "IMG" && attributeName == "src";
308
309
  let { content, indexResource, duplicate } = await batchRequest.addURL(
309
310
  resourceURL,
310
- { asBinary: true, expectedType, groupDuplicates: options.groupDuplicateImages && resourceElement.tagName.toUpperCase() == "IMG" && attributeName == "src" });
311
+ { asBinary: true, expectedType, contentType: declaredContentType, groupDuplicates });
311
312
  if (originURL) {
312
313
  if (this.testEmptyResource(content)) {
313
314
  try {
@@ -321,6 +322,7 @@ function getProcessorHelperClass(utilInstance) {
321
322
  asBinary: true,
322
323
  inline: true,
323
324
  expectedType,
325
+ contentType: declaredContentType,
324
326
  maxResourceSize: options.maxResourceSize,
325
327
  maxResourceSizeEnabled: options.maxResourceSizeEnabled,
326
328
  frameId: options.windowId,
@@ -449,23 +451,28 @@ function getProcessorHelperClass(utilInstance) {
449
451
  return {};
450
452
  }
451
453
 
452
- async processScript(element, resourceURL, options, charset) {
453
- const content = await util.getContent(resourceURL, {
454
- asBinary: true,
455
- inline: true,
456
- charset: charset != UTF8_CHARSET && charset,
457
- maxResourceSize: options.maxResourceSize,
458
- maxResourceSizeEnabled: options.maxResourceSizeEnabled,
459
- frameId: options.windowId,
460
- resourceReferrer: options.resourceReferrer,
461
- baseURI: options.baseURI,
462
- blockMixedContent: options.blockMixedContent,
463
- expectedType: "script",
464
- acceptHeaders: options.acceptHeaders,
465
- networkTimeout: options.networkTimeout
466
- });
467
- content.data = getUpdatedResourceContent(resourceURL, content, options);
468
- element.setAttribute("src", content.data);
454
+ async processScript(element, resourceURL, options, charset, batchRequest) {
455
+ let content = getUpdatedResourceContent(resourceURL, options);
456
+ if (content) {
457
+ content = await toDataURI(content, "text/javascript", charset);
458
+ } else {
459
+ const result = await batchRequest.addURL(resourceURL, {
460
+ asBinary: true,
461
+ inline: true,
462
+ charset: charset != UTF8_CHARSET && charset,
463
+ maxResourceSize: options.maxResourceSize,
464
+ maxResourceSizeEnabled: options.maxResourceSizeEnabled,
465
+ frameId: options.windowId,
466
+ resourceReferrer: options.resourceReferrer,
467
+ baseURI: options.baseURI,
468
+ blockMixedContent: options.blockMixedContent,
469
+ expectedType: "script",
470
+ acceptHeaders: options.acceptHeaders,
471
+ networkTimeout: options.networkTimeout
472
+ });
473
+ content = result.content;
474
+ }
475
+ element.setAttribute("src", content);
469
476
  }
470
477
 
471
478
  setMetaCSP(metaElement) {
@@ -154,7 +154,7 @@ function getProcessorHelperClass(utilInstance) {
154
154
  if (existingStylesheet) {
155
155
  stylesheets.set({ urlNode }, { url: resourceURL, stylesheet: existingStylesheet[1].stylesheet, scoped });
156
156
  } else {
157
- content.data = getUpdatedResourceContent(resourceURL, content, options);
157
+ content.data = getUpdatedResourceContent(resourceURL, options) || content.data;
158
158
  stylesheetInfo.stylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
159
159
  await this.resolveImportURLs(stylesheetInfo, resourceURL, options, workStylesheet, resources, stylesheets);
160
160
  stylesheets.set({ urlNode }, stylesheetInfo);
@@ -202,7 +202,7 @@ function getProcessorHelperClass(utilInstance) {
202
202
  mediaText: stylesheetInfo.mediaText
203
203
  });
204
204
  } else {
205
- content.data = getUpdatedResourceContent(content.resourceURL, content, options);
205
+ content.data = getUpdatedResourceContent(content.resourceURL, options) || content.data;
206
206
  stylesheetInfo.stylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
207
207
  await this.resolveImportURLs(stylesheetInfo, resourceURL, options, workStylesheet, resources, stylesheets);
208
208
  stylesheets.set({ element }, stylesheetInfo);
@@ -280,8 +280,9 @@ function getProcessorHelperClass(utilInstance) {
280
280
  // ignored
281
281
  }
282
282
  if (testValidURL(resourceURL)) {
283
+ const declaredContentType = ["OBJECT", "EMBED"].includes(resourceElement.tagName.toUpperCase()) ? resourceElement.getAttribute("type") : "";
283
284
  let { content, indexResource, extension, contentType } = await batchRequest.addURL(resourceURL,
284
- { asBinary: true, expectedType });
285
+ { asBinary: true, expectedType, contentType: declaredContentType });
285
286
  if (originURL) {
286
287
  if (this.testEmptyResource(content)) {
287
288
  try {
@@ -294,6 +295,7 @@ function getProcessorHelperClass(utilInstance) {
294
295
  content = (await util.getContent(resourceURL, {
295
296
  asBinary: true,
296
297
  expectedType,
298
+ contentType: declaredContentType,
297
299
  maxResourceSize: options.maxResourceSize,
298
300
  maxResourceSizeEnabled: options.maxResourceSizeEnabled,
299
301
  frameId: options.windowId,
@@ -376,8 +378,8 @@ function getProcessorHelperClass(utilInstance) {
376
378
  };
377
379
  }
378
380
 
379
- async processScript(element, resourceURL, options, charset) {
380
- const content = await util.getContent(resourceURL, {
381
+ async processScript(element, resourceURL, options, charset, batchRequest, resources) {
382
+ let { content, indexResource, extension, contentType } = await batchRequest.addURL(resourceURL, {
381
383
  asBinary: true,
382
384
  charset: charset != UTF8_CHARSET && charset,
383
385
  maxResourceSize: options.maxResourceSize,
@@ -390,8 +392,10 @@ function getProcessorHelperClass(utilInstance) {
390
392
  acceptHeaders: options.acceptHeaders,
391
393
  networkTimeout: options.networkTimeout
392
394
  });
393
- content.data = getUpdatedResourceContent(resourceURL, content, options);
394
- element.setAttribute("src", content.data);
395
+ content = getUpdatedResourceContent(resourceURL, options) || content;
396
+ const name = "scripts/" + indexResource + extension;
397
+ element.setAttribute("src", name);
398
+ resources.scripts.set(indexResource, { name, content, extension, contentType, url: resourceURL });
395
399
  }
396
400
 
397
401
  setMetaCSP(metaElement) {
package/core/util.js CHANGED
@@ -286,7 +286,10 @@ function getInstance(utilOptions) {
286
286
  // ignored
287
287
  }
288
288
  if (!contentType || (contentType == CONTENT_TYPE_OCTET_STREAM && options.asBinary)) {
289
- contentType = guessMIMEType(options.expectedType, buffer, options.asBinary);
289
+ contentType = guessMIMEType(options.expectedType, buffer);
290
+ if (!contentType) {
291
+ contentType = options.contentType ? options.contentType : options.asBinary ? CONTENT_TYPE_OCTET_STREAM : "";
292
+ }
290
293
  }
291
294
  if (!charset && options.charset) {
292
295
  charset = options.charset;
@@ -366,7 +369,7 @@ async function getFetchResponse(resourceURL, options, data, charset, contentType
366
369
  return { data, resourceURL, charset, contentType };
367
370
  }
368
371
 
369
- function guessMIMEType(expectedType, buffer, asBinary) {
372
+ function guessMIMEType(expectedType, buffer) {
370
373
  if (expectedType == "image") {
371
374
  if (compareBytes([255, 255, 255, 255], [0, 0, 1, 0])) {
372
375
  return "image/x-icon";
@@ -463,7 +466,6 @@ function guessMIMEType(expectedType, buffer, asBinary) {
463
466
  return "audio/3gpp";
464
467
  }
465
468
  }
466
- return asBinary ? CONTENT_TYPE_OCTET_STREAM : "";
467
469
 
468
470
  function compareBytes(mask, pattern) {
469
471
  let patternMatch = true, index = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -62,7 +62,8 @@ async function extract(content, { password, prompt = () => { }, shadowRootScript
62
62
  "ttf": "font/ttf",
63
63
  "woff": "font/woff",
64
64
  "woff2": "font/woff2",
65
- "eot": "application/vnd.ms-fontobject"
65
+ "eot": "application/vnd.ms-fontobject",
66
+ "pdf": "application/pdf"
66
67
  };
67
68
  const REGEXP_MATCH_STYLESHEET = /stylesheet_[0-9]+\.css/;
68
69
  const REGEXP_MATCH_SCRIPT = /scripts\/[0-9]+\.js/;