frontfire 0.1.4 → 0.2.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/.github/workflows/publish-npm.yml +27 -0
- package/LICENSE +21 -21
- package/package.json +8 -3
- package/src/DefaultConfig.js +112 -0
- package/src/FrontFire.js +53 -93
- package/src/index.js +70 -15
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Publish to NPM
|
|
2
|
+
on:
|
|
3
|
+
release:
|
|
4
|
+
types: [created]
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
strategy:
|
|
9
|
+
matrix:
|
|
10
|
+
node-version: [ 16.x ]
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout
|
|
13
|
+
uses: actions/checkout@v3
|
|
14
|
+
|
|
15
|
+
- name: Setup Node
|
|
16
|
+
uses: actions/setup-node@v3
|
|
17
|
+
with:
|
|
18
|
+
node-version: '16.x'
|
|
19
|
+
registry-url: 'https://registry.npmjs.org'
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies 🔧
|
|
22
|
+
run: npm install
|
|
23
|
+
|
|
24
|
+
- name: Publish package on NPM 📦
|
|
25
|
+
run: npm publish
|
|
26
|
+
env:
|
|
27
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023 InfrontJS
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 InfrontJS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frontfire",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"start": "node src/index.js",
|
|
8
9
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -22,9 +23,13 @@
|
|
|
22
23
|
"chalk": "^5.2.0",
|
|
23
24
|
"commander": "^10.0.0",
|
|
24
25
|
"conf": "^11.0.1",
|
|
25
|
-
"esbuild": "^0.17.
|
|
26
|
+
"esbuild": "^0.17.8",
|
|
26
27
|
"esbuild-copy-static-files": "^0.1.0",
|
|
27
|
-
"esbuild-plugin-copy": "^2.0.2"
|
|
28
|
+
"esbuild-plugin-copy": "^2.0.2",
|
|
29
|
+
"fs-extra": "^11.1.1",
|
|
30
|
+
"lodash": "^4.17.21",
|
|
31
|
+
"php-server": "^1.0.0",
|
|
32
|
+
"prettier": "^2.8.4"
|
|
28
33
|
},
|
|
29
34
|
"bin": {
|
|
30
35
|
"frontfire": "src/index.js"
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Core
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
// Plugins
|
|
6
|
+
import { copy } from "esbuild-plugin-copy";
|
|
7
|
+
import copyStaticFiles from "esbuild-copy-static-files";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const rootFiles = fs.readdirSync( `src${path.sep}`, { withFileTypes : true } );
|
|
11
|
+
const rootFilesToCopy = [];
|
|
12
|
+
for ( let ri = 0; ri < rootFiles.length; ri++ )
|
|
13
|
+
{
|
|
14
|
+
if ( rootFiles[ ri ].isDirectory() )
|
|
15
|
+
{
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Note
|
|
20
|
+
// It seems that the copy plugin requires normal slashes no matter what OS we are on
|
|
21
|
+
rootFilesToCopy.push( `src/` + rootFiles[ ri ].name );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const buildDir = 'build';
|
|
25
|
+
const entryPoints = [
|
|
26
|
+
`src${path.sep}app${path.sep}main.js`,
|
|
27
|
+
`src${path.sep}app${path.sep}app.css`
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const outDirDebug = `${buildDir}${path.sep}debug${path.sep}app${path.sep}`
|
|
31
|
+
const outDirRelease = `${buildDir}${path.sep}release${path.sep}app${path.sep}`;
|
|
32
|
+
|
|
33
|
+
const staticAssetsDestDebug = `${buildDir}${path.sep}debug${path.sep}assets`;
|
|
34
|
+
const staticAssetsDestRelease = `${buildDir}${path.sep}release${path.sep}assets`;
|
|
35
|
+
|
|
36
|
+
export default {
|
|
37
|
+
"buildDir" : buildDir,
|
|
38
|
+
"debug" : {
|
|
39
|
+
"server" : {
|
|
40
|
+
"indexType" : "html",
|
|
41
|
+
"port" : 3000
|
|
42
|
+
},
|
|
43
|
+
"esbuild" : {
|
|
44
|
+
bundle: true,
|
|
45
|
+
sourcemap: true,
|
|
46
|
+
minify: false,
|
|
47
|
+
logLevel: "info",
|
|
48
|
+
entryPoints : entryPoints,
|
|
49
|
+
outdir : outDirDebug,
|
|
50
|
+
loader: {
|
|
51
|
+
".html" : "text",
|
|
52
|
+
".png" : "file"
|
|
53
|
+
},
|
|
54
|
+
banner : {
|
|
55
|
+
js: "(() => { (new EventSource(\"/esbuild\")).addEventListener('change', () => location.reload() ); })();"
|
|
56
|
+
},
|
|
57
|
+
plugins: [
|
|
58
|
+
copy({
|
|
59
|
+
resolveFrom : 'cwd',
|
|
60
|
+
assets: [
|
|
61
|
+
{
|
|
62
|
+
from: rootFilesToCopy,
|
|
63
|
+
to: [`${buildDir}${path.sep}debug` ]
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}),
|
|
67
|
+
copyStaticFiles({
|
|
68
|
+
src: 'src/assets',
|
|
69
|
+
dest: staticAssetsDestDebug,
|
|
70
|
+
dereference: true,
|
|
71
|
+
errorOnExist: false,
|
|
72
|
+
preserveTimestamps: true,
|
|
73
|
+
recursive: true
|
|
74
|
+
})
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
"release" : {
|
|
79
|
+
"esbuild" : {
|
|
80
|
+
bundle: true,
|
|
81
|
+
sourcemap: true,
|
|
82
|
+
minify: true,
|
|
83
|
+
logLevel: "error",
|
|
84
|
+
entryPoints : entryPoints,
|
|
85
|
+
outdir : outDirRelease,
|
|
86
|
+
loader: {
|
|
87
|
+
".html" : "text",
|
|
88
|
+
".png" : "file"
|
|
89
|
+
},
|
|
90
|
+
plugins: [
|
|
91
|
+
copy({
|
|
92
|
+
resolveFrom : 'cwd',
|
|
93
|
+
assets: [
|
|
94
|
+
{
|
|
95
|
+
from: rootFilesToCopy,
|
|
96
|
+
to: [`${buildDir}${path.sep}release` ]
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}),
|
|
100
|
+
copyStaticFiles({
|
|
101
|
+
src: 'src/assets',
|
|
102
|
+
dest: staticAssetsDestRelease,
|
|
103
|
+
dereference: true,
|
|
104
|
+
errorOnExist: false,
|
|
105
|
+
preserveTimestamps: true,
|
|
106
|
+
recursive: true
|
|
107
|
+
})
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
package/src/FrontFire.js
CHANGED
|
@@ -1,93 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import esbuild from "esbuild";
|
|
3
|
+
import http from "node:http";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import fse from "fs-extra";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import * as child from "child_process";
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
import DEFAULT_CONFIG from "./DefaultConfig.js";
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
export default async function frontFire( isWatch, cfg = {} )
|
|
11
12
|
{
|
|
12
|
-
|
|
13
|
+
const config = _.merge( DEFAULT_CONFIG, cfg );
|
|
14
|
+
|
|
15
|
+
const indexType = _.get( config, 'debug.server.indexType', 'html' );
|
|
16
|
+
|
|
17
|
+
const rootBuildDir = _.get( config, 'buildDir', null );
|
|
18
|
+
if ( null === rootBuildDir )
|
|
13
19
|
{
|
|
14
|
-
|
|
15
|
-
fs.rmSync( rootBuildDir, { recursive: true, force: true } );
|
|
20
|
+
throw new Error( 'No valid buildDir.' );
|
|
16
21
|
}
|
|
17
|
-
}
|
|
18
|
-
catch( ie )
|
|
19
|
-
{
|
|
20
|
-
// Fail silently
|
|
21
|
-
console.error( ie );
|
|
22
|
-
}
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
fse.ensureDirSync( rootBuildDir );
|
|
24
|
+
fse.ensureDirSync( `${rootBuildDir}${path.sep}debug` );
|
|
25
|
+
fse.ensureDirSync( `${rootBuildDir}${path.sep}release` );
|
|
26
|
+
|
|
27
|
+
try
|
|
29
28
|
{
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
let dirToDelete = isWatch ? 'debug' : 'release';
|
|
30
|
+
dirToDelete = `${rootBuildDir}${path.sep}${dirToDelete}`;
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
32
|
+
if ( fs.statSync( dirToDelete ) )
|
|
33
|
+
{
|
|
34
|
+
console.log( "Cleaning build directory: " + dirToDelete );
|
|
35
|
+
fs.rmSync( dirToDelete, { recursive: true, force: true } );
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch( ie )
|
|
39
|
+
{
|
|
40
|
+
// Fail silently
|
|
41
|
+
console.error( ie );
|
|
42
|
+
}
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
{
|
|
40
|
-
let opts = {
|
|
41
|
-
// esbuild options
|
|
42
|
-
bundle: true,
|
|
43
|
-
sourcemap: true,
|
|
44
|
-
minify: isWatch ? false : true,
|
|
45
|
-
logLevel: isWatch ? "info" : "error",
|
|
46
|
-
entryPoints : [ `src${path.sep}app${path.sep}main.js`, `src${path.sep}app${path.sep}app.css` ],
|
|
47
|
-
outdir : isWatch ? `${rootBuildDir}${path.sep}debug${path.sep}app${path.sep}` : `${rootBuildDir}${path.sep}release${path.sep}app${path.sep}`,
|
|
48
|
-
loader: {
|
|
49
|
-
".html" : "text",
|
|
50
|
-
".png" : "file"
|
|
51
|
-
},
|
|
52
|
-
plugins: [
|
|
53
|
-
copy({
|
|
54
|
-
resolveFrom : 'cwd',
|
|
55
|
-
assets: [
|
|
56
|
-
{
|
|
57
|
-
from: rootFilesToCopy,
|
|
58
|
-
to: [ isWatch ? `${rootBuildDir}${path.sep}debug` : `${rootBuildDir}${path.sep}release` ]
|
|
59
|
-
}
|
|
60
|
-
]
|
|
61
|
-
}),
|
|
62
|
-
copyStaticFiles({
|
|
63
|
-
src: 'src/assets',
|
|
64
|
-
dest: isWatch ? `${rootBuildDir}${path.sep}debug${path.sep}assets` : `${rootBuildDir}${path.sep}release${path.sep}assets`,
|
|
65
|
-
dereference: true,
|
|
66
|
-
errorOnExist: false,
|
|
67
|
-
preserveTimestamps: true,
|
|
68
|
-
recursive: true
|
|
69
|
-
})
|
|
70
|
-
]
|
|
71
|
-
|
|
72
|
-
};
|
|
44
|
+
let esbuildOpts = null;
|
|
73
45
|
|
|
74
46
|
if ( true === isWatch )
|
|
75
47
|
{
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
48
|
+
esbuildOpts = _.get( config, 'debug.esbuild' );
|
|
49
|
+
}
|
|
50
|
+
else
|
|
51
|
+
{
|
|
52
|
+
esbuildOpts = _.get( config, 'release.esbuild' );
|
|
79
53
|
}
|
|
80
54
|
|
|
81
|
-
let ctx = await esbuild.context(
|
|
55
|
+
let ctx = await esbuild.context( esbuildOpts );
|
|
82
56
|
|
|
83
57
|
if ( true === isWatch )
|
|
84
58
|
{
|
|
85
|
-
console.log( "Adding additional watcher..." );
|
|
86
59
|
fs.watchFile( `src${path.sep}index.html`, async ( curr, prev ) =>
|
|
87
60
|
{
|
|
88
|
-
console.log( "Index.html changed..." );
|
|
89
61
|
const result = await ctx.rebuild();
|
|
90
|
-
console.log( result );
|
|
91
62
|
});
|
|
92
63
|
await ctx.watch();
|
|
93
64
|
let { host, port } = await ctx.serve(
|
|
@@ -96,17 +67,6 @@ async function frontFire( isWatch )
|
|
|
96
67
|
}
|
|
97
68
|
);
|
|
98
69
|
|
|
99
|
-
|
|
100
|
-
let sseResponse = null;
|
|
101
|
-
/*
|
|
102
|
-
// DOES NOT WORK
|
|
103
|
-
setInterval( () =>
|
|
104
|
-
{
|
|
105
|
-
console.log( "Sending sse..." );
|
|
106
|
-
sseResponse.write( 'data: esbuild' );
|
|
107
|
-
}, 5000 );
|
|
108
|
-
*/
|
|
109
|
-
|
|
110
70
|
// Then start a proxy server on port 3000
|
|
111
71
|
http.createServer((req, res) => {
|
|
112
72
|
|
|
@@ -116,8 +76,15 @@ async function frontFire( isWatch )
|
|
|
116
76
|
path: req.url,
|
|
117
77
|
method: req.method,
|
|
118
78
|
headers: req.headers,
|
|
119
|
-
}
|
|
79
|
+
};
|
|
120
80
|
|
|
81
|
+
if ( 'php' === indexType && req.url === "/" && req.method.toLowerCase() === 'get' )
|
|
82
|
+
{
|
|
83
|
+
const indexHtml = child.execSync( `php src${path.sep}index.php`);
|
|
84
|
+
res.writeHead( 200, { 'Content-Type': 'text/html' });
|
|
85
|
+
res.end( indexHtml.toString() );
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
121
88
|
|
|
122
89
|
// Check if path is a valid state route
|
|
123
90
|
// then pass it as "index.html" to the server
|
|
@@ -127,17 +94,12 @@ async function frontFire( isWatch )
|
|
|
127
94
|
// If esbuild returns "not found", send a custom 404 page
|
|
128
95
|
if (proxyRes.statusCode === 404) {
|
|
129
96
|
res.writeHead(404, { 'Content-Type': 'text/html' })
|
|
130
|
-
res.end('<h1>A custom 404 page</h1>')
|
|
131
|
-
return
|
|
97
|
+
res.end('<h1>A custom 404 page</h1>');
|
|
98
|
+
return;
|
|
132
99
|
}
|
|
133
100
|
|
|
134
101
|
// Otherwise, forward the response from esbuild to the client
|
|
135
|
-
res.writeHead(proxyRes.statusCode, proxyRes.headers)
|
|
136
|
-
|
|
137
|
-
if ( req.url === '/esbuild' )
|
|
138
|
-
{
|
|
139
|
-
sseResponse = res;
|
|
140
|
-
}
|
|
102
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
141
103
|
|
|
142
104
|
proxyRes.pipe(res, { end: true })
|
|
143
105
|
});
|
|
@@ -154,6 +116,4 @@ async function frontFire( isWatch )
|
|
|
154
116
|
await ctx.rebuild();
|
|
155
117
|
ctx.dispose();
|
|
156
118
|
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
module.exports = frontFire;
|
|
119
|
+
};
|
package/src/index.js
CHANGED
|
@@ -1,16 +1,71 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
import _ from "lodash";
|
|
6
|
+
import prettier from "prettier";
|
|
7
|
+
import { program } from "commander";
|
|
8
|
+
|
|
9
|
+
import frontFire from "./FrontFire.js";
|
|
10
|
+
import defaultConfig from "./DefaultConfig.js";
|
|
11
|
+
|
|
12
|
+
async function performInit()
|
|
13
|
+
{
|
|
14
|
+
// Deep Copy
|
|
15
|
+
const initConfig = JSON.parse( JSON.stringify( defaultConfig ) );
|
|
16
|
+
|
|
17
|
+
_.unset( initConfig, 'debug.esbuild.plugins' );
|
|
18
|
+
_.unset( initConfig, 'release.esbuild.plugins' );
|
|
19
|
+
|
|
20
|
+
fs.writeFileSync(
|
|
21
|
+
'frontfire.json',
|
|
22
|
+
prettier.format(
|
|
23
|
+
JSON.stringify( initConfig ),
|
|
24
|
+
{
|
|
25
|
+
semi: false,
|
|
26
|
+
parser: "json"
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Try to load custom config
|
|
35
|
+
let customConfig = null;
|
|
36
|
+
|
|
37
|
+
if ( fs.statSync( 'frontfire.json', { "throwIfNoEntry": false } ) )
|
|
38
|
+
{
|
|
39
|
+
customConfig = fs.readFileSync( 'frontfire.json' );
|
|
40
|
+
if ( customConfig )
|
|
41
|
+
{
|
|
42
|
+
try
|
|
43
|
+
{
|
|
44
|
+
customConfig = JSON.parse( customConfig );
|
|
45
|
+
}
|
|
46
|
+
catch( e )
|
|
47
|
+
{
|
|
48
|
+
console.warn( 'Cannot read frontfire.json' );
|
|
49
|
+
customConfig = null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
program
|
|
55
|
+
.command( 'run-dev' )
|
|
56
|
+
.description( 'Running development server.' )
|
|
57
|
+
.action( function() { frontFire( true, customConfig ); } );
|
|
58
|
+
|
|
59
|
+
program
|
|
60
|
+
.command( 'init' )
|
|
61
|
+
.description( 'Creates frontfire default configuration file in current directory.' )
|
|
62
|
+
.action( function() { performInit(); } );
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
program
|
|
66
|
+
.command( 'build' )
|
|
67
|
+
.description( 'Building for production.' )
|
|
68
|
+
.action( function() { frontFire( false, customConfig ); } );
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
program.parse();
|