UNPKG

html-build

Version:

Utility script to build HTML documents - Appends scripts and styles, removes debug parts, append HTML partials, template options, etc.

232 lines (231 loc) 8.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = require("path"); const URL = require("url"); const _ = require("lodash"); const beautify = require("js-beautify"); const log = require("./log"); const tag_1 = require("./tag"); const util_1 = require("./util"); const processFileRegex = /\$\(([^\)]*)\)/, templates = { "script": "<script <%= attributes %> src=\"<%= src %>\"></script>", "script-inline": "<script <%= attributes %>><%= src %></script>", "style": "<link <%= attributes %> href=\"<%= src %>\" />", "style-inline": "<style <%= attributes %>><%= src %></style>" }, validators = { script: validateBlockWithName, style: validateBlockWithName, section: validateBlockWithName, process: validateBlockAlways, remove: validateBlockAlways, //base method validate(tag, params) { if (!validators[tag.type]) { return false; } return validators[tag.type](tag, params); } }, processors = { script: processHtmlTag, style: processHtmlTag, section(options) { return options.files.map(function (f) { const content = util_1.readFile(f); return options.recursive ? transformContent(content, options.params, options.dest) : content; }).join(options.EOL); }, process(options) { return options.lines .map(l => processTemplate(l, options)) .join(options.EOL) .replace(new RegExp(options.regexTagStart), "") .replace(new RegExp(options.regexTagEnd), ""); }, remove(options) { if (!options.name) return ""; const targets = options.name.split(","); if (options.target && targets.indexOf(options.target) < 0) { return options.lines .join(options.EOL) .replace(new RegExp(options.regexTagStart), "") .replace(new RegExp(options.regexTagEnd), ""); } return ""; }, //base method transform(options) { return processors[options.type](options); } }; function transformContent(content, params, dest) { const tags = getBuildTags(content, params); tags.forEach(tag => { const raw = tag.lines.join(params.EOL), tagFiles = validators.validate(tag, params); let result = "", prefix = "", suffix = ""; if (tagFiles) { const options = tag_1.createTagOptions(tag, params, tagFiles === true ? [] : tagFiles, dest); result = processors.transform(options); } else if (tagFiles === false) { log.warn(`Unknown tag detected: "${tag.type}"`); if (!params.allowUnknownTags) { log.fail.warn(`Use "parseTag" or "allowUnknownTags" options to avoid this issue`); } } else if (tag.optional) { if (params.logOptionals) { log.warn(`Tag with type: "${tag.type}" and name: "${tag.name}" is not configured in your Gruntfile.js but is set optional, deleting block !`); } } else { log.fail.warn(`Tag with type "${tag.type}" and name: "${tag.name}" is not configured in your Gruntfile.js !`); } if (params.keepTags) { prefix = raw.match(new RegExp(params.regexTagStart + "\\s*"))[0]; suffix = raw.match(new RegExp(params.regexTagEnd + "\\s*"))[0]; } content = content.replace(raw, () => prefix + result + suffix); }); if (params.beautify) { content = beautify.html(content, typeof params.beautify === "object" ? params.beautify : {}); } return content; } exports.transformContent = transformContent; function getBuildTags(content, params) { const lines = content.replace(/\r?\n/g, "\n").split(/\n/), tags = []; let tag = false, last; lines.forEach(l => { const tagStart = l.match(new RegExp(params.regexTagStart)), tagEnd = new RegExp(params.regexTagEnd).test(l); if (tagStart) { tag = true; last = { type: tagStart[1], inline: !!tagStart[2], optional: !!tagStart[3], recursive: !!tagStart[4], noprocess: !!tagStart[5], name: tagStart[6], attributes: tagStart[7], lines: [] }; tags.push(last); } // switch back tag flag when endbuild if (tag && tagEnd) { last.lines.push(l); tag = false; } if (tag && last) { last.lines.push(l); } }); return tags; } function validateBlockWithName(tag, params) { const keys = tag.name.split("."), ln = keys.length; let src = params[tag.type + "s"]; for (let i = 0; i < ln; i++) { src = src[keys[i]]; // Search target } if (src) { // check if we want to use current file name, if so update src where necessary if (params.useFileName && src.toString().match(/_CURRENT_FILE_NAME_/g)) { src = src.toString().replace("_CURRENT_FILE_NAME_", params.filesContext.path); } let opt = {}, files = src; if (typeof src === "object") { if (src.files) { opt = _.omit(src, "files"); files = src.files; } else { // if paths are named, just take values files = _.values(src); } } if (!Array.isArray(files)) { files = [files]; } if (params.processFiles) { const filesContext = params.filesContext; files = files.map(f => f.replace(processFileRegex, (val, name) => filesContext[name] || val)); } return params.processPath(files, params, opt); } return false; } function validateBlockAlways(tag, params) { return true; } function processHtmlTag(options) { if (options.inline) { const content = options.files.map(util_1.readFile).join(options.EOL); return processHtmlTagTemplate(options, content); } else { const destDir = options.relative && util_1.isFile(options.dest) ? path.dirname(options.dest) : options.dest; return options.files.map(f => { let url = (options.relative && !/^((http|https):)?(\\|\/\/)/.test(f)) ? path.relative(destDir, f) : f; url = url.replace(/\\/g, "/"); if (options.prefix) { url = URL.resolve(options.prefix.replace(/\\/g, "/"), url); } if (options.suffix) { const suffix = typeof options.suffix === "function" ? options.suffix(f, url) : options.suffix; if (suffix) { url += "?" + suffix; } } return processHtmlTagTemplate(options, url); }).join(options.EOL); } } function processHtmlTagTemplate(options, src) { const template = templates[options.type + (options.inline ? "-inline" : "")], attrs = createAttributes(options, src); if (!options.inline || options.noprocess) { return template .replace(/\<\%\= (src|attributes) \%\>/g, (_, p1) => { if (p1 === "src") return src; else if (p1 === "attributes") return attrs; else return ""; }); } else { return processTemplate(template, options, src, attrs); } } function processTemplate(template, options, src, attrs) { const data = createTemplateData(options, src, attrs); return _.template(template)(data); } function createAttributes(options, src) { let attrs = options.attributes || ""; if (options.type === "script") { attrs = `type="text/javascript" ${attrs}`; } else if (options.type === "style" && !options.inline) { if (path.extname(src) === ".less") { attrs = `type="text/css" rel="stylesheet/less" ${attrs}`; } else { attrs = `type="text/css" rel="stylesheet" ${attrs}`; } } return attrs.trim(); } function createTemplateData(options, src, attrs) { const extend = { src: src || "", attributes: attrs || "" }; return Object.assign({}, options.data, extend); }