@vindo/cli 0.0.2 → 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,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,70 +1,120 @@
1
1
  const util = require('./util')
2
- const config = util.use('@vindo/core/lib/config')
2
+ const option = require('./option')
3
3
 
4
4
 
5
- module.exports = {
6
- /**
7
- * Development script
8
- */
9
- get devScript() {
10
- return `http://127.0.0.1:${this.option.port}/development`
11
- },
5
+ const file = util.file
6
+ const object = util.object
7
+ const resolve = util.resolve
8
+ const parseArgs = util.parseArgs
12
9
 
13
- /**
14
- * Vindo config
15
- */
16
- get vindo() {
17
-
18
- return config.config({
19
- buildOption: {
20
- port: 2001,
21
- minify: true,
22
- watch: [
23
- './src'
24
- ],
25
- output: './public'
26
- }
27
- })
28
- },
29
10
 
30
- /**
31
- * Build Options
32
- */
33
- get option() {
34
- const opt = this.vindo.buildOption
35
- const args = this.argv()
36
-
37
- return Object.assign(opt, {
38
- ...args,
39
- settings: {
40
- bundle: true,
41
- format: 'esm',
42
- logLevel: 'error',
43
- allowOverwrite: true,
44
- },
45
- output: typeof args.output == 'string' ? args.output : opt.output,
46
- })
47
- },
11
+ /**
12
+ * Exports
13
+ */
14
+ module.exports = exports = Object.create({}, {
15
+ args: {get: getArgs},
16
+ option: {get: getOption},
17
+ })
48
18
 
49
- /**
50
- * Get args
51
- */
52
- argv() {
53
- const args = {}
54
- const argv = process.argv
55
19
 
56
- for(var i in argv) {
57
- 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
39
+
40
+ const command = args[2]
41
+ if(!option.commands.includes(command)) {
42
+ throw new ReferenceError('You need to provide at least one command.')
43
+ }
44
+
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
+ }
58
53
 
59
- if(i < 2) {
60
- continue
61
- }
62
- var arg = argv[i].split(pat, 3).filter(String)
63
54
 
64
- if(arg) {
65
- args[arg[0].substring(2)] = arg[1] ? arg[1] : true
66
- }
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`,
74
+ }
75
+ }
76
+
77
+
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
67
86
  }
68
- return args
87
+
88
+ var pkg = file.get(pkg)
89
+ if(pkg) {
90
+ exports.bundle(null, pkg.bundles)
91
+ }
92
+ }
93
+ }
94
+
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 = {}
69
102
  }
103
+ /**
104
+ * Add new bundle
105
+ */
106
+ if(name) {
107
+ vindo.bundles[name] = toStr(data)
108
+ }
109
+ /**
110
+ * Merge to existing bundles
111
+ */
112
+ else {
113
+ vindo.bundles = object.merge(vindo.bundles, data)
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]
70
120
  }
package/bin/index.js CHANGED
@@ -1,27 +1,28 @@
1
1
  #! /usr/bin/env node
2
2
 
3
3
 
4
- const util = require('./util')
5
- const build = require('./build')
6
- const server = require('./server')
7
- const config = require('./config')
4
+ const builder = require('./builder')
5
+ const server = require('./builder/server')
8
6
 
9
- const {watch} = require('chokidar')
10
7
  const kill = require('tree-kill')
11
- const {exec, spawn} = require('node:child_process')
12
-
13
-
14
- const {log, read, events, colors, resolve} = util
8
+ const chokidar = require('chokidar')
9
+ const childProcess = require('node:child_process')
15
10
 
16
11
 
17
12
  var child = null
18
- var option = config.option
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
  * Set to global environment
22
23
  */
23
- process.env.DEV_SERVER = config.devScript
24
- process.env.DEV_SERVER_PORT = config.option.port
24
+ process.env.DEV_PORT = option.port
25
+ process.env.DEV_SERVER = option.devScript
25
26
 
26
27
 
27
28
  /**
@@ -32,8 +33,7 @@ async function watcher(dirs, cb) {
32
33
  if(!Array.isArray(dirs)) {
33
34
  throw TypeError('Expected a value of type `Array` but received a `String`.')
34
35
  }
35
- watch(dirs, {
36
- // ignored: (path, stats) => stats?.isFile() && !path.endsWith('.js'), // only watch js files
36
+ chokidar.watch(dirs, {
37
37
  persistent: true
38
38
  })
39
39
  .on('change', (path) => cb({path}))
@@ -47,13 +47,18 @@ async function watcher(dirs, cb) {
47
47
  /**
48
48
  *
49
49
  */
50
- function execute() {
50
+ function execute(cmd) {
51
51
  var i = 0
52
- var sp = exec(option.execute, {
52
+
53
+ if(Array.isArray(cmd)) {
54
+ cmd = cmd.join(' && ')
55
+ }
56
+
57
+ var sp = childProcess.exec(cmd, {
53
58
  env: process.env,
54
59
  cwd: process.cwd(),
55
60
  })
56
- log('green', `[vindo] Running ${option.execute}`)
61
+ log('green', `[vindo] Running ${cmd}`)
57
62
 
58
63
  /**
59
64
  * Emit change event to reload client
@@ -94,7 +99,7 @@ function execute() {
94
99
  * Restart server and rebuild
95
100
  */
96
101
  function restart(event) {
97
- log('gray', `[vindo] File changed '${event.path}'`)
102
+ log('green', `[vindo] File changed '${event.path}'`)
98
103
  log('green', `[vindo] Rebuilding...`)
99
104
  log('green', `[vindo] Restarting server...`)
100
105
 
@@ -103,36 +108,18 @@ function restart(event) {
103
108
  if(child) {
104
109
  kill(child.pid)
105
110
  }
106
- child = execute()
111
+ child = execute(option.execute)
107
112
  }
108
113
 
109
114
 
110
115
  /**
111
- * Create server
116
+ * Start building and execute
112
117
  */
113
- const http = server(async (req, res) => {
114
- if(req.isStream()) {
115
- events.on('change', () => {
116
- res.write(`data: {changed: true}\n\n`)
117
- })
118
- }
119
-
120
- if(req.is('development')) {
121
- res.headers({
122
- 'Content-Type':
123
- 'application/javascript'
124
- })
125
- res.end(await read(resolve.curr('development.js')))
126
- }
127
- })
128
-
129
-
130
-
131
- http.listen(option.port, function() {
132
- child = execute()
133
-
118
+ function start() {
134
119
  build(option)
135
120
 
121
+ child = execute(option.execute)
122
+
136
123
  if(option.watch) {
137
124
  log('yellow', '[vindo] v0.0.1')
138
125
  log('yellow', `[vindo] Watching at ${option.watch}`)
@@ -140,4 +127,22 @@ http.listen(option.port, function() {
140
127
 
141
128
  watcher(option.watch, restart)
142
129
  }
143
- })
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
+ }
@@ -1,19 +1,13 @@
1
- const {styleText:color} = require('util')
2
- const {file, events, string} = require('@vindo/utility')
1
+ const util = require('util')
2
+ const {file, events, string, object} = require('@vindo/utility')
3
3
 
4
4
 
5
-
6
- module.exports = exports = Object.create(file, {
7
- string: {value: string},
8
- events: {value: events},
9
- })
10
-
11
5
  /**
12
6
  * Set colors
13
7
  */
14
8
  const colors = {
15
9
  set(name, text) {
16
- return color(name, text)
10
+ return util.styleText(name, text)
17
11
  },
18
12
  colorize(text) {
19
13
  var text = text.toString()
@@ -31,46 +25,47 @@ const colors = {
31
25
  }
32
26
 
33
27
  for(var item of Object.values(pattern)) {
34
- text = text.replace(item.pat, color(item.color, item.text))
28
+ text = text.replace(item.pat, util.styleText(item.color, item.text))
35
29
  }
36
30
  return text.trim()
37
31
  }
38
32
  }
39
33
 
40
- function replace(string, data = {}) {
41
- if(Object.keys(data).length == 0) {
42
- return string
43
- }
44
-
45
- function repl(v) {
46
- var value = data[v.match(/([a-z_]+)/g)]
47
- if(value) {
48
- return value
49
- }
50
- return ''
51
- }
52
-
53
- if(string) {
54
- var patt = string.match(/(\{[a-z_]+\})/g)
55
- if(patt) {
56
- return string.replace(new RegExp(patt.join('|'), 'g'), repl)
57
- }
58
- }
59
- return string
60
- }
61
-
62
34
  /**
63
35
  * Resolve path
64
36
  */
65
37
  const resolve = {
66
- main(...args) {
67
- return file.path.resolve(process.cwd(), ...args)
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)
68
48
  },
69
- curr(...args) {
70
- return file.path.resolve(__dirname, ...args)
49
+ /**
50
+ * Resolve files of this cli package.
51
+ */
52
+ clidir(...args) {
53
+ return file.path.resolve(file.path.dirname(__dirname), ...args)
71
54
  },
55
+ /**
56
+ * Resolve file from current directory.
57
+ */
72
58
  dirname(path, ...args) {
73
- return resolve.main(file.path.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)
74
69
  },
75
70
  }
76
71
 
@@ -79,42 +74,25 @@ const resolve = {
79
74
  */
80
75
  function log(name, text) {
81
76
  if(name) {
82
- return console.log(color(name, text.toString().trim()))
77
+ return console.log(util.styleText(name, text.toString().trim()))
83
78
  }
84
79
  return console.log(text)
85
80
  }
86
81
 
87
- /**
88
- * Use module
89
- */
90
- function use(name) {
91
- return require(resolve.main('node_modules', name))
92
- }
93
-
94
- /**
95
- *
96
- * File system utilities
97
- *
98
- */
99
- async function copy(dest) {
100
- const src = resolve.curr(
101
- path.basename(dest)
102
- )
103
- return await file.promises.cp(src, dest)
104
- }
105
82
  /**
106
83
  *
107
84
  */
108
- async function makedir(path) {
85
+ file.makedir = async function makedir(path) {
109
86
  try {
110
87
  await file.promises.mkdir(path)
111
88
  }
112
89
  catch(e) {}
113
90
  }
91
+
114
92
  /**
115
93
  *
116
94
  */
117
- async function unlink(path) {
95
+ file.unlink = async function unlink(path) {
118
96
  try {
119
97
  await file.promises.unlink(path)
120
98
  }
@@ -122,10 +100,13 @@ async function unlink(path) {
122
100
  }
123
101
 
124
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
+
125
110
  exports.log = log
126
- exports.use = use
127
- exports.copy = copy
128
- exports.unlink = unlink
129
- exports.makedir = makedir
130
111
  exports.colors = colors
131
112
  exports.resolve = resolve
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vindo/cli",
3
- "version": "0.0.2",
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,9 +24,10 @@
24
24
  },
25
25
  "homepage": "https://github.com/rmindo/vindo/tree/main/packages/cli",
26
26
  "dependencies": {
27
- "@vindo/utility": "^0.0.7",
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",
30
31
  "tree-kill": "^1.2.2"
31
32
  }
32
33
  }
package/bin/build.js DELETED
@@ -1,172 +0,0 @@
1
-
2
- const es = require('esbuild')
3
- const {
4
- path,
5
- read,
6
- exists,
7
- string,
8
- unlink,
9
- makedir,
10
- resolve,
11
- readdir,
12
- promises,
13
- }
14
- = require('./util')
15
-
16
-
17
- /**
18
- * Get basename
19
- * @param {string} file
20
- */
21
- function getname(file) {
22
- return path.parse(file).name
23
- }
24
-
25
- /**
26
- * Get imported files
27
- * @param {object} opt
28
- * @param {string} data
29
- */
30
- async function getImports(entry, data) {
31
- var imp = []
32
- var matches = data.matchAll(/import\s(.*)\sfrom.*('|"|\/)([a-zA-Z_-]+)('|")/g)
33
-
34
- for(var item of matches) {
35
- if(/(\.\/|)components\//.test(item[0])) {
36
- imp.push(`var ${item[1]} = ${item[3]}`)
37
- }
38
- else if(/(\.\/)/.test(item[0])) {
39
- imp.push(item[0].replace(/('|")(\.\/)/, `$1${resolve.dirname(entry)}/`))
40
- }
41
- else {
42
- imp.push(item[0])
43
- }
44
- }
45
- return imp
46
- }
47
-
48
-
49
- /**
50
- * Get all codes from components
51
- * @param {object} opt
52
- */
53
- async function getComponents(opt) {
54
- var files = await readdir(opt.components, {recursive: true})
55
- var files = files.filter(file => {
56
- return /\.(tsx|jsx)$/.test(file.name)
57
- })
58
-
59
- var code = []
60
-
61
- for(var {name, parentPath} of files) {
62
- var name = getname(name)
63
- if(name) {
64
- var file = path.join(parentPath, name)
65
-
66
- if(name == 'index') {
67
- file = parentPath
68
- name = getname(parentPath)
69
- }
70
- code.push(`import ${name} from '${file}'`, `chunk.${name} = ${name}`)
71
- }
72
- }
73
- return code
74
- }
75
-
76
-
77
- /**
78
- * Collect components and assemble before building
79
- *
80
- * @param {object} opt option
81
- */
82
- async function build(opt) {
83
- const data = read(opt.entry)
84
- const temp = read(opt.template)
85
-
86
- const impo = await getImports(opt.entry, data)
87
- const comp = await getComponents(opt)
88
-
89
- const code = string.replace(temp, {
90
- CHUNK_IMPORTS: comp.join('\n'),
91
- ENTRY_IMPORTS: impo.join('\n'),
92
- HEAD_COMPONENT: data.match(/<head.*>((.|\n)*)<\/head>/g)[0],
93
- BODY_COMPONENT: data.match(/<Provider.*>((.|\n)*)<\/Provider>/g)[0]
94
- })
95
-
96
- await promises.writeFile(opt.chunk, code, {flag: 'w'})
97
- }
98
-
99
-
100
- /**
101
- * Create a bundle file
102
- *
103
- * @param {object} opt option
104
- */
105
- async function bundle(opt) {
106
- if(!exists(opt.chunk)) {
107
- return
108
- }
109
-
110
- const {metafile} = await es.build({
111
- ...opt.settings,
112
- metafile: true,
113
- minify: opt.minify,
114
- outfile: opt.bundle,
115
- entryPoints: [opt.chunk],
116
- entryNames: '[name]-[hash]'
117
- })
118
-
119
- const file = Object.keys(metafile.outputs)[0]
120
- const data = {
121
- file,
122
- hash: file.match(/-(.*).js/)[1],
123
- bundle: file.match(/.*(\/.*)$/)[1]
124
- }
125
-
126
- /**
127
- * Delete old bundle
128
- */
129
- const manifest = resolve.main(opt.output, 'manifest.json')
130
- if(exists(manifest)) {
131
- const files = readdir(resolve.main(opt.output))
132
-
133
- files.forEach((file) => {
134
- const matched = file.name.match(/^bundle-([A-Z0-9+]{8})\.js$/)
135
- if(matched) {
136
- if(data.hash == matched[1]) {
137
- return
138
- }
139
- unlink(path.resolve(file.parentPath, file.name))
140
- }
141
- })
142
- }
143
-
144
- await promises.writeFile(manifest, JSON.stringify(data), {flag: 'w'})
145
- unlink(opt.chunk)
146
- }
147
-
148
-
149
- /**
150
- *
151
- * @param {object} config
152
- */
153
- module.exports = async function(opt) {
154
- /**
155
- * Create build directory
156
- */
157
- await makedir(resolve.main(opt.output))
158
-
159
- opt.entry = resolve.main(opt.entry)
160
- opt.chunk = resolve.main(opt.output, 'chunk.jsx')
161
- opt.bundle = resolve.main(opt.output, 'bundle.js')
162
- opt.template = resolve.dirname(__filename, 'template.js')
163
- opt.components = resolve.dirname(opt.entry, 'components')
164
-
165
- try {
166
- await build(opt)
167
- await bundle(opt)
168
- }
169
- catch(e) {
170
- console.log(e)
171
- }
172
- }
package/bin/template.js DELETED
@@ -1,29 +0,0 @@
1
-
2
- import React from 'react'
3
- import client from '@vindo/react/client'
4
-
5
- const chunk = {}
6
-
7
- {CHUNK_IMPORTS}
8
- {ENTRY_IMPORTS}
9
-
10
-
11
- if(chunk.head || chunk.body) {
12
- throw new SyntaxError(`Component name '${chunk.head ? 'head' : 'body'}' is a reserved name for SSR client components.`)
13
- }
14
-
15
- chunk.head = function head(meta) {
16
- const {props:{children}} = React.cloneElement((
17
- {HEAD_COMPONENT}
18
- ))
19
- return children
20
- }
21
-
22
- chunk.body = function body(props) {
23
- const {props:{children}, type} = React.cloneElement((
24
- {BODY_COMPONENT}
25
- ))
26
- return type({children, ...props})
27
- }
28
-
29
- client.render(document, chunk)
File without changes