xshell 1.1.8 → 1.1.10

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.
@@ -1,11 +1,10 @@
1
1
  /** 在浏览器端修改 prototype,需要更加小心 */
2
- import byte_size from 'byte-size';
3
2
  import EmojiRegex from 'emoji-regex';
4
3
  import { t } from "./i18n/instance.js";
5
4
  export const emoji_regex = EmojiRegex();
6
5
  export const noop = () => { };
7
6
  export const ident = (x) => x;
8
- export const build_selector = (key) => (obj) => obj[key];
7
+ export const build_mapper = (key) => (obj) => obj[key];
9
8
  /** value 不为 null 或 undefined */
10
9
  export const not_empty = (value) => value !== null && value !== undefined;
11
10
  export const empty = (value) => value === undefined || value === null;
@@ -43,16 +42,6 @@ export const brackets = {
43
42
  fat: ['【', '】'],
44
43
  tortoise_shell: ['〔', '〕'],
45
44
  };
46
- function to_fp(str) {
47
- if (!str)
48
- return str;
49
- const fp = str.replaceAll('\\', '/');
50
- // 转换小写盘符开头的路径
51
- return fp[1] === ':' && 'a' <= fp[0] && fp[0] <= 'z' ?
52
- fp[0].toUpperCase() + fp.slice(1)
53
- :
54
- fp;
55
- }
56
45
  // ------------------------------------ String.prototype
57
46
  Object.defineProperties(String.prototype, {
58
47
  ...to_getter_property_descriptors({
@@ -224,6 +213,15 @@ Object.defineProperties(String.prototype, {
224
213
  return Object.fromEntries(Object.entries($placeholders)
225
214
  .map(([name, $i]) => [name, matches[$i] || '']));
226
215
  },
216
+ /** 查找子串或字符出现的次数 */
217
+ count(search) {
218
+ if (!search)
219
+ throw new Error('count 的 search 不能为空');
220
+ let count = 0;
221
+ for (let i = 0; (i = this.indexOf(search, i)) !== -1; i += search.length)
222
+ count++;
223
+ return count;
224
+ },
227
225
  quote(type = 'single') {
228
226
  if (type === 'psh')
229
227
  return `& ${this.quote()}`;
@@ -313,9 +311,9 @@ Object.defineProperties(String.prototype, {
313
311
  .replace(/(["']+)\s*(.+?)\s*(["']+)/g, '$1$2$3')
314
312
  .replace(new RegExp(cjk + '([\\+\\-\\*\\/=&\\\\\\|<>])([A-Za-z0-9])', 'g'), '$1 $2 $3')
315
313
  .replace(new RegExp('([A-Za-z0-9])([\\+\\-\\*\\/=&\\\\\\|<>])' + cjk, 'g'), '$1 $2 $3');
316
- const textBak = text_;
314
+ const text_bak = text_;
317
315
  text_ = text_.replace(new RegExp(cjk + '([\\(\\[\\{<\u201c]+(.*?)[\\)\\]\\}>\u201d]+)' + cjk, 'g'), '$1 $2 $4');
318
- if (text_ === textBak)
316
+ if (text_ === text_bak)
319
317
  text_ = text_
320
318
  .replace(new RegExp(cjk + '([\\(\\[\\{<\u201c>])', 'g'), '$1 $2')
321
319
  .replace(new RegExp('([\\)\\]\\}>\u201d<])' + cjk, 'g'), '$1 $2');
@@ -324,7 +322,7 @@ Object.defineProperties(String.prototype, {
324
322
  .replace(new RegExp(cjk + '([~!;:,\\.\\?\u2026])([A-Za-z0-9])', 'g'), '$1$2 $3')
325
323
  .replace(new RegExp(cjk + '([A-Za-z0-9`\\$%\\^&\\*\\-=\\+\\\\\\|\\/@\u00a1-\u00ff\u2022\u2027\u2150-\u218f])', 'g'), '$1 $2')
326
324
  .replace(new RegExp('([A-Za-z0-9`\\$%\\^&\\*\\-=\\+\\\\\\|\\/@\u00a1-\u00ff\u2022\u2027\u2150-\u218f])' + cjk, 'g'), '$1 $2');
327
- },
325
+ }
328
326
  }),
329
327
  // ------------ 文件路径操作
330
328
  ...to_getter_property_descriptors({
@@ -332,30 +330,43 @@ Object.defineProperties(String.prototype, {
332
330
  return this.endsWith('/');
333
331
  },
334
332
  fp() {
335
- return to_fp(this);
336
- },
337
- fpd() {
338
333
  if (!this)
339
334
  return this;
340
- const fp = to_fp(this);
335
+ const fp = this.replaceAll('\\', '/');
336
+ // 转换小写盘符开头的路径
337
+ return fp[1] === ':' && 'a' <= fp[0] && fp[0] <= 'z'
338
+ ? fp[0].toUpperCase() + fp.slice(1)
339
+ : fp;
340
+ },
341
+ fpd() {
342
+ const { fp } = this;
341
343
  return fp.endsWith('/') ? fp : `${fp}/`;
342
344
  },
343
345
  fdir() {
344
- const fname = (this.endsWith('/') ? this.slice(0, -1) : this).split('/').at(-1);
345
- return this.slice(0, this.length - (fname.length + (this.endsWith('/') ? 1 : 0)));
346
+ return this.strip_end(this.fname);
346
347
  },
347
348
  fname() {
348
- const fname = (this.endsWith('/') ? this.slice(0, -1) : this).split('/').at(-1);
349
- if (fname)
350
- return `${fname}${this.endsWith('/') ? '/' : ''}`;
351
- else
352
- return '';
349
+ const ilast = this.lastIndexOf('/');
350
+ if (ilast === -1)
351
+ return this; // 没有斜杠时返回整个字符串
352
+ // 以斜杠结尾的情况
353
+ if (ilast === this.length - 1) {
354
+ const iprev = this.lastIndexOf('/', ilast - 1);
355
+ return iprev === -1
356
+ ? this // 只有一个斜杠且在末尾
357
+ : this.slice(iprev + 1);
358
+ }
359
+ // 返回最后一个斜杠后的内容
360
+ return this.slice(ilast + 1);
353
361
  },
354
362
  fext() {
355
- const index = this.lastIndexOf('.');
356
- return index === -1 ? '' : this.slice(index);
357
- },
358
- }),
363
+ const { fname } = this;
364
+ const index = fname.lastIndexOf('.');
365
+ return index <= 0
366
+ ? ''
367
+ : fname.slice(index + 1);
368
+ }
369
+ })
359
370
  });
360
371
  // ------------------------------------ Date.prototype
361
372
  Object.defineProperties(Date.prototype, to_method_property_descriptors({
@@ -411,16 +422,14 @@ function get_time_str(date, hour, ms, splitter) {
411
422
  return String(hour).padStart(2, '0') + splitter +
412
423
  String(date.getMinutes()).padStart(2, '0') + splitter +
413
424
  String(date.getSeconds()).padStart(2, '0') +
414
- (ms ?
415
- '.' + String(date.getMilliseconds()).padStart(3, '0')
416
- :
417
- '');
425
+ (ms
426
+ ? '.' + String(date.getMilliseconds()).padStart(3, '0')
427
+ : '');
418
428
  }
419
429
  // ------------------------------------ Number.prototype
420
430
  Object.defineProperties(Number.prototype, to_method_property_descriptors({
421
- to_fsize_str(units = 'iec') {
422
- const { value, unit } = byte_size(this, { units });
423
- return `${value} ${unit.replace('i', '')}`;
431
+ to_fsize_str() {
432
+ return byte_size(this);
424
433
  },
425
434
  to_bin_str() {
426
435
  return `0b${this.toString(2)}`;
@@ -467,20 +476,27 @@ Object.defineProperties(Array.prototype, {
467
476
  const indent = character.repeat(width);
468
477
  return this.map(line => indent + line);
469
478
  },
470
- sum() {
471
- return this.length
472
- ? this.reduce((acc, x) => acc + x)
473
- : undefined;
474
- },
475
- max(selector = ident) {
479
+ sum(mapper) {
476
480
  if (!this.length)
477
481
  return undefined;
478
- if (typeof selector === 'string')
479
- selector = build_selector(selector);
480
- let max = selector(this[0]);
482
+ // 快捷路径
483
+ const first = this[0];
484
+ if ((typeof first === 'number' || typeof first === 'bigint') && !mapper)
485
+ return this.reduce((acc, x) => acc + x, first);
486
+ if (is_key_type(mapper))
487
+ mapper = build_mapper(mapper);
488
+ mapper ??= ident;
489
+ return this.reduce((acc, x) => acc + mapper(x), mapper(first));
490
+ },
491
+ max(mapper = ident) {
492
+ if (!this.length)
493
+ return undefined;
494
+ if (is_key_type(mapper))
495
+ mapper = build_mapper(mapper);
496
+ let max = mapper(this[0]);
481
497
  let imax = 0;
482
498
  for (let i = 0; i < this.length; i++) {
483
- const value = selector(this[i]);
499
+ const value = mapper(this[i]);
484
500
  if (value > max) {
485
501
  max = value;
486
502
  imax = i;
@@ -488,15 +504,15 @@ Object.defineProperties(Array.prototype, {
488
504
  }
489
505
  return this[imax];
490
506
  },
491
- min(selector = ident) {
507
+ min(mapper = ident) {
492
508
  if (!this.length)
493
509
  return undefined;
494
- if (typeof selector === 'string')
495
- selector = build_selector(selector);
496
- let min = selector(this[0]);
510
+ if (is_key_type(mapper))
511
+ mapper = build_mapper(mapper);
512
+ let min = mapper(this[0]);
497
513
  let imin = 0;
498
514
  for (let i = 0; i < this.length; i++) {
499
- const value = selector(this[i]);
515
+ const value = mapper(this[i]);
500
516
  if (value < min) {
501
517
  min = value;
502
518
  imin = i;
@@ -504,14 +520,14 @@ Object.defineProperties(Array.prototype, {
504
520
  }
505
521
  return this[imin];
506
522
  },
507
- unique(selector) {
508
- if (!selector)
523
+ unique(mapper) {
524
+ if (!mapper)
509
525
  return [...new Set(this)];
510
- if (is_key_type(selector))
511
- selector = build_selector(selector);
526
+ if (is_key_type(mapper))
527
+ mapper = build_mapper(mapper);
512
528
  let map = new Map();
513
529
  for (const x of this)
514
- map.set(selector(x), x);
530
+ map.set(mapper(x), x);
515
531
  return [...map.values()];
516
532
  },
517
533
  join_lines(append = true) {
@@ -520,6 +536,9 @@ Object.defineProperties(Array.prototype, {
520
536
  })
521
537
  });
522
538
  Object.defineProperties(BigInt.prototype, to_method_property_descriptors({
539
+ to_fsize_str() {
540
+ return byte_size(this);
541
+ },
523
542
  toJSON() {
524
543
  return this.toString();
525
544
  }
@@ -583,4 +602,17 @@ export function is_codepoint_fullwidth(codepoint) {
583
602
  // cjk unified ideographs extension b .. tertiary ideographic plane
584
603
  (0x20000 <= codepoint && codepoint <= 0x3fffd)));
585
604
  }
605
+ const units = ['b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb'];
606
+ const bytes_table = units.map((unit, i) => ({
607
+ start: i === 0 ? 0 : 2 ** (i * 10),
608
+ end: 2 ** ((i + 1) * 10),
609
+ unit
610
+ }));
611
+ export function byte_size(bytes) {
612
+ bytes = Number(bytes);
613
+ const sign = bytes < 0 ? '-' : '';
614
+ bytes = Math.abs(bytes);
615
+ const { unit, start } = bytes_table.find(range => bytes >= range.start && bytes < range.end);
616
+ return `${sign}${start === 0 ? bytes : (bytes / start).toFixed()} ${unit}`;
617
+ }
586
618
  //# sourceMappingURL=prototype.browser.js.map
package/prototype.d.ts CHANGED
@@ -59,6 +59,8 @@ declare global {
59
59
  - flags?: `''` 正则匹配选项
60
60
  - pattern_placeholder?: `/\{.*?\}/g` */
61
61
  find(this: string, pattern: string, preservations?: string, flags?: string, pattern_placeholder?: RegExp): Record<string, string>;
62
+ /** 查找子串或字符出现的次数 */
63
+ count(this: string, search: string): number;
62
64
  /** - type?: `'single'` 引号类型 */
63
65
  quote(this: string, type?: keyof typeof quotes | 'psh'): string;
64
66
  /** - type?: `'single'` 引号类型 */
@@ -69,8 +71,7 @@ declare global {
69
71
  surround_tag(this: string, tag_name: string): string;
70
72
  to_lf(this: string): string;
71
73
  /** 'xxx'.replace(/pattern/g, '')
72
- 如果 pattern 是 string 则在创建 RegExp 时自动加上 flags (默认 'g'), 否则忽略 flags
73
- */
74
+ 如果 pattern 是 string 则在创建 RegExp 时自动加上 flags (默认 'g'), 否则忽略 flags */
74
75
  rm(this: string, pattern: string | RegExp, flags?: string): string;
75
76
  readonly red: string;
76
77
  readonly red_: string;
@@ -125,17 +126,25 @@ declare global {
125
126
  slice_to(this: string, search: string, options?: SliceOptions): string;
126
127
  /** 等价于 .endsWith('/') */
127
128
  isdir: boolean;
128
- /** `/` 分割的路径,可能以 / 结尾 */
129
+ /** 转换为以 `/` 分割的路径,可能以 / 结尾 */
129
130
  fp: string;
130
- /** `/` 分割的文件夹路径,一定以 / 结尾 */
131
+ /** 转换为以 `/` 分割的文件夹路径,一定以 / 结尾 */
131
132
  fpd: string;
132
- /** 父文件夹路径 (path.dirname),一定以 `/` 结尾 */
133
+ /** 父文件夹路径,一定以 `/` 结尾,或为空
134
+ 特殊情况:
135
+ - '/'.fdir === ''
136
+ - 'D:/'.fdir === '' */
133
137
  fdir: string;
134
- /** 文件名 (path.basename 的结果), 保留结尾的 /,如:
138
+ /** 文件或文件夹名, 保留结尾的 /
139
+ 常规情况:
135
140
  - D:/0/aaa.txt -> aaa.txt
136
- - D:/aaa/ -> aaa/ */
141
+ - D:/aaa/ -> aaa/
142
+ 特殊情况:
143
+ - '/'.fname === '/'
144
+ - 'D:/'.fname === 'D:/' */
137
145
  fname: string;
138
- /** .txt */
146
+ /** 文件后缀名,不带点,如: txt, zip,没有后缀时返回空字符串
147
+ 特殊情况: .aaa 的 fext 为 '' */
139
148
  fext: string;
140
149
  to_backslash(this: string): string;
141
150
  }
@@ -157,8 +166,8 @@ declare global {
157
166
  to_formal_str(this: Date, ms?: boolean): string;
158
167
  }
159
168
  interface Number {
160
- /** 12.4 KB (1 KB = 1024 B) */
161
- to_fsize_str(this: number, units?: 'iec' | 'metric'): string;
169
+ /** 12.4 kb (1 kb = 1024 b) */
170
+ to_fsize_str(this: number): string;
162
171
  to_bin_str(this: number): string;
163
172
  /** 转换为 0x???? 这样的十六进制形式
164
173
  - length?: 位数自动对齐到 4 的倍数 */
@@ -171,15 +180,15 @@ declare global {
171
180
  log(this: string[], limit?: number): void;
172
181
  indent(this: string[], width?: number, c?: string): string[];
173
182
  indent2to4(this: string[]): string[];
174
- /** 对数组中所有元素求和 (+), 返回结果 */
175
- sum(this: T[]): T;
176
- /** 查找数组中最大的元素,可传入 selector 选择某个属性,或者计算出某个值,用作大小比较 */
177
- max(this: T[], selector?: keyof T | KeySelector<T>): T;
178
- /** 查找数组中最小的元素,可传入 selector 选择某个属性,或者计算出某个值,用作大小比较 */
179
- min(this: T[], selector?: keyof T | KeySelector<T>): T;
180
- /** 去除重复元素(可按 selector 去重),重复值保留最后出现的那个
181
- - selector?: 可以是 key (string, number, symbol) 或 (obj: any) => any */
182
- unique(this: T[], selector?: keyof T | KeySelector<T>): T[];
183
+ /** 对数组中所有元素求和 (+), 返回结果,可传入 mapper 计算出某个值,用作求和 */
184
+ sum<TReturn = T>(this: T[], mapper?: keyof T | Mapper<T>): TReturn;
185
+ /** 查找数组中最大的元素,可传入 mapper 计算出某个值,用作大小比较 */
186
+ max(this: T[], mapper?: keyof T | Mapper<T>): T;
187
+ /** 查找数组中最小的元素,可传入 mapper 计算出某个值,用作大小比较 */
188
+ min(this: T[], mapper?: keyof T | Mapper<T>): T;
189
+ /** 去除重复元素(可按 mapper 选择或计算某个值来去重),重复值保留最后出现的那个
190
+ - mapper?: 可以是 key (string, number, symbol) 或 (obj: any) => any */
191
+ unique(this: T[], mapper?: keyof T | Mapper<T>): T[];
183
192
  /**
184
193
  - trim_line?: `true`
185
194
  - rm_empty_lines?: `true`
@@ -199,26 +208,26 @@ declare global {
199
208
  join_lines(append?: boolean): string;
200
209
  }
201
210
  interface BigInt {
211
+ /** 12.4 kb (1 kb = 1024 b) */
212
+ to_fsize_str(this: bigint): string;
202
213
  toJSON(this: bigint): string;
203
214
  }
204
215
  interface Error {
205
216
  toJSON(this: Error): string;
206
217
  }
207
218
  interface Set<T> {
208
- map<TResult>(mapfn: (value: T, index: number) => TResult): TResult[];
219
+ map<TResult>(mapper: (value: T, index: number) => TResult): TResult[];
209
220
  }
210
221
  }
211
222
  interface SliceOptions {
212
223
  include?: boolean;
213
224
  last?: boolean;
214
225
  }
215
- import chalk from 'chalk';
216
226
  export declare const emoji_regex: RegExp;
217
- export { chalk };
218
227
  export declare const noop: () => void;
219
228
  export declare const ident: <T>(x: T) => T;
220
- export declare const build_selector: <TObj>(key: keyof TObj) => (obj: TObj) => TObj[keyof TObj];
221
- export type KeySelector<TObj = any, TKey extends keyof TObj = keyof TObj> = (key: TObj) => TObj[TKey];
229
+ export declare const build_mapper: <TObj>(key: keyof TObj) => (obj: TObj) => TObj[keyof TObj];
230
+ export type Mapper<TObj = any, TKey extends keyof TObj = keyof TObj> = (obj: TObj) => TObj[TKey];
222
231
  /** value 不为 null 或 undefined */
223
232
  export declare const not_empty: (value: any) => boolean;
224
233
  export declare const empty: (value: any) => boolean;
@@ -248,3 +257,5 @@ export declare const brackets: {
248
257
  export declare function to_json(obj: any, replacer?: any): string;
249
258
  export declare function to_json_safely(obj: any, replacer?: any): string;
250
259
  export declare function is_codepoint_fullwidth(codepoint: number): boolean;
260
+ export declare function byte_size(bytes: number | bigint): string;
261
+ export {};
package/prototype.js CHANGED
@@ -1,15 +1,11 @@
1
- import byte_size from 'byte-size';
1
+ import util from 'util';
2
2
  import EmojiRegex from 'emoji-regex';
3
+ export const emoji_regex = EmojiRegex();
3
4
  import strip_ansi from 'strip-ansi';
4
- import chalk from 'chalk';
5
- import { to_fp, dirname, basename, extname } from "./path.js";
6
5
  import { t } from "./i18n/instance.js";
7
- chalk.level = 2;
8
- export const emoji_regex = EmojiRegex();
9
- export { chalk };
10
6
  export const noop = () => { };
11
7
  export const ident = (x) => x;
12
- export const build_selector = (key) => (obj) => obj[key];
8
+ export const build_mapper = (key) => (obj) => obj[key];
13
9
  /** value 不为 null 或 undefined */
14
10
  export const not_empty = (value) => value !== null && value !== undefined;
15
11
  export const empty = (value) => value === undefined || value === null;
@@ -47,7 +43,6 @@ export const brackets = {
47
43
  fat: ['【', '】'],
48
44
  tortoise_shell: ['〔', '〕'],
49
45
  };
50
- const color_map = Object.fromEntries(['red_', 'green_', 'yellow_', 'blue_', 'magenta_', 'cyan_'].map(color => [color, `${color.slice(0, -1)}Bright`]));
51
46
  if (!globalThis.my_prototype_defined) {
52
47
  // ------------------------------------ String.prototype
53
48
  Object.defineProperties(String.prototype, {
@@ -222,6 +217,15 @@ if (!globalThis.my_prototype_defined) {
222
217
  return Object.fromEntries(Object.entries($placeholders)
223
218
  .map(([name, $i]) => [name, matches[$i] || '']));
224
219
  },
220
+ /** 查找子串或字符出现的次数 */
221
+ count(search) {
222
+ if (!search)
223
+ throw new Error('count 的 search 不能为空');
224
+ let count = 0;
225
+ for (let i = 0; (i = this.indexOf(search, i)) !== -1; i += search.length)
226
+ count++;
227
+ return count;
228
+ },
225
229
  quote(type = 'single') {
226
230
  if (type === 'psh')
227
231
  return `& ${this.quote()}`;
@@ -242,9 +246,6 @@ if (!globalThis.my_prototype_defined) {
242
246
  to_lf() {
243
247
  return this.replace(/\r\n/g, '\n');
244
248
  },
245
- to_crlf() {
246
- return this.replace(/\n/g, '\r\n');
247
- },
248
249
  rm(pattern, flags = 'g') {
249
250
  if (typeof pattern === 'string')
250
251
  pattern = new RegExp(pattern, flags);
@@ -345,45 +346,68 @@ if (!globalThis.my_prototype_defined) {
345
346
  .replace(new RegExp('([A-Za-z0-9`\\$%\\^&\\*\\-=\\+\\\\\\|\\/@\u00a1-\u00ff\u2022\u2027\u2150-\u218f])' + cjk, 'g'), '$1 $2');
346
347
  }
347
348
  }),
348
- // ------------ chalk colors
349
+ // ------------ colors
349
350
  ...Object.fromEntries([
350
351
  'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'grey',
351
352
  'red_', 'green_', 'yellow_', 'blue_', 'magenta_', 'cyan_',
352
353
  'underline',
353
- ].map(color => ([color, {
354
- configurable: true,
355
- get() {
356
- return chalk[color_map[color] || color](this);
357
- }
358
- }]))),
354
+ ].map(color => {
355
+ const style = color.endsWith('_') ? `${color.slice(0, -1)}Bright` : color;
356
+ return [color, {
357
+ configurable: true,
358
+ get() {
359
+ // @ts-ignore
360
+ return util.styleText(style, this, { validateStream: false });
361
+ }
362
+ }];
363
+ })),
359
364
  // ------------ 文件路径操作
360
365
  ...to_getter_property_descriptors({
361
366
  isdir() {
362
367
  return this.endsWith('/');
363
368
  },
364
369
  fp() {
365
- return to_fp(this);
370
+ if (!this)
371
+ return this;
372
+ const fp = this.replaceAll('\\', '/');
373
+ // 转换小写盘符开头的路径
374
+ return fp[1] === ':' && 'a' <= fp[0] && fp[0] <= 'z'
375
+ ? fp[0].toUpperCase() + fp.slice(1)
376
+ : fp;
366
377
  },
367
378
  fpd() {
368
- const fp = to_fp(this);
379
+ const { fp } = this;
369
380
  return fp.endsWith('/') ? fp : `${fp}/`;
370
381
  },
371
382
  fdir() {
372
- const fpd = dirname(this);
373
- // 有可能 fpd 是 '/'
374
- return fpd.endsWith('/') ? fpd : `${fpd}/`;
383
+ return this.strip_end(this.fname);
375
384
  },
376
385
  fname() {
377
- return `${basename(this)}${this.endsWith('/') ? '/' : ''}`;
386
+ const ilast = this.lastIndexOf('/');
387
+ if (ilast === -1)
388
+ return this; // 没有斜杠时返回整个字符串
389
+ // 以斜杠结尾的情况
390
+ if (ilast === this.length - 1) {
391
+ const iprev = this.lastIndexOf('/', ilast - 1);
392
+ return iprev === -1
393
+ ? this // 只有一个斜杠且在末尾
394
+ : this.slice(iprev + 1);
395
+ }
396
+ // 返回最后一个斜杠后的内容
397
+ return this.slice(ilast + 1);
378
398
  },
379
399
  fext() {
380
- return extname(this);
381
- },
400
+ const { fname } = this;
401
+ const index = fname.lastIndexOf('.');
402
+ return index <= 0
403
+ ? ''
404
+ : fname.slice(index + 1);
405
+ }
382
406
  }),
383
407
  ...to_method_property_descriptors({
384
408
  to_backslash() {
385
409
  return this.replaceAll('/', '\\');
386
- },
410
+ }
387
411
  })
388
412
  });
389
413
  // ------------------------------------ Date.prototype
@@ -440,16 +464,14 @@ if (!globalThis.my_prototype_defined) {
440
464
  return String(hour).padStart(2, '0') + splitter +
441
465
  String(date.getMinutes()).padStart(2, '0') + splitter +
442
466
  String(date.getSeconds()).padStart(2, '0') +
443
- (ms ?
444
- '.' + String(date.getMilliseconds()).padStart(3, '0')
445
- :
446
- '');
467
+ (ms
468
+ ? '.' + String(date.getMilliseconds()).padStart(3, '0')
469
+ : '');
447
470
  }
448
471
  // ------------------------------------ Number.prototype
449
472
  Object.defineProperties(Number.prototype, to_method_property_descriptors({
450
- to_fsize_str(units = 'iec') {
451
- const { value, unit } = byte_size(this, { units });
452
- return `${value} ${unit.replace('i', '')}`;
473
+ to_fsize_str() {
474
+ return byte_size(this);
453
475
  },
454
476
  to_bin_str() {
455
477
  return `0b${this.toString(2)}`;
@@ -520,20 +542,27 @@ if (!globalThis.my_prototype_defined) {
520
542
  return this.split_indents()
521
543
  .map(line => ' '.repeat(line.indent * 2) + line.text);
522
544
  },
523
- sum() {
524
- return this.length
525
- ? this.reduce((acc, x) => acc + x)
526
- : undefined;
527
- },
528
- max(selector = ident) {
545
+ sum(mapper) {
529
546
  if (!this.length)
530
547
  return undefined;
531
- if (is_key_type(selector))
532
- selector = build_selector(selector);
533
- let max = selector(this[0]);
548
+ // 快捷路径
549
+ const first = this[0];
550
+ if ((typeof first === 'number' || typeof first === 'bigint') && !mapper)
551
+ return this.reduce((acc, x) => acc + x, first);
552
+ if (is_key_type(mapper))
553
+ mapper = build_mapper(mapper);
554
+ mapper ??= ident;
555
+ return this.reduce((acc, x) => acc + mapper(x), mapper(first));
556
+ },
557
+ max(mapper = ident) {
558
+ if (!this.length)
559
+ return undefined;
560
+ if (is_key_type(mapper))
561
+ mapper = build_mapper(mapper);
562
+ let max = mapper(this[0]);
534
563
  let imax = 0;
535
564
  for (let i = 0; i < this.length; i++) {
536
- const value = selector(this[i]);
565
+ const value = mapper(this[i]);
537
566
  if (value > max) {
538
567
  max = value;
539
568
  imax = i;
@@ -541,15 +570,15 @@ if (!globalThis.my_prototype_defined) {
541
570
  }
542
571
  return this[imax];
543
572
  },
544
- min(selector = ident) {
573
+ min(mapper = ident) {
545
574
  if (!this.length)
546
575
  return undefined;
547
- if (is_key_type(selector))
548
- selector = build_selector(selector);
549
- let min = selector(this[0]);
576
+ if (is_key_type(mapper))
577
+ mapper = build_mapper(mapper);
578
+ let min = mapper(this[0]);
550
579
  let imin = 0;
551
580
  for (let i = 0; i < this.length; i++) {
552
- const value = selector(this[i]);
581
+ const value = mapper(this[i]);
553
582
  if (value < min) {
554
583
  min = value;
555
584
  imin = i;
@@ -557,14 +586,14 @@ if (!globalThis.my_prototype_defined) {
557
586
  }
558
587
  return this[imin];
559
588
  },
560
- unique(selector) {
561
- if (!selector)
589
+ unique(mapper) {
590
+ if (!mapper)
562
591
  return [...new Set(this)];
563
- if (is_key_type(selector))
564
- selector = build_selector(selector);
592
+ if (is_key_type(mapper))
593
+ mapper = build_mapper(mapper);
565
594
  let map = new Map();
566
595
  for (const x of this)
567
- map.set(selector(x), x);
596
+ map.set(mapper(x), x);
568
597
  return [...map.values()];
569
598
  },
570
599
  join_lines(append = true) {
@@ -573,6 +602,9 @@ if (!globalThis.my_prototype_defined) {
573
602
  })
574
603
  });
575
604
  Object.defineProperties(BigInt.prototype, to_method_property_descriptors({
605
+ to_fsize_str() {
606
+ return byte_size(this);
607
+ },
576
608
  toJSON() {
577
609
  return this.toString();
578
610
  }
@@ -643,4 +675,17 @@ export function is_codepoint_fullwidth(codepoint) {
643
675
  // cjk unified ideographs extension b .. tertiary ideographic plane
644
676
  (0x20000 <= codepoint && codepoint <= 0x3fffd)));
645
677
  }
678
+ const units = ['b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb'];
679
+ const bytes_table = units.map((unit, i) => ({
680
+ start: i === 0 ? 0 : 2 ** (i * 10),
681
+ end: 2 ** ((i + 1) * 10),
682
+ unit
683
+ }));
684
+ export function byte_size(bytes) {
685
+ bytes = Number(bytes);
686
+ const sign = bytes < 0 ? '-' : '';
687
+ bytes = Math.abs(bytes);
688
+ const { unit, start } = bytes_table.find(range => bytes >= range.start && bytes < range.end);
689
+ return `${sign}${start === 0 ? bytes : (bytes / start).toFixed()} ${unit}`;
690
+ }
646
691
  //# sourceMappingURL=prototype.js.map