UNPKG

8.86 kBJavaScriptView Raw
1const path = require('path');
2const readline = require('readline');
3const fs = require('fs');
4const childProcess = require('child_process');
5const Transform = require('stream').Transform;
6const util = require('util');
7const kill = require('tree-kill');
8const chalk = require('chalk');
9const glob = require('fast-glob');
10const deepMerge = require('merge-deep');
11const root = process.cwd();
12
13function writeTemplateByPackageType(packageType, templateName) {
14 const generator = require(path.resolve(__dirname, '../templates/generator', `./${templateName}.js`));
15 return generator(packageType);
16}
17
18function end(exitCode = 0) { process.exit(exitCode); }
19
20function query(message) {
21 const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
22 return new Promise(resolve => rl.question(message, resolve));
23}
24
25function getPackageJson({ throwErrorIfNotFound = false, packagePath = root } = {}) {
26 const packageJsonPath = path.resolve(packagePath, './package.json');
27 if (!fs.existsSync(packageJsonPath)) {
28 if (throwErrorIfNotFound) { throw new Error('Unable to find a package.json file at this location.'); }
29 return;
30 }
31 return { ...require(packageJsonPath) }; // Make a clone of this so that we aren't modifying what is in the require cache
32}
33
34function writePackageJson(packageJson, packagePath) {
35 packagePath = packagePath || root;
36 const packageJsonFileName = path.resolve(packagePath, './package.json');
37 const currentPackageJson = getPackageJson({ packagePath });
38 const stringifiedNewPackageJson = JSON.stringify(packageJson, null, 2);
39 const stringifiedCurrentPackageJson = JSON.stringify(currentPackageJson, null, 2);
40 if (stringifiedNewPackageJson === stringifiedCurrentPackageJson) { return; }
41 fs.writeFileSync(packageJsonFileName, stringifiedNewPackageJson);
42}
43
44function getPackageJsonAnuxSettings(defaults, packagePath) {
45 const { anux } = getPackageJson({ packagePath });
46 return deepMerge(defaults, anux || {});
47}
48
49
50let cachedIdentifiedPackage;
51function identifyAnuxPackage(packageJson) {
52 let identifiedPackage = packageJson ? undefined : cachedIdentifiedPackage;
53 if (!identifiedPackage) {
54 const anuxPackageRegex = /anux(-\S+)?-package/gi;
55 const { name, devDependencies, dependencies } = (packageJson || getPackageJson());
56 const allDependencies = Object.keys({ ...(dependencies || {}), ...(devDependencies || {}) });
57 anuxPackageRegex.lastIndex = 0;
58 identifiedPackage = allDependencies.find(dependency => anuxPackageRegex.test(dependency));
59 anuxPackageRegex.lastIndex = 0;
60 if (!identifiedPackage && anuxPackageRegex.test(name)) { identifiedPackage = name; }
61 if (!packageJson) { cachedIdentifiedPackage = identifiedPackage; }
62 }
63 return identifiedPackage;
64}
65
66function hasDependency(name) {
67 const { dependencies, devDependencies } = getPackageJson();
68 return dependencies[name] || devDependencies[name];
69}
70
71let cachedRootPath;
72function getAnuxPath(subPath) {
73 if (!cachedRootPath) {
74 const packageRoot = identifyAnuxPackage();
75 if (!packageRoot) { throw new Error('Unable to determine the anux package root.'); }
76 cachedRootPath = path.resolve(root, 'node_modules', packageRoot);
77 }
78 if (subPath) {
79 return path.resolve(cachedRootPath, subPath);
80 } else {
81 return cachedRootPath;
82 }
83}
84
85async function resolveFile(file) {
86 let packageJson = getPackageJson();
87 const localPath = path.join(root, file);
88 if (fs.existsSync(localPath)) { return localPath; }
89 let currentAnuxPackage;
90 const nodeModuleRoot = path.join(root, 'node_modules');
91 do {
92 currentAnuxPackage = identifyAnuxPackage(packageJson)
93 if (currentAnuxPackage) {
94 while (true) { // eslint-disable-line no-constant-condition
95 const anuxPackagePaths = await glob(`**/${currentAnuxPackage}`, { cwd: nodeModuleRoot, onlyDirectories: true, absolute: true, followSymbolicLinks: true });
96 if (anuxPackagePaths.length === 0) {
97 throw new Error(`Unable to find ${currentAnuxPackage} within the node modules for this package, please install this package first...`);
98 }
99 const anuxPackagePath = anuxPackagePaths[0]
100 const filePath = path.join(anuxPackagePath, file);
101 if (fs.existsSync(filePath)) { return filePath; }
102 packageJson = require(path.join(anuxPackagePath, 'package.json'));
103 break;
104 }
105 }
106 } while (currentAnuxPackage);
107 return undefined;
108}
109
110function ModifyStream(prefix) {
111 if (!(this instanceof ModifyStream)) { return new ModifyStream(prefix); }
112 this.prefix = prefix;
113 Transform.call(this, { decodeStrings: false });
114}
115
116util.inherits(ModifyStream, Transform);
117
118ModifyStream.prototype._transform = function (chunk, encoding, done) {
119 /** @type string **/
120 let data = (encoding === 'utf8' ? chunk : chunk.toString());
121 if (data.charCodeAt(0) === 13 && data.charCodeAt(1) === 10) { data = data.substr(2); }
122 done(null, chalk`{black.bgBlueBright ${this.prefix}} ${data}`);
123};
124
125function shell(command, options = {}) {
126 const { cwd, stdout } = {
127 cwd: undefined,
128 stdout: true,
129 ...options,
130 }
131 let ignoreErrors = false;
132 const env = { ...process.env };
133 env.FORCE_COLOR = 1;
134 const commandProcess = childProcess.exec(command, { async: true, env, cwd });
135 const lines = [];
136 const errorLines = [];
137 commandProcess.stdout.on('data', line => {
138 // eslint-disable-next-line no-console
139 if (stdout) { console.log(line); }
140 lines.push(line.toString());
141 while (lines.length > 500) { lines.shift(); }
142 });
143 commandProcess.stderr.on('data', line => {
144 // eslint-disable-next-line no-console
145 if (stdout) { console.error(line); }
146 errorLines.push(line.toString());
147 while (errorLines.length > 500) { errorLines.shift(); }
148 });
149 const promise = new Promise((resolve, reject) => commandProcess.on('exit', exitCode => {
150 if (ignoreErrors) { return resolve(); }
151 exitCode = exitCode == null ? 0 : exitCode;
152 if (exitCode === 0) {
153 resolve({ exitCode, stdout: lines.join('') });
154 } else {
155 reject({ exitCode, stdout: lines.join(''), stderr: errorLines.join('') });
156 }
157 }));
158 promise.kill = async () => {
159 ignoreErrors = true;
160 await new Promise((resolve, reject) => kill(commandProcess.pid, error => error != null ? reject(error) : resolve()));
161 await promise;
162 };
163 return promise;
164}
165
166function getArg(args, argName, hasValue, defaultValue) {
167 hasValue = hasValue === true;
168 argName = argName.toLowerCase()
169 const indexOfArg = args.findIndex(arg => argName === arg.toLowerCase());
170 if (!hasValue) { return indexOfArg >= 0; }
171 if (indexOfArg === args.length - 1) { return defaultValue === undefined ? true : defaultValue; }
172 return args[indexOfArg + 1];
173}
174
175function getLastModifiedDateOf(file) {
176 if (!fs.existsSync(file)) { return -1; }
177 const stats = fs.statSync(file);
178 return stats.mtime;
179}
180
181function copyFile(source, dest, options) {
182 const {
183 silentFailIfExists = false,
184 failIfExists = false,
185 } = options || {};
186 if (!fs.existsSync(source)) { throw new Error(`Unable to find the source file to copy (source: ${source}).`); }
187 if (!fs.existsSync(path.dirname(dest))) { fs.mkdirSync(path.dirname(dest), { recursive: true }); }
188 if (fs.existsSync(dest)) {
189 if (silentFailIfExists) return;
190 if (failIfExists) throw new Error(`The destination file "${dest}" already exists.`);
191 }
192 fs.copyFileSync(source, dest);
193}
194
195async function packPackage(packagePath) {
196 const result = await shell('npm pack --loglevel error', { cwd: packagePath, stdout: false });
197 const tarballFileName = path.resolve(packagePath, 'package.tgz');
198 const sourceTarballFileName = path.resolve(packagePath, result.stdout.trim());
199 copyFile(sourceTarballFileName, tarballFileName);
200 fs.unlinkSync(sourceTarballFileName);
201 return tarballFileName;
202}
203
204let isShuttingDown = false;
205async function waitForAnyKeyToEnd(delegate) {
206 // eslint-disable-next-line @typescript-eslint/no-use-before-define
207 const attachOrDetachFromProcessSignals = isAttaching => ['SIGINT', 'SIGTERM'].forEach(signal => process[isAttaching ? 'on' : 'off'](signal, endProcess));
208 const endProcess = async () => {
209 if (isShuttingDown) { return; }
210 isShuttingDown = true;
211 attachOrDetachFromProcessSignals(false);
212 await delegate();
213 };
214
215 attachOrDetachFromProcessSignals(true);
216 await query('');
217 await endProcess();
218}
219
220module.exports = {
221 end,
222 query,
223 getPackageJson,
224 writePackageJson,
225 getPackageJsonAnuxSettings,
226 getAnuxPath,
227 resolveFile,
228 hasDependency,
229 writeTemplateByPackageType,
230 shell,
231 getArg,
232 getLastModifiedDateOf,
233 copyFile,
234 waitForAnyKeyToEnd,
235 packPackage,
236}
\No newline at end of file