sptc 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,75 @@
1
1
  # Simple Pretreat Toolkit CLI
2
2
 
3
3
  This project is developing..
4
+
5
+ ---
6
+
7
+ ### sptc
8
+
9
+ ```
10
+ Usage: sptc [options]
11
+
12
+ sptc
13
+
14
+ Options:
15
+ -V, --version output the version number
16
+ -f, --filename <string> Sptc filename.
17
+ -d, --defines <string> Macro definition switches. If you want to use multiple switches, separate them with ",".
18
+ (default: "")
19
+ -h, --help display help for command
20
+ ```
21
+
22
+ ### sptcd
23
+ ```
24
+ Usage: sptc http server [options]
25
+
26
+ A simple http server
27
+
28
+ Options:
29
+ -V, --version output the version number
30
+ -p, --port <number> Serve port. (default: 9090)
31
+ -l, --locally Only accepts local connections.
32
+ -w, --workdir <string> Specify the working directory. (default: ".")
33
+ -r, --router <string> Specify a file as the router entry. If specified, all requests will pass to this file.
34
+ -e, --exts <string> Specify the valid extensions of executable sptc files. (default: ".sjs,.shtml")
35
+ -n, --workers <number> Workers count. (default: 1)
36
+ -s, --slient Slient mode.
37
+ -h, --help display help for command
38
+ ```
39
+
40
+ ### webpack loader
41
+ ```
42
+ Usage:
43
+
44
+ Webpack configuration:
45
+
46
+ {
47
+ test: /\.(jsx?)$/,
48
+ exclude: /node_modules/,
49
+ options: {
50
+ file: path.resolve(__dirname+'/sptc.inject.js'),
51
+ },
52
+ loader: 'sptc/dist/webpack.loader.js',
53
+ }
54
+
55
+
56
+
57
+ **sptc.inject.js**
58
+
59
+ module.exports={
60
+ EXTENDS: (ctx)=>({
61
+ get_module_dir: _=>{
62
+ return ctx.fn.replace(/\\+/g, '/').replace(/(^.*?src\/modules\/[^/]+).*$/, '$1')+'/'
63
+ },
64
+ IS_NODE_TARGET: ctx.webpackLoaderThis.target==='node',
65
+ }),
66
+ TPLS: [
67
+ /\.jsx?$/, ctx=>{
68
+ let {str, fn}=ctx
69
+ return `console.log("// ${fn}")\n`+str
70
+ }
71
+ ],
72
+ }
73
+
74
+
75
+ ```
package/bin/sptc.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {executeSptcFile}=require('../engine')
4
+ const {Command}=require('commander')
5
+ const version=require('../package.json').version
6
+ const program=new Command()
7
+ .name(`sptc`)
8
+ .description(`sptc`)
9
+ .version(version)
10
+ .requiredOption('-f, --filename <string>', 'Sptc filename.')
11
+ .option('-d, --defines <string>', 'Macro definition switches. If you want to use multiple switches, separate them with ",".', '')
12
+ .action(({
13
+ filename,
14
+ defines,
15
+ })=>{
16
+ executeSptcFile(filename, {}, {
17
+ write: x=>process.stdout.write(x),
18
+ onError: e=>console.log(e),
19
+ __DEV__: true,
20
+ macroOption: {
21
+ defs: defines.split(',').map(x=>x.trim()).filter(x=>x),
22
+ },
23
+ })
24
+ })
25
+ .parse()
package/bin/sptcd.js ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {FastCGI, FastCGI_FPM}=require('../dist/httpServer')
4
+ const {Command}=require('commander')
5
+ const version=require('../package.json').version
6
+ const program=new Command()
7
+ .name(`sptc http server`)
8
+ .description(`A simple http server`)
9
+ .version(version)
10
+ .requiredOption('-p, --port <number>', 'Serve port.', 9090)
11
+ .option('-l, --locally', 'Only accepts local connections.')
12
+ .option('-w, --workdir <string>', 'Specify the working directory.', '.')
13
+ .option('-r, --router <string>', 'Specify a file as the router entry. If specified, all requests will pass to this file.')
14
+ .option('-e, --exts <string>', 'Specify the valid extensions of executable sptc files.', '.sjs,.shtml')
15
+ .option('-n, --workers <number>', 'Workers count.', 1)
16
+ .option('-s, --slient', 'Slient mode.')
17
+ .action(({
18
+ port,
19
+ locally,
20
+ workdir,
21
+ router,
22
+ exts,
23
+ workers,
24
+ slient,
25
+ })=>{
26
+ const argv=[port, locally, {
27
+ serverDir: workdir,
28
+ routerEntry: router,
29
+ exts: exts.split(',').map(x=>x.trim()).filter(x=>x),
30
+ debug: true,
31
+ slientMode: slient,
32
+ }]
33
+ if(workers>1) {
34
+ FastCGI_FPM(workers, ...argv)
35
+ }else{
36
+ FastCGI(...argv)
37
+ }
38
+ })
39
+ .parse()
@@ -0,0 +1,147 @@
1
+ const {
2
+ fpm,
3
+ safe_path,
4
+ fileExists,
5
+ getExtension,
6
+ getLocalIpv4Addresses,
7
+ }=require('../utils')
8
+ const {executeSptcFile}=require('../engine')
9
+
10
+ process.on('uncaughtException', e=>{
11
+ console.log('uncaughtException:', e)
12
+ })
13
+
14
+ function FastCGI_FPM(n, port, locally, option={}) {
15
+ option.env={
16
+ RUNTIME: 'FastCGI_FPM',
17
+ }
18
+ fpm(n, _=>FastCGI(port, locally, option))
19
+ }
20
+
21
+ function FastCGI(port, locally, option) {
22
+ const http=require('http')
23
+ const path=require('path')
24
+ const {
25
+ serverDir='.',
26
+ routerEntry=null,
27
+ slientMode=false,
28
+ ..._option
29
+ }=option
30
+ _option.env=_option.env || {
31
+ RUNTIME: 'FastCGI',
32
+ }
33
+ _option.srvDir=path.resolve(serverDir)
34
+ _option.router=routerEntry? safe_path(_option.srvDir, routerEntry): null
35
+ http.createServer((req, res)=>{
36
+ CGI(req, res, _option)
37
+ }).listen(port, locally? '127.0.0.1': '0.0.0.0', _=>{
38
+ console.log()
39
+ console.log('`sptc-http-server` has been launched with the following option:')
40
+ const o={
41
+ workdir: _option.srvDir,
42
+ routerEntry: _option.router,
43
+ }
44
+ if(locally) {
45
+ o.serve='127.0.0.1:'+port
46
+ }else{
47
+ o.serves=getLocalIpv4Addresses().map(x=>x+':'+port)
48
+ }
49
+ console.log(o)
50
+ console.log()
51
+ })
52
+ }
53
+
54
+ function CGI(req, res, option) {
55
+ const {
56
+ srvDir,
57
+ exts=['.sjs', '.shtml'],
58
+ debug=false,
59
+ router=null,
60
+ env,
61
+ }=option
62
+ const [reqCtx, handler]=buildRequestContext(req, res, {srvDir, debug, env})
63
+ const file=reqCtx.$_REQUEST_FILE || {}
64
+ const fn=router || file.fullname
65
+ if(!fileExists(fn)) {
66
+ res.writeHead(404, {'content-type': 'text/plain'})
67
+ res.end(`\`${router || file.pathname}\` does not exist`)
68
+ return
69
+ }
70
+ const ext=getExtension(fn)
71
+ if(!exts.includes(ext)) {
72
+ res.writeHead(403, {'content-type': 'text/plain'})
73
+ res.end(`\`${ext}\` does not match`)
74
+ return
75
+ }
76
+ executeSptcFile(fn, reqCtx, handler)
77
+ }
78
+
79
+ function buildRequestContext(req, res, {srvDir, debug, env}) {
80
+ const {fullname, pathname}=getRequestFile(req, srvDir)
81
+ const reqCtx={
82
+ $_RAW_REQUEST: req,
83
+ $_RAW_RESPONSE: res,
84
+ $_ENV: env,
85
+ $_REQUEST_FILE: {
86
+ fullname,
87
+ pathname,
88
+ },
89
+ $_DEBUG: debug,
90
+ $_WORKDIR: srvDir,
91
+ }
92
+ reqCtx.$_GLOBAL=reqCtx
93
+
94
+ const state={
95
+ statusCode: 200,
96
+ statusText: 'OK',
97
+ responseHeaders: {'content-type': 'text/html'},
98
+ error: null,
99
+ headerFlushed: false,
100
+ }
101
+ function flushHeader() {
102
+ if(state.headerFlushed) return;
103
+ state.headerFlushed=true
104
+ res.writeHead(state.statusCode, state.statusText, state.responseHeaders)
105
+ }
106
+
107
+ reqCtx.setStatus=(code, text)=>{
108
+ state.statusCode=200
109
+ if(text!==undefined) state.statusText=text
110
+ }
111
+ reqCtx.setResponseHeaders=headers=>{
112
+ Object.assign(state.responseHeaders, headers)
113
+ }
114
+ reqCtx.flushHeader=_=>{
115
+ flushHeader()
116
+ }
117
+ reqCtx.setDebug=x=>{
118
+ reqCtx.$_DEBUG=x
119
+ }
120
+
121
+ return [reqCtx, {
122
+ write: x=>{
123
+ flushHeader()
124
+ res.write(x)
125
+ },
126
+ end: _=>{
127
+ flushHeader()
128
+ res.end()
129
+ },
130
+ onError: e=>{
131
+ if(reqCtx.$_DEBUG) console.log(e)
132
+ },
133
+ }]
134
+ }
135
+ function getRequestFile(req, srvDir) {
136
+ const _url=require('url')
137
+ const {pathname}=_url.parse(req.url)
138
+ return {
139
+ fullname: safe_path(srvDir, pathname),
140
+ pathname,
141
+ }
142
+ }
143
+
144
+ module.exports={
145
+ FastCGI,
146
+ FastCGI_FPM,
147
+ }
@@ -0,0 +1,48 @@
1
+ const {executeSptcFileEx}=require('../engine')
2
+ const path=require(`path`)
3
+
4
+ module.exports=async function(str) {
5
+ let query=this.query || {}
6
+ if(this.query) {
7
+ const {file}=this.query
8
+ if(file) {
9
+ query=Object.assign({}, require(file))
10
+ }
11
+ }
12
+
13
+ const {
14
+ EXTENDS=(wCtx=>({})),
15
+ TPLS=[],
16
+ DEFINES=[],
17
+ }=query
18
+
19
+ let wCtx={
20
+ str: str,
21
+ fn: path.resolve(this.resourcePath).replace(/\\+/g, '/'),
22
+ webpackLoaderThis: this,
23
+ }
24
+ const globals=Object.assign(EXTENDS(wCtx), {wCtx})
25
+ wCtx.runtimeGlobals=globals
26
+
27
+ let callback=this.async()
28
+ try{
29
+ const output=await executeSptcFileEx(wCtx.fn, globals, {
30
+ __DEV__: true,
31
+ macroOption: {
32
+ defs: DEFINES,
33
+ },
34
+ mockFileContent: wCtx.str,
35
+ })
36
+
37
+ wCtx.str=output
38
+
39
+ for(let i=0; i<TPLS.length; i+=2){
40
+ const [_match, _handler]=[TPLS[i], TPLS[i+1]]
41
+ if(wCtx.fn.match(_match)) wCtx.str=_handler(wCtx, globals)
42
+ }
43
+ callback(null, wCtx.str)
44
+ }catch(e) {
45
+ callback(e)
46
+ }
47
+
48
+ }
@@ -0,0 +1,237 @@
1
+
2
+ const vm=require('vm')
3
+ const {compileFile}=require('../utils')
4
+
5
+ const Tokens={
6
+ T_IFDEF: '#ifdef',
7
+ T_ELSE: '#else',
8
+ T_ENDIF: '#endif',
9
+ T_IFNDEF: '#ifndef',
10
+
11
+ T_DEF: '#def',
12
+ T_UNDEF: '#undef',
13
+
14
+ T_INCLUDE: '#include',
15
+
16
+ T_DEFINE: '#define',
17
+ T_CALL_DEFINE: '@',
18
+ }
19
+
20
+ function lexer(content) {
21
+ const {
22
+ T_IFDEF,
23
+ T_ELSE,
24
+ T_ENDIF,
25
+ T_IFNDEF,
26
+
27
+ T_DEF,
28
+ T_UNDEF,
29
+
30
+ T_INCLUDE,
31
+
32
+ T_DEFINE,
33
+ T_CALL_DEFINE,
34
+ }=Tokens
35
+
36
+ const tokens=[]
37
+ const lines=content.split('\n')
38
+ let reStr='((?:'+[
39
+ T_IFDEF,
40
+ T_ELSE,
41
+ T_ENDIF,
42
+ T_IFNDEF,
43
+ T_DEF,
44
+ T_UNDEF,
45
+ T_INCLUDE,
46
+ T_DEFINE,
47
+ ].join('|')+')\\b)((?:\\s+).+)?|(.+)'
48
+ const re=new RegExp('^'+reStr, 'g')
49
+ const subre=new RegExp(T_CALL_DEFINE+'([A-Za-z\\d_]+)(\\([^)]*?\\)|\\b)|(.)', 'g')
50
+
51
+ for(let i=0; i<lines.length; i++) {
52
+ if(!lines[i]) {
53
+ tokens.push({frags: true, str: '\n'})
54
+ continue
55
+ }
56
+ lines[i].replace(re, (_, tk, tk_params, subline)=>{
57
+ if(tk) {
58
+ tokens.push({tk, tk_params: (tk_params || '').trim()})
59
+ }else if(subline) {
60
+ subline.replace(subre, (_, call_define, call_define_params, frags)=>{
61
+ if(call_define) {
62
+ tokens.push({call_define, call_define_params, source: _})
63
+ }else if(frags) {
64
+ tokens.push({frags: true, str: frags})
65
+ }
66
+ })
67
+ tokens.push({frags: true, str: '\n'})
68
+ }
69
+ })
70
+ }
71
+ const v=tokens.pop()
72
+ if(!(v.frags===true && v.str==='\n')) {
73
+ tokens.push(v)
74
+ }
75
+ return tokens
76
+ }
77
+
78
+ const O_IFDEF=Symbol()
79
+ const O_STR=Symbol()
80
+ const O_DEFINE_CONST=Symbol()
81
+ const O_DEFINE_CALL=Symbol()
82
+ const O_CALL_CONST=Symbol()
83
+ const O_CALL_DEFINE=Symbol()
84
+ const O_DEF=Symbol()
85
+ const O_INCLUDE=Symbol()
86
+ const O_UNDEF=Symbol()
87
+ function transformToAst(tokens) {
88
+ const {
89
+ T_IFDEF,
90
+ T_ELSE,
91
+ T_ENDIF,
92
+ T_IFNDEF,
93
+
94
+ T_DEF,
95
+ T_UNDEF,
96
+
97
+ T_INCLUDE,
98
+
99
+ T_DEFINE,
100
+ T_CALL_DEFINE,
101
+ }=Tokens
102
+
103
+ const tree=[]
104
+ const if_stacks=[]
105
+ function _get_if_stack() {
106
+ if(!if_stacks.length) return;
107
+ const p=if_stacks[if_stacks.length-1]
108
+ const {_push_consequent, _reverse, consequent, alternate}=p
109
+ let cd=[consequent, alternate]
110
+ if(_reverse) cd.reverse()
111
+ return cd[_push_consequent? 0: 1] // .push(if_statement)
112
+ }
113
+
114
+ for(let i=0; i<tokens.length; i++) {
115
+ const x=tokens[i]
116
+ if(x.tk===T_IFDEF || x.tk===T_IFNDEF) {
117
+ const if_statement={
118
+ type: O_IFDEF,
119
+ match: x.tk_params,
120
+ consequent: [],
121
+ alternate: [],
122
+ _push_consequent: true,
123
+ _reverse: x.tk===T_IFNDEF,
124
+ }
125
+ const _cur_if_stack=_get_if_stack()
126
+ _cur_if_stack && _cur_if_stack.push(if_statement)
127
+ if_stacks.push(if_statement)
128
+ }else if(x.tk===T_ELSE) {
129
+ const t=if_stacks[if_stacks.length-1]
130
+ if(!t || t.type!==O_IFDEF) throw new Error('not match '+T_ELSE)
131
+ t._push_consequent=false
132
+ }else if(x.tk===T_ENDIF) {
133
+ const t=if_stacks[if_stacks.length-1]
134
+ if(!t || t.type!==O_IFDEF) throw new Error('not match '+T_ENDIF)
135
+ const p=if_stacks.pop()
136
+ if(!if_stacks.length) {
137
+ tree.push(p)
138
+ }
139
+ }else{
140
+ let d={}
141
+ if(x.tk===T_DEF) {
142
+ Object.assign(d, {
143
+ type: O_DEF,
144
+ def: x.tk_params,
145
+ })
146
+ }else if(x.tk===T_UNDEF) {
147
+ Object.assign(d, {
148
+ type: O_UNDEF,
149
+ def: x.tk_params,
150
+ })
151
+ }else if(x.tk===T_INCLUDE) {
152
+ Object.assign(d, {
153
+ type: O_INCLUDE,
154
+ include: x.tk_params,
155
+ })
156
+ }else if(x.tk===T_DEFINE) {
157
+ const re_define_func=/^([A-Za-z\d_]+)\(([^)]+)\)\s+(.+)/
158
+ const re_define_const=/^([A-Za-z\d_]+)\s+(.+)/
159
+ const matched_func=x.tk_params.match(re_define_func)
160
+ const matched_const=x.tk_params.match(re_define_const)
161
+ if(matched_func) {
162
+ const [, funcname, argv, func_body]=matched_func
163
+ Object.assign(d, {
164
+ type: O_DEFINE_CALL,
165
+ fname: funcname,
166
+ fcall: new vm.Script(`(${argv})=>(${func_body})`).runInNewContext({require}),
167
+ })
168
+ }else if(matched_const) {
169
+ const [, constant, value]=matched_const
170
+ Object.assign(d, {
171
+ type: O_DEFINE_CONST,
172
+ cname: constant,
173
+ cvalue: value+'',
174
+ })
175
+ }else{
176
+ throw new Error('unsupported token: '+JSON.stringify(x))
177
+ }
178
+ }else if(x.call_define) {
179
+ if(x.call_define_params) {
180
+ const argv=x.call_define_params.substr(1, x.call_define_params.length-2).split(',').map(a=>a.trim())
181
+ Object.assign(d, {
182
+ type: O_CALL_DEFINE,
183
+ call: x.call_define,
184
+ argv,
185
+ source: x.source,
186
+ })
187
+ }else{
188
+ Object.assign(d, {
189
+ type: O_CALL_CONST,
190
+ call: x.call_define,
191
+ source: x.source,
192
+ })
193
+ }
194
+ }else if(x.frags) {
195
+ Object.assign(d, {
196
+ type: O_STR,
197
+ str: x.str,
198
+ })
199
+ }else{
200
+ throw new Error('unsupported token: '+JSON.stringify(x))
201
+ }
202
+ const ad=_get_if_stack() || tree
203
+ const _f=ad[ad.length-1]
204
+ if(d.type===O_STR && _f && _f.type===O_STR) {
205
+ _f.str+=d.str
206
+ }else{
207
+ ad.push(d)
208
+ }
209
+ }
210
+ }
211
+ return tree
212
+ }
213
+
214
+ const caches={}
215
+ function compileSptcMacroFile(filename, mockFileContent) {
216
+ return compileFile(filename, {
217
+ customCache: caches,
218
+ compileFunc: content=>transformToAst(lexer(content)),
219
+ mockFileContent,
220
+ })
221
+ }
222
+
223
+
224
+ module.exports={
225
+ compileSptcMacroFile,
226
+ MACRO_TYPES: {
227
+ O_IFDEF,
228
+ O_STR,
229
+ O_DEFINE_CONST,
230
+ O_DEFINE_CALL,
231
+ O_CALL_CONST,
232
+ O_CALL_DEFINE,
233
+ O_DEF,
234
+ O_INCLUDE,
235
+ O_UNDEF,
236
+ },
237
+ }
@@ -0,0 +1,81 @@
1
+
2
+ const vm=require('vm')
3
+ const path=require('path')
4
+
5
+ const {compileFile}=require('../utils')
6
+
7
+ const T_JS=Symbol()
8
+ const T_TEXT=Symbol()
9
+ function lexer(content) {
10
+ const FLAG_OPEN='<?js'
11
+ const FLAG_CLOSE='?>'
12
+ const tokens=[]
13
+ const _t_flags=[]
14
+ let c1=-1, c2=-1
15
+ function _append_token(tk, left, right) {
16
+ const tk_params=content.substr(left, right-left)
17
+ if(!tk_params.length) return;
18
+ tokens.push({tk, tk_params})
19
+ }
20
+ for(;;) {
21
+ c1=content.indexOf(FLAG_OPEN, c1)
22
+ c2=content.indexOf(FLAG_CLOSE, c2)
23
+ if(c1===-1 && c1===-1) break
24
+ if(c1>-1) {
25
+ _t_flags.push({fk: FLAG_OPEN, idx: c1})
26
+ c1+=FLAG_OPEN.length
27
+ }
28
+ if(c2>-1) {
29
+ _t_flags.push({fk: FLAG_CLOSE, idx: c2})
30
+ c2+=FLAG_CLOSE.length
31
+ }
32
+ }
33
+ if(_t_flags.length) {
34
+ const p=_t_flags[_t_flags.length-1]
35
+ if(p.fk!==FLAG_CLOSE) _t_flags.push({
36
+ fk: FLAG_CLOSE,
37
+ idx: content.length,
38
+ })
39
+ for(let i=0; i<_t_flags.length; i+=2) {
40
+ _append_token(T_TEXT, i>0? _t_flags[i-1].idx+FLAG_CLOSE.length: 0, _t_flags[i].idx)
41
+ _append_token(T_JS, _t_flags[i].idx+FLAG_OPEN.length, _t_flags[i+1].idx)
42
+ }
43
+ _append_token(T_TEXT, _t_flags[_t_flags.length-1].idx+FLAG_CLOSE.length, content.length)
44
+ }else{
45
+ tokens.push({
46
+ tk: T_TEXT,
47
+ tk_params: content,
48
+ })
49
+ }
50
+ return tokens
51
+ }
52
+
53
+ function transformToAst(tokens, filename) {
54
+ let ret=''
55
+
56
+ const __filefullname=path.resolve(filename)
57
+ const {dir: __dirname, base: __filename}=path.parse(__filefullname)
58
+ const ctx0={__dirname, __filename, __filefullname}
59
+ for(let k in ctx0) {
60
+ ret+=`const ${k}=${JSON.stringify(ctx0[k])};`
61
+ }
62
+ for(let i=0; i<tokens.length; i++) {
63
+ if(tokens[i].tk===T_JS) {
64
+ ret+=tokens[i].tk_params+'\n'
65
+ }else if(tokens[i].tk===T_TEXT) {
66
+ ret+='\necho('+JSON.stringify(tokens[i].tk_params)+');\n'
67
+ }
68
+ }
69
+ return [ctx0, new vm.Script(ret+'; Symbol()[0]=_=>0', filename)]
70
+ }
71
+
72
+ function compileSptcFile(filename, option) {
73
+ return compileFile(filename, {
74
+ compileFunc: content=>transformToAst(lexer(content), filename),
75
+ ...(option || {}),
76
+ })
77
+ }
78
+
79
+ module.exports={
80
+ compileSptcFile,
81
+ }