@vindo/cli 0.0.1 → 0.0.3

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.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Live reload
3
+ */
4
+ (function({origin}) {
5
+ (new EventSource(origin)).onmessage = function() {
6
+ location.reload()
7
+ }
8
+ })
9
+ (new URL(document.currentScript.src))
@@ -0,0 +1,240 @@
1
+
2
+ const es = require('esbuild')
3
+ const sass = require('sass')
4
+ const util = require('../util')
5
+ const config = require('../config')
6
+
7
+
8
+ module.exports = exports = Object.create(util, {
9
+ config: {
10
+ value: config
11
+ }
12
+ })
13
+ const {file, string, resolve} = util
14
+
15
+
16
+ /**
17
+ * Get basename
18
+ * @param {string} file
19
+ */
20
+ function getname(val) {
21
+ return file.path.parse(val).name
22
+ }
23
+
24
+
25
+ /**
26
+ * Get chunk
27
+ * @param {string} dir
28
+ */
29
+ async function getChunk(dir) {
30
+ var code = []
31
+ var files = await file.readdir(dir, {recursive: true})
32
+
33
+ for(var {name, parentPath} of files) {
34
+ if(!/\.(js|tsx|jsx)$/.test(name)) {
35
+ continue
36
+ }
37
+
38
+ var name = getname(name)
39
+ if(name) {
40
+ var _file = file.path.join(parentPath, name)
41
+
42
+ if(name == 'index') {
43
+ _file = parentPath
44
+ name = getname(parentPath)
45
+ }
46
+ code.push(`import ${name} from '${_file}'`, `data.${name} = ${name}`)
47
+ }
48
+ }
49
+ return code
50
+ }
51
+
52
+
53
+ /**
54
+ * Get imported files
55
+ * @param {object} opt
56
+ * @param {string} data
57
+ */
58
+ async function getImports(entry, data) {
59
+ var imp = []
60
+ var matches = data.matchAll(/import\s(.*)\sfrom.*('|"|\/)([a-zA-Z0-9_-]+)('|")/g)
61
+
62
+ for(var item of matches) {
63
+ if(/(\.\/|)components\//.test(item[0])) {
64
+ imp.push(`var ${item[1]} = ${item[3]}`)
65
+ }
66
+ else if(/(\.\/)/.test(item[0])) {
67
+ imp.push(item[0].replace(/('|")(\.\/)/, `$1${resolve.dirname(entry)}/`))
68
+ }
69
+ else {
70
+ imp.push(item[0])
71
+ }
72
+ }
73
+ return imp
74
+ }
75
+
76
+
77
+ /**
78
+ * Get all imports from components directory
79
+ * @param {object} opt
80
+ */
81
+ async function getComponents(opt) {
82
+ var data = []
83
+
84
+ if(Array.isArray(opt.includes)) {
85
+ for(var item of opt.includes) {
86
+ const dir = file.path.resolve(process.cwd(), 'node_modules', item)
87
+ if(!file.exists(dir)) {
88
+ continue
89
+ }
90
+ data = data.concat(await getChunk(dir))
91
+ }
92
+ }
93
+
94
+ return data.concat(await getChunk(opt.components))
95
+ }
96
+
97
+
98
+ /**
99
+ * Collect components and assemble before building
100
+ *
101
+ * @param {object} opt option
102
+ */
103
+ async function startBuild(opt) {
104
+
105
+ if(!file.exists(opt.entry)) {
106
+ throw new Error(`Entry file not found. "${opt.entry}"`)
107
+ }
108
+
109
+ const data = file.read(opt.entry)
110
+ const temp = file.read(opt.template)
111
+
112
+ const impo = await getImports(opt.entry, data)
113
+ const comp = await getComponents(opt)
114
+
115
+ const code = string.replace(temp, {
116
+ REACT_COMPONENTS: comp.join('\n'),
117
+ IMPORTED_COMPONENTS: impo.join('\n'),
118
+ HEAD_COMPONENT: data.match(/<head.*>((.|\n)*)<\/head>/g)[0],
119
+ BODY_COMPONENT: data.match(/<body.*>((.|\n)*)<\/body>/g)[0]
120
+ })
121
+
122
+ await file.promises.writeFile(opt.chunk, code, {flag: 'w'})
123
+ }
124
+
125
+
126
+ /**
127
+ * Create a bundle file
128
+ *
129
+ * @param {object} opt option
130
+ */
131
+ async function startBundle(opt) {
132
+
133
+ if(!file.exists(opt.chunk)) {
134
+ return
135
+ }
136
+
137
+ const build = await es.build({
138
+ ...opt.settings,
139
+ minify: opt.minify,
140
+ outfile: opt.outfile,
141
+ entryPoints: [opt.chunk],
142
+ })
143
+ const meta = Object.keys(build.metafile.outputs)[0]
144
+
145
+ /**
146
+ * Append bundle hash to vindo config
147
+ */
148
+ const name = opt.bundleName
149
+ const hash = await config.bundle(opt.name ?? name, [meta.match(/-(.*).js/)[1], name])
150
+ /**
151
+ * Delete old bundle
152
+ */
153
+ const files = file.readdir(resolve.root(opt.output))
154
+ files.forEach((item) => {
155
+ const matched = item.name.match(
156
+ new RegExp(`^${name}-([A-Z0-9+]{8})\.js$`)
157
+ )
158
+ if(matched) {
159
+ if(hash == matched[1]) {
160
+ return
161
+ }
162
+ file.unlink(file.path.resolve(item.parentPath, item.name))
163
+ }
164
+ })
165
+
166
+ if(file.exists(opt.chunk)) {
167
+ file.unlink(opt.chunk)
168
+ }
169
+ }
170
+
171
+
172
+ /**
173
+ * Compile scss file
174
+ * @param {object} opt
175
+ */
176
+ async function startCompile(opt) {
177
+ if(!opt.compileSass) {
178
+ return
179
+ }
180
+ const input = resolve.root(opt.compileSass.input)
181
+ const output = resolve.root(opt.compileSass.output)
182
+
183
+ /**
184
+ * Create output dir if not exists
185
+ */
186
+ if(!file.exists(input)) {
187
+ throw new Error(`No sass input directory found. "${input}"\n`)
188
+ }
189
+
190
+ /**
191
+ * Create output dir if not exists
192
+ */
193
+ if(!file.exists(output)) {
194
+ await file.makedir(output)
195
+ }
196
+
197
+ function scss(name) {
198
+ return resolve.root(opt.compileSass.input, name)
199
+ }
200
+
201
+
202
+ const files = await file.readdir(input)
203
+ for(var item of files) {
204
+
205
+ if(item.isFile()) {
206
+ const url = scss(item.name)
207
+
208
+ if(file.exists(url)) {
209
+ const css = sass.compile(url).css
210
+
211
+ const out = resolve.root(
212
+ output,
213
+ item.name.replace(/\.scss/, '.css')
214
+ )
215
+ await file.promises.writeFile(out, css, {flag: 'w'})
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+
222
+ /**
223
+ *
224
+ * @param {object} config
225
+ */
226
+ exports.build = async function build(opt) {
227
+ /**
228
+ * Create build directory
229
+ */
230
+ await file.makedir(resolve.root(opt.output))
231
+
232
+ try {
233
+ await startBuild(opt)
234
+ await startBundle(opt)
235
+ await startCompile(opt)
236
+ }
237
+ catch(e) {
238
+ console.log(e)
239
+ }
240
+ }
@@ -2,8 +2,8 @@ const {parse} = require('node:path')
2
2
  const {createServer} = require('node:http')
3
3
 
4
4
 
5
- module.exports = function(cb) {
6
5
 
6
+ function server(cb) {
7
7
  return createServer((req, res) => {
8
8
  const url = parse(req.url)
9
9
 
@@ -37,7 +37,38 @@ module.exports = function(cb) {
37
37
  }
38
38
  res.headers({'Access-Control-Allow-Origin': '*'})
39
39
 
40
-
40
+
41
41
  cb(req, res)
42
42
  })
43
+ }
44
+
45
+
46
+ module.exports = function(builder, cb) {
47
+ var file = builder.file
48
+ var events = builder.events
49
+ var resolve = builder.resolve
50
+ var option = builder.config.option
51
+
52
+ /**
53
+ * Run development server and create bundle
54
+ */
55
+ const http = server(async function(req, res) {
56
+ const data = await file.read(resolve.clidir('builder/development.js'))
57
+
58
+ if(req.isStream()) {
59
+ events.on('change', () => {
60
+ res.write(`data: {changed: true}\n\n`)
61
+ })
62
+ }
63
+
64
+ if(req.is('development')) {
65
+ res.headers({
66
+ 'Content-Type':
67
+ 'application/javascript'
68
+ })
69
+ res.end(data)
70
+ }
71
+ })
72
+
73
+ http.listen(option.port, cb)
43
74
  }
@@ -0,0 +1,43 @@
1
+
2
+ import React from 'react'
3
+ import * as client from '@vindo/react/client'
4
+
5
+ /**
6
+ * This will be the holder of all components that will be imported
7
+ */
8
+ const data = {}
9
+
10
+ /**
11
+ * Template for components to be imported
12
+ */
13
+ {REACT_COMPONENTS}
14
+ {IMPORTED_COMPONENTS}
15
+
16
+
17
+ data.Link = client.Link
18
+
19
+
20
+ if(data.head || data.body) {
21
+ throw new SyntaxError(`Component name '${data.head ? 'head' : 'body'}' is a reserved name for SSR client components.`)
22
+ }
23
+
24
+ data.head = function head(meta) {
25
+ const {props:{children}} = React.cloneElement((
26
+ {HEAD_COMPONENT}
27
+ ))
28
+ return children
29
+ }
30
+
31
+ data.body = function body({meta}) {
32
+ var {props:{children}} = React.cloneElement(({BODY_COMPONENT}))
33
+
34
+ if(!Array.isArray(children)) {
35
+ children = React.Children.toArray(children)
36
+ }
37
+
38
+ return React.Children.map(children, (child, key) => {
39
+ return React.cloneElement(child, {key, ...arguments[0]})
40
+ })
41
+ }
42
+
43
+ client.render(document, data)
package/bin/config.js CHANGED
@@ -1,58 +1,120 @@
1
- const {file} = require('@vindo/utility')
1
+ const util = require('./util')
2
+ const option = require('./option')
2
3
 
3
4
 
4
- const port = 2001
5
- const args = {}
6
- const argv = process.argv
5
+ const file = util.file
6
+ const object = util.object
7
+ const resolve = util.resolve
8
+ const parseArgs = util.parseArgs
7
9
 
8
10
 
9
- const vindo = file.read(file.join(process.cwd(), 'vindo.json'))
11
+ /**
12
+ * Exports
13
+ */
14
+ module.exports = exports = Object.create({}, {
15
+ args: {get: getArgs},
16
+ option: {get: getOption},
17
+ })
10
18
 
11
19
 
12
- for(var i in argv) {
13
- const pat = /^([a-z-]+)=/
20
+ var path = resolve.root('vindo.json')
21
+ if(!file.exists(path)) {
22
+ throw Error(`vindo.json file does not exists.`)
23
+ }
24
+ var vindo = file.get(path)
25
+
26
+
27
+ /**
28
+ * To string
29
+ */
30
+ function toStr(data) {
31
+ return Buffer.from(JSON.stringify(data)).toString('base64').replace(/=/g, '')
32
+ }
33
+
34
+ /**
35
+ * Get arguments
36
+ */
37
+ function getArgs() {
38
+ const args = process.argv
14
39
 
15
- if(i < 2) {
16
- continue
40
+ const command = args[2]
41
+ if(!option.commands.includes(command)) {
42
+ throw new ReferenceError('You need to provide at least one command.')
17
43
  }
18
- var arg = argv[i].split(pat, 3).filter(String)
19
44
 
20
- if(arg) {
21
- args[arg[0].substring(2)] = arg[1] ? arg[1] : true
45
+ const {values} = parseArgs({
46
+ args: args.slice(3),
47
+ allowPositionals: true,
48
+ options: option.argsOptions,
49
+ })
50
+
51
+ return object.merge(values, {[command]: true, command})
52
+ }
53
+
54
+
55
+ /**
56
+ * Get build option and arguments
57
+ */
58
+ function getOption() {
59
+ var opt = object.merge(option.buildOptions, vindo.buildOptions)
60
+ var opt = object.merge(opt, getArgs())
61
+
62
+ return {
63
+ ...opt,
64
+ name: vindo.name,
65
+ sass: option.sass,
66
+ settings: option.settings,
67
+
68
+ entry: resolve.root(opt.entry),
69
+ chunk: resolve.root(opt.output, 'chunk.jsx'),
70
+ template: resolve.clidir('builder', 'template.js'),
71
+ components: resolve.dirname(opt.entry, 'components'),
72
+ outfile: resolve.root(`${opt.output}/${opt.bundleName}.js`),
73
+ devScript: `http://127.0.0.1:${opt.port}/development`,
22
74
  }
23
75
  }
24
76
 
25
77
 
26
- module.exports = {
27
- /**
28
- * Development script
29
- */
30
- get devScript() {
31
- return `http://127.0.0.1:${this.option.port}/browser.js`
32
- },
78
+ /**
79
+ * Add bundle
80
+ */
81
+ exports.add = function add(opt) {
82
+ if(opt.package) {
83
+ var pkg = resolve.current('node_modules', opt.package, 'vindo.json')
84
+ if(!file.exists(pkg)) {
85
+ return
86
+ }
87
+
88
+ var pkg = file.get(pkg)
89
+ if(pkg) {
90
+ exports.bundle(null, pkg.bundles)
91
+ }
92
+ }
93
+ }
94
+
33
95
 
96
+ /**
97
+ * Add bundle metadata to vindo config
98
+ */
99
+ exports.bundle = async function bundle(name, data) {
100
+ if(!vindo.bundles) {
101
+ vindo.bundles = {}
102
+ }
34
103
  /**
35
- * Build Options
104
+ * Add new bundle
36
105
  */
37
- get option() {
38
- const {buildOption} = JSON.parse(vindo)
39
-
40
- return Object.assign({port}, buildOption, {
41
- ...args,
42
- output: typeof args.output == 'string' ? args.output : buildOption.output,
43
- })
44
- },
45
-
106
+ if(name) {
107
+ vindo.bundles[name] = toStr(data)
108
+ }
46
109
  /**
47
- * Console log color pattern
110
+ * Merge to existing bundles
48
111
  */
49
- pattern: {
50
- ref: {color: 'cyan', pat: /(<.*>)/g, text: '$1'},
51
- symbol: {color: 'green', pat: /(Symbol\(.*\))/g, text: '$1'},
52
- undefined: {color: 'gray', pat: /(undefined)/g, text: '$1'},
53
- function: {color: 'cyan', pat: /\[(.*)\]/g, text: '[$1]'},
54
- boolean: {color: 'yellow', pat: /(false|true)/g, text: '$1'},
55
- string: {color: 'green', pat: /('.*')/g, text: '$1'},
56
- number: {color: 'yellow', pat: /:\s(-?\d+)/g, text: ': $1'},
112
+ else {
113
+ vindo.bundles = object.merge(vindo.bundles, data)
57
114
  }
115
+ /**
116
+ * Update the config file
117
+ */
118
+ await file.promises.writeFile(path, JSON.stringify(vindo, null, 2), {flag: 'w'})
119
+ return data[0]
58
120
  }
package/bin/index.js CHANGED
@@ -1,79 +1,39 @@
1
1
  #! /usr/bin/env node
2
2
 
3
3
 
4
+ const builder = require('./builder')
5
+ const server = require('./builder/server')
4
6
 
5
- const {file} = require('@vindo/utility')
6
- const {exec, spawn} = require('node:child_process')
7
- const {watch:watcher} = require('chokidar')
8
- const {styleText:color} = require('node:util')
7
+ const kill = require('tree-kill')
8
+ const chokidar = require('chokidar')
9
+ const childProcess = require('node:child_process')
9
10
 
10
11
 
11
- var evt = {}
12
- var cwd = process.cwd()
13
12
  var child = null
14
-
15
-
16
- const build = require('./build')
17
- const server = require('./server')
18
- const {option, pattern, devScript} = require('./config')
13
+ var log = builder.log
14
+ var build = builder.build
15
+ var config = builder.config
16
+ var events = builder.events
17
+ var colors = builder.colors
18
+ var option = builder.config.option
19
19
 
20
20
 
21
21
  /**
22
22
  * Set to global environment
23
23
  */
24
- process.env.DEV_SERVER = devScript
25
- process.env.DEV_SERVER_PORT = option.port
26
-
27
-
28
- /**
29
- * Add event
30
- */
31
- function on(name, cb) {
32
- evt[name] = cb
33
- }
34
-
35
- /**
36
- * Call the event
37
- */
38
- function emit(name, ...args) {
39
- if(evt[name]) {
40
- evt[name].call(...args)
41
- }
42
- }
43
-
44
- /**
45
- * Log with colors
46
- */
47
- function log(name, text) {
48
- if(name) {
49
- return console.log(color(name, text.toString().trim()))
50
- }
51
- return console.log(text)
52
- }
53
-
54
- /**
55
- *
56
- */
57
- function setColors(text) {
58
- var text = text.toString()
59
-
60
- for(var item of Object.values(pattern)) {
61
- text = text.replace(item.pat, color(item.color, item.text))
62
- }
63
- return text.trim()
64
- }
24
+ process.env.DEV_PORT = option.port
25
+ process.env.DEV_SERVER = option.devScript
65
26
 
66
27
 
67
28
  /**
68
29
  *
69
30
  */
70
- async function watch(dirs, cb) {
31
+ async function watcher(dirs, cb) {
71
32
  try {
72
33
  if(!Array.isArray(dirs)) {
73
34
  throw TypeError('Expected a value of type `Array` but received a `String`.')
74
35
  }
75
- watcher(dirs, {
76
- // ignored: (path, stats) => stats?.isFile() && !path.endsWith('.js'), // only watch js files
36
+ chokidar.watch(dirs, {
77
37
  persistent: true
78
38
  })
79
39
  .on('change', (path) => cb({path}))
@@ -87,13 +47,18 @@ async function watch(dirs, cb) {
87
47
  /**
88
48
  *
89
49
  */
90
- function execute() {
50
+ function execute(cmd) {
91
51
  var i = 0
92
- var sp = exec(option.execute, {
93
- cwd,
94
- env: process.env
52
+
53
+ if(Array.isArray(cmd)) {
54
+ cmd = cmd.join(' && ')
55
+ }
56
+
57
+ var sp = childProcess.exec(cmd, {
58
+ env: process.env,
59
+ cwd: process.cwd(),
95
60
  })
96
- log('green', `[vindo] Running ${option.execute}`)
61
+ log('green', `[vindo] Running ${cmd}`)
97
62
 
98
63
  /**
99
64
  * Emit change event to reload client
@@ -105,9 +70,9 @@ function execute() {
105
70
  * It means the server runs successfully.
106
71
  */
107
72
  if(i == 1) {
108
- emit('change')
73
+ events.emit('change')
109
74
  }
110
- console.log(setColors(data))
75
+ console.log(colors.colorize(data))
111
76
  })
112
77
 
113
78
  /**
@@ -118,11 +83,11 @@ function execute() {
118
83
 
119
84
  for(var i in text) {
120
85
  if(i == 0) {
121
- text[0] = color('red', text[0])
86
+ text[0] = colors.set('red', text[0])
122
87
  }
123
- text[i] = text[i].replace(/(at.*)/, color('gray', '$1'))
124
- text[i] = text[i].replace(/:\s('.*')/, color('green', ': $1'))
125
- text[i] = text[i].replace(/\s(-?\d+)/, color('yellow', ' $1'))
88
+ text[i] = text[i].replace(/(at.*)/, colors.set('gray', '$1'))
89
+ text[i] = text[i].replace(/:\s('.*')/, colors.set('green', ': $1'))
90
+ text[i] = text[i].replace(/\s(-?\d+)/, colors.set('yellow', ' $1'))
126
91
  }
127
92
  log(null, text.join('\r\n'))
128
93
  })
@@ -134,58 +99,50 @@ function execute() {
134
99
  * Restart server and rebuild
135
100
  */
136
101
  function restart(event) {
137
- log('gray', `[vindo] File changed '${event.path}'`)
102
+ log('green', `[vindo] File changed '${event.path}'`)
138
103
  log('green', `[vindo] Rebuilding...`)
139
104
  log('green', `[vindo] Restarting server...`)
140
105
 
141
- if(option.output) {
142
- build(option)
143
- }
106
+ build(option)
107
+
144
108
  if(child) {
145
- if(process.platform === 'win32') {
146
- spawn('taskkill', ['/pid', child.pid, '/f', '/t'])
147
- }
148
- else {
149
- child.kill('SIGINT')
150
- }
109
+ kill(child.pid)
151
110
  }
152
- child = execute()
111
+ child = execute(option.execute)
153
112
  }
154
113
 
155
114
 
156
115
  /**
157
- * Create server
116
+ * Start building and execute
158
117
  */
159
- const http = server((req, res) => {
160
- if(req.isStream()) {
161
- on('change', () => {
162
- res.write(`data: {changed: true}\n\n`)
163
- })
164
- }
165
-
166
- if(req.is('reload')) {
167
- res.headers({
168
- 'Content-Type':
169
- 'application/javascript'
170
- })
171
- res.end(file.read(__dirname.concat('/reload.js')))
172
- }
173
- })
174
-
118
+ function start() {
119
+ build(option)
175
120
 
176
-
177
- http.listen(option.port, function() {
178
- child = execute()
179
-
180
- if(option.output) {
181
- build(option)
182
- }
121
+ child = execute(option.execute)
183
122
 
184
123
  if(option.watch) {
185
124
  log('yellow', '[vindo] v0.0.1')
186
125
  log('yellow', `[vindo] Watching at ${option.watch}`)
187
126
  log('yellow', `[vindo] Building at ${option.output}`)
188
127
 
189
- watch(option.watch, restart)
128
+ watcher(option.watch, restart)
190
129
  }
191
- })
130
+ }
131
+
132
+
133
+ /**
134
+ * Command specific tasks
135
+ */
136
+ switch(option.command) {
137
+ case 'add':
138
+ config.add(option)
139
+ break
140
+
141
+ case 'run':
142
+ server(builder, start)
143
+ break
144
+
145
+ case 'build':
146
+ build(option)
147
+ break
148
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "buildOptions": {
3
+ "port": 2001,
4
+ "minify": true,
5
+ "watch": [
6
+ "./src"
7
+ ],
8
+ "output": "./public",
9
+ "bundleName": "main"
10
+ },
11
+ "sass": {
12
+ "style": "compressed"
13
+ },
14
+ "settings": {
15
+ "bundle": true,
16
+ "metafile": true,
17
+ "format": "esm",
18
+ "jsx": "automatic",
19
+ "logLevel": "error",
20
+ "outfile": "dist/bundle.js",
21
+ "loader": {
22
+ ".js": "jsx",
23
+ ".jsx": "jsx",
24
+ ".ts": "tsx",
25
+ ".tsx": "tsx"
26
+ },
27
+ "entryNames": "[name]-[hash]",
28
+ "allowOverwrite": true,
29
+ "jsxImportSource": "react"
30
+ },
31
+ "commands": [
32
+ "run",
33
+ "add",
34
+ "build",
35
+ "bundle"
36
+ ],
37
+ "argsOptions": {
38
+ "add": {"type": "string"},
39
+ "port": {"type": "string"},
40
+ "package": {"type": "string"},
41
+ "output": {"type": "string"},
42
+ "mergeTo": {"type": "string"},
43
+ "execute": {"type": "string"},
44
+ "bundleName": {"type": "string"}
45
+ }
46
+ }
@@ -0,0 +1,112 @@
1
+ const util = require('util')
2
+ const {file, events, string, object} = require('@vindo/utility')
3
+
4
+
5
+ /**
6
+ * Set colors
7
+ */
8
+ const colors = {
9
+ set(name, text) {
10
+ return util.styleText(name, text)
11
+ },
12
+ colorize(text) {
13
+ var text = text.toString()
14
+ /**
15
+ * Console log color pattern
16
+ */
17
+ const pattern = {
18
+ ref: {color: 'cyan', pat: /(<.*>)/g, text: '$1'},
19
+ string: {color: 'green', pat: /('.*')/g, text: '$1'},
20
+ symbol: {color: 'green', pat: /(Symbol\(.*\))/g, text: '$1'},
21
+ number: {color: 'yellow', pat: /:\s(-?\d+)/g, text: ': $1'},
22
+ boolean: {color: 'yellow', pat: /(false|true)/g, text: '$1'},
23
+ function: {color: 'cyan', pat: /\[(.*)\]/g, text: '[$1]'},
24
+ undefined: {color: 'gray', pat: /(undefined)/g, text: '$1'},
25
+ }
26
+
27
+ for(var item of Object.values(pattern)) {
28
+ text = text.replace(item.pat, util.styleText(item.color, item.text))
29
+ }
30
+ return text.trim()
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Resolve path
36
+ */
37
+ const resolve = {
38
+ /**
39
+ * Resolve either a root directory
40
+ * of where a command executed or the current working directory.
41
+ */
42
+ root(...args) {
43
+ var path = process.env.INIT_CWD
44
+ if(!path) {
45
+ path = process.cwd()
46
+ }
47
+ return file.path.resolve(path, ...args)
48
+ },
49
+ /**
50
+ * Resolve files of this cli package.
51
+ */
52
+ clidir(...args) {
53
+ return file.path.resolve(file.path.dirname(__dirname), ...args)
54
+ },
55
+ /**
56
+ * Resolve file from current directory.
57
+ */
58
+ dirname(path, ...args) {
59
+ if(!path) {
60
+ return
61
+ }
62
+ return resolve.root(file.path.dirname(path), ...args)
63
+ },
64
+ /**
65
+ * Resolve current working directory
66
+ */
67
+ current(...args) {
68
+ return file.path.resolve(process.cwd(), ...args)
69
+ },
70
+ }
71
+
72
+ /**
73
+ * Log with colors
74
+ */
75
+ function log(name, text) {
76
+ if(name) {
77
+ return console.log(util.styleText(name, text.toString().trim()))
78
+ }
79
+ return console.log(text)
80
+ }
81
+
82
+ /**
83
+ *
84
+ */
85
+ file.makedir = async function makedir(path) {
86
+ try {
87
+ await file.promises.mkdir(path)
88
+ }
89
+ catch(e) {}
90
+ }
91
+
92
+ /**
93
+ *
94
+ */
95
+ file.unlink = async function unlink(path) {
96
+ try {
97
+ await file.promises.unlink(path)
98
+ }
99
+ catch(e) {}
100
+ }
101
+
102
+
103
+ module.exports = exports = Object.create(util, {
104
+ file: {value: file},
105
+ string: {value: string},
106
+ object: {value: object},
107
+ events: {value: events},
108
+ })
109
+
110
+ exports.log = log
111
+ exports.colors = colors
112
+ exports.resolve = resolve
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vindo/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Command line for running vindo server and react SSR for development.",
5
5
  "main": "bin/index.js",
6
6
  "publishConfig": {
@@ -24,8 +24,10 @@
24
24
  },
25
25
  "homepage": "https://github.com/rmindo/vindo/tree/main/packages/cli",
26
26
  "dependencies": {
27
- "@vindo/utility": "^0.0.6",
27
+ "@vindo/utility": "^0.0.13",
28
28
  "chokidar": "^4.0.3",
29
- "esbuild": "^0.25.4"
29
+ "esbuild": "^0.27.3",
30
+ "sass": "^1.98.0",
31
+ "tree-kill": "^1.2.2"
30
32
  }
31
33
  }
package/bin/build.js DELETED
@@ -1,139 +0,0 @@
1
-
2
- const fs = require('fs')
3
- const path = require('node:path')
4
- const {file} = require('@vindo/utility')
5
- const {writeFile} = require('node:fs/promises')
6
- const {build:esbuild} = require('esbuild')
7
-
8
-
9
- const option = {
10
- bundle: true,
11
- format: 'esm',
12
- logLevel: 'error',
13
- }
14
-
15
-
16
- /**
17
- * Resolve path
18
- */
19
- function resolve(...args) {
20
- return path.resolve(process.cwd(), ...args)
21
- }
22
-
23
-
24
- /**
25
- * Directory name
26
- */
27
- function dirname(...args) {
28
- return path.resolve(__dirname, ...args)
29
- }
30
-
31
-
32
- /**
33
- * Resolve alias in the bundle
34
- */
35
- function resolveAlias(buildPath) {
36
- return {
37
- name: 'resolve-alias',
38
- setup(build) {
39
- build.onResolve({filter: /.*/}, ({path}) => {
40
- const build_ = /@build/
41
-
42
- if(path.match(/^node_modules\//)) {
43
- var absPath = resolve(path, 'index.js')
44
-
45
- if(!file.exists(absPath)) {
46
- absPath = resolve(path.concat('.js'))
47
- }
48
- return {path: absPath}
49
- }
50
-
51
- if(path.match(build_)) {
52
- return {
53
- path: resolve(path.replace(build_, buildPath).concat('.js'))
54
- }
55
- }
56
- return null
57
- })
58
- }
59
- }
60
- }
61
-
62
-
63
- /**
64
- * Create a chunk of components
65
- * @param {object} conf
66
- */
67
- async function chunk(conf) {
68
- var imp = ''
69
-
70
- const out = resolve(conf.output, 'chunk.js')
71
- const dir = resolve(conf.entries)
72
-
73
-
74
- if(file.isDir(dir)) {
75
- const dir_ = conf.entries.split('/')
76
-
77
- const files = file.readdir(dir)
78
- if(files.length == 0) {
79
- return
80
- }
81
-
82
- for(var item of files) {
83
- const name = path.parse(item.name).name
84
- if(name) {
85
- imp += `import ${name}_ from '${dir_.slice(1).join('/')}/${name}'\n`
86
- imp += `export const ${name} = ${name}_\n`
87
- }
88
- }
89
- }
90
-
91
- await writeFile(out, imp, {flag: 'w'})
92
- await esbuild({
93
- ...option,
94
- outfile: out,
95
- packages: 'external',
96
- entryPoints: [
97
- out
98
- ],
99
- allowOverwrite: true
100
- })
101
-
102
- return out
103
- }
104
-
105
-
106
- /**
107
- * Create a bundle file
108
- * @param {string} outbundle
109
- * @param {object} conf
110
- */
111
- async function bundle(outbundle, conf) {
112
- option.minify = conf.minify ?? false
113
-
114
- const out = await chunk(conf)
115
- await esbuild({
116
- ...option,
117
- outfile: resolve(conf.output, outbundle),
118
- entryPoints: [
119
- dirname(outbundle)
120
- ],
121
- plugins: [resolveAlias(conf.output)]
122
- })
123
-
124
- if(file.exists(out)) fs.unlinkSync(out)
125
- }
126
-
127
-
128
- /**
129
- *
130
- * @param {object} conf
131
- */
132
- module.exports = async function build(conf) {
133
- try {
134
- await bundle('bundle.js', conf)
135
- }
136
- catch(e) {
137
- console.log(e)
138
- }
139
- }
package/bin/bundle.js DELETED
@@ -1,83 +0,0 @@
1
- import * as chunk from '@build/chunk'
2
-
3
- import ReactDom from 'node_modules/react-dom/client'
4
- import {
5
- createElement as element,
6
- isValidElement as isValid,
7
- }
8
- from 'node_modules/react'
9
-
10
-
11
- const url = new URL(document.scripts.bundle.src)
12
-
13
- const hash = url.searchParams.get('hash')
14
- const port = url.searchParams.get('port')
15
- const name = url.searchParams.get('name')
16
-
17
- const get = fetch(
18
- hash.concat('?',
19
- (new URLSearchParams({name})).toString()
20
- )
21
- )
22
-
23
-
24
- /**
25
- * Set element type
26
- */
27
- function children(data) {
28
- if(isValid(data)) {
29
- return data
30
- }
31
-
32
- if(!Array.isArray(data)) {
33
- data = [data]
34
- }
35
-
36
- return data.map((item, key) => {
37
- const props = item.props
38
-
39
- if(!props) {
40
- return item
41
- }
42
- if(props.children) {
43
- props.children = children(props.children)
44
- }
45
-
46
- if(!item.type) {
47
- var name = props.name
48
- if(!name) {
49
- throw ReferenceError('Component name is not defined.')
50
- }
51
-
52
- const comp = chunk[name.toLowerCase()]
53
- if(comp) {
54
- return element.bind(null, comp)({...props, key})
55
- }
56
- }
57
- return {...item, key, $$typeof: Symbol.for('react.transitional.element')}
58
- })
59
- }
60
-
61
-
62
- /**
63
- * Get the props in the backend
64
- */
65
- async function map(res) {
66
- var data = await res.json()
67
- if(data) {
68
- ReactDom.hydrateRoot(document.getElementById('root'), children(data))
69
- }
70
- }
71
- get.then(map)
72
-
73
- /**
74
- * Live reload for development
75
- */
76
- function reload({origin}) {
77
- if(port) {
78
- (new EventSource(origin.replace(/\d+/, port))).onmessage = function() {
79
- location.reload()
80
- }
81
- }
82
- }
83
- reload(location)
package/bin/reload.js DELETED
@@ -1,6 +0,0 @@
1
- function reload({origin}) {
2
- (new EventSource(origin)).onmessage = function() {
3
- location.reload()
4
- }
5
- }
6
- reload(new URL(document.currentScript.src))