-
Notifications
You must be signed in to change notification settings - Fork 0
/
tinybuild.js
287 lines (225 loc) · 13.1 KB
/
tinybuild.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
//tinybuid.js
export * from './tinybuild/packager.js'
import path from 'path'
//uncomment and run `node tinybuild.js`
import {packager, serve} from './tinybuild/packager.js'
import * as commandUtil from './tinybuild/commands/command.js'
import {parseArgs} from './tinybuild/commands/command.js'
import { checkBoilerPlate, checkCoreExists, checkNodeModules, runAndWatch, runOnChange } from './tinybuild/repo.js'
// let config = {
// bundler:{
// entryPoints: ['test.js'], //entry file, relative to this file
// outfile: 'dist/built', //exit file
// ////outdir:'dist' //exit point folder, define for multiple entryPoints
// bundleBrowser: true, //plain js format
// bundleESM: false, //.esm format
// bundleTypes: false, //entry point should be a ts or jsx (or other typescript) file
// bundleHTML: true //can wrap the built outfile (or first file in outdir) automatically and serve it or click and run the file without hosting.
// },
// server:defaultServer
// }
// //bundle and serve
// packager(config);
// /// or import and run initRepo, cd to that repo and run the tinybuild.js.
// TINYBUILD SCRIPTS
import * as fs from 'fs';
import {exec, execSync, spawn} from 'child_process';
//import nodemon from 'nodemon';
function exitHandler(options, exitCode) {
//if (exitCode || exitCode === 0) console.log('tinybuild exit code: ',exitCode);
if (options.exit) process.exit();
}
//do something when app is closing
process.on('exit', exitHandler.bind(null,{cleanup:true}));
//catches ctrl+c event
process.on('SIGINT', exitHandler.bind(null, {exit:true}));
//pass string argument array or pass a config object
export async function runTinybuild(args) {
console.time('\n🚀 Starting tinybuild...');
//console.log(args)
let tinybuildCfg = {}
let cmdargs = [];
process.argv.forEach((v,i) => {
if (v === 'bundle') process.argv[i] = 'build'; // Build is the default, bundle is an alias
})
if(Array.isArray(args)) {
cmdargs = args;
tinybuildCfg = parseArgs(args);
} //to pass to the restart scripts
else if (typeof args === 'object') {
tinybuildCfg = args;
cmdargs = process.argv;
let dupResults = (str) => {
if(tinybuildCfg.server?.[str]) {
if(Array.isArray(tinybuildCfg.server[str])) tinybuildCfg.server[str] = tinybuildCfg.server[str].join(',');
let cmdarg, argidx;
for(let i = 0; i<cmdargs.length; i++) {
if(cmdargs[i].includes(str)) {
cmdarg = cmdargs[i].split('=')[1];
if(cmdarg.includes('[')) cmdarg = JSON.parse(cmdarg);
if(Array.isArray(cmdarg)) cmdarg = cmdarg.join(',');
argidx = i;
break;
}
}
if(cmdarg) {
cmdargs[argidx] = str+'='+tinybuildCfg.server[str]+','+cmdarg;
}
else cmdargs.push(str+'='+tinybuildCfg.server[str]);
}
}
dupResults('watch');
dupResults('ignore');
dupResults('extensions');
}
//check global module path for node_modules folder
let BUILD_PROCESS;
let SERVER_PROCESS;
//let fileName = __filename.split('/'); fileName = fileName[fileName.length-1]; //try to account for command line position and if the commands are for the current file
// Get CLI Arguments
const cliArgs = parseArgs(process.argv)
// let scriptsrc = path.join(process.cwd(), (cliArgs.path) ? cliArgs.path : 'tinybuild.js')
// const hasScript= fs.existsSync(scriptsrc)
const config = cliArgs.cfgPath ? path.join(cliArgs.cfgpath,...path.split(cliArgs)) : path.join(process.cwd(),'tinybuild.config.js');
const script = path.join(process.cwd(),'tinybuild.js');
const global = path.join(tinybuildCfg.GLOBAL,'global_packager.js');
//todo: make this stuff smarter
if(!tinybuildCfg.path && fs.existsSync(config) && !fs.existsSync(script)) tinybuildCfg.path = global;
if(!tinybuildCfg.path && fs.existsSync(script)) tinybuildCfg.path = script;
if(!tinybuildCfg.path && tinybuildCfg.GLOBAL) tinybuildCfg.path = global;
if(!tinybuildCfg.path) tinybuildCfg.path = 'tinybuild.js';
//scenarios:
/*
"start": "npm run startdev",
"build": "cd example && node tinybuild.js",
"init": "node tinybuild/init.js",
"concurrent": "concurrently \"npm run python\" \"npm run startdev\"",
"dev": "npm run pip && npm i --save-dev concurrently && npm i --save-dev nodemon && npm run concurrent",
"startdev": "nodemon --exec \"cd example && node tinybuild.js\" -e ejs,js,ts,jsx,tsx,css,html,jpg,png,scss,txt,csv",
"python": "python tinybuild/python/server.py",
"pip": "pip install quart && pip install websockets",
"pwa": "npm i workbox-cli && workbox generateSW tinybuild/node_server/pwa/workbox-config.js && npm run build && npm start"
*/
//e.g. typing 'parcel' with a global install will target your entry files and bundle/serve with the dev server.
// 'tinybuild' should look for the necessary basic files (index.js, index.html, and package.json), if core=true it looks for the source folder
// if any not found create the missing ones
// if found run the default bundle and serve configurations as if running the default packager.
// if bundle/serve configs found in init (using URI encoded stringified objects) apply those settings
// if mode=python, copy and run the python server in the project repo (since its meant as a template)
// if mode=library disable the server and generate the esm files (just a quick setting I thought of)
// otherwise can apply additional settings used in tinybuild/init.js
// with the extra settings we can apply them to the packager config
if(!fs.existsSync(path.join(process.cwd(),'node_modules','tinybuild'))) {
execSync('npm link tinybuild');
}
if(cliArgs.mode !== 'help') {
if(tinybuildCfg.server?.hotreload) { //hotreloading active
let WATCHFOLDERS;
let OUTFILE;
if(tinybuildCfg.bundler) {
if(tinybuildCfg.bundler.outfile) {
OUTFILE = tinybuildCfg.bundler.outfile.split('/').pop();
if(path.extname(OUTFILE)) OUTFILE = OUTFILE.replace(path.extname(OUTFILE),'');
let split = tinybuildCfg.bundler.outfile.split('/');
split.pop();
let joined = split.join('/');
WATCHFOLDERS = [joined];
} else if (tinybuildCfg.bundler.outdir) {
OUTFILE = tinybuildCfg.bundler.entryPoints[0];
if(path.extname(OUTFILE)) OUTFILE = OUTFILE.replace(path.extname(OUTFILE),'');
WATCHFOLDERS = [tinybuildCfg.bundler.outdir];
}
}
tinybuildCfg.server.hotreloadwatch = WATCHFOLDERS;
tinybuildCfg.server.hotreloadoutfile = OUTFILE; //this is for reloading css
}
if(tinybuildCfg.start) { //execute the tinybuild.js in the working directory instead of our straight packager.
if(!cliArgs.path && (!fs.existsSync(path.join(process.cwd(),'package.json')) || !fs.existsSync(path.join(process.cwd(),tinybuildCfg.path)) || (tinybuildCfg.bundleTypes && !fs.existsSync(path.join(process.cwd(),'tsconfig.json'))))) {
await checkBoilerPlate(tinybuildCfg);
}
exec('node '+ tinybuildCfg.path,(err,stdout,stderr) => {});
}
else if (cliArgs.mode === 'python') { //make sure your node_server config includes a python port otherwise it will serve the index.html and dist
//check if python server.py folder exists, copy if not
if(!cliArgs.path && (!fs.existsSync(path.join(process.cwd(),'package.json')) || !fs.existsSync(path.join(process.cwd(),tinybuildCfg.path)) || !fs.existsSync(path.join(process.cwd(),'tinybuild.js')) || (tinybuildCfg.bundleTypes && !fs.existsSync(path.join(process.cwd(),'tsconfig.json'))))) {
checkCoreExists();
await checkBoilerPlate(tinybuildCfg);
}
let distpath = 'dist/index';
if(tinybuildCfg.bundler?.outfile && !tinybuildCfg.bundler.outfile.endsWith('js')) distpath = tinybuildCfg.bundler.outfile + '.js';
else if (tinybuildCfg.bundler.entryPoints) {
let entry = tinybuildCfg.bundler.entryPoints.split('.');
entry.pop();
entry.join('.');
entry.split('/');
entry = entry.pop();
distpath = 'dist/'+entry;
}
spawn('python',['tinybuild/python/server.py']); //this can exit independently or the node server will send a kill signal
if(!cliArgs.path && (!fs.existsSync(path.join(process.cwd(),'package.json')) || !fs.existsSync(path.join(process.cwd(),tinybuildCfg.path))))
await checkBoilerPlate(tinybuildCfg)
let server = tinybuildCfg.server;
tinybuildCfg.server = false;
BUILD_PROCESS = runAndWatch(tinybuildCfg.path, cmdargs, tinybuildCfg.server.ignore, tinybuildCfg.server.extensions, tinybuildCfg.server.delay); //runNodemon(tinybuildCfg.path);
SERVER_PROCESS = serve(server, BUILD_PROCESS);
}
else if (cliArgs.mode === 'dev') { //run a local dev server copy
//check if dev server folder exists, copy if not
if(!cliArgs.path && (!fs.existsSync(path.join(process.cwd(),'package.json')) || !fs.existsSync(path.join(process.cwd(), tinybuildCfg.path)) || !fs.existsSync(path.join(process.cwd(),'tinybuild.js')) || (tinybuildCfg.bundleTypes && !fs.existsSync(path.join(process.cwd(),'tsconfig.json'))))) {
checkCoreExists();
checkNodeModules();
await checkBoilerPlate(tinybuildCfg);
}
let server = tinybuildCfg.server;
tinybuildCfg.server = false;
BUILD_PROCESS = runAndWatch(tinybuildCfg.path, cmdargs, tinybuildCfg.server.ignore, tinybuildCfg.server.extensions, tinybuildCfg.server.delay); //runNodemon(tinybuildCfg.path);
SERVER_PROCESS = serve(server, BUILD_PROCESS); //separate server
}
// else if (tinybuildCfg.build || cmdargs.includes('bundle')) {
// delete tinybuildCfg.serve; //don't use either arg to run both
// tinybuildCfg.server = null;
// runOnChange('node',[tinybuildCfg.path, `config=${(JSON.stringify(tinybuildCfg))}`, ...cmdargs])
// }
else if (tinybuildCfg.serve || cmdargs.includes('serve')) {
delete tinybuildCfg.build; //don't use either arg to run both
tinybuildCfg.bundler = null;
let server = tinybuildCfg.server;
tinybuildCfg.server = false;
BUILD_PROCESS = runAndWatch(tinybuildCfg.path, [`--cfgpath`, config, '--build',...cmdargs], tinybuildCfg.server.ignore, tinybuildCfg.server.extensions, tinybuildCfg.server.delay);
SERVER_PROCESS = serve(server, BUILD_PROCESS); //separate server
}
else {
if(!cliArgs.path && (!fs.existsSync(path.join(process.cwd(),'package.json')) || (!fs.existsSync(path.join(process.cwd(),'tinybuild.config.js')) && !fs.existsSync(path.join(process.cwd(),'tinybuild.js'))) || (tinybuildCfg.bundleTypes && !fs.existsSync(path.join(process.cwd(),'tsconfig.json')))))
{
await checkBoilerPlate(tinybuildCfg); //install boilerplate if repo lacks package.json
tinybuildCfg.path = path.join(process.cwd(),'tinybuild.config.js');
}
//console.log('spawning!!', tinybuildCfg)
if((tinybuildCfg.server && !tinybuildCfg.build && !cmdargs.includes('build')) || tinybuildCfg.path.includes('tinybuild.js')) {
let server = tinybuildCfg.server;
tinybuildCfg.server = false;
BUILD_PROCESS = runAndWatch(tinybuildCfg.path, [`--cfgpath`, config, '--build', ...cmdargs], tinybuildCfg.server.ignore, tinybuildCfg.server.extensions, tinybuildCfg.server.delay);
SERVER_PROCESS = serve(server, BUILD_PROCESS); //separate server
}
else packager(tinybuildCfg); //else just run the bundler and quit
}
}
console.timeEnd('\n🚀 Starting tinybuild...');
return {BUILD_PROCESS, SERVER_PROCESS};
}
const args = commandUtil.get(process.argv);
let cfgPath = args.path
if(!cfgPath) cfgPath = 'tinybuild.config.js';
if(args.GLOBAL) {
if(fs.existsSync(path.join(process.cwd(),cfgPath))) {
import('file:///'+process.cwd()+'/'+cfgPath).then((m) => {
if(typeof m.default?.bundler !== 'undefined' || typeof m.default?.server !== 'undefined' ) {
console.log('Config:',cfgPath)
runTinybuild(Object.assign({GLOBAL:args.GLOBAL},m.default));
} else {
runTinybuild(process.argv);
}
})
}
else runTinybuild(process.argv);
} else runTinybuild(process.argv);