| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- const U2 = require("uglify-js");
- const htmlMinify = require('html-minifier').minify;
- const CleanCSS = require('clean-css');
- const svgo = require('svgo');
- const convert = require('xml-js');
- defaultOptions = {
- collapseWhitespace: true,
- minifyCSS: true,
- minifySVG: true,
- svgOptions: {
- plugins: svgo.extendDefaultPlugins([
- {
- name: 'removeViewBox',
- active: false
- }
- ]),
- }
- }
- /**
- * Extract element containing token to Cdata element
- * @param {string} token
- * @param {XMLElement} elt
- * @returns {XMLElement} tranform XML tree by stringifying element containing the token
- */
- const toCData = function (elt, token) {
- let transform = elt.name == token;
- if (elt.type == "text" && elt.text.includes(token)) {
- transform = true
- } else if ("attributes" in elt) {
- for (const k of Object.keys(elt.attributes)) {
- if (k.includes(token)) {
- transform = true;
- } else if (elt.attributes[k].includes(token)) {
- transform = true
- }
- if (transform) {
- break
- }
- }
- }
- if (transform) {
- const newElt = {
- type: "cdata",
- cdata: token + JSON.stringify(elt)
- }
- return newElt
- } else {
- if ('elements' in elt) {
- elt.elements = elt.elements.map(e => toCData(e, token))
- }
- return elt
- }
- }
- /**
- * Recover extracted element containing token from Cdata element
- * @param {string} token
- * @param {XMLElement} elt
- * @returns {XMLElement} restored XML tree
- */
- const fromCData = function (elt, token) {
- if (elt.type == "cdata" && elt.cdata.startsWith(token)) {
- return JSON.parse(elt.cdata.substr(token.length))
- } else {
- if ('elements' in elt) {
- elt.elements = elt.elements.map(e => fromCData(e, token))
- }
- return elt
- }
- }
- const processASTNode = function(node,htmlOptions,cssOptions,svgOptions){
- if (node instanceof U2.AST_Template
- && node.tag
- && ['html', 'css', 'svg'].includes(node.tag.name)) {
- const templateContent = node.strings.map(s => s.replace(/[\n|\r]/gm, ' '))
- if (node.tag.name == "html") {
- const input = templateContent.join('${}');
- const output = htmlMinify(input, htmlOptions)
- node.strings = output.split("${}");
- } else if (node.tag.name == "css" && cssOptions) {
- if (templateContent.length > 1) {
- throw new Error("css literal cannot use template.\r\nMore info https://lit-element.polymer-project.org/api/modules/_lit_element_.html#css ")
- }
- const input = templateContent[0];
- const output = new CleanCSS(cssOptions).minify(input).styles
- node.strings = [output];
- } else if (node.tag.name == "svg" && svgOptions) {
- const token = 'validTokenMlet'
- const input = templateContent.join(token);
- // Parse XML
- let obj = convert.xml2js(input)
- // Transform elements containing template to CData elt
- obj = toCData(obj, token)
- // Optimise svg (it does not affect Cdata elements)
- let xml = convert.js2xml(obj);
- xml = svgo.optimize(xml, svgOptions).data
- // Revert CData elt to their original form
- obj = convert.xml2js(xml)
- obj = fromCData(obj, token)
- // Reconstruct template
- node.strings = convert.js2xml(obj).split(token);
- }
- }
- }
- module.exports = function (code, options) {
- let mergedOptions = { ...defaultOptions, ...options }
-
- let cssOptions = false;
- if (mergedOptions.minifyCSS) {
- cssOptions = typeof mergedOptions.minifyCSS == 'object' ? mergedOptions.minifyCSS : {}
- }
- let svgOptions = false;
- if (mergedOptions.minifySVG) {
- svgOptions = mergedOptions.svgOptions ? mergedOptions.svgOptions : {};
- }
- const walker = new U2.TreeWalker((node)=>processASTNode(node,mergedOptions,cssOptions,svgOptions));
- const ast = U2.parse(code)
- ast.walk(walker)
- return ast.print_to_string({ beautify: false });;
- }
|