xshell 0.0.15 → 0.0.20

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.
Files changed (63) hide show
  1. package/chalk.browser.d.ts +2 -0
  2. package/chalk.browser.js +21 -0
  3. package/chalk.browser.js.map +1 -0
  4. package/file.js +1 -2
  5. package/file.js.map +1 -1
  6. package/i18n/README.md +283 -0
  7. package/i18n/dict.d.ts +30 -0
  8. package/i18n/dict.js +34 -0
  9. package/i18n/dict.js.map +1 -0
  10. package/i18n/i18n-scan.d.ts +2 -0
  11. package/i18n/i18n-scan.js +21 -0
  12. package/i18n/i18n-scan.js.map +1 -0
  13. package/i18n/i18n.ico +0 -0
  14. package/i18n/index.d.ts +65 -0
  15. package/i18n/index.js +118 -0
  16. package/i18n/index.js.map +1 -0
  17. package/i18n/rwdict.d.ts +42 -0
  18. package/i18n/rwdict.js +110 -0
  19. package/i18n/rwdict.js.map +1 -0
  20. package/i18n/scanner/checker.d.ts +5 -0
  21. package/i18n/scanner/checker.js +73 -0
  22. package/i18n/scanner/checker.js.map +1 -0
  23. package/i18n/scanner/index.d.ts +52 -0
  24. package/i18n/scanner/index.js +319 -0
  25. package/i18n/scanner/index.js.map +1 -0
  26. package/i18n/scanner/parser.d.ts +3 -0
  27. package/i18n/scanner/parser.js +154 -0
  28. package/i18n/scanner/parser.js.map +1 -0
  29. package/i18n/utils.d.ts +1 -0
  30. package/i18n/utils.js +14 -0
  31. package/i18n/utils.js.map +1 -0
  32. package/myfont.sass +11 -0
  33. package/net.browser.d.ts +23 -0
  34. package/net.browser.js +70 -0
  35. package/net.browser.js.map +1 -0
  36. package/net.js +2 -4
  37. package/net.js.map +1 -1
  38. package/package.json +36 -7
  39. package/prototype.browser.d.ts +130 -0
  40. package/prototype.browser.js +421 -0
  41. package/prototype.browser.js.map +1 -0
  42. package/repl.js +4 -8
  43. package/repl.js.map +1 -1
  44. package/scroll-bar.sass +35 -0
  45. package/server.d.ts +1 -0
  46. package/server.js +2 -3
  47. package/server.js.map +1 -1
  48. package/toaster.browser.d.ts +9 -0
  49. package/toaster.browser.js +45 -0
  50. package/toaster.browser.js.map +1 -0
  51. package/toaster.sass +22 -0
  52. package/ufs.js +3 -3
  53. package/ufs.js.map +1 -1
  54. package/utils.browser.d.ts +3 -0
  55. package/utils.browser.js +27 -0
  56. package/utils.browser.js.map +1 -0
  57. package/extension.d.ts +0 -3
  58. package/extension.js +0 -68
  59. package/extension.js.map +0 -1
  60. package/net.browser.ts +0 -141
  61. package/prototype.browser.ts +0 -728
  62. package/tsconfig.json +0 -73
  63. package/utils.browser.ts +0 -25
@@ -0,0 +1,319 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scanner = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const i18next_scanner_1 = (0, tslib_1.__importDefault)(require("i18next-scanner"));
6
+ const upath_1 = (0, tslib_1.__importDefault)(require("upath"));
7
+ const ejs_1 = (0, tslib_1.__importDefault)(require("ejs"));
8
+ const vinyl_fs_1 = (0, tslib_1.__importDefault)(require("vinyl-fs"));
9
+ const map_stream_1 = (0, tslib_1.__importDefault)(require("map-stream"));
10
+ const gulp_sort_1 = (0, tslib_1.__importDefault)(require("gulp-sort"));
11
+ const ora_1 = (0, tslib_1.__importDefault)(require("ora"));
12
+ const cli_truncate_1 = (0, tslib_1.__importDefault)(require("cli-truncate"));
13
+ const vinyl_1 = (0, tslib_1.__importDefault)(require("vinyl"));
14
+ const through2_1 = (0, tslib_1.__importDefault)(require("through2"));
15
+ const cli_table3_1 = (0, tslib_1.__importDefault)(require("cli-table3"));
16
+ const index_1 = require("../index");
17
+ const rwdict_1 = (0, tslib_1.__importDefault)(require("../rwdict"));
18
+ const utils_1 = require("../utils");
19
+ require("../../prototype.js");
20
+ const parser_1 = require("./parser");
21
+ /** 默认 i18next 扫描配置 */
22
+ const DEFAULT_CONFIG = {
23
+ debug: false,
24
+ input: [
25
+ // 'src/**/*.{js,jsx,ts,tsx}',
26
+ '!i18n/**', // Use ! to filter out files or directories
27
+ ],
28
+ // 相对于根目录
29
+ output: 'i18n/',
30
+ // 若是相对路径,则以 output 为基准进行解析
31
+ dict: ['dict.json', 'scanneds.json'],
32
+ lngs: ['zh', 'en', 'ja', 'ko'],
33
+ ns: ['translation'],
34
+ defaultLng: 'zh',
35
+ defaultNs: 'translation',
36
+ func: {
37
+ list: ['i18next.t', 'i18n.t', 'i18n.__', 't', '__'],
38
+ extensions: [], // 避免在 transform 中执行原生的 parseFuncFromString
39
+ },
40
+ trans: {
41
+ extensions: [],
42
+ fallbackKey: true,
43
+ babylon: {
44
+ sourceType: 'module',
45
+ allowAwaitOutsideFunction: true,
46
+ // https://babeljs.io/docs/en/babel-parser
47
+ plugins: [
48
+ // Language extensions
49
+ 'jsx',
50
+ 'typescript',
51
+ // ECMAScript proposals
52
+ 'classProperties',
53
+ 'classPrivateProperties',
54
+ 'classPrivateMethods',
55
+ 'classStaticBlock',
56
+ 'decimal',
57
+ ['decorators', { decoratorsBeforeExport: true }],
58
+ 'doExpressions',
59
+ 'exportDefaultFrom',
60
+ 'functionBind',
61
+ 'importAssertions',
62
+ 'moduleBlocks',
63
+ 'moduleStringNames',
64
+ 'partialApplication',
65
+ ['pipelineOperator', { proposal: 'smart' }],
66
+ 'privateIn',
67
+ ['recordAndTuple', { syntaxType: 'bar' }],
68
+ 'throwExpressions',
69
+ 'topLevelAwait',
70
+ ],
71
+ },
72
+ // 实际并没有用到 acorn, 用了 babel
73
+ acorn: {
74
+ ecmaVersion: 'latest',
75
+ sourceType: 'module', // defaults to 'module'
76
+ // Check out https://github.com/acornjs/acorn/tree/master/acorn#interface for additional options
77
+ }
78
+ },
79
+ // 禁用 : 和 . 作为 seperator
80
+ keySeparator: false,
81
+ nsSeparator: false,
82
+ // Context Form
83
+ context: true,
84
+ contextFallback: true,
85
+ contextSeparator: '_',
86
+ // Plural
87
+ // whether to add plural form key
88
+ plural(language, ns, key, options /** Config */) {
89
+ return language === 'en';
90
+ },
91
+ pluralFallback: true,
92
+ pluralSeparator: '_',
93
+ // interpolation options
94
+ interpolation: {
95
+ prefix: '{{',
96
+ suffix: '}}' // suffix for interpolation
97
+ }
98
+ };
99
+ const VALID_EXTENTIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.ejs']);
100
+ /** 扫描源码中的词条,以及收集未翻译的词条。生成 scanneds.json 和 untranslateds.json
101
+ * rootdir 要扫描根目录 [ process.cwd() ]
102
+ * config 配置信息
103
+ */
104
+ function scanner(rootdir = upath_1.default.normalize(process.cwd()), config = {}) {
105
+ const output = upath_1.default.resolve(rootdir, config.output || DEFAULT_CONFIG.output);
106
+ const input = (config.input || DEFAULT_CONFIG.input).map(fp => upath_1.default.resolve(rootdir, fp));
107
+ config = {
108
+ ...DEFAULT_CONFIG,
109
+ ...config,
110
+ input,
111
+ output,
112
+ resource: {
113
+ loadPath: '',
114
+ savePath: upath_1.default.resolve(output, 'translation/{{lng}}.js'),
115
+ jsonIndent: 4,
116
+ lineEnding: '\n'
117
+ }
118
+ };
119
+ let dict = config.dict.reduce((dict, fp_dict) => dict.merge((0, utils_1.try_require)(upath_1.default.resolve(output, fp_dict)), { print: false, overwrite: true }), new rwdict_1.default());
120
+ let c_files = 0;
121
+ let c_scanneds = 0;
122
+ let errors = [];
123
+ // 扫描出的词条及已有的翻译
124
+ let scanneds = {};
125
+ // 所有语言的扫描统计信息
126
+ let stats = Object.fromEntries(index_1.LANGUAGES.map((language) => [language, { translateds: new Set(), untranslateds: new Set() }]));
127
+ let spinner = (0, ora_1.default)({ interval: 66 }).start('Scanning...');
128
+ function on_scanned(text, { language, key, defaultValue, count, context }) {
129
+ // console.log(text, { language, key, defaultValue, count, context })
130
+ text = text || defaultValue;
131
+ if (!key)
132
+ key = context ? `${text}_${context}` : text;
133
+ if (!language) {
134
+ for (const language of index_1.LANGUAGES)
135
+ on_scanned(text, { language, key, count, context });
136
+ return;
137
+ }
138
+ // console.log(text, { language, key, defaultValue, count, context })
139
+ // debugger
140
+ const stat = stats[language];
141
+ // 获取已有翻译
142
+ const translation = dict.get(key, language) ||
143
+ language === 'zh' && text ||
144
+ '';
145
+ if (language === 'zh' && !context)
146
+ return;
147
+ scanneds[key] = {
148
+ ...scanneds[key],
149
+ [language]: translation
150
+ };
151
+ if (translation)
152
+ stat.translateds.add(key);
153
+ else
154
+ stat.untranslateds.add(key);
155
+ if (language === 'en' && count !== undefined)
156
+ on_scanned(text, { language, key: `${key}_plural`, context });
157
+ }
158
+ function new_vinyl_file(_path, data) {
159
+ return new vinyl_1.default({
160
+ cwd: rootdir,
161
+ base: rootdir,
162
+ path: upath_1.default.resolve(config.output, _path),
163
+ contents: Buffer.from(typeof data === 'string' ? data : JSON.stringify(data, null, 4))
164
+ });
165
+ }
166
+ // ------------ scan by file
167
+ vinyl_fs_1.default
168
+ .src(config.input, { cwd: rootdir, sync: false })
169
+ // 每个文件扫描前,统计文件数量
170
+ .pipe((0, map_stream_1.default)((file, cb, count) => {
171
+ // 支持 `// @i18n-noscan` 忽略扫描
172
+ if (/\/\/\s*@i18n-noscan\s/.test(file.contents.toString()))
173
+ return cb();
174
+ c_files++;
175
+ cb(null, file);
176
+ }))
177
+ // 对文件进行排序,保证词条有一定的顺序
178
+ .pipe((0, gulp_sort_1.default)())
179
+ // 分析代码提取词条
180
+ .pipe(i18next_scanner_1.default.createStream(config, function transform(file, encoding, callback) {
181
+ const { parser } = this;
182
+ const ext = upath_1.default.extname(file.path);
183
+ // 只扫描源码文件
184
+ if (!VALID_EXTENTIONS.has(ext)) {
185
+ callback();
186
+ return;
187
+ }
188
+ c_scanneds++;
189
+ const percent = Math.round((100 * c_scanneds) / c_files);
190
+ const text = `Scanning (${percent}%): ${file.path.green}`;
191
+ spinner.text = (0, cli_truncate_1.default)(text, process.stdout.columns - 5, { position: 'middle', });
192
+ let code = file.contents.toString();
193
+ if (ext === '.ejs')
194
+ code = ejs_1.default.compile(code, { filename: file.path, client: true, legacyInclude: true }).toString();
195
+ // --- 添加代码中扫描到的 i18n.t('key') 中的 key 到 parser
196
+ // parser.parseFuncFromString 使用 esprima 来解析代码,esprima 仍然不支持 optional chaining !!
197
+ parser.parseFuncFromString(code.replace(/\?\.\[/g, '[').replace(/\?\.\(/g, '(').replace(/\?\./g, '.'), on_scanned);
198
+ // --- 添加代码中扫描到的 Trans 组件中的 key 到 parser
199
+ if (ext === '.jsx' || ext === '.tsx') {
200
+ // parser.parseTransFromString 使用 acorn 解析代码,不支持 TypeScript,添加 parser.parseTransFromStringByBabel
201
+ (0, parser_1.mix_parse_trans_from_string_by_babel)(parser);
202
+ parser.parseTransFromStringByBabel(code, { filepath: file.path }, on_scanned, (error) => { errors.push(error); });
203
+ }
204
+ setTimeout(callback, 0);
205
+ }))
206
+ // 创建词条文件
207
+ .pipe(through2_1.default.obj(
208
+ /** i18n-scanner 会把扫描结果以每个语言一个文件的形式提供,这里解析扫描结果
209
+ * file: 翻译 resource 文件,其中 file.contents 包含翻译的扫描结果
210
+ */
211
+ function write(file, encoding, cb) { cb(); },
212
+ /** 生成 stats.json, unmarkeds.md; 打印 untranslated / unmarkeds */
213
+ function flush(cb) {
214
+ // ------------ stats.json
215
+ this.push(new_vinyl_file('stats.json', Object.fromEntries(Object.entries(stats).map(([l, { translateds, untranslateds }]) => [l, { translateds: Array.from(translateds), untranslateds: Array.from(untranslateds) }]))));
216
+ // ------------ 打印 cli 统计表
217
+ const table = new cli_table3_1.default({
218
+ head: [
219
+ 'Language',
220
+ 'Untranslated'.red,
221
+ 'Translated'.green,
222
+ ],
223
+ colAligns: ['right', 'right', 'right', 'right'],
224
+ style: { head: [] },
225
+ chars: {
226
+ top: '',
227
+ 'top-mid': '',
228
+ 'top-left': '',
229
+ 'top-right': '',
230
+ bottom: '',
231
+ 'bottom-mid': '',
232
+ 'bottom-left': '',
233
+ 'bottom-right': '',
234
+ left: '',
235
+ 'left-mid': '',
236
+ mid: '',
237
+ 'mid-mid': '',
238
+ right: '',
239
+ 'right-mid': '',
240
+ middle: ' ',
241
+ },
242
+ });
243
+ Object.entries(stats).forEach(([lang, stat]) => {
244
+ table.push([
245
+ lang,
246
+ String(stat.untranslateds.size).red,
247
+ String(stat.translateds.size).green
248
+ ]);
249
+ });
250
+ spinner.stop();
251
+ console.log(`Scanned ${c_files} files. Occured ${errors.length} errors.`);
252
+ console.log(table.toString());
253
+ // ------------ 生成 unmarkeds.md 统计
254
+ /*
255
+ const fp_unmarked = path.resolve(config.output, 'unmarkeds.md')
256
+
257
+ if (fs.existsSync(fp_unmarked))
258
+ rimraf.sync(fp_unmarked)
259
+
260
+ if (unmarkeds.length) {
261
+ console.log(colors.yellow(`\n⚠️ 发现未标记的中文字符 ${unmarkeds.length} 处:\n`))
262
+ unmarkeds.forEach(({ value, filepath, loc: { start } }, index) => {
263
+ if (index >= 5) return
264
+ console.log( ` ${colors.white(`'${value}'`)}\t${colors.blue.underline(`${path.relative(rootdir, filepath)}:${start.line}:${start.column + 1}`)}` )
265
+ })
266
+ }
267
+
268
+ this.push( new_vinyl_file( fp_unmarked,
269
+ unmarkeds.map( ({ value, filepath, loc }) =>
270
+ '- [' + value.trim() + '](' + path.relative( config.output, path.resolve(rootdir, filepath || '') ) + '#L' + loc.start.line + ')'
271
+ ).join('\n') + '\n'
272
+ ))
273
+
274
+
275
+ if (unmarkeds.length > 5) {
276
+ console.log(' ...')
277
+ console.log(colors.yellow(`\n 完整未标记词条请查看 ${colors.blue.underline(path.relative(rootdir, fp_unmarked))}`))
278
+ }
279
+ */
280
+ const en_untranslateds = stats.en.untranslateds;
281
+ if (en_untranslateds.size) {
282
+ console.log('\n未翻译的英文词条:'.yellow);
283
+ Array.from(en_untranslateds).slice(0, 10).forEach(untranslated => {
284
+ console.log(untranslated);
285
+ });
286
+ if (en_untranslateds.size > 10) {
287
+ console.log('...');
288
+ console.log(`--- 共 ${en_untranslateds.size} 个未翻译的英文词条 ---`);
289
+ }
290
+ }
291
+ else
292
+ console.log('\n所有词条都至少含有英文翻译.'.green);
293
+ // ------------ 生成 scanneds.json
294
+ const fp_scanneds = upath_1.default.resolve(config.output, 'scanneds.json');
295
+ this.push(new_vinyl_file(fp_scanneds, scanneds));
296
+ // ------------ 写入 dict.json
297
+ const fp_dict_new = upath_1.default.resolve(output, 'dict.json');
298
+ this.push(new_vinyl_file(fp_dict_new, dict.to_json(true) + '\n'));
299
+ console.log(`\n\n${'⚠️ 请检查扫描结果'.yellow} ${fp_scanneds.underline.blue} ${'和新生成的词典文件'.yellow} ${fp_dict_new.underline.blue}`);
300
+ console.log('\n请手动补全扫描结果 scanneds.json 中未翻译的词条'.yellow);
301
+ console.log('\n');
302
+ console.log('通过以上方法补充翻译后重新运行扫描,会根据 scanneds.json 更新 dict.json'.yellow);
303
+ console.log('最后 dict.json 所包含的词条会被打包进 js, 通过 new I18N(<dict.json>) 或 i18n.init(<dict.json>) 加载'.yellow);
304
+ console.log();
305
+ console.log(`${'详细文档请查看:'.yellow} ${'https://github.com/ShenHongFei/xshell/i18n'.blue.underline}`);
306
+ cb();
307
+ }))
308
+ // 写入词条文件
309
+ .pipe(vinyl_fs_1.default.dest(rootdir))
310
+ .on('end', () => {
311
+ errors.forEach(errorHandler => errorHandler());
312
+ if (!errors.length)
313
+ return;
314
+ console.log('https://www.i18next.com/translation-function/essentials\n以上错误可能是由不规范的词条标记导致,标记规范可见:%s', ''.blue.underline);
315
+ });
316
+ }
317
+ exports.scanner = scanner;
318
+ exports.default = scanner;
319
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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","sourcesContent":["import i18n_scanner from 'i18next-scanner'\nimport path from 'upath'\nimport ejs from 'ejs'\nimport vfs from 'vinyl-fs'\nimport map from 'map-stream'\nimport sort from 'gulp-sort'\nimport ora from 'ora'\nimport cli_truncate from 'cli-truncate'\nimport Vinyl from 'vinyl'\nimport through2 from 'through2'\nimport CliTable from 'cli-table3'\nimport type { Transform } from 'stream'\n\nimport { LANGUAGES } from '../index'\nimport type { Language } from '../index'\nimport Dict from '../rwdict'\nimport type { _Dict } from '../dict'\nimport { try_require } from '../utils'\n\nimport '../../prototype.js'\n\nimport { mix_parse_trans_from_string_by_babel } from './parser'\n\n\n\n/** 默认 i18next 扫描配置 */\nconst DEFAULT_CONFIG = {\n debug: false,\n \n input: [\n // 'src/**/*.{js,jsx,ts,tsx}',\n '!i18n/**', // Use ! to filter out files or directories\n ],\n \n // 相对于根目录\n output: 'i18n/',\n \n // 若是相对路径,则以 output 为基准进行解析\n dict: ['dict.json', 'scanneds.json'],\n \n lngs: ['zh', 'en', 'ja', 'ko'],\n ns: ['translation'],\n defaultLng: 'zh',\n defaultNs: 'translation',\n\n func: {\n list: [ 'i18next.t', 'i18n.t', 'i18n.__', 't', '__' ],\n extensions: [ ], // 避免在 transform 中执行原生的 parseFuncFromString\n },\n \n trans: {\n extensions: [ ], // 避免在 transform 中执行原生的 parseTransFromString\n fallbackKey: true,\n \n babylon: {\n sourceType: 'module',\n \n allowAwaitOutsideFunction: true,\n \n // https://babeljs.io/docs/en/babel-parser\n plugins: [\n // Language extensions\n 'jsx',\n 'typescript',\n \n // ECMAScript proposals\n 'classProperties',\n 'classPrivateProperties',\n 'classPrivateMethods',\n 'classStaticBlock',\n 'decimal',\n ['decorators', { decoratorsBeforeExport: true }],\n 'doExpressions',\n 'exportDefaultFrom',\n 'functionBind',\n 'importAssertions',\n 'moduleBlocks',\n 'moduleStringNames',\n 'partialApplication',\n ['pipelineOperator', { proposal: 'smart' }],\n 'privateIn',\n ['recordAndTuple', { syntaxType: 'bar' }],\n 'throwExpressions',\n 'topLevelAwait',\n ],\n } as import('@babel/parser').ParserOptions,\n \n // 实际并没有用到 acorn, 用了 babel\n acorn: {\n ecmaVersion: 'latest',\n sourceType: 'module', // defaults to 'module'\n // Check out https://github.com/acornjs/acorn/tree/master/acorn#interface for additional options\n }\n },\n \n // 禁用 : 和 . 作为 seperator\n keySeparator: false, // char to separate keys\n nsSeparator: false, // char to split namespace from key\n \n // Context Form\n context: true, // whether to add context form key\n contextFallback: true, // whether to add a fallback key as well as the context form key\n contextSeparator: '_', // char to split context from key\n\n // Plural\n // whether to add plural form key\n plural (language: string, ns: string, key: string, options: any /** Config */) {\n return language === 'en'\n }, \n pluralFallback: true, // whether to add a fallback key as well as the plural form key\n pluralSeparator: '_', // char to split plural from key\n \n // interpolation options\n interpolation: {\n prefix: '{{', // prefix for interpolation\n suffix: '}}' // suffix for interpolation\n }\n}\n\nconst VALID_EXTENTIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.ejs'])\n\nexport type Config = Partial<(typeof DEFAULT_CONFIG) & {\n defaultValue?: string\n resource?: { loadPath?: string, savePath?: string, jsonIndent?: number, lineEnding?: '\\n' }\n}>\n\n\n/** 扫描源码中的词条,以及收集未翻译的词条。生成 scanneds.json 和 untranslateds.json\n* rootdir 要扫描根目录 [ process.cwd() ]\n* config 配置信息\n*/\nexport function scanner (rootdir: string = path.normalize(process.cwd()), config: Config = { }) {\n const output = path.resolve(rootdir, config.output || DEFAULT_CONFIG.output)\n const input = (config.input || DEFAULT_CONFIG.input).map( fp => path.resolve(rootdir, fp) )\n \n config = {\n ...DEFAULT_CONFIG,\n ...config,\n input,\n output,\n resource: {\n loadPath: '',\n savePath: path.resolve(output, 'translation/{{lng}}.js'),\n jsonIndent: 4,\n lineEnding: '\\n'\n }\n }\n \n \n let dict = config.dict.reduce( (dict, fp_dict) => \n dict.merge( try_require( path.resolve(output, fp_dict)), { print: false, overwrite: true }),\n new Dict())\n \n let c_files = 0\n let c_scanneds = 0\n let errors = []\n \n // 扫描出的词条及已有的翻译\n let scanneds: _Dict = { }\n \n // 所有语言的扫描统计信息\n let stats = Object.fromEntries(\n LANGUAGES.map( (language) => [language, { translateds: new Set<string>(), untranslateds: new Set<string>() }])\n )\n \n let spinner = ora({ interval: 66 }).start('Scanning...')\n \n \n \n function on_scanned (text: string, { language, key, defaultValue, count, context }: { language?: Language, key?: string, defaultValue?: string, count?: number, context?: string }) {\n // console.log(text, { language, key, defaultValue, count, context })\n \n text = text || defaultValue\n \n if (!key)\n key = context ? `${text}_${context}` : text\n \n if (!language) {\n for (const language of LANGUAGES)\n on_scanned(text, { language, key, count, context })\n return\n }\n \n // console.log(text, { language, key, defaultValue, count, context })\n // debugger\n \n const stat = stats[language]\n \n // 获取已有翻译\n const translation = \n dict.get(key, language) || \n language === 'zh' && text || \n ''\n \n if (language === 'zh' && !context) return\n \n scanneds[key] = {\n ... scanneds[key],\n [language]: translation\n }\n \n if (translation)\n stat.translateds.add(key)\n else\n stat.untranslateds.add(key)\n \n if (language === 'en' && count !== undefined)\n on_scanned(text, { language, key: `${key}_plural`, context })\n }\n \n \n function new_vinyl_file (_path: string, data: string | object) {\n return new Vinyl({\n cwd: rootdir,\n base: rootdir,\n path: path.resolve(config.output, _path),\n contents: Buffer.from(typeof data === 'string' ? data : JSON.stringify(data, null, 4))\n })\n }\n \n \n // ------------ scan by file\n vfs\n .src(config.input, { cwd: rootdir, sync: false })\n \n // 每个文件扫描前,统计文件数量\n .pipe(\n map( (file: Vinyl, cb: Function, count: number) => {\n // 支持 `// @i18n-noscan` 忽略扫描\n if (/\\/\\/\\s*@i18n-noscan\\s/.test(file.contents.toString())) return cb()\n c_files++\n cb(null, file)\n })\n )\n \n // 对文件进行排序,保证词条有一定的顺序\n .pipe( sort() )\n \n // 分析代码提取词条\n .pipe(\n i18n_scanner.createStream( config, function transform (this: { parser: any }, file: Vinyl, encoding: string, callback: Function): void {\n const { parser } = this\n const ext = path.extname(file.path)\n \n // 只扫描源码文件\n if (!VALID_EXTENTIONS.has(ext)) {\n callback()\n return\n }\n \n c_scanneds++\n const percent = Math.round( (100 * c_scanneds) / c_files )\n const text = `Scanning (${percent}%): ${file.path.green}`\n spinner.text = cli_truncate(text, process.stdout.columns - 5, { position: 'middle', })\n \n let code = file.contents.toString()\n \n if (ext === '.ejs')\n code = ejs.compile( code, { filename: file.path, client: true, legacyInclude: true } as any ).toString()\n \n \n // --- 添加代码中扫描到的 i18n.t('key') 中的 key 到 parser\n // parser.parseFuncFromString 使用 esprima 来解析代码,esprima 仍然不支持 optional chaining !!\n parser.parseFuncFromString(code.replace(/\\?\\.\\[/g, '[').replace(/\\?\\.\\(/g, '(').replace(/\\?\\./g, '.'), on_scanned)\n \n // --- 添加代码中扫描到的 Trans 组件中的 key 到 parser\n if (ext === '.jsx' || ext === '.tsx') {\n // parser.parseTransFromString 使用 acorn 解析代码,不支持 TypeScript,添加 parser.parseTransFromStringByBabel\n mix_parse_trans_from_string_by_babel(parser)\n parser.parseTransFromStringByBabel(\n code,\n { filepath: file.path },\n on_scanned,\n (error: Error) => { errors.push(error) }\n )\n }\n \n setTimeout(callback, 0)\n })\n )\n \n // 创建词条文件\n .pipe(\n through2.obj(\n /** i18n-scanner 会把扫描结果以每个语言一个文件的形式提供,这里解析扫描结果\n * file: 翻译 resource 文件,其中 file.contents 包含翻译的扫描结果\n */\n function write (this: Transform, file: Vinyl, encoding: string, cb: Function) { cb() },\n \n /** 生成 stats.json, unmarkeds.md; 打印 untranslated / unmarkeds */\n function flush (this: Transform, cb: Function) {\n // ------------ stats.json\n this.push(new_vinyl_file('stats.json', \n Object.fromEntries(\n Object.entries(stats).map( ([l, { translateds, untranslateds }]) => \n [l, { translateds: Array.from(translateds), untranslateds: Array.from(untranslateds) }])\n )\n ))\n \n \n // ------------ 打印 cli 统计表\n const table = new CliTable({\n head: [\n 'Language',\n 'Untranslated'.red,\n 'Translated'.green,\n ],\n colAligns: ['right', 'right', 'right', 'right'],\n style: { head: [] },\n chars: {\n top: '',\n 'top-mid': '',\n 'top-left': '',\n 'top-right': '',\n bottom: '',\n 'bottom-mid': '',\n 'bottom-left': '',\n 'bottom-right': '',\n left: '',\n 'left-mid': '',\n mid: '',\n 'mid-mid': '',\n right: '',\n 'right-mid': '',\n middle: ' ',\n },\n })\n \n Object.entries(stats).forEach( ([lang, stat]) => {\n table.push([\n lang, \n String(stat.untranslateds.size).red, \n String(stat.translateds.size).green\n ] as any)\n })\n \n \n \n spinner.stop()\n console.log(`Scanned ${c_files} files. Occured ${errors.length} errors.`)\n console.log(table.toString())\n \n \n // ------------ 生成 unmarkeds.md 统计\n /*\n const fp_unmarked = path.resolve(config.output, 'unmarkeds.md')\n \n if (fs.existsSync(fp_unmarked))\n rimraf.sync(fp_unmarked)\n \n if (unmarkeds.length) {\n console.log(colors.yellow(`\\n⚠️ 发现未标记的中文字符 ${unmarkeds.length} 处:\\n`))\n unmarkeds.forEach(({ value, filepath, loc: { start } }, index) => {\n if (index >= 5) return\n console.log( ` ${colors.white(`'${value}'`)}\\t${colors.blue.underline(`${path.relative(rootdir, filepath)}:${start.line}:${start.column + 1}`)}` )\n })\n }\n \n this.push( new_vinyl_file( fp_unmarked, \n unmarkeds.map( ({ value, filepath, loc }) =>\n '- [' + value.trim() + '](' + path.relative( config.output, path.resolve(rootdir, filepath || '') ) + '#L' + loc.start.line + ')'\n ).join('\\n') + '\\n'\n ))\n \n \n if (unmarkeds.length > 5) {\n console.log(' ...')\n console.log(colors.yellow(`\\n 完整未标记词条请查看 ${colors.blue.underline(path.relative(rootdir, fp_unmarked))}`))\n }\n */\n \n const en_untranslateds = stats.en.untranslateds\n if (en_untranslateds.size) {\n console.log('\\n未翻译的英文词条:'.yellow)\n Array.from(en_untranslateds).slice(0, 10).forEach( untranslated => {\n console.log(untranslated)\n })\n if (en_untranslateds.size > 10) {\n console.log('...')\n console.log(`--- 共 ${en_untranslateds.size} 个未翻译的英文词条 ---`)\n }\n } else\n console.log('\\n所有词条都至少含有英文翻译.'.green)\n \n \n // ------------ 生成 scanneds.json\n const fp_scanneds = path.resolve(config.output, 'scanneds.json')\n this.push(\n new_vinyl_file(fp_scanneds, scanneds)\n )\n \n // ------------ 写入 dict.json\n const fp_dict_new = path.resolve(output, 'dict.json')\n this.push( new_vinyl_file( fp_dict_new, dict.to_json(true) + '\\n' ))\n \n console.log(`\\n\\n${'⚠️ 请检查扫描结果'.yellow} ${fp_scanneds.underline.blue} ${'和新生成的词典文件'.yellow} ${fp_dict_new.underline.blue}`)\n console.log('\\n请手动补全扫描结果 scanneds.json 中未翻译的词条'.yellow)\n console.log('\\n')\n console.log('通过以上方法补充翻译后重新运行扫描,会根据 scanneds.json 更新 dict.json'.yellow)\n console.log('最后 dict.json 所包含的词条会被打包进 js, 通过 new I18N(<dict.json>) 或 i18n.init(<dict.json>) 加载'.yellow)\n console.log()\n console.log(`${'详细文档请查看:'.yellow} ${'https://github.com/ShenHongFei/xshell/i18n'.blue.underline}`)\n \n cb()\n }\n )\n )\n \n // 写入词条文件\n .pipe(\n vfs.dest(rootdir)\n )\n \n .on('end', () => {\n errors.forEach( errorHandler => errorHandler() )\n if (!errors.length) return\n console.log(\n 'https://www.i18next.com/translation-function/essentials\\n以上错误可能是由不规范的词条标记导致,标记规范可见:%s',\n ''.blue.underline\n )\n })\n}\n\nexport default scanner\n"]}
@@ -0,0 +1,3 @@
1
+ import '../../prototype.js';
2
+ /** file:///D:/0/i18next-scanner/src/parser.js */
3
+ export declare function mix_parse_trans_from_string_by_babel(parser: any): void;
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mix_parse_trans_from_string_by_babel = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const lodash_1 = require("lodash");
6
+ const traverse_1 = (0, tslib_1.__importDefault)(require("@babel/traverse"));
7
+ const parser_1 = require("@babel/parser");
8
+ const t = (0, tslib_1.__importStar)(require("@babel/types"));
9
+ require("../../prototype.js");
10
+ // import { Checker } from './checker'
11
+ /** file:///D:/0/i18next-scanner/src/parser.js */
12
+ function mix_parse_trans_from_string_by_babel(parser) {
13
+ parser.parseTransFromStringByBabel = function (code, options = {}, customHandler = null, onError = () => { }) {
14
+ if ((0, lodash_1.isFunction)(options)) {
15
+ customHandler = options;
16
+ options = {};
17
+ }
18
+ const { transformOptions = {}, // object
19
+ component = this.options.trans.component, // string
20
+ i18nKey = this.options.trans.i18nKey, // string
21
+ defaultsKey = this.options.trans.defaultsKey, // string
22
+ fallbackKey = this.options.trans.fallbackKey, // boolean|function
23
+ babylon: babylon_options = this.options.trans.babylon, // object
24
+ filepath, } = options;
25
+ const parseJSXElement = ({ node }) => {
26
+ if (!node)
27
+ return;
28
+ if (node.openingElement.name.name !== component)
29
+ return;
30
+ const getLiteralValue = literal => {
31
+ if (t.isTemplateLiteral(literal))
32
+ return literal.quasis.map(element => element.value.cooked).join("");
33
+ return literal.value;
34
+ };
35
+ const attr = (0, lodash_1.castArray)(node.openingElement.attributes).reduce((acc, attribute) => {
36
+ if (!t.isJSXAttribute(attribute) ||
37
+ !t.isJSXIdentifier(attribute.name))
38
+ return acc;
39
+ const { name } = attribute.name;
40
+ const value = attribute.value;
41
+ if (t.isLiteral(value))
42
+ acc[name] = getLiteralValue(value);
43
+ else if (t.isJSXExpressionContainer(value)) {
44
+ const expression = value.expression;
45
+ if (t.isIdentifier(expression))
46
+ acc[name] = expression.name;
47
+ else if (t.isLiteral(expression))
48
+ acc[name] = getLiteralValue(expression);
49
+ else if (t.isObjectExpression(expression)) {
50
+ const properties = (0, lodash_1.castArray)(expression.properties);
51
+ acc[name] = properties.reduce((obj, property) => {
52
+ if (!t.isObjectProperty(property))
53
+ return obj;
54
+ if (t.isLiteral(property.value))
55
+ obj[property.key.name] = getLiteralValue(property.value);
56
+ else // Unable to get value of the property
57
+ obj[property.key.name] = "";
58
+ return obj;
59
+ }, {});
60
+ /**
61
+ * 防止 count 被忽略,如
62
+ * ```jsx
63
+ * <Trans count={arr.length}>
64
+ * 一二三{{ count: arr.length }}
65
+ * </Trans>
66
+ * ```
67
+ */
68
+ }
69
+ else if (name === "count")
70
+ acc[name] = 0;
71
+ }
72
+ return acc;
73
+ }, {});
74
+ const transKey = (0, lodash_1.trim)(attr[i18nKey]);
75
+ const defaultsString = attr[defaultsKey] || "";
76
+ if (typeof defaultsString !== "string")
77
+ this.log(`defaults value must be a static string, saw ${defaultsString.yellow}`);
78
+ // https://www.i18next.com/translation-function/essentials#overview-options
79
+ const tOptions = attr.tOptions;
80
+ const options = {
81
+ ...tOptions,
82
+ defaultValue: defaultsString || nodes_to_string(node.children, filepath, onError),
83
+ fallbackKey,
84
+ };
85
+ if (Object.prototype.hasOwnProperty.call(attr, "count"))
86
+ options.count = Number(attr.count) || 0;
87
+ if (Object.prototype.hasOwnProperty.call(attr, "ns")) {
88
+ if (typeof options.ns !== "string")
89
+ this.log(`The ns attribute must be a string, saw ${attr.ns?.yellow}`);
90
+ options.ns = attr.ns;
91
+ }
92
+ if (customHandler) {
93
+ customHandler(transKey, options);
94
+ return;
95
+ }
96
+ this.set(transKey, options);
97
+ };
98
+ try {
99
+ const ast = (0, parser_1.parse)(code, { ...babylon_options });
100
+ (0, traverse_1.default)(ast, { JSXElement: parseJSXElement, });
101
+ // traverse(ast, Checker({ filepath }))
102
+ }
103
+ catch (err) {
104
+ onError(() => {
105
+ console.error('');
106
+ const { line, column } = (err && err.loc) || { line: 1, column: 1 };
107
+ console.error([filepath, line, column].join(":").yellow);
108
+ console.error(`Unable to parse ${component?.blue} component.\n`.red);
109
+ if (!filepath)
110
+ console.error(String(code).red);
111
+ console.error((" " + err.message).red);
112
+ });
113
+ }
114
+ return this;
115
+ };
116
+ }
117
+ exports.mix_parse_trans_from_string_by_babel = mix_parse_trans_from_string_by_babel;
118
+ function nodes_to_string(nodes, filepath, onError) {
119
+ let memo = '';
120
+ let nodeIndex = 0;
121
+ nodes.forEach((node, i) => {
122
+ if (t.isJSXText(node) || t.isStringLiteral(node)) {
123
+ const value = node.value
124
+ .replace(/^[\r\n]+\s*/g, "") // remove leading spaces containing a leading newline character
125
+ .replace(/[\r\n]+\s*$/g, "") // remove trailing spaces containing a leading newline character
126
+ .replace(/[\r\n]+\s*/g, " "); // replace spaces containing a leading newline character with a single space character
127
+ if (!value)
128
+ return;
129
+ memo += value;
130
+ }
131
+ else if (t.isJSXExpressionContainer(node)) {
132
+ const { expression = {} } = node;
133
+ if (t.isNumericLiteral(expression)) // Numeric literal is ignored in react-i18next
134
+ memo += '';
135
+ if (t.isStringLiteral(expression))
136
+ memo += expression.value;
137
+ else if (t.isObjectExpression(expression) &&
138
+ t.isObjectProperty((0, lodash_1.get)(expression, 'properties[0]')))
139
+ memo += '{{' + expression.properties[0].key.name + '}}';
140
+ else
141
+ onError(() => {
142
+ const { line, column } = (node.expression && node.expression.loc.start) || { line: 1, column: 1 };
143
+ console.error('');
144
+ console.error([filepath, line, column].join(":").yellow);
145
+ console.error('Unsupported JSX expression. Only static values or {{interpolation}} blocks are supported.'.red);
146
+ });
147
+ }
148
+ else if (node.children)
149
+ memo += `<${nodeIndex}>${nodes_to_string(node.children, filepath, onError)}</${nodeIndex}>`;
150
+ nodeIndex++;
151
+ });
152
+ return memo;
153
+ }
154
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["parser.ts"],"names":[],"mappings":"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","sourcesContent":["import { isFunction, castArray, trim, get as _get } from 'lodash'\nimport traverse from '@babel/traverse'\nimport { parse } from '@babel/parser'\nimport * as t from '@babel/types'\n\nimport '../../prototype.js'\n\n// import { Checker } from './checker'\n\n/** file:///D:/0/i18next-scanner/src/parser.js */\nexport function mix_parse_trans_from_string_by_babel (parser) {\n parser.parseTransFromStringByBabel = function(code: string, options = { }, customHandler = null, onError: (callback: Function) => void = () => { } ) {\n if (isFunction(options)) {\n customHandler = options\n options = { }\n }\n \n const {\n transformOptions = { }, // object\n component = this.options.trans.component, // string\n i18nKey = this.options.trans.i18nKey, // string\n defaultsKey = this.options.trans.defaultsKey, // string\n fallbackKey = this.options.trans.fallbackKey, // boolean|function\n babylon: babylon_options = this.options.trans.babylon, // object\n filepath,\n } = options as any\n \n const parseJSXElement = ({ node }) => {\n if (!node) return\n \n if (node.openingElement.name.name !== component) return\n \n \n const getLiteralValue = literal => {\n if (t.isTemplateLiteral(literal))\n return literal.quasis.map(element => element.value.cooked).join(\"\")\n \n return literal.value\n }\n \n const attr = castArray(node.openingElement.attributes).reduce(\n (acc, attribute) => {\n if (\n !t.isJSXAttribute(attribute) ||\n !t.isJSXIdentifier(attribute.name)\n ) return acc\n \n const { name } = attribute.name\n const value = attribute.value\n if (t.isLiteral(value))\n acc[name] = getLiteralValue(value)\n else if (t.isJSXExpressionContainer(value)) {\n const expression = value.expression\n if (t.isIdentifier(expression))\n acc[name] = expression.name\n else if (t.isLiteral(expression))\n acc[name] = getLiteralValue(expression)\n else if (t.isObjectExpression(expression)) {\n const properties = castArray(expression.properties)\n acc[name] = properties.reduce((obj, property) => {\n if (!t.isObjectProperty(property)) return obj\n if (t.isLiteral(property.value))\n obj[(property.key as any).name] = getLiteralValue(property.value)\n else // Unable to get value of the property\n obj[(property.key as any).name] = \"\"\n return obj\n }, { })\n /**\n * 防止 count 被忽略,如\n * ```jsx\n * <Trans count={arr.length}>\n * 一二三{{ count: arr.length }}\n * </Trans>\n * ```\n */\n } else if (name === \"count\")\n acc[name] = 0\n }\n return acc\n },\n { }\n )\n const transKey = trim(attr[i18nKey])\n \n const defaultsString = attr[defaultsKey] || \"\"\n if (typeof defaultsString !== \"string\")\n this.log(`defaults value must be a static string, saw ${defaultsString.yellow}`)\n \n \n // https://www.i18next.com/translation-function/essentials#overview-options\n const tOptions = attr.tOptions\n const options = {\n ...tOptions,\n defaultValue: defaultsString || nodes_to_string(node.children, filepath, onError),\n fallbackKey,\n }\n \n if (Object.prototype.hasOwnProperty.call(attr, \"count\"))\n options.count = Number(attr.count) || 0\n \n \n if (Object.prototype.hasOwnProperty.call(attr, \"ns\")) {\n if (typeof options.ns !== \"string\")\n this.log(`The ns attribute must be a string, saw ${attr.ns?.yellow}`)\n \n options.ns = attr.ns\n }\n \n if (customHandler) {\n customHandler(transKey, options)\n return\n }\n \n this.set(transKey, options)\n }\n \n try {\n const ast = parse(code, { ...babylon_options })\n traverse(ast, { JSXElement: parseJSXElement, })\n // traverse(ast, Checker({ filepath }))\n } catch (err) {\n onError(() => {\n console.error('')\n const { line, column } = (err && err.loc) || { line: 1, column: 1 }\n console.error([filepath, line, column].join(\":\").yellow)\n console.error(`Unable to parse ${component?.blue} component.\\n`.red)\n if (!filepath)\n console.error(String(code).red)\n console.error((\" \" + err.message).red)\n })\n }\n \n return this\n }\n}\n\n\nfunction nodes_to_string (nodes, filepath, onError) {\n let memo = ''\n let nodeIndex = 0\n nodes.forEach((node, i) => {\n if (t.isJSXText(node) || t.isStringLiteral(node)) {\n const value = node.value\n .replace(/^[\\r\\n]+\\s*/g, \"\") // remove leading spaces containing a leading newline character\n .replace(/[\\r\\n]+\\s*$/g, \"\") // remove trailing spaces containing a leading newline character\n .replace(/[\\r\\n]+\\s*/g, \" \") // replace spaces containing a leading newline character with a single space character\n \n if (!value) return\n \n memo += value\n } else if (t.isJSXExpressionContainer(node)) {\n const { expression = { } } = node\n \n if (t.isNumericLiteral(expression)) // Numeric literal is ignored in react-i18next\n memo += ''\n if (t.isStringLiteral(expression))\n memo += expression.value\n else if (\n t.isObjectExpression(expression) &&\n t.isObjectProperty(_get(expression, 'properties[0]'))\n )\n memo += '{{' + (expression.properties[0] as any).key.name + '}}'\n else\n onError(() => {\n const { line, column } = (node.expression && node.expression.loc.start) || { line: 1, column: 1 }\n console.error('')\n console.error([filepath, line, column].join(\":\").yellow)\n console.error('Unsupported JSX expression. Only static values or {{interpolation}} blocks are supported.'.red)\n })\n \n } else if (node.children)\n memo += `<${nodeIndex}>${nodes_to_string(node.children, filepath, onError)}</${nodeIndex}>`\n \n \n nodeIndex++\n })\n \n return memo\n}\n\n"]}
@@ -0,0 +1 @@
1
+ export declare function try_require(modpath: string): any;
package/i18n/utils.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.try_require = void 0;
4
+ function try_require(modpath) {
5
+ try {
6
+ return require(modpath);
7
+ }
8
+ catch {
9
+ // console.error('未找到或解析错误,跳过加载:' + modpath)
10
+ return {};
11
+ }
12
+ }
13
+ exports.try_require = try_require;
14
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":";;;AAAA,SAAgB,WAAW,CAAE,OAAe;IACxC,IAAI;QACA,OAAO,OAAO,CAAC,OAAO,CAAC,CAAA;KAC1B;IAAC,MAAM;QACJ,4CAA4C;QAC5C,OAAO,EAAG,CAAA;KACb;AACL,CAAC;AAPD,kCAOC","sourcesContent":["export function try_require (modpath: string) {\n try {\n return require(modpath)\n } catch {\n // console.error('未找到或解析错误,跳过加载:' + modpath)\n return { }\n }\n}\n"]}
package/myfont.sass ADDED
@@ -0,0 +1,11 @@
1
+ @font-face
2
+ font-family: 'MyFont'
3
+ src: local('MyFont'), url('https://cos.shenhongfei.com/myfont.woff2')
4
+
5
+ @font-face
6
+ font-family: 'MyFont'
7
+ src: local('MyFont Bold'), url('https://cos.shenhongfei.com/myfontb.woff2')
8
+ font-weight: bold
9
+
10
+ html
11
+ font-family: 'MyFont', sans-serif
@@ -0,0 +1,23 @@
1
+ export interface RequestOptions {
2
+ method?: 'get' | 'post' | 'put' | 'head' | 'delete' | 'patch';
3
+ queries?: Record<string, any>;
4
+ headers?: Record<string, string>;
5
+ body?: string | object | HTMLFormElement;
6
+ type?: 'application/json' | 'application/x-www-form-urlencoded' | 'multipart/form-data';
7
+ cors?: boolean;
8
+ by?: 'fetch' | 'GM_xmlhttpRequest';
9
+ }
10
+ export interface RequestRawOptions extends RequestOptions {
11
+ raw: true;
12
+ }
13
+ /**
14
+ - url: 可以只有 pathname 部分
15
+ - options:
16
+ - type: `'application/json'` 请求的 content-type 头 (如果有 body)
17
+ - by: `window.GM_xmlhttpRequest ? 'GM_xmlhttpRequest' : 'fetch'` 发起请求所使用的底层方法
18
+ */
19
+ export declare function request(url: string | URL): Promise<string>;
20
+ export declare function request(url: string | URL, options: RequestRawOptions): Promise<Response>;
21
+ export declare function request(url: string | URL, options: RequestOptions): Promise<string>;
22
+ /** 发起 http 请求并将响应体作为 json 解析 */
23
+ export declare function request_json<T = any>(url: string, options?: RequestOptions): Promise<T>;