@push.rocks/smartnpm 2.1.3 → 2.1.4

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.
@@ -0,0 +1,364 @@
1
+ import * as plugins from './smartnpm.plugins.js';
2
+ import { parseIni } from './smartnpm.ini.js';
3
+ import { homeBeforeNpmrc as rcHome } from './smartnpm.npmrc.js';
4
+
5
+ /**
6
+ * resolves the registry url from npm configuration when none is configured,
7
+ * the way rc('npm') reads it: defaults < config files < npm_* env < process arguments
8
+ */
9
+
10
+ const stripJsonComments = (str: string): string => {
11
+ const strip = (s: string, start?: number, end?: number) => s.slice(start, end).replace(/\S/g, ' ');
12
+ let insideString = false;
13
+ let insideComment: false | 'single' | 'multi' = false;
14
+ let offset = 0;
15
+ let ret = '';
16
+ for (let i = 0; i < str.length; i++) {
17
+ const currentChar = str[i];
18
+ const nextChar = str[i + 1];
19
+ if (!insideComment && currentChar === '"') {
20
+ const escaped = str[i - 1] === '\\' && str[i - 2] !== '\\';
21
+ if (!escaped) {
22
+ insideString = !insideString;
23
+ }
24
+ }
25
+ if (insideString) {
26
+ continue;
27
+ }
28
+ if (!insideComment && currentChar + nextChar === '//') {
29
+ ret += str.slice(offset, i);
30
+ offset = i;
31
+ insideComment = 'single';
32
+ i++;
33
+ } else if (insideComment === 'single' && currentChar + nextChar === '\r\n') {
34
+ i++;
35
+ insideComment = false;
36
+ ret += strip(str, offset, i);
37
+ offset = i;
38
+ continue;
39
+ } else if (insideComment === 'single' && currentChar === '\n') {
40
+ insideComment = false;
41
+ ret += strip(str, offset, i);
42
+ offset = i;
43
+ } else if (!insideComment && currentChar + nextChar === '/*') {
44
+ ret += str.slice(offset, i);
45
+ offset = i;
46
+ insideComment = 'multi';
47
+ i++;
48
+ continue;
49
+ } else if (insideComment === 'multi' && currentChar + nextChar === '*/') {
50
+ i++;
51
+ insideComment = false;
52
+ ret += strip(str, offset, i + 1);
53
+ offset = i + 1;
54
+ continue;
55
+ }
56
+ }
57
+ return ret + (insideComment ? strip(str.substr(offset)) : str.substr(offset));
58
+ };
59
+
60
+ const parseConfig = (content: string): any => {
61
+ if (/^\s*{/.test(content)) {
62
+ return JSON.parse(stripJsonComments(content));
63
+ }
64
+ return parseIni(content);
65
+ };
66
+
67
+ const readConfigFile = (file: any): string | undefined => {
68
+ if (file == null || typeof file !== 'string') {
69
+ return undefined;
70
+ }
71
+ try {
72
+ return plugins.fs.readFileSync(plugins.path.join(file), 'utf-8');
73
+ } catch {
74
+ return undefined;
75
+ }
76
+ };
77
+
78
+ const findUp = (rel: string): string | undefined => {
79
+ const find = (start: string): string | undefined => {
80
+ const file = plugins.path.join(start, rel);
81
+ try {
82
+ plugins.fs.statSync(file);
83
+ return file;
84
+ } catch {
85
+ if (plugins.path.dirname(start) !== start) {
86
+ return find(plugins.path.dirname(start));
87
+ }
88
+ }
89
+ };
90
+ return find(process.cwd());
91
+ };
92
+
93
+ const envConfig = (prefix: string): Record<string, any> => {
94
+ const obj: Record<string, any> = {};
95
+ for (const k in process.env) {
96
+ if (k.toLowerCase().indexOf(prefix.toLowerCase()) !== 0) {
97
+ continue;
98
+ }
99
+ const keypath = k.substring(prefix.length).split('__').filter((subkey) => subkey !== '');
100
+ let cursor: any = obj;
101
+ keypath.forEach((subkey, i) => {
102
+ if (!subkey || typeof cursor !== 'object') {
103
+ return;
104
+ }
105
+ if (i === keypath.length - 1) {
106
+ cursor[subkey] = process.env[k];
107
+ }
108
+ if (cursor[subkey] === undefined) {
109
+ cursor[subkey] = {};
110
+ }
111
+ cursor = cursor[subkey];
112
+ });
113
+ }
114
+ return obj;
115
+ };
116
+
117
+ const isNumber = (x: any): boolean => {
118
+ if (typeof x === 'number') {
119
+ return true;
120
+ }
121
+ if (/^0x[0-9a-f]+$/i.test(x)) {
122
+ return true;
123
+ }
124
+ return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
125
+ };
126
+
127
+ /**
128
+ * parses process arguments without options (minimist semantics)
129
+ */
130
+ const parseArgv = (argsArg: string[]): Record<string, any> => {
131
+ let args = argsArg;
132
+ const argv: Record<string, any> = { _: [] };
133
+ const isConstructorOrProto = (obj: any, key: string) =>
134
+ (key === 'constructor' && typeof obj[key] === 'function') || key === '__proto__';
135
+ const setKey = (obj: any, keys: string[], value: any) => {
136
+ let o = obj;
137
+ for (let i = 0; i < keys.length - 1; i++) {
138
+ const key = keys[i];
139
+ if (isConstructorOrProto(o, key)) {
140
+ return;
141
+ }
142
+ if (o[key] === undefined) {
143
+ o[key] = {};
144
+ }
145
+ if (o[key] === Object.prototype || o[key] === Number.prototype || o[key] === String.prototype) {
146
+ o[key] = {};
147
+ }
148
+ if (o[key] === Array.prototype) {
149
+ o[key] = [];
150
+ }
151
+ o = o[key];
152
+ }
153
+ const lastKey = keys[keys.length - 1];
154
+ if (isConstructorOrProto(o, lastKey)) {
155
+ return;
156
+ }
157
+ if (o === Object.prototype || o === Number.prototype || o === String.prototype) {
158
+ o = {};
159
+ }
160
+ if (o === Array.prototype) {
161
+ o = [];
162
+ }
163
+ if (o[lastKey] === undefined || typeof o[lastKey] === 'boolean') {
164
+ o[lastKey] = value;
165
+ } else if (Array.isArray(o[lastKey])) {
166
+ o[lastKey].push(value);
167
+ } else {
168
+ o[lastKey] = [o[lastKey], value];
169
+ }
170
+ };
171
+ const setArg = (key: string, val: any) => {
172
+ setKey(argv, key.split('.'), isNumber(val) ? Number(val) : val);
173
+ };
174
+
175
+ let notFlags: string[] = [];
176
+ if (args.indexOf('--') !== -1) {
177
+ notFlags = args.slice(args.indexOf('--') + 1);
178
+ args = args.slice(0, args.indexOf('--'));
179
+ }
180
+
181
+ for (let i = 0; i < args.length; i++) {
182
+ const arg = args[i];
183
+ let key: string;
184
+ let next: string;
185
+ if (/^--.+=/.test(arg)) {
186
+ const m = arg.match(/^--([^=]+)=([\s\S]*)$/)!;
187
+ setArg(m[1], m[2]);
188
+ } else if (/^--no-.+/.test(arg)) {
189
+ setArg(arg.match(/^--no-(.+)/)![1], false);
190
+ } else if (/^--.+/.test(arg)) {
191
+ key = arg.match(/^--(.+)/)![1];
192
+ next = args[i + 1];
193
+ if (next !== undefined && !/^(-|--)[^-]/.test(next)) {
194
+ setArg(key, next);
195
+ i += 1;
196
+ } else if (/^(true|false)$/.test(next)) {
197
+ setArg(key, next === 'true');
198
+ i += 1;
199
+ } else {
200
+ setArg(key, true);
201
+ }
202
+ } else if (/^-[^-]+/.test(arg)) {
203
+ const letters = arg.slice(1, -1).split('');
204
+ let broken = false;
205
+ for (let j = 0; j < letters.length; j++) {
206
+ next = arg.slice(j + 2);
207
+ if (next === '-') {
208
+ setArg(letters[j], next);
209
+ continue;
210
+ }
211
+ if (/[A-Za-z]/.test(letters[j]) && next[0] === '=') {
212
+ setArg(letters[j], next.slice(1));
213
+ broken = true;
214
+ break;
215
+ }
216
+ if (/[A-Za-z]/.test(letters[j]) && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
217
+ setArg(letters[j], next);
218
+ broken = true;
219
+ break;
220
+ }
221
+ if (letters[j + 1] && letters[j + 1].match(/\W/)) {
222
+ setArg(letters[j], arg.slice(j + 2));
223
+ broken = true;
224
+ break;
225
+ } else {
226
+ setArg(letters[j], true);
227
+ }
228
+ }
229
+ key = arg.slice(-1)[0];
230
+ if (!broken && key !== '-') {
231
+ if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1])) {
232
+ setArg(key, args[i + 1]);
233
+ i += 1;
234
+ } else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) {
235
+ setArg(key, args[i + 1] === 'true');
236
+ i += 1;
237
+ } else {
238
+ setArg(key, true);
239
+ }
240
+ }
241
+ } else {
242
+ argv._.push(!isNumber(arg) ? arg : Number(arg));
243
+ }
244
+ }
245
+ for (const k of notFlags) {
246
+ argv._.push(k);
247
+ }
248
+ return argv;
249
+ };
250
+
251
+ const isSpecificValue = (val: any) =>
252
+ val instanceof Buffer || val instanceof Date || val instanceof RegExp;
253
+
254
+ const cloneSpecificValue = (val: any): any => {
255
+ if (val instanceof Buffer) {
256
+ const x = Buffer.alloc(val.length);
257
+ val.copy(x);
258
+ return x;
259
+ } else if (val instanceof Date) {
260
+ return new Date(val.getTime());
261
+ } else if (val instanceof RegExp) {
262
+ return new RegExp(val);
263
+ }
264
+ throw new Error('Unexpected situation');
265
+ };
266
+
267
+ const deepCloneArray = (arr: any[]): any[] => {
268
+ const clone: any[] = [];
269
+ arr.forEach((item, index) => {
270
+ if (typeof item === 'object' && item !== null) {
271
+ if (Array.isArray(item)) {
272
+ clone[index] = deepCloneArray(item);
273
+ } else if (isSpecificValue(item)) {
274
+ clone[index] = cloneSpecificValue(item);
275
+ } else {
276
+ clone[index] = deepExtend({}, item);
277
+ }
278
+ } else {
279
+ clone[index] = item;
280
+ }
281
+ });
282
+ return clone;
283
+ };
284
+
285
+ const deepExtend = (target: any, ...sources: any[]): any => {
286
+ const safeGetProperty = (object: any, property: string) =>
287
+ property === '__proto__' ? undefined : object[property];
288
+ for (const obj of sources) {
289
+ if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
290
+ continue;
291
+ }
292
+ for (const key of Object.keys(obj)) {
293
+ const src = safeGetProperty(target, key);
294
+ const val = safeGetProperty(obj, key);
295
+ if (val === target) {
296
+ continue;
297
+ } else if (typeof val !== 'object' || val === null) {
298
+ target[key] = val;
299
+ } else if (Array.isArray(val)) {
300
+ target[key] = deepCloneArray(val);
301
+ } else if (isSpecificValue(val)) {
302
+ target[key] = cloneSpecificValue(val);
303
+ } else if (typeof src !== 'object' || src === null || Array.isArray(src)) {
304
+ target[key] = deepExtend({}, val);
305
+ } else {
306
+ target[key] = deepExtend(src, val);
307
+ }
308
+ }
309
+ }
310
+ return target;
311
+ };
312
+
313
+ const readNpmRc = (): Record<string, any> => {
314
+ const name = 'npm';
315
+ const argv = parseArgv(process.argv.slice(2));
316
+ const env = envConfig(name + '_');
317
+ const configs: any[] = [{ registry: 'https://registry.npmjs.org/' }];
318
+ const configFiles: string[] = [];
319
+ const addConfigFile = (file: any) => {
320
+ if (configFiles.indexOf(file) >= 0) {
321
+ return;
322
+ }
323
+ const fileConfig = readConfigFile(file);
324
+ if (fileConfig) {
325
+ configs.push(parseConfig(fileConfig));
326
+ configFiles.push(file);
327
+ }
328
+ };
329
+ if (process.platform !== 'win32') {
330
+ addConfigFile(plugins.path.join('/etc', name, 'config'));
331
+ addConfigFile(plugins.path.join('/etc', name + 'rc'));
332
+ }
333
+ if (rcHome) {
334
+ addConfigFile(plugins.path.join(rcHome, '.config', name, 'config'));
335
+ addConfigFile(plugins.path.join(rcHome, '.config', name));
336
+ addConfigFile(plugins.path.join(rcHome, '.' + name, 'config'));
337
+ addConfigFile(plugins.path.join(rcHome, '.' + name + 'rc'));
338
+ }
339
+ addConfigFile(findUp('.' + name + 'rc'));
340
+ if (env.config) {
341
+ addConfigFile(env.config);
342
+ }
343
+ if (argv.config) {
344
+ addConfigFile(argv.config);
345
+ }
346
+ return deepExtend(
347
+ {},
348
+ ...configs,
349
+ env,
350
+ argv,
351
+ configFiles.length
352
+ ? { configs: configFiles, config: configFiles[configFiles.length - 1] }
353
+ : undefined,
354
+ );
355
+ };
356
+
357
+ /**
358
+ * the registry url npm would use for a package scope, always ending in a slash
359
+ */
360
+ export const getDefaultRegistryUrl = (scope: string): string => {
361
+ const result = readNpmRc();
362
+ const url = result[`${scope}:registry`] || result.config_registry || result.registry;
363
+ return url.slice(-1) === '/' ? url : `${url}/`;
364
+ };