1 | const path = require('path');
|
2 | const readline = require('readline');
|
3 | const fs = require('fs');
|
4 | const childProcess = require('child_process');
|
5 | const Transform = require('stream').Transform;
|
6 | const util = require('util');
|
7 | const kill = require('tree-kill');
|
8 | const chalk = require('chalk');
|
9 | const glob = require('fast-glob');
|
10 | const deepMerge = require('merge-deep');
|
11 | const root = process.cwd();
|
12 |
|
13 | function writeTemplateByPackageType(packageType, templateName) {
|
14 | const generator = require(path.resolve(__dirname, '../templates/generator', `./${templateName}.js`));
|
15 | return generator(packageType);
|
16 | }
|
17 |
|
18 | function end(exitCode = 0) { process.exit(exitCode); }
|
19 |
|
20 | function query(message) {
|
21 | const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
22 | return new Promise(resolve => rl.question(message, resolve));
|
23 | }
|
24 |
|
25 | function 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) };
|
32 | }
|
33 |
|
34 | function 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 |
|
44 | function getPackageJsonAnuxSettings(defaults, packagePath) {
|
45 | const { anux } = getPackageJson({ packagePath });
|
46 | return deepMerge(defaults, anux || {});
|
47 | }
|
48 |
|
49 |
|
50 | let cachedIdentifiedPackage;
|
51 | function 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 |
|
66 | function hasDependency(name) {
|
67 | const { dependencies, devDependencies } = getPackageJson();
|
68 | return dependencies[name] || devDependencies[name];
|
69 | }
|
70 |
|
71 | let cachedRootPath;
|
72 | function 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 |
|
85 | async 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) {
|
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 |
|
110 | function ModifyStream(prefix) {
|
111 | if (!(this instanceof ModifyStream)) { return new ModifyStream(prefix); }
|
112 | this.prefix = prefix;
|
113 | Transform.call(this, { decodeStrings: false });
|
114 | }
|
115 |
|
116 | util.inherits(ModifyStream, Transform);
|
117 |
|
118 | ModifyStream.prototype._transform = function (chunk, encoding, done) {
|
119 |
|
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 |
|
125 | function 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 |
|
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 |
|
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 |
|
166 | function 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 |
|
175 | function getLastModifiedDateOf(file) {
|
176 | if (!fs.existsSync(file)) { return -1; }
|
177 | const stats = fs.statSync(file);
|
178 | return stats.mtime;
|
179 | }
|
180 |
|
181 | function 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 |
|
195 | async 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 |
|
204 | let isShuttingDown = false;
|
205 | async function waitForAnyKeyToEnd(delegate) {
|
206 |
|
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 |
|
220 | module.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 |