@cabloy/cli 3.0.66 → 3.0.68
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/dist/index.js +1615 -6
- package/dist/lib/local.common.d.ts +2 -0
- package/dist/types/template.d.ts +7 -0
- package/package.json +7 -8
- package/dist/config.js +0 -38
- package/dist/lib/bean.cli.base.js +0 -144
- package/dist/lib/bean.cli.js +0 -33
- package/dist/lib/cli.js +0 -217
- package/dist/lib/commands.js +0 -52
- package/dist/lib/index.js +0 -7
- package/dist/lib/local.common.js +0 -149
- package/dist/lib/local.console.js +0 -44
- package/dist/lib/local.helper.js +0 -329
- package/dist/lib/local.template.js +0 -291
- package/dist/registry.js +0 -14
- package/dist/start.js +0 -93
- package/dist/types/argv.js +0 -1
- package/dist/types/console.js +0 -1
- package/dist/types/helper.js +0 -1
- package/dist/types/index.js +0 -4
- package/dist/types/template.js +0 -3
- package/dist/utils.js +0 -46
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { catchError } from '@cabloy/utils';
|
|
5
|
-
import ejs from '@zhennann/ejs';
|
|
6
|
-
import { globby } from 'globby';
|
|
7
|
-
import gogocode from 'gogocode';
|
|
8
|
-
import isTextOrBinary from 'istextorbinary';
|
|
9
|
-
import { commandsConfig } from "../config.js";
|
|
10
|
-
export class LocalTemplate {
|
|
11
|
-
cli;
|
|
12
|
-
constructor(cli) {
|
|
13
|
-
this.cli = cli;
|
|
14
|
-
}
|
|
15
|
-
get options() {
|
|
16
|
-
return this.cli.options;
|
|
17
|
-
}
|
|
18
|
-
get context() {
|
|
19
|
-
return this.cli.options.context;
|
|
20
|
-
}
|
|
21
|
-
get console() {
|
|
22
|
-
return this.cli.console;
|
|
23
|
-
}
|
|
24
|
-
get helper() {
|
|
25
|
-
return this.cli.helper;
|
|
26
|
-
}
|
|
27
|
-
get moduleConfig() {
|
|
28
|
-
return commandsConfig;
|
|
29
|
-
}
|
|
30
|
-
get fileMapping() {
|
|
31
|
-
return this.moduleConfig.template.render.fileMapping;
|
|
32
|
-
}
|
|
33
|
-
get filesIgnore() {
|
|
34
|
-
return this.moduleConfig.template.render.ignore;
|
|
35
|
-
}
|
|
36
|
-
resolveTemplatePath(setName, _path) {
|
|
37
|
-
if (path.isAbsolute(_path))
|
|
38
|
-
return _path;
|
|
39
|
-
const sets = this.moduleConfig.sets;
|
|
40
|
-
const require = createRequire(import.meta.url);
|
|
41
|
-
const modulePath = require.resolve(`${sets[process.env.CabloyCliBrandName][setName]}/package.json`);
|
|
42
|
-
return path.join(path.dirname(modulePath), 'cli/templates', _path);
|
|
43
|
-
}
|
|
44
|
-
async renderBoilerplateAndSnippets({ targetDir, setName, snippetsPath, boilerplatePath, }) {
|
|
45
|
-
await this.helper.ensureDir(targetDir);
|
|
46
|
-
// first
|
|
47
|
-
if (snippetsPath) {
|
|
48
|
-
const snippetsDir = this.resolveTemplatePath(setName, snippetsPath);
|
|
49
|
-
await this.applySnippets(targetDir, snippetsDir);
|
|
50
|
-
}
|
|
51
|
-
// then
|
|
52
|
-
if (boilerplatePath) {
|
|
53
|
-
const templateDir = this.resolveTemplatePath(setName, boilerplatePath);
|
|
54
|
-
await this.renderDir(targetDir, templateDir);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
async renderDir(targetDir, templateDir) {
|
|
58
|
-
const { argv } = this.context;
|
|
59
|
-
// files
|
|
60
|
-
const files = await globby('**/*', {
|
|
61
|
-
cwd: templateDir,
|
|
62
|
-
dot: true,
|
|
63
|
-
onlyFiles: false,
|
|
64
|
-
});
|
|
65
|
-
// loop
|
|
66
|
-
for (const file of files) {
|
|
67
|
-
const { dir: dirname, base: basename } = path.parse(file);
|
|
68
|
-
if (this.filesIgnore.includes(basename))
|
|
69
|
-
continue;
|
|
70
|
-
const templateFile = path.join(templateDir, file);
|
|
71
|
-
const fileName = this.parseFileBaseName(basename);
|
|
72
|
-
const parentPath = path.join(targetDir, dirname);
|
|
73
|
-
const targetFile = path.join(parentPath, this.replaceTemplate(fileName, argv));
|
|
74
|
-
await this.renderFile({ targetFile, templateFile });
|
|
75
|
-
if (fileName !== '.gitkeep') {
|
|
76
|
-
await this.helper.removeGitkeep(parentPath);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return files;
|
|
80
|
-
}
|
|
81
|
-
replaceTemplate(content, scope) {
|
|
82
|
-
if (!content)
|
|
83
|
-
return null;
|
|
84
|
-
return content.toString().replace(/(\\)?\{\{ *([\w.]+) *\}\}/g, (block, skip, key) => {
|
|
85
|
-
if (skip) {
|
|
86
|
-
return block.substring(skip.length);
|
|
87
|
-
}
|
|
88
|
-
const value = this.getProperty(scope, key);
|
|
89
|
-
return value !== undefined ? value : '';
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
getProperty(obj, name, sep) {
|
|
93
|
-
return this._getProperty(obj, name, sep, false);
|
|
94
|
-
}
|
|
95
|
-
_getProperty(obj, name, sep, forceObject) {
|
|
96
|
-
if (!obj)
|
|
97
|
-
return undefined;
|
|
98
|
-
const names = name.split(sep || '.');
|
|
99
|
-
// loop
|
|
100
|
-
for (const name of names) {
|
|
101
|
-
if (obj[name] === undefined || obj[name] === null) {
|
|
102
|
-
if (forceObject) {
|
|
103
|
-
obj[name] = {};
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
obj = obj[name];
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
obj = obj[name];
|
|
111
|
-
}
|
|
112
|
-
return obj;
|
|
113
|
-
}
|
|
114
|
-
parseFileBaseName(basename) {
|
|
115
|
-
let fileName = this.fileMapping[basename] || basename;
|
|
116
|
-
if (fileName.lastIndexOf('_') === fileName.length - 1) {
|
|
117
|
-
fileName = fileName.substring(0, fileName.length - 1);
|
|
118
|
-
}
|
|
119
|
-
return fileName;
|
|
120
|
-
}
|
|
121
|
-
async renderFile({ targetFile, templateFile }) {
|
|
122
|
-
const stats = fs.lstatSync(templateFile);
|
|
123
|
-
if (stats.isSymbolicLink()) {
|
|
124
|
-
const target = fs.readlinkSync(templateFile);
|
|
125
|
-
fs.symlinkSync(target, targetFile);
|
|
126
|
-
await this.console.log(`${targetFile} link to ${target}`);
|
|
127
|
-
}
|
|
128
|
-
else if (stats.isDirectory()) {
|
|
129
|
-
await this.helper.ensureDir(targetFile);
|
|
130
|
-
}
|
|
131
|
-
else if (stats.isFile()) {
|
|
132
|
-
const content = fs.readFileSync(templateFile);
|
|
133
|
-
await this.console.log(`write to ${targetFile}`);
|
|
134
|
-
// check if content is a text file
|
|
135
|
-
let result;
|
|
136
|
-
let changed;
|
|
137
|
-
if (!isTextOrBinary.isTextSync(templateFile, content)) {
|
|
138
|
-
result = content;
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
const _content = content.toString('utf8');
|
|
142
|
-
result = await this.renderContent({ content: _content });
|
|
143
|
-
changed = _content !== result;
|
|
144
|
-
}
|
|
145
|
-
// save
|
|
146
|
-
fs.writeFileSync(targetFile, result);
|
|
147
|
-
// format
|
|
148
|
-
if (changed && !this.context.argv.noformat) {
|
|
149
|
-
await catchError(() => {
|
|
150
|
-
return this.helper.formatFile({ fileName: targetFile, logPrefix: 'format: ' });
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
await this.console.log(`ignore ${templateFile}, only support file, dir, symlink`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
async renderContent({ content }) {
|
|
159
|
-
if (!content)
|
|
160
|
-
return content;
|
|
161
|
-
const data = this.getEjsData();
|
|
162
|
-
const options = this.getEjsOptions();
|
|
163
|
-
return await ejs.render(content, data, options);
|
|
164
|
-
}
|
|
165
|
-
getEjsOptions() {
|
|
166
|
-
return {
|
|
167
|
-
async: true,
|
|
168
|
-
cache: false,
|
|
169
|
-
compileDebug: true,
|
|
170
|
-
outputFunctionName: 'echo',
|
|
171
|
-
rmWhitespace: false,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
getEjsData() {
|
|
175
|
-
return {
|
|
176
|
-
cli: this.cli,
|
|
177
|
-
...this.context,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
getAstData(ast, snippet) {
|
|
181
|
-
return {
|
|
182
|
-
cli: this.cli,
|
|
183
|
-
ast,
|
|
184
|
-
snippet,
|
|
185
|
-
...this.context,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
getInitData(targetFile) {
|
|
189
|
-
return {
|
|
190
|
-
cli: this.cli,
|
|
191
|
-
targetFile,
|
|
192
|
-
...this.context,
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
async applySnippets(targetDir, snippetsDir) {
|
|
196
|
-
// snippets
|
|
197
|
-
let files = await globby('*.{cjs,ts}', {
|
|
198
|
-
cwd: snippetsDir,
|
|
199
|
-
onlyFiles: true,
|
|
200
|
-
});
|
|
201
|
-
// snippets sort
|
|
202
|
-
files = files
|
|
203
|
-
.filter(item => item[0] !== '-')
|
|
204
|
-
.sort((a, b) => this._parseSnippetFilePrefix(a) - this._parseSnippetFilePrefix(b));
|
|
205
|
-
// for
|
|
206
|
-
for (const file of files) {
|
|
207
|
-
const snippetTemplatePath = path.join(snippetsDir, file);
|
|
208
|
-
await this._loadSnippetInstance(snippetTemplatePath, async (snippet) => {
|
|
209
|
-
if (!snippet.file) {
|
|
210
|
-
throw new Error(`should provider file path for: ${file}`);
|
|
211
|
-
}
|
|
212
|
-
let fileName;
|
|
213
|
-
if (typeof snippet.file === 'function') {
|
|
214
|
-
fileName = snippet.file(this.getEjsData());
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
fileName = await this.renderContent({ content: snippet.file });
|
|
218
|
-
}
|
|
219
|
-
if (!fileName) {
|
|
220
|
-
// means ignore, so do nothing
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
const targetFile = path.join(targetDir, fileName);
|
|
224
|
-
await this.applySnippet(targetFile, snippet);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
async _loadSnippetInstance(snippetTemplatePath, fn) {
|
|
230
|
-
return await this.helper.importDynamic(snippetTemplatePath, instance => {
|
|
231
|
-
return fn(instance.default);
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
async applySnippet(targetFile, snippet) {
|
|
235
|
-
await this.console.log(`apply changes to ${targetFile}`);
|
|
236
|
-
// source code
|
|
237
|
-
let sourceCode;
|
|
238
|
-
if (fs.existsSync(targetFile)) {
|
|
239
|
-
sourceCode = fs.readFileSync(targetFile);
|
|
240
|
-
sourceCode = sourceCode.toString('utf8');
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
if (!snippet.init) {
|
|
244
|
-
throw new Error(`should provider init content for: ${targetFile}`);
|
|
245
|
-
}
|
|
246
|
-
let content;
|
|
247
|
-
if (typeof snippet.init === 'function') {
|
|
248
|
-
content = await snippet.init(this.getInitData(targetFile));
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
251
|
-
content = snippet.init;
|
|
252
|
-
}
|
|
253
|
-
sourceCode = await this.renderContent({ content });
|
|
254
|
-
}
|
|
255
|
-
// language
|
|
256
|
-
const language = snippet.language;
|
|
257
|
-
// transform
|
|
258
|
-
let outputCode;
|
|
259
|
-
if (language === 'plain') {
|
|
260
|
-
const ast = sourceCode;
|
|
261
|
-
const outAst = await snippet.transform(this.getAstData(ast, snippet));
|
|
262
|
-
outputCode = outAst;
|
|
263
|
-
}
|
|
264
|
-
else if (language === 'json') {
|
|
265
|
-
const ast = JSON.parse(sourceCode);
|
|
266
|
-
const outAst = await snippet.transform(this.getAstData(ast, snippet));
|
|
267
|
-
outputCode = outAst === undefined ? outAst : JSON.stringify(outAst, null, 2);
|
|
268
|
-
}
|
|
269
|
-
else {
|
|
270
|
-
const ast = gogocode(sourceCode, { parseOptions: snippet.parseOptions });
|
|
271
|
-
const outAst = await snippet.transform(this.getAstData(ast, snippet));
|
|
272
|
-
outputCode = outAst === undefined ? outAst : outAst.root().generate();
|
|
273
|
-
}
|
|
274
|
-
if (outputCode !== undefined) {
|
|
275
|
-
// save
|
|
276
|
-
fs.writeFileSync(targetFile, outputCode);
|
|
277
|
-
// format
|
|
278
|
-
if (!this.context.argv.noformat) {
|
|
279
|
-
await catchError(() => {
|
|
280
|
-
return this.helper.formatFile({ fileName: targetFile, logPrefix: 'format: ' });
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
_parseSnippetFilePrefix(fileName) {
|
|
286
|
-
const num = fileName.split('-')[0];
|
|
287
|
-
if (!num || Number.isNaN(num))
|
|
288
|
-
return 10000;
|
|
289
|
-
return Number.parseInt(num);
|
|
290
|
-
}
|
|
291
|
-
}
|
package/dist/registry.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import NPMConfig from '@npmcli/config';
|
|
2
|
-
import npmDefinitions from '@npmcli/config/lib/definitions/index.js';
|
|
3
|
-
let __registry;
|
|
4
|
-
export async function getRegistry() {
|
|
5
|
-
if (!__registry) {
|
|
6
|
-
const npmConfig = new NPMConfig(Object.assign({ npmPath: '' }, npmDefinitions));
|
|
7
|
-
await npmConfig.load();
|
|
8
|
-
__registry = npmConfig.get('registry') || 'https://registry.npmjs.org/';
|
|
9
|
-
if (!__registry.endsWith('/')) {
|
|
10
|
-
__registry = `${__registry}/`;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return __registry;
|
|
14
|
-
}
|
package/dist/start.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import CommonBin from '@zhennann/common-bin';
|
|
2
|
-
import { BeanCli } from "./lib/bean.cli.js";
|
|
3
|
-
import { CliCommand } from "./lib/cli.js";
|
|
4
|
-
import { collectCommands } from "./lib/commands.js";
|
|
5
|
-
import { checkForUpdates } from "./utils.js";
|
|
6
|
-
const DISPATCH = Symbol.for('eb:Command#dispatch');
|
|
7
|
-
const PARSE = Symbol.for('eb:Command#parse');
|
|
8
|
-
export class CabloyCommand extends CommonBin {
|
|
9
|
-
brandName;
|
|
10
|
-
defaultSetName;
|
|
11
|
-
constructor(brandName, rawArgv) {
|
|
12
|
-
super(rawArgv);
|
|
13
|
-
this.usage = `Usage: ${brandName} [command] [options]`;
|
|
14
|
-
this.defaultSetName = brandName === 'zova' ? 'front' : 'api';
|
|
15
|
-
this.brandName = brandName;
|
|
16
|
-
process.env.CabloyCliBrandName = brandName;
|
|
17
|
-
}
|
|
18
|
-
async [DISPATCH]() {
|
|
19
|
-
const parsed = await this[PARSE](this.rawArgv);
|
|
20
|
-
if (parsed._.length === 0) {
|
|
21
|
-
await super[DISPATCH]();
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
// checkForUpdates
|
|
25
|
-
checkForUpdates(`${this.brandName}-cli`);
|
|
26
|
-
// collectCommands
|
|
27
|
-
await collectCommands();
|
|
28
|
-
// cli
|
|
29
|
-
await this._handleCli();
|
|
30
|
-
}
|
|
31
|
-
async _handleCli() {
|
|
32
|
-
// get parsed argument without handling helper and version
|
|
33
|
-
const parsed = await this[PARSE](this.rawArgv);
|
|
34
|
-
// argv
|
|
35
|
-
const argv = {
|
|
36
|
-
projectPath: process.cwd(),
|
|
37
|
-
};
|
|
38
|
-
// indexBrandName
|
|
39
|
-
const indexBrandName = this.rawArgv.indexOf(this.brandName);
|
|
40
|
-
// cli
|
|
41
|
-
const indexCommand = indexBrandName > -1 ? indexBrandName + 1 : 0;
|
|
42
|
-
Object.assign(argv, this._prepareCliFullName(parsed._[indexCommand]));
|
|
43
|
-
// cli meta
|
|
44
|
-
const context = { brandName: this.brandName, argv };
|
|
45
|
-
const beanCli = new BeanCli();
|
|
46
|
-
const meta = await beanCli.meta({ context });
|
|
47
|
-
// cli run
|
|
48
|
-
const rawArgv = this.rawArgv.slice();
|
|
49
|
-
if (indexBrandName > -1) {
|
|
50
|
-
rawArgv.splice(0, indexBrandName + 2);
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
rawArgv.splice(0, 1);
|
|
54
|
-
}
|
|
55
|
-
const command = new CliCommand(rawArgv, { meta, argv });
|
|
56
|
-
await command[DISPATCH]();
|
|
57
|
-
// force exit
|
|
58
|
-
process.exit(0);
|
|
59
|
-
}
|
|
60
|
-
_prepareCliFullName(cliName) {
|
|
61
|
-
if (!cliName) {
|
|
62
|
-
return { cliFullName: `${this.defaultSetName}:default:list` };
|
|
63
|
-
// throw new Error('Please specify the cli name');
|
|
64
|
-
}
|
|
65
|
-
const parts = cliName.split(':');
|
|
66
|
-
if (parts.length === 1) {
|
|
67
|
-
// means show module's commands
|
|
68
|
-
parts[1] = '';
|
|
69
|
-
}
|
|
70
|
-
if (parts.length === 2) {
|
|
71
|
-
if (parts[1]) {
|
|
72
|
-
// means show group's commands
|
|
73
|
-
parts[2] = '';
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
// means show module's commands
|
|
77
|
-
if (!parts[0])
|
|
78
|
-
parts[0] = this.defaultSetName;
|
|
79
|
-
return { cliFullName: `${this.defaultSetName}:default:list`, set: parts[0] };
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
if (!parts[0])
|
|
83
|
-
parts[0] = this.defaultSetName;
|
|
84
|
-
if (!parts[1])
|
|
85
|
-
parts[1] = 'default';
|
|
86
|
-
if (!parts[2]) {
|
|
87
|
-
// means show group's commands
|
|
88
|
-
return { cliFullName: `${this.defaultSetName}:default:list`, set: parts[0], group: parts[1] };
|
|
89
|
-
}
|
|
90
|
-
// default
|
|
91
|
-
return { cliFullName: parts.join(':') };
|
|
92
|
-
}
|
|
93
|
-
}
|
package/dist/types/argv.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types/console.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types/helper.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types/index.js
DELETED
package/dist/types/template.js
DELETED
package/dist/utils.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
|
-
import boxen from 'boxen';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import semver from 'semver';
|
|
5
|
-
import urllib from 'urllib';
|
|
6
|
-
import { getRegistry } from "./registry.js";
|
|
7
|
-
const boxenOptions = { padding: 1, margin: 1, align: 'center', borderColor: 'yellow', borderStyle: 'round' };
|
|
8
|
-
export async function checkForUpdates(packageName) {
|
|
9
|
-
try {
|
|
10
|
-
// version old
|
|
11
|
-
const require = createRequire(import.meta.url);
|
|
12
|
-
const pkg = require(`${packageName}/package.json`);
|
|
13
|
-
const versionOld = pkg.version;
|
|
14
|
-
// version new
|
|
15
|
-
const info = await getPackageInfo(packageName);
|
|
16
|
-
const versionNew = info.version;
|
|
17
|
-
// check
|
|
18
|
-
const lt = semver.lt(versionOld, versionNew);
|
|
19
|
-
if (!lt)
|
|
20
|
-
return;
|
|
21
|
-
// log
|
|
22
|
-
let message = `[${chalk.keyword('cyan')(packageName)}] new version available: ${chalk.keyword('yellow')(versionOld)} → ${chalk.keyword('orange')(versionNew)}`;
|
|
23
|
-
message += `\nRun ${chalk.keyword('orange')(`> pnpm add -g ${packageName} <`)} to update!`;
|
|
24
|
-
// eslint-disable-next-line
|
|
25
|
-
console.log('\n' + boxen(message, boxenOptions));
|
|
26
|
-
}
|
|
27
|
-
catch (_err) {
|
|
28
|
-
// donothing
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
export async function getPackageInfo(packageName) {
|
|
32
|
-
const registry = await getRegistry();
|
|
33
|
-
const result = await urllib.request(`${registry}${packageName}/latest`, {
|
|
34
|
-
dataType: 'json',
|
|
35
|
-
followRedirect: true,
|
|
36
|
-
maxRedirects: 5,
|
|
37
|
-
});
|
|
38
|
-
if (result.status !== 200) {
|
|
39
|
-
const message = `npm info ${packageName} got error: ${result.status}, ${result.data.reason}`;
|
|
40
|
-
throw new Error(message);
|
|
41
|
-
}
|
|
42
|
-
return result.data;
|
|
43
|
-
}
|
|
44
|
-
export function patchFlavor(flavor) {
|
|
45
|
-
return Array.isArray(flavor) ? flavor[flavor.length - 1] : flavor;
|
|
46
|
-
}
|