-
-
Save saionaro/a20915c22d3c8481c4a7c2e6b6a1faa3 to your computer and use it in GitHub Desktop.
The tool for inlining html resources
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
["yargs:dev", "jsdom:dev"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const yargs = require("yargs"); | |
const { JSDOM } = require("jsdom"); | |
const fs = require("fs"); | |
const { join, resolve } = require("path"); | |
const { promisify } = require("util"); | |
const readdir = promisify(fs.readdir); | |
const readfile = promisify(fs.readFile); | |
const writefile = promisify(fs.writeFile); | |
const unlink = promisify(fs.unlink); | |
const HTML_REGEX = /\.html$/; | |
const ENCODING = "UTF8"; | |
async function findHtmls(dir) { | |
let contents = []; | |
try { | |
contents = await readdir(dir); | |
} catch (e) { | |
console.error(e); | |
} | |
return contents.filter((filename) => HTML_REGEX.test(filename)); | |
} | |
async function getContent(path) { | |
let content = ""; | |
try { | |
content = await readfile(path, ENCODING); | |
} catch (e) { | |
console.error(e); | |
} | |
return content; | |
} | |
async function writeContent(path, content) { | |
try { | |
content = await writefile(path, content); | |
} catch (e) { | |
console.error(e); | |
} | |
} | |
/** | |
* | |
* @param {object} config | |
* @param {string} config.directoryPath | |
* @param {object} config.vDom | |
* @param {string} config.selector | |
* @param {string} config.referenceAttr | |
* @param {string} config.inlineElement | |
*/ | |
async function inlineResource(config) { | |
const usedResources = []; | |
try { | |
const elements = config.vDom.window.document.querySelectorAll( | |
config.selector | |
); | |
for (const element of elements) { | |
const resourcePath = join( | |
config.directoryPath, | |
element[config.referenceAttr] | |
); | |
const virtualElement = config.vDom.window.document.createElement( | |
config.inlineElement | |
); | |
usedResources.push(resourcePath); | |
const resourceContent = await getContent(resourcePath); | |
virtualElement.innerHTML = resourceContent; | |
element.parentElement.insertBefore(virtualElement, element); | |
element.remove(); | |
} | |
} catch (e) { | |
console.error(e); | |
} | |
return usedResources; | |
} | |
async function cleanupInlinedResources(paths) { | |
try { | |
for (const path of paths) { | |
await unlink(path); | |
} | |
} catch (e) { | |
console.error(e); | |
} | |
} | |
async function performWork({ buildDir, crop = false }) { | |
const directoryPath = resolve(buildDir); | |
const htmls = await findHtmls(directoryPath); | |
const inlinedResources = []; | |
for (const html of htmls) { | |
const htmlPath = join(directoryPath, html); | |
const content = await getContent(htmlPath); | |
const vDom = new JSDOM(content); | |
const sharedConfig = { | |
directoryPath, | |
vDom, | |
}; | |
const configs = [ | |
{ | |
...sharedConfig, | |
selector: `link[rel="stylesheet"]`, | |
referenceAttr: "href", | |
inlineElement: "style", | |
}, | |
{ | |
...sharedConfig, | |
selector: "script[src]", | |
referenceAttr: "src", | |
inlineElement: "script", | |
}, | |
]; | |
for (const config of configs) { | |
const usedResources = await inlineResource(config); | |
inlinedResources.push(...usedResources); | |
} | |
let contentFinal = ""; | |
if (crop) { | |
const styles = vDom.window.document.querySelectorAll("style"); | |
for (const style of styles) { | |
contentFinal += style.outerHTML; | |
} | |
contentFinal += vDom.window.document.body.innerHTML; | |
} else { | |
content = vDom.serialize(); | |
} | |
await writeContent(htmlPath, contentFinal); | |
} | |
await cleanupInlinedResources([...new Set(inlinedResources)]); | |
} | |
yargs | |
.usage("Usage: $0 <command> [options]") | |
.command({ | |
command: "inline <buildDir>", | |
desc: "Initialize new project", | |
aliases: ["i"], | |
builder: (yargs) => { | |
yargs.positional("buildDir", { | |
describe: "Build directory", | |
type: "string", | |
}); | |
yargs.boolean("crop"); | |
}, | |
handler: performWork, | |
}) | |
.help().argv; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"inline": "node ./scripts/inliner.js inline ./dist --crop" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment