@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.
- package/bin/builder/development.js +9 -0
- package/bin/builder/index.js +240 -0
- package/bin/{server.js → builder/server.js} +33 -2
- package/bin/builder/template.js +43 -0
- package/bin/config.js +100 -38
- package/bin/index.js +60 -103
- package/bin/option.json +46 -0
- package/bin/util/index.js +112 -0
- package/package.json +5 -3
- package/bin/build.js +0 -139
- package/bin/bundle.js +0 -83
- package/bin/reload.js +0 -6
|
@@ -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
|
|
1
|
+
const util = require('./util')
|
|
2
|
+
const option = require('./option')
|
|
2
3
|
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
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
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Exports
|
|
13
|
+
*/
|
|
14
|
+
module.exports = exports = Object.create({}, {
|
|
15
|
+
args: {get: getArgs},
|
|
16
|
+
option: {get: getOption},
|
|
17
|
+
})
|
|
10
18
|
|
|
11
19
|
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
*
|
|
104
|
+
* Add new bundle
|
|
36
105
|
*/
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
*
|
|
110
|
+
* Merge to existing bundles
|
|
48
111
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
6
|
-
const
|
|
7
|
-
const
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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.
|
|
25
|
-
process.env.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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 ${
|
|
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(
|
|
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] =
|
|
86
|
+
text[0] = colors.set('red', text[0])
|
|
122
87
|
}
|
|
123
|
-
text[i] = text[i].replace(/(at.*)/,
|
|
124
|
-
text[i] = text[i].replace(/:\s('.*')/,
|
|
125
|
-
text[i] = text[i].replace(/\s(-?\d+)/,
|
|
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('
|
|
102
|
+
log('green', `[vindo] File changed '${event.path}'`)
|
|
138
103
|
log('green', `[vindo] Rebuilding...`)
|
|
139
104
|
log('green', `[vindo] Restarting server...`)
|
|
140
105
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
106
|
+
build(option)
|
|
107
|
+
|
|
144
108
|
if(child) {
|
|
145
|
-
|
|
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
|
-
*
|
|
116
|
+
* Start building and execute
|
|
158
117
|
*/
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/bin/option.json
ADDED
|
@@ -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.
|
|
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.
|
|
27
|
+
"@vindo/utility": "^0.0.13",
|
|
28
28
|
"chokidar": "^4.0.3",
|
|
29
|
-
"esbuild": "^0.
|
|
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)
|