sptc 0.0.8 → 0.0.10

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/bin/sptcd.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const {FastCGI, FastCGI_FPM}=require('../dist/httpServer')
4
4
  const {Command}=require('commander')
@@ -14,6 +14,7 @@ const program=new Command()
14
14
  .option('-e, --exts <string>', 'Specify the valid extensions of executable sptc files.', '.s,.sjs,.sptc')
15
15
  .option('-n, --workers <number>', 'Workers count.', 1)
16
16
  .option('-s, --slient', 'Slient mode.')
17
+ .option('-t, --traverse', 'If pathname is a directory, execute the index file under the pathname directory if any index file exists. If no index file is found, traverse the directory. If a router file is specified, this option will be ignored.')
17
18
  .action(({
18
19
  port,
19
20
  locally,
@@ -22,26 +23,39 @@ const program=new Command()
22
23
  exts,
23
24
  workers,
24
25
  slient,
26
+ traverse,
25
27
  })=>{
28
+ const disableLog=slient || require('cluster').isWorker
29
+
30
+ if(router && traverse) {
31
+ traverse=false
32
+ if(!disableLog) {
33
+ console.log('`--traverse` option has been ignored due to the router file has been specified.')
34
+ }
35
+ }
36
+
26
37
  const argv=[port, locally, {
27
38
  serverDir: workdir,
28
39
  routerEntry: router,
29
40
  exts: exts.split(',').map(x=>x.trim()).filter(x=>x),
30
41
  debug: true,
42
+ traverse,
31
43
  }]
44
+
32
45
  if(workers>1) {
33
46
  FastCGI_FPM(workers, ...argv)
34
47
  }else{
35
48
  FastCGI(...argv)
36
49
  }
37
50
 
38
- if(slient || require('cluster').isWorker) return;
51
+ if(disableLog) return;
39
52
  const {getLocalIpv4Addresses}=require('../utils')
40
53
  console.log('`sptc-http-server` has been launched with the following option:')
41
54
  const o={
42
55
  workdir,
43
- routerEntry: router,
56
+ routerEntry: router || null,
44
57
  }
58
+ o.traverse=traverse? true: false
45
59
  if(locally) {
46
60
  o.serve='127.0.0.1:'+port
47
61
  }else{
@@ -3,6 +3,7 @@ const {
3
3
  safe_path,
4
4
  fileExists,
5
5
  getExtension,
6
+ listdir,
6
7
  }=require('../utils')
7
8
  const {executeSptcFile}=require('../engine')
8
9
 
@@ -42,26 +43,51 @@ function CGI(req, res, option) {
42
43
  exts=['.s', '.sptc', '.sjs'],
43
44
  debug=false,
44
45
  router=null,
46
+ traverse=false,
45
47
  env,
46
48
  }=option
47
- const [reqCtx, handler]=buildRequestContext(req, res, {srvDir, debug, env})
49
+ const [reqCtx, handler]=buildRequestContext(req, res, {
50
+ srvDir,
51
+ debug,
52
+ env,
53
+ isRouterMode: router,
54
+ })
48
55
  const file=reqCtx.$_REQUEST_FILE || {}
49
56
  const fn=router || file.fullname
50
- if(!fileExists(fn)) {
51
- res.writeHead(404, {'content-type': 'text/plain'})
52
- res.end(`file does not exist: \`${router || file.pathname}\``)
57
+
58
+ function _executeFile(fn) {
59
+ const ext=getExtension(fn)
60
+ if(!exts.includes(ext)) {
61
+ res.writeHead(403, {'content-type': 'text/plain'})
62
+ res.end(`extensions does not match: \`${ext}\``)
63
+ return
64
+ }
65
+ executeSptcFile(fn, reqCtx, handler)
66
+ }
67
+
68
+ if(fileExists(fn)) {
69
+ _executeFile(fn)
53
70
  return
54
71
  }
55
- const ext=getExtension(fn)
56
- if(!exts.includes(ext)) {
57
- res.writeHead(403, {'content-type': 'text/plain'})
58
- res.end(`extensions does not match: \`${ext}\``)
59
- return
72
+
73
+ if(!traverse) {
74
+ res.writeHead(404, {'content-type': 'text/html'})
75
+ res.end(`<h3>File does not exist: \`${router || file.pathname}\`</h3>`)
76
+ return
77
+ }
78
+
79
+ for(let e of exts) {
80
+ if(!fileExists(fn+'/index'+e)) continue
81
+ _executeFile(fn+'/index'+e)
82
+ return
60
83
  }
61
- executeSptcFile(fn, reqCtx, handler)
84
+
85
+ res.writeHead(404, {'content-type': 'text/html'})
86
+ res.end(traverseDirectory(srvDir, file.pathname, exts))
87
+
62
88
  }
63
89
 
64
- function buildRequestContext(req, res, {srvDir, debug, env}) {
90
+ function buildRequestContext(req, res, {srvDir, debug, env, isRouterMode}) {
65
91
  const {fullname, pathname}=getRequestFile(req, srvDir)
66
92
  const reqCtx={
67
93
  $_RAW_REQUEST: req,
@@ -105,7 +131,7 @@ function buildRequestContext(req, res, {srvDir, debug, env}) {
105
131
  }
106
132
  reqCtx.isDebug=_=>state.is_debug
107
133
 
108
- return [reqCtx, {
134
+ const handler={
109
135
  write: x=>{
110
136
  flushHeader()
111
137
  res.write(x)
@@ -119,7 +145,10 @@ function buildRequestContext(req, res, {srvDir, debug, env}) {
119
145
  flushHeader()
120
146
  res.end(state.is_debug? e.stack: '')
121
147
  },
122
- }]
148
+ isEntry: true,
149
+ }
150
+
151
+ return [reqCtx, handler]
123
152
  }
124
153
  function getRequestFile(req, srvDir) {
125
154
  const _url=require('url')
@@ -130,6 +159,31 @@ function getRequestFile(req, srvDir) {
130
159
  }
131
160
  }
132
161
 
162
+ function traverseDirectory(ref, dir, exts) {
163
+ let ret=`
164
+ <!doctype html>
165
+ <meta charset=utf8 />
166
+ <style>*{line-height: 1.8;}b{font-size: 16px;}a{font-size: 18px; color: #2222ff; cursor: pointer;}a.dir{color: #555555;}</style>`
167
+ const {dirs, files, error}=listdir(ref, dir)
168
+ ret+=`<a style="font-size: 22px;" href="${dir!=='/'? '../': ''}">back</a>`
169
+ ret+=`<h3>${dir}</h3>`
170
+ if(error) {
171
+ ret+=`<h3>${error.message}</h3>`
172
+ }else{
173
+ for(let i=0; i<dirs.length; i++) {
174
+ ret+=`<a class="dir" href="${encodeURIComponent(dirs[i].fn)}/">${dirs[i].fn}</a> <br/>`
175
+ }
176
+ for(let i=0; i<files.length; i++) {
177
+ if(exts.includes(getExtension(files[i].fn))) {
178
+ ret+=`<a href="${encodeURIComponent(files[i].fn)}">${files[i].fn}</a> <br/>`
179
+ }else{
180
+ ret+=`<b>${files[i].fn}</b> ${files[i].size} <br/>`
181
+ }
182
+ }
183
+ }
184
+ return ret
185
+ }
186
+
133
187
  module.exports={
134
188
  FastCGI,
135
189
  FastCGI_FPM,
package/engine/index.js CHANGED
@@ -17,6 +17,10 @@ function executeSptcFile(filename, payload, option={}) {
17
17
  undefined,
18
18
  }
19
19
 
20
+ if(option.isEntry) {
21
+ _option.isEntry=option.isEntry
22
+ }
23
+
20
24
  const [ctx0, vm]=Compiler.compileSptcFile(filename, _option)
21
25
  const [ctx, priv]=Interpreter.buildContext({...payload, ...ctx0}, option)
22
26
  Interpreter.executeVm(vm, ctx, priv)
@@ -133,6 +133,7 @@ function buildContext(ctx0, option) {
133
133
  onError=NOOP,
134
134
  masterPriv=null,
135
135
  readAsBinary=false,
136
+ isEntry=false,
136
137
  }=option
137
138
 
138
139
  const ctx=buildGlobal()
@@ -144,6 +145,9 @@ function buildContext(ctx0, option) {
144
145
  _defers: [],
145
146
  __autoload_func: null,
146
147
  __autoload_vars: {},
148
+ _defined: {},
149
+ cacheVersion: '',
150
+ statExpires: -1,
147
151
  }
148
152
 
149
153
  Object.assign(ctx, {
@@ -152,6 +156,16 @@ function buildContext(ctx0, option) {
152
156
  isMaster: !masterPriv,
153
157
  })
154
158
 
159
+ // Dynamically set the cache configuration.
160
+ ctx.configSptcFileCache=(cacheVersion, cacheSeconds)=>{
161
+ if(!isEntry) {
162
+ console.log('Warning: this function only works in the entry file.')
163
+ return
164
+ }
165
+ priv.cacheVersion=cacheVersion
166
+ priv.statExpires=cacheSeconds
167
+ }
168
+
155
169
  ctx.echo=(...x)=>{
156
170
  if(priv.isEnd) return;
157
171
  priv.echos.push(...x)
@@ -184,6 +198,13 @@ function buildContext(ctx0, option) {
184
198
  priv._defers.push(fn)
185
199
  }
186
200
 
201
+ ctx.define=(k, v)=>{
202
+ if(k in priv._defined) {
203
+ throw new Error(`duplicate key: ${k}`)
204
+ }
205
+ priv._defined[k]=v
206
+ }
207
+
187
208
  try{
188
209
  ctx.require=_module.createRequire(ctx0.__filefullname)
189
210
  }catch(e) {
@@ -202,13 +223,20 @@ function buildContext(ctx0, option) {
202
223
 
203
224
  ctx.include=(inc_filename, inc_payload={})=>{
204
225
  const inc=path.resolve(ctx0.__dirname, inc_filename)
205
- const [_ctx0, _vm]=Compiler.compileSptcFile(inc)
206
- const [_ctx, _]=buildContext({...ctx0, ...inc_payload, ..._ctx0}, {
226
+ const [_ctx0, _vm]=Compiler.compileSptcFile(inc, {
227
+ statExpires: priv.statExpires,
228
+ cacheVersion: priv.cacheVersion,
229
+ })
230
+ const [_ctx, _]=buildContext({
231
+ ...ctx0,
232
+ ...inc_payload,
233
+ ..._ctx0,
234
+ }, {
207
235
  write: option.write,
208
- // end: ctx.end,
209
236
  onError: option.onError,
210
237
  masterPriv: priv,
211
238
  readAsBinary,
239
+ isEntry: false,
212
240
  })
213
241
  const ret=executeVm(_vm, _ctx, priv)
214
242
  return Object.assign({}, _ctx._exports, {__getter__: (...x)=>ret.constructor('return '+(x.length>1? '['+x.join(',')+']': x))()})
@@ -222,6 +250,9 @@ function buildContext(ctx0, option) {
222
250
  pctx=new Proxy(pctx, {
223
251
  get: (target, prop, receiver)=>{
224
252
  if(false===prop in target) {
253
+ if(prop in priv._defined) {
254
+ return priv._defined[prop]
255
+ }
225
256
  if(prop in priv.__autoload_vars) {
226
257
  return priv.__autoload_vars[prop]
227
258
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sptc",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "Simple Pretreat Toolkit CLI",
5
5
  "main": "index.js",
6
6
  "engines": {
package/tests/www/cc.s ADDED
@@ -0,0 +1,5 @@
1
+ <?js
2
+ //configSptcFileCache('ll', 1919)
3
+ echo(`aaaD=>`+DDD)
4
+ define('RRR', 44)
5
+ echo($_GLOBAL['x'], '::>JJJv,v2222:')
@@ -1,5 +1,18 @@
1
1
  <?js
2
+
3
+
4
+ $_GLOBAL['x']='999x'
5
+ configSptcFileCache('eob0', 100)
6
+
2
7
  echo($_REQUEST_FILE)
8
+
9
+ define('DDD', 2)
10
+ include(__dirname+'/cc.s')
11
+
12
+ echo('oaawecR=>'+RRR, Math.random())
13
+
14
+ /*
15
+
3
16
  echo('aaaa<pre>')
4
17
  var_dump($_GLOBAL, this)
5
18
 
@@ -15,3 +28,4 @@ Sync.Push((async _=>{
15
28
  }
16
29
  echo('--done--')
17
30
  })())
31
+ */
@@ -3,23 +3,25 @@ const {mtime, readTextFile}=require('./base')
3
3
  const _caches={}
4
4
  function compileFile(filename, option) {
5
5
  const {
6
- cacheKeyWrapper=x=>x,
7
6
  contentWrapper=x=>x,
8
7
  mockFileContent=null,
9
8
  compileFunc=x=>x,
10
- statExpires=-1,
9
+ statExpires=-1, cacheVersion='',
11
10
  customCache,
12
11
  }=option
13
12
 
13
+ const cacheKeyWrapper=statExpires>0?
14
+ fn=>(option.cacheVersion || '')+'-'+fn:
15
+ fn=>fn
16
+
14
17
  if(mockFileContent) {
15
18
  return compileFunc(contentWrapper(mockFileContent))
16
19
  }
17
20
 
18
21
  const caches=customCache || _caches
19
-
20
22
  const cacheKey=cacheKeyWrapper(filename)
21
23
  let _e=caches[filename]
22
- const file_mt=(!_e || statExpires<=0 || _e.modify_t-Date.now()>statExpires)?
24
+ const file_mt=(!_e || statExpires<=0 || Date.now()-_e.modify_t>statExpires*1e3)?
23
25
  mtime(filename):
24
26
  _e.file_mt
25
27
 
package/utils/dir.js ADDED
@@ -0,0 +1,46 @@
1
+ const fs=require('fs')
2
+ const {safe_path}=require('./base')
3
+
4
+ function fsize(b) {
5
+ const u=['b', 'kb', 'mb', 'gb']
6
+ let i=0;
7
+ for(; i<u.length-1; i++) {
8
+ if(b<1024) break
9
+ b/=1024
10
+ }
11
+ return b.toFixed(1)+u[i]
12
+ }
13
+
14
+ function listdir(ref, dir) {
15
+ const ret={
16
+ dirs: [],
17
+ files: [],
18
+ }
19
+ try{
20
+ const ls=fs.readdirSync(safe_path(ref, dir))
21
+ for(let a of ls) try{
22
+ const fn=safe_path(ref, dir+'/'+a)
23
+ const stat=fs.statSync(fn)
24
+ const dfn=safe_path(dir.substr(1), a)
25
+ if(stat.isFile()) {
26
+ ret.files.push({
27
+ dfn,
28
+ fn: a,
29
+ size: fsize(stat.size),
30
+ })
31
+ }else{
32
+ ret.dirs.push({
33
+ dfn,
34
+ fn: a,
35
+ })
36
+ }
37
+ }catch(e) {}
38
+ }catch(e) {
39
+ ret.error=new Error('`'+dir+'` permission denied')
40
+ }
41
+ return ret
42
+ }
43
+
44
+ module.exports={
45
+ listdir,
46
+ }
package/utils/index.js CHANGED
@@ -3,4 +3,5 @@ module.exports={
3
3
  ...require('./compileFile'),
4
4
  ...require('./base'),
5
5
  ...require('./fpm'),
6
+ ...require('./dir'),
6
7
  }