1 | #!/usr/bin/env node
|
2 |
|
3 | 'use strict'
|
4 |
|
5 | const fs = require('fs')
|
6 | const path = require('path')
|
7 | const transformer = require('jstransformer')
|
8 |
|
9 | const ymd = transformer(require('jstransformer-ymd'))
|
10 | const coffee = transformer(require('jstransformer-coffee-script'))
|
11 | const scss = transformer(require('jstransformer-scss'))
|
12 | const marked = transformer(require('jstransformer-marked'))
|
13 | const minify = transformer(require('jstransformer-html-minifier'))
|
14 | const uglify = transformer(require('jstransformer-uglify-js'))
|
15 |
|
16 | function move_match(state, reg) {
|
17 | reg.lastIndex = state.i
|
18 | const token = reg.exec(state.str)
|
19 | state.i += token[0].length
|
20 | return token
|
21 | }
|
22 |
|
23 | const helpers = {
|
24 | nattoppet_dir: __dirname,
|
25 |
|
26 | nattoppet_parse(hascontent = false) {
|
27 | const opts = [], args = []
|
28 | const parse_option = () => {
|
29 | const token = move_match(this.state, /\.([\w\-]+)/g)
|
30 | opts.push(token[1])
|
31 | }
|
32 | const parse_argument = () => {
|
33 | const token = move_match(this.state, /\((.*?)\)|{(.*?)}/g)
|
34 | args.push(token[1] || token[2])
|
35 | }
|
36 | const parse_block = () => {
|
37 | const token = move_match(this.state, />+/g)
|
38 | return this.capture_until('<'.repeat(token[0].length))
|
39 | }
|
40 |
|
41 | while (true) {
|
42 | switch (this.peek()) {
|
43 | case '.':
|
44 | parse_option()
|
45 | break
|
46 | case '(':
|
47 | case '{':
|
48 | parse_argument()
|
49 | break
|
50 | case '>':
|
51 | return { opts, args, block: parse_block() }
|
52 | case ' ':
|
53 | if (hascontent)
|
54 | return { opts, args, block: this.capture_line() }
|
55 | default:
|
56 | return { opts, args }
|
57 | }
|
58 | }
|
59 | },
|
60 |
|
61 | read(file, type='utf8') {
|
62 | if (file.startsWith('@nattoppet')) {
|
63 | file = path.join(this.nattoppet_dir, file.substring(10))
|
64 | } else if (!file.startsWith('/')) {
|
65 | file = path.join(this.base_dir, file)
|
66 | }
|
67 | return fs.readFileSync(file, type)
|
68 | },
|
69 |
|
70 | extname(file) {
|
71 | return path.extname(file).slice(1)
|
72 | },
|
73 |
|
74 | basename(file) {
|
75 | return path.basename(file)
|
76 | },
|
77 |
|
78 | render_coffee(str) {
|
79 | return uglify.render(coffee.render(str).body).body
|
80 | },
|
81 |
|
82 | render_scss(str) {
|
83 | return scss.render(str, {outputStyle: 'compressed'}).body
|
84 | },
|
85 |
|
86 | render_markdown(str) {
|
87 | return marked.render(str).body
|
88 | }
|
89 | }
|
90 |
|
91 | function render_files(env, ...files) {
|
92 | const str = files.map(x=>fs.readFileSync(x, 'utf8')).reduce((x, y) => x + y)
|
93 | return ymd.render(str, null, env).body
|
94 | }
|
95 |
|
96 | const rpath = (...x) => path.join(__dirname, ...x)
|
97 |
|
98 | const render = file => {
|
99 | const seg = path.basename(file).split('.')
|
100 | const theme = seg[seg.length-2]
|
101 | const base_dir = path.dirname(path.resolve(file))
|
102 | const env = {base_dir, ...helpers}
|
103 | const html = ['koa', 'ppt', 'vue', 'tml'].includes(theme)
|
104 | ? render_files(env, rpath(theme, 'before.ymd'), file, rpath(theme, 'after.ymd'), rpath('nattoppet.ymd'))
|
105 | : render_files(env, file, rpath('nattoppet.ymd'))
|
106 | return minify.render(html, { removeAttributeQuotes:true, removeComments:true }).body.trim()
|
107 | }
|
108 |
|
109 | const file = process.argv[2]
|
110 |
|
111 | if (process.argv.length != 3 || file == "--help")
|
112 | return console.log("Usage: nattoppet file.{koa,ppt,vue,tml}.ymd > file.html")
|
113 |
|
114 | process.stdout.write(render(file))
|