Initial commit, basic command line JSTizer as unix filter
authorNick Downing <nick@ndcode.org>
Mon, 19 Nov 2018 03:57:50 +0000 (14:57 +1100)
committerNick Downing <nick@ndcode.org>
Mon, 19 Nov 2018 03:57:50 +0000 (14:57 +1100)
.gitignore [new file with mode: 0644]
jstize.js [new file with mode: 0755]
package.json [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..b7dd79b
--- /dev/null
@@ -0,0 +1,5 @@
+/ndcode-jstize-*.tgz
+/node_modules
+/package-lock.json
+/yarn.lock
+/yarn-error.log
diff --git a/jstize.js b/jstize.js
new file mode 100755 (executable)
index 0000000..f6af17a
--- /dev/null
+++ b/jstize.js
@@ -0,0 +1,193 @@
+#!/usr/bin/env node
+
+let CleanCSS = require('@ndcode/clean-css')
+let assert = require('assert')
+let fs = require('fs')
+let html_minifier = require('@ndcode/html-minifier')
+let uglify_es = require('uglify-es')
+
+let clean_css = new CleanCSS({format: 'beautify'})
+
+let tags = []
+let indent = []
+let buffer = []
+let parse = text => {
+  new html_minifier.HTMLParser(
+    text,
+    {
+      html5: true,
+      start: (tag, attrs, unary) => { //, unarySlash, autoGenerated) {
+        //let out = `<${tag}`
+        //for (let i = 0; i < attrs.length; ++i) {
+        //  out += ` ${attrs[i].name}`
+        //  if (attrs[i].value !== undefined)
+        //    out += `="${attrs[i].value}"`
+        //}
+        //out += '>'
+        let out = tag
+        let out1 = ''
+        let prefix = '('
+        let suffix = ''
+        for (let i = 0; i < attrs.length; ++i) {
+          if (attrs[i].name == 'class') {
+            let fields = attrs[i].value.split(' ')
+            for (let j = 0; j < fields.length; ++j)
+              out = out.replace(
+                /([.#])([^.#]*-[0-9]*)$/,
+                (match, sep, name) => `${sep}'${name}'`
+              ) + '.' + fields[j]
+          }
+          else if (attrs[i].name == 'id') {
+            let fields = attrs[i].value.split(' ')
+            for (let j = 0; j < fields.length; ++j)
+              out += '#' + fields[j]
+          }
+          else {
+            out1 += prefix + attrs[i].name
+            if (attrs[i].value !== undefined)
+              out1 += `="${attrs[i].value}"`
+            prefix = ' '
+            suffix = ')'
+          }
+        }
+        out = out.replace(
+          /([.#])style$/,
+          (match, sep) => `${sep}'style'`
+        ).replace(
+          /([.#])([^.#]*-style)$/,
+          (match, sep, name) => `${sep}'${name}'`
+        )
+        buffer.push(
+          indent.join('') + out + out1 + suffix + (unary ? ' {}\n' : ' {\n')
+        )
+        if (!unary) {
+          tags.push(tag)
+          indent.push('  ')
+        }
+      },
+      end: tag => { //, attrs, autoGenerated) => {
+        assert(tag === tags.pop())
+        indent.pop()
+        //buffer.push(`</${tag}>`)
+        buffer.push(
+          (
+            buffer.length && buffer[buffer.length - 1].slice(-2) == '{\n' ?
+              buffer.pop().slice(0, -1) :
+              indent.join('')
+          ) +
+          '}\n'
+        )
+      },
+      chars: text => { //, prevTag, nextTag) {
+        let out
+        if (tags.length && tags[tags.length - 1] === 'script') {
+          let render = uglify_es.minify(
+            text,
+            {
+              compress: false,
+              //ecma: undefined,
+              //ie8: false,
+              //keep_classnames: undefined,
+              //keep_fnames: false,
+              //mangle: {},
+              //nameCache: null,
+              output: {
+                //ascii_only       : false,
+                beautify         : true, //false,
+                //bracketize       : false,
+                //comments         : false,
+                //ecma             : 5,
+                //ie8              : false,
+                indent_level     : 2, //4,
+                //indent_start     : 0,
+                //inline_script    : true,
+                //keep_quoted_props: false,
+                //max_line_len     : false,
+                //preamble         : null,
+                //preserve_line    : false,
+                //quote_keys       : false,
+                //quote_style      : 0,
+                //safari10         : false,
+                //semicolons       : true,
+                shebang          : false //true,
+                //shorthand        : undefined,
+                //source_map       : null,
+                //webkit           : false,
+                //width            : 80,
+                //wrap_iife        : false
+              },
+              //parse: {
+                //bare_returns   : false,
+                //ecma           : 8,
+                //expression     : false,
+                //filename       : null,
+                //html5_comments : true,
+                //shebang        : true,
+                //strict         : false,
+                //toplevel       : null
+              //},
+              //rename: undefined,
+              //safari10: false,
+              //sourceMap: false,
+              //timings: false,
+              toplevel: true //false,
+              //warnings: false,
+              //wrap: false
+            }
+          )
+          if (render.error !== undefined)
+            throw render.error
+          //buffer.push(render.code)
+          out = render.code.split('\n')
+          if (out.length && out[out.length - 1].length === 0)
+            out.pop()
+        }
+        else if (tags.length && tags[tags.length - 1] === 'style') {
+          let render = clean_css.minify(text)
+          //for (let i = 0; i < render.warnings.length; ++i)
+          //  console.log(`clean-css warning: ${render.warnings[i]}`)
+          //buffer.push(render.styles)
+          out = render.styles.split('\n')
+          if (out.length && out[out.length - 1].length === 0)
+            out.pop()
+        }
+        else
+          //buffer.push(text)
+          out = [`'${text}'`]
+        for (let i = 0; i < out.length; ++i)
+          buffer.push(indent.join('') + out[i] + '\n')
+      },
+      comment: (text, nonStandard) => {
+        let prefix = nonStandard ? '<!' : '<!--'
+        let suffix = nonStandard ? '>' : '-->'
+        let match = text.match(/^(\[if\s[^\]]+]>)([\s\S]*?)(<!\[endif])$/)
+        if (match) {
+          buffer.push(
+            `${indent.join('')}_out.push('${prefix}${match[1]}')\n`
+          )
+          parse(match[2])
+          buffer.push(
+            `${indent.join('')}_out.push('${match[3]}${suffix}')\n`
+          )
+        }
+        else
+          buffer.push(
+            `${indent.join('')}_out.push('${prefix}${text}${suffix}')\n`
+          )
+      },
+      doctype: doctype => {
+        //buffer.push(doctype)
+        buffer.push(
+          `${indent.join('')}_out.push('${doctype}')\n`
+        )
+      }
+    }
+  )
+}
+parse(
+  html_minifier.minify(
+    fs.readFileSync(0, {encoding: 'utf-8'}),
+    {collapseWhitespace: true}
+  )
+)
+fs.writeSync(1, buffer.join(''), {encoding: 'utf-8'}) 
diff --git a/package.json b/package.json
new file mode 100644 (file)
index 0000000..82e0bc9
--- /dev/null
@@ -0,0 +1,32 @@
+{
+  "name": "@ndcode/jstize",
+  "version": "0.1.0",
+  "description": "Utility to convert regular HTML into JST template.",
+  "keywords": [
+    "HTML",
+    "JST",
+    "template",
+    "convert"
+  ],
+  "homepage": "https://www.ndcode.org",
+  "repository": {
+    "type": "git",
+    "url": "https://git.ndcode.org/public/jstize.git"
+  },
+  "bugs": {
+    "email": "nick@ndcode.org"
+  },
+  "main": "index.js",
+  "directories": {},
+  "dependencies": {
+    "@ndcode/clean-css": "^4.2.1",
+    "@ndcode/html-minifier": "^3.5.21",
+    "assert": "^1.4.1",
+    "fs": "^0.0.1-security",
+    "uglify-es": "^3.3.9"
+  },
+  "devDependencies": {},
+  "scripts": {},
+  "author": "Nick Downing <nick@ndcode.org>",
+  "license": "MIT"
+}