UNPKG

7.55 kBJavaScriptView Raw
1import Program from 'commander';
2import path from 'path';
3import fs from 'fs';
4import ChildProcess from 'child_process';
5import executablePaths from './executable-paths';
6import FindCommand from './find-command';
7import packageDetails from '../package.json';
8
9const { spawn } = ChildProcess;
10
11const processDir = process.cwd();
12
13const cleanExit = function cleanExit() { process.exit(); };
14process.on('SIGINT', cleanExit); // catch ctrl-c
15process.on('SIGTERM', cleanExit); // catch kill
16
17
18export default class CliHandler {
19 program = Program;
20
21 projectRoot = null;
22
23 libRoot = null;
24
25 searchCommand = null;
26
27 pawConfigManualPath = false;
28
29 projectRootManualPath = false;
30
31 constructor() {
32 // Initialize the env with defaults
33 this.initProcessEnv();
34 this.updateConfigPath = this.updateConfigPath.bind(this);
35 this.updateProjectRoot = this.updateProjectRoot.bind(this);
36 this.startServer = this.startServer.bind(this);
37 }
38
39 initProcessEnv() {
40 // PawJS library root, i.e. the folder where the script file is located
41 process.env.LIB_ROOT = path.resolve(path.join(__dirname, '..'));
42 this.libRoot = process.env.LIB_ROOT;
43
44 // Set paw cache to true by default
45 process.env.PAW_CACHE = 'true';
46
47 process.env.PAW_VERBOSE = 'false';
48
49 // Set default env as development
50 process.env.PAW_ENV = 'development';
51
52 // Project root - Assuming the command is executed from project root
53 process.env.PROJECT_ROOT = (
54 // If already stored then reuse the PROJECT_ROOT
55 process.env.PROJECT_ROOT
56
57 // if not try to get the project root from env variable PROJECT_ROOT
58 || process.env.PROJECT_ROOT
59
60 // If not provided then consider the current directory of the process as project root
61 || (processDir + path.sep)
62 );
63 this.updateProjectRoot(process.env.PROJECT_ROOT, false);
64
65 process.env.PAW_CONFIG_PATH = path.join(this.projectRoot, 'pawconfig.json');
66 }
67
68 onSetProjectRoot() {
69 const newNodePath = executablePaths(this.projectRoot, this.libRoot).join(path.delimiter);
70 if (!process.env.NODE_PATH) {
71 process.env.NODE_PATH = newNodePath;
72 } else {
73 process.env.NODE_PATH = [process.env.NODE_ENV, newNodePath].join(path.delimiter);
74 }
75 process.env.PATH = process.env.NODE_PATH;
76
77 if (!this.pawConfigManualPath) {
78 process.env.PAW_CONFIG_PATH = path.join(this.projectRoot, 'pawconfig.json');
79 }
80
81 this.updateCommandSearch();
82 }
83
84 updateCommandSearch() {
85 // get the search command with the executable path list
86 this.searchCommand = FindCommand.factory(executablePaths(this.projectRoot, this.libRoot));
87 }
88
89 /**
90 * Update config path for pawconfig.json
91 * @param configPath
92 * @param manual
93 */
94 updateConfigPath(configPath, manual = true) {
95 // store the absolute value of project root
96 let pawConfig = configPath;
97 if (!path.isAbsolute(configPath)) {
98 pawConfig = path.resolve(processDir, configPath);
99 }
100 const pathStats = fs.lstatSync(pawConfig);
101 if (!pathStats.isFile()) {
102 // eslint-disable-next-line
103 console.warn(`WARNING:: Invalid config file path specified ${configPath}, using ${process.env.PAW_CONFIG_PATH} instead`);
104 return;
105 }
106 if (manual) {
107 this.pawConfigManualPath = true;
108 }
109
110 process.env.PAW_CONFIG_PATH = pawConfig;
111 }
112
113 /**
114 * Specify the project root directory
115 * @param projectRootDir
116 * @param manual
117 */
118 updateProjectRoot(projectRootDir, manual = true) {
119 // store the absolute value of project root
120 let pRoot = projectRootDir;
121 if (!path.isAbsolute(projectRootDir)) {
122 pRoot = path.resolve(processDir, projectRootDir);
123 }
124 const pathStats = fs.lstatSync(pRoot);
125 if (!pathStats.isDirectory()) {
126 // eslint-disable-next-line
127 console.warn(`WARNING:: Invalid root directory specified ${projectRootDir}, using ${this.projectRoot} instead`);
128 return;
129 }
130 if (manual) {
131 this.projectRootManualPath = true;
132 }
133 process.env.PROJECT_ROOT = pRoot;
134 this.projectRoot = process.env.PROJECT_ROOT;
135 this.onSetProjectRoot();
136 }
137
138 /**
139 * Start server depending on the env variable
140 */
141 startServer() {
142 process.env.PAW_HOT = typeof process.env.PAW_HOT !== 'undefined' ? process.env.PAW_HOT : 'true';
143 // eslint-disable-next-line
144 require(path.resolve(this.libRoot, 'src/server/webpack-start.js'));
145 }
146
147 buildProd = () => {
148 process.env.PAW_HOT = typeof process.env.PAW_HOT !== 'undefined' ? process.env.PAW_HOT : 'false';
149 // eslint-disable-next-line
150 require(path.resolve(this.libRoot, 'src/server/webpack-build.js'));
151 };
152
153 test() {
154 const env = Object.create(process.env);
155 env.NODE_ENV = 'test';
156 spawn(this.searchCommand('jest'), [], {
157 env,
158 stdio: [process.stdin, process.stdout, 'pipe'],
159 });
160 }
161
162 run() {
163 this.program
164 .version(packageDetails.version, '-V, --version');
165
166 this.program.option('-v, --verbose', 'Start with detailed comments and explanation');
167 this.program.option('-e, --env <env>', 'Set the application environment default is dev env');
168 this.program.option('-nc, --no-cache', 'Disable cache. Ideal for PawJS core/plugin development');
169 this.program.option('-r, --root <projectRootDir>', 'Set the project root');
170 this.program.option('-c, --config <configPath>', 'Set path to pawconfig.json');
171
172 this.program
173 .command('start')
174 .description('Start the application')
175 .action(this.startServer.bind(this));
176
177 this.program
178 .command('build')
179 .description('Compile the project for production.')
180 .action(this.buildProd.bind(this));
181
182 this.program
183 .command('test')
184 .description('Run the test cases for the project.')
185 .action(this.test.bind(this));
186
187
188 // Set PAW_VERBOSE to true
189 this.program.on('option:verbose', () => {
190 process.env.PAW_VERBOSE = 'true';
191 });
192
193 // Set Environment to
194 this.program.on('option:env', (e) => {
195 let env = e;
196 if (!env) return;
197 env = env.toLowerCase();
198 if (env === 'dev') {
199 // eslint-disable-next-line
200 console.info("NOTE:: Setting env to development. Please use --env=development instead");
201 env = 'development';
202 }
203
204 if (env === 'prod') {
205 // eslint-disable-next-line
206 console.info("NOTE:: Setting env to production. Please use --env=production instead");
207 env = 'production';
208 }
209 process.env.PAW_ENV = env;
210
211 // Force set NODE_ENV & ENV to production
212 if (process.env.PAW_ENV === 'production' && typeof process.env.NODE_ENV === 'undefined') {
213 process.env.NODE_ENV = 'production';
214 process.env.ENV = 'production';
215 }
216 });
217
218 this.program.on('option:cache', () => {
219 if (!this.program.cache) {
220 // set PAW_CACHE to false
221 process.env.PAW_CACHE = 'false';
222
223 // Disable babel cache if no-cache is specified
224 process.env.BABEL_DISABLE_CACHE = true;
225 }
226 });
227
228 // Update the project root based on the root option
229 this.program.on('option:root', this.updateProjectRoot);
230
231 // Update the pawconfig path
232 this.program.on('option:config', this.updateConfigPath);
233
234 this.program.on('command:*', () => {
235 // eslint-disable-next-line
236 console.error("Invalid command: %s\nSee --help for a list of available commands.", Program.args.join(" "));
237 process.exit(1);
238 });
239
240 this.program.parse(process.argv);
241
242 if (!process.argv.slice(2).length) {
243 this.startServer();
244 }
245 }
246}