Advertisement
captaindavepdx

Untitled

Aug 12th, 2019
188
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Swift 12.67 KB | None | 0 0
  1. //
  2. //  SingleFile.swift
  3. //  SingleFile
  4. //
  5. //  Created by David Littlefield on 8/5/19.
  6. //  Copyright © 2019 David Littlefield. All rights reserved.
  7. //
  8.  
  9. import Cocoa
  10. import Foundation
  11.  
  12. class SingleFile {
  13.    
  14.     static let shared = SingleFile()
  15.    
  16.     private let javaScript = JavaScript.shared
  17.     private let fileManager = FileManager.default
  18.     private let defaults = UserDefaults.standard
  19.    
  20.     private let libraryFilePaths = [
  21.         "/index.js",
  22.         "/lib/hooks/content/content-hooks-web.js",
  23.         "/lib/hooks/content/content-hooks.js",
  24.         "/lib/hooks/content/content-hooks-frames-web.js",
  25.         "/lib/hooks/content/content-hooks-frames.js",
  26.         "/lib/fetch/content/content-fetch-resources.js",
  27.         "/lib/frame-tree/content/content-frame-tree.js",
  28.         "/lib/lazy/content/content-lazy-loader.js",
  29.         "/lib/single-file/single-file-util.js",
  30.         "/lib/single-file/single-file-helper.js",
  31.         "/lib/single-file/vendor/css-tree.js",
  32.         "/lib/single-file/vendor/html-srcset-parser.js",
  33.         "/lib/single-file/vendor/css-minifier.js",
  34.         "/lib/single-file/vendor/css-font-property-parser.js",
  35.         "/lib/single-file/vendor/css-media-query-parser.js",
  36.         "/lib/single-file/modules/html-minifier.js",
  37.         "/lib/single-file/modules/css-fonts-minifier.js",
  38.         "/lib/single-file/modules/css-fonts-alt-minifier.js",
  39.         "/lib/single-file/modules/css-matched-rules.js",
  40.         "/lib/single-file/modules/css-medias-alt-minifier.js",
  41.         "/lib/single-file/modules/css-rules-minifier.js",
  42.         "/lib/single-file/modules/html-images-alt-minifier.js",
  43.         "/lib/single-file/modules/html-serializer.js",
  44.         "/lib/single-file/single-file-core.js",
  45.         "/lib/single-file/single-file.js"
  46.     ]
  47.    
  48.     private let optionsFilePath = [
  49.         "/extension/core/bg/config.js"
  50.     ]
  51.    
  52.     private let singleFileFilePath = [
  53.         "/cli/back-ends/puppeteer.js"
  54.     ]
  55.    
  56.     let fileNames = [
  57.         "demo",
  58.         "extension",
  59.         "LICENSE",
  60.         "privacy.md",
  61.         "faq.md",
  62.         "index.js",
  63.         "cli",
  64.         "README.MD",
  65.         ".gitignore",
  66.         ".github",
  67.         "manifest.json",
  68.         ".eslintrc.js",
  69.         "lib",
  70.         "_locales",
  71.         "build-extension.sh"
  72.     ]
  73.    
  74.     init() {}
  75.    
  76.     func convert() {
  77.         convertLibrary()
  78.         convertOptions()
  79.         convertSingleFile()
  80.         deleteDirectory()
  81.     }
  82.    
  83.     private func convertLibrary() {
  84.         convertContentHooksWeb()
  85.         convertContentHooks()
  86.         convertContentHooksFramesWeb()
  87.         convertContentFetchResources()
  88.         convertIndex()
  89.         convertFiles()
  90.         mergeFiles()
  91.     }
  92.    
  93.     private func convertContentHooksWeb() {
  94.         let filePath = libraryFilePaths[1]
  95.         let defaultStart = "(() => {"
  96.         let defaultEnd = "})();"
  97.         let customStart = "var contentHooksWeb = `"
  98.         let customEnd = "`"
  99.         javaScript.replace(javaScript: defaultStart, withJavaScript: customStart, inPath: filePath)
  100.         javaScript.replace(javaScript: defaultEnd, withJavaScript: customEnd, inPath: filePath)
  101.         javaScript.removeNewLines(inPath: filePath)
  102.     }
  103.    
  104.     private func convertContentHooks() {
  105.         let filePath = libraryFilePaths[2]
  106.         let defaultStart = """
  107.        if (this.browser && browser.runtime && browser.runtime.getURL) {
  108.            scriptElement.src = browser.runtime.getURL("/lib/hooks/content/content-hooks-web.js");
  109.            scriptElement.async = false;
  110.        } else if (this.singlefile.lib.getFileContent) {
  111.            scriptElement.textContent = this.singlefile.lib.getFileContent("/lib/hooks/content/content-hooks-web.js");
  112.        }
  113.        """
  114.         let defaultEnd = "scriptElement.async = false;"
  115.         let customEnd = "scriptElement.textContent = contentHooksWeb;"
  116.         //javaScript.replace(javaScript: defaultStart, withJavaScript: customStart, inPath: filePath)
  117.         javaScript.remove(javaScript: defaultStart, inPath: filePath)
  118.         javaScript.insert(javaScript: customEnd, afterJavaScript: defaultEnd, inPath: filePath)
  119.         javaScript.removeNewLines(inPath: filePath)
  120.     }
  121.    
  122.     private func convertContentHooksFramesWeb() {
  123.         let filePath = libraryFilePaths[3]
  124.         let defaultStart = "(() => {"
  125.         let defaultEnd = "})();"
  126.         let customStart = "var contentHooksFramesWeb = `"
  127.         let customEnd = "`"
  128.         javaScript.replace(javaScript:defaultStart, withJavaScript: customStart, inPath: filePath)
  129.         javaScript.replace(javaScript: defaultEnd, withJavaScript: customEnd, inPath: filePath)
  130.         javaScript.removeNewLines(inPath: filePath)
  131.     }
  132.    
  133.     private func convertContentHooksFramesFile() {
  134.         let filePath = libraryFilePaths[4]
  135.         let defaultStart = """
  136.        if (this.browser && browser.runtime && browser.runtime.getURL) {
  137.            scriptElement.src = browser.runtime.getURL("/lib/hooks/content/content-hooks-frames-web.js");
  138.            scriptElement.async = false;
  139.        } else if (this.singlefile.lib.getFileContent) {
  140.            scriptElement.textContent = this.singlefile.lib.getFileContent("/lib/hooks/content/content-hooks-frames-web.js");
  141.        }
  142.        """
  143.         let defaultEnd = "scriptElement.async = false;"
  144.         let customEnd = "scriptElement.textContent = contentHooksFramesWeb;"
  145.         javaScript.remove(javaScript: defaultStart, inPath: filePath)
  146.         javaScript.insert(javaScript: customEnd, afterJavaScript: defaultEnd, inPath: filePath)
  147.         javaScript.removeNewLines(inPath: filePath)
  148.     }
  149.    
  150.     private func convertContentFetchResources() {
  151.         let filePath = libraryFilePaths[5]
  152.         let defaultStart = """
  153.        const FETCH_REQUEST_EVENT = "single-file-request-fetch";
  154.        const FETCH_RESPONSE_EVENT = "single-file-response-fetch";
  155.        """
  156.         let customStart = ""
  157.         let defaultMiddle1 = """
  158.        this.singlefile.lib.fetch.content.resources = this.singlefile.lib.fetch.content.resources || (() => {
  159.        """
  160.         let customMiddle1 = "const pendingMessages = new Map();"
  161.         let defaultMiddle2 = "response = await hostFetch(url);"
  162.         let customMiddle2 = "return nativeFetch(url);"
  163.         let defaultMiddle3 = "catch (error) {"
  164.         let customMiddle3 = "return nativeFetch(url);"
  165.         let customEnd1 = "}"
  166.         let customEnd2 = "},"
  167.         let customEnd3 = """
  168.        callbackFetch: message => {
  169.            const callbacks = pendingMessages.get(message.url);
  170.            if (callbacks) {
  171.                pendingMessages.delete(message.url);
  172.                if (message.error) {
  173.                    callbacks.forEach(callback => callback.reject(message.error));
  174.                } else {
  175.                    const data = {
  176.                        status: message.status,
  177.                        headers: { get: name => message.headers[name] },
  178.                        arrayBuffer: async () => new Uint8Array(message.body).buffer
  179.                    };
  180.                    callbacks.forEach(callback => callback.resolve(data));
  181.                }
  182.            }
  183.        }
  184.        """
  185.         let customEnd4 = "};"
  186.         let customEnd5 = """
  187.        function nativeFetch(url) {
  188.            return new Promise((resolve, reject) => {
  189.                webkit.messageHandlers.performHttpRequest.postMessage(url);
  190.                let callbacks = pendingMessages.get(url);
  191.                if (!callbacks) {
  192.                    callbacks = [];
  193.                    pendingMessages.set(url, callbacks);
  194.                }
  195.                callbacks.push({ resolve, reject });
  196.            });
  197.        }
  198.        """
  199.         let customEnd6 = "})();"
  200.         javaScript.replace(javaScript: defaultStart, withJavaScript: customStart, inPath: filePath)
  201.         javaScript.insert(javaScript: customMiddle1, afterJavaScript: defaultMiddle1, inPath: filePath)
  202.         javaScript.replace(javaScript: defaultMiddle2, withJavaScript: customMiddle2, inPath: filePath)
  203.         javaScript.trim(javaScript: defaultMiddle3, toEnd: true, inPath: filePath)
  204.         javaScript.insert(javaScript: customMiddle3, afterJavaScript: defaultMiddle3, inPath: filePath)
  205.         javaScript.insert(javaScript: customEnd1, atEnd: true, inPath: filePath)
  206.         javaScript.insert(javaScript: customEnd2, atEnd: true, inPath: filePath)
  207.         javaScript.insert(javaScript: customEnd3, atEnd: true, inPath: filePath)
  208.         javaScript.insert(javaScript: customEnd4, atEnd: true, inPath: filePath)
  209.         javaScript.insert(javaScript: customEnd5, atEnd: true, inPath: filePath)
  210.         javaScript.insert(javaScript: customEnd6, atEnd: true, inPath: filePath)
  211.         javaScript.removeNewLines(inPath: filePath)
  212.     }
  213.    
  214.     private func convertIndex() {
  215.         let filePath = libraryFilePaths[0]
  216.         let defaultStart = "this.singlefile = this.singlefile || {"
  217.         let customStart = "var singlefile = {"
  218.         javaScript.replace(javaScript: defaultStart, withJavaScript: customStart, inPath: filePath)
  219.         javaScript.removeNewLines(inPath: filePath)
  220.     }
  221.    
  222.     private func convertFiles() {
  223.         let filePaths = libraryFilePaths
  224.         for index in 1 ..< filePaths.count {
  225.             let filePath = filePaths[index]
  226.             let defaultStart = "this.singlefile"
  227.             let customStart = "window.singlefile"
  228.             javaScript.replace(javaScript: defaultStart, withJavaScript: customStart, inPath: filePath)
  229.             javaScript.removeNewLines(inPath: filePath)
  230.         }
  231.     }
  232.    
  233.     private func mergeFiles() {
  234.         let directoryPath = fileManager.currentDirectoryPath
  235.         let filePaths = libraryFilePaths
  236.         var string = ""
  237.         for index in 0 ..< filePaths.count {
  238.             let filePath = directoryPath + filePaths[index]
  239.             let source = try! String(contentsOfFile: filePath, encoding: .utf8)
  240.             string.append(contentsOf: source)
  241.             string.append(contentsOf: "\n\n")
  242.             if index == filePaths.count - 1 {
  243.                 javaScript.save(asFileName: "library.js", fromSource: string)
  244.             }
  245.         }
  246.     }
  247.    
  248.     private func convertOptions() {
  249.         let directoryPath = fileManager.currentDirectoryPath
  250.         let startFilePath = optionsFilePath[0]
  251.         let endFilePath = directoryPath + "options.js"
  252.         let source = try? String(contentsOfFile: endFilePath)
  253.         try? source?.write(toFile: endFilePath, atomically: true, encoding: .utf8)
  254.         let defaultStart = "const DEFAULT_CONFIG = {"
  255.         let customStart = "var options = { "
  256.         let defaultEnd = "};"
  257.         let customEnd = " }"
  258.         javaScript.replace(javaScript: defaultStart, withJavaScript: customStart, inPath: startFilePath)
  259.         javaScript.trim(javaScript: customStart, fromStart: true, inPath: startFilePath)
  260.         javaScript.trim(javaScript: defaultEnd, toEnd: true, inPath: startFilePath)
  261.         javaScript.replace(javaScript: defaultEnd, withJavaScript: customEnd, inPath: startFilePath)
  262.         javaScript.save(asFileName: "options.js", inPath: startFilePath)
  263.     }
  264.  
  265.     private func convertSingleFile() {
  266.         let filePath = singleFileFilePath[0]
  267.         let defaultStart = "return await page.evaluate(async options => {"
  268.         let defaultEnd = "}, options);"
  269.         let customStart = "async function runSingleFile() {"
  270.         let customEnd =  "webkit.messageHandlers.websiteHasBeenSaved.postMessage(html);"
  271.         let defaultMiddle = "return await singleFile.getPageData();"
  272.         let customMiddle = "var html = (await singleFile.getPageData()).content;"
  273.         javaScript.trim(javaScript: defaultStart, fromStart: true, inPath: filePath)
  274.         javaScript.trim(javaScript: defaultEnd, toEnd: true, inPath: filePath)
  275.         javaScript.replace(javaScript: defaultMiddle, withJavaScript: customMiddle, inPath: filePath)
  276.         javaScript.replace(javaScript: defaultStart, withJavaScript: customStart, inPath: filePath)
  277.         javaScript.replace(javaScript: defaultEnd, withJavaScript: customEnd, inPath: filePath)
  278.         javaScript.insert(javaScript: "}", atEnd: true, inPath: filePath)
  279.         javaScript.save(asFileName: "singlefile.js", inPath: filePath)
  280.     }
  281.    
  282.     private func deleteDirectory() {
  283.         let directoryPath = fileManager.currentDirectoryPath
  284.         let directoryFiles = try? fileManager.contentsOfDirectory(atPath: directoryPath)
  285.         var files = directoryFiles?.filter({ fileNames.contains($0) })
  286.         files = files?.compactMap({ directoryPath + "/" + $0 })
  287.         let urls = files?.compactMap({ URL(fileURLWithPath: $0) })
  288.         let _ = urls?.map({ try? fileManager.removeItem(at: $0) })
  289.     }
  290. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement