@dune2/cli 0.0.5 → 0.0.6

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.
@@ -24,16 +24,17 @@ async function getConfig() {
24
24
  const defaultI18nConfig = {
25
25
  i18nDir: "./src/i18n",
26
26
  i18nFileName: "{locale}.i18n.json",
27
- cwd: process.cwd(),
28
27
  position: { key: "B", zh: "C", en: "D", in: "E" },
29
28
  parseStartIndex: 2
30
29
  };
31
30
  let config = res.data ?? {};
31
+ config.cwd ??= process.cwd();
32
32
  if (!config.i18n || !((_a = config.i18n) == null ? void 0 : _a.length)) {
33
33
  config.i18n = [defaultI18nConfig];
34
34
  }
35
35
  config.i18n = config.i18n.map((item) => {
36
36
  item = Object.assign({}, defaultI18nConfig, item);
37
+ item.cwd ??= config.cwd;
37
38
  item.locales = Object.keys(item.position).filter((item2) => item2 !== "key");
38
39
  item.sheetId = resolveSheetId(item.sheetId);
39
40
  item.keySorter = (a, b) => {
@@ -41,6 +42,12 @@ async function getConfig() {
41
42
  };
42
43
  return item;
43
44
  });
45
+ config.api ??= [];
46
+ config.api = config.api.map((item) => {
47
+ item.output ??= "./src/apis";
48
+ item.requestFnImportPath ??= `import requestFn from '@/utils/request';`;
49
+ return item;
50
+ });
44
51
  return config;
45
52
  }
46
53
 
@@ -48,4 +55,4 @@ export {
48
55
  configName,
49
56
  getConfig
50
57
  };
51
- //# sourceMappingURL=chunk-BEIVJU3S.js.map
58
+ //# sourceMappingURL=chunk-UPESJ46X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/config.ts","../src/shared/resolveSheetId.ts"],"sourcesContent":["import JoyCon from \"joycon\";\nimport { resolveSheetId } from \"./resolveSheetId\";\n\nconst joycon = new JoyCon();\n\nexport interface I18nConfig {\n // 语言文件存放目录,默认为项目根目录的 \"./src/i18n\"\n i18nDir?: string;\n // 语言文件名,默认为 \"{locale}.i18n.json\",其中 {locale} 会被替换为 locales 中的语言\n i18nFileName?: string;\n // 默认语言,默认为 \"zh\",影响到提出出来的文案的默认值\n defaultLocale?: string;\n /**\n * 可以是 id 或者 url,如果是 url 则会自动解析出 id\n * id: 1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk\n * url: https://docs.google.com/spreadsheets/d/1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk/edit#gid=1740568548\n */\n sheetId?: string;\n // google sheets 工作表名称\n sheetRange?: string;\n /**\n * 对应 语言、i18nKey 所在的列标识符 如(A,B,C...)\n * 传入时将忽略默认的配置\n * @default {key: \"B\", zh: \"C\", en: \"D\", in: \"E\",}\n */\n position?: {\n key: string;\n [key: string]: string;\n };\n\n /**\n * 从 Google Sheets 哪一行开始解析\n * 默认从第2行开始,模板文件第一行是表头\n * @default 2\n */\n parseStartIndex?: number;\n\n /**\n * 默认是 命令运行的目录,一般是项目根目录\n * @internal\n */\n cwd?: string;\n /**\n * 支持的语言列表,从 position 中解析出来\n * @internal\n */\n locales?: string[];\n\n /**\n * 翻译key的排序\n * @internal\n */\n keySorter?: (a: string, b: string) => number;\n}\n\nexport interface ApiConfig {\n /**\n * swagger JSON 的路径 可以是 本地 可以是远程\n */\n swaggerJSONPath: string;\n /**\n * 用在生成代码里快捷连接跳转到 swagger ui\n * 必须携带 urls.primaryName 参数\n * @example http://192.168.104.10:31082/swagger/?urls.primaryName=%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%E7%9B%B8%E5%85%B3API\n */\n swaggerUiUrl?: string;\n /**\n * 输出的文件夹路径\n * @default \"./src/apis\"\n */\n output?: string;\n /**\n * 配置`requestFn` 方法引入的路径,生成的代码里会用到,生成代码如下\n * 引入的名称必须是`requestFn`\n * ```ts\n * // 需要命名为 requestFn\n * import requestFn from '@/utils/request'\n * import { foo as requestFn } from '@/utils/request'\n * ```\n * @default `import requestFn from '@/utils/request'`\n */\n requestFnImportPath?: string;\n}\n\nexport interface Config {\n i18n?: I18nConfig[];\n api?: ApiConfig[];\n /**\n * 默认是 命令运行的目录,一般是项目根目录\n * @internal\n */\n cwd?: string;\n}\nexport const configName = \"dune.config.js\";\n\nexport async function getConfig(): Promise<Config> {\n const res = await joycon.load([configName]);\n\n const defaultI18nConfig: I18nConfig = {\n i18nDir: \"./src/i18n\",\n i18nFileName: \"{locale}.i18n.json\",\n position: { key: \"B\", zh: \"C\", en: \"D\", in: \"E\" },\n parseStartIndex: 2,\n };\n\n let config = (res.data ?? {}) as Config;\n config.cwd ??= process.cwd();\n\n //#region i18n 配置标准化\n if (!config.i18n || !config.i18n?.length) {\n config.i18n = [defaultI18nConfig];\n }\n\n // 格式化 i18n 配置\n config.i18n = config.i18n.map((item) => {\n item = Object.assign({}, defaultI18nConfig, item);\n item.cwd ??= config.cwd;\n item.locales = Object.keys(item.position!).filter((item) => item !== \"key\");\n item.sheetId = resolveSheetId(item.sheetId);\n item.keySorter = (a, b) => {\n return a.localeCompare(b);\n };\n return item;\n });\n //#endregion\n\n //#region api 配置标准化\n config.api ??= [];\n config.api = config.api.map((item) => {\n item.output ??= \"./src/apis\";\n item.requestFnImportPath ??= `import requestFn from '@/utils/request';`;\n return item;\n });\n //#endregion\n\n return config;\n}\n","/**\n * input:\n * \"https://docs.google.com/spreadsheets/d/1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk/edit#gid=1740568548\"\n * output:\n * \"1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk\"\n * --\n * input: \"1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk\"\n * output: \"1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk\"\n */\nimport { match } from \"path-to-regexp\";\n\nexport function resolveSheetId(str?: string) {\n if (!str || !str?.startsWith(\"http\")) {\n return str;\n }\n const url = new URL(str);\n const matched = match<{ id: string[] }>(\"/spreadsheets/d/:id*\", {\n decode: decodeURIComponent,\n })(url.pathname);\n return matched ? matched.params.id?.[0] : str;\n}\n"],"mappings":";AAAA,OAAO,YAAY;;;ACSnB,SAAS,aAAa;AAEf,SAAS,eAAe,KAAc;AAX7C;AAYE,MAAI,CAAC,OAAO,EAAC,2BAAK,WAAW,UAAS;AACpC,WAAO;AAAA,EACT;AACA,QAAM,MAAM,IAAI,IAAI,GAAG;AACvB,QAAM,UAAU,MAAwB,wBAAwB;AAAA,IAC9D,QAAQ;AAAA,EACV,CAAC,EAAE,IAAI,QAAQ;AACf,SAAO,WAAU,aAAQ,OAAO,OAAf,mBAAoB,KAAK;AAC5C;;;ADjBA,IAAM,SAAS,IAAI,OAAO;AA0FnB,IAAM,aAAa;AAE1B,eAAsB,YAA6B;AA/FnD;AAgGE,QAAM,MAAM,MAAM,OAAO,KAAK,CAAC,UAAU,CAAC;AAE1C,QAAM,oBAAgC;AAAA,IACpC,SAAS;AAAA,IACT,cAAc;AAAA,IACd,UAAU,EAAE,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI;AAAA,IAChD,iBAAiB;AAAA,EACnB;AAEA,MAAI,SAAU,IAAI,QAAQ,CAAC;AAC3B,SAAO,QAAQ,QAAQ,IAAI;AAG3B,MAAI,CAAC,OAAO,QAAQ,GAAC,YAAO,SAAP,mBAAa,SAAQ;AACxC,WAAO,OAAO,CAAC,iBAAiB;AAAA,EAClC;AAGA,SAAO,OAAO,OAAO,KAAK,IAAI,CAAC,SAAS;AACtC,WAAO,OAAO,OAAO,CAAC,GAAG,mBAAmB,IAAI;AAChD,SAAK,QAAQ,OAAO;AACpB,SAAK,UAAU,OAAO,KAAK,KAAK,QAAS,EAAE,OAAO,CAACA,UAASA,UAAS,KAAK;AAC1E,SAAK,UAAU,eAAe,KAAK,OAAO;AAC1C,SAAK,YAAY,CAAC,GAAG,MAAM;AACzB,aAAO,EAAE,cAAc,CAAC;AAAA,IAC1B;AACA,WAAO;AAAA,EACT,CAAC;AAID,SAAO,QAAQ,CAAC;AAChB,SAAO,MAAM,OAAO,IAAI,IAAI,CAAC,SAAS;AACpC,SAAK,WAAW;AAChB,SAAK,wBAAwB;AAC7B,WAAO;AAAA,EACT,CAAC;AAGD,SAAO;AACT;","names":["item"]}
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  configName,
4
4
  getConfig
5
- } from "./chunk-BEIVJU3S.js";
5
+ } from "./chunk-UPESJ46X.js";
6
6
 
7
7
  // src/commands/extract.ts
8
8
  import fs3 from "fs-extra";
@@ -215,7 +215,7 @@ async function extract() {
215
215
  }
216
216
  }
217
217
 
218
- // src/commands/generate.ts
218
+ // src/commands/download.ts
219
219
  import pMap3 from "p-map";
220
220
 
221
221
  // src/shared/google/sheet.ts
@@ -511,9 +511,9 @@ var SheetData = class {
511
511
  }
512
512
  };
513
513
 
514
- // src/commands/generate.ts
514
+ // src/commands/download.ts
515
515
  var log7 = createLogger("generate");
516
- async function generate() {
516
+ async function download() {
517
517
  const config = await getConfig();
518
518
  let i = 0;
519
519
  for (const configItem of config.i18n ?? []) {
@@ -536,12 +536,156 @@ async function generate() {
536
536
  }
537
537
  }
538
538
 
539
- // src/commands/initConfig.ts
539
+ // src/commands/generateApi/index.ts
540
+ import _ from "lodash";
541
+ import pMap4 from "p-map";
540
542
  import fs5 from "fs-extra";
543
+ import * as os from "os";
544
+ import SwaggerParser from "@apidevtools/swagger-parser";
545
+ import { compile } from "json-schema-to-typescript";
541
546
  import path3 from "path";
542
- var log8 = createLogger("initConfig");
543
- var tpl = `
544
- /**
547
+ var log8 = createLogger("generateApi");
548
+ async function generateApi() {
549
+ const config = await getConfig();
550
+ for (const apiConfig of config.api ?? []) {
551
+ log8.info(`\u6E05\u9664\u65E7\u7684api\u6587\u4EF6: ${apiConfig.output}`);
552
+ await fs5.emptydir(apiConfig.output);
553
+ }
554
+ for (const apiConfig of config.api ?? []) {
555
+ log8.info("\u5F00\u59CB\u89E3\u6790 %s", apiConfig.swaggerJSONPath);
556
+ const parsed = await SwaggerParser.dereference(
557
+ apiConfig.swaggerJSONPath
558
+ );
559
+ await pMap4(Object.entries(parsed.paths), async ([url, pathItemObject]) => {
560
+ log8.info("\u5F00\u59CB\u751F\u6210 %s", url);
561
+ if (pathItemObject) {
562
+ await pMap4(
563
+ ["get", "put", "post", "delete", "patch"],
564
+ async (method) => {
565
+ const operationObject = pathItemObject[method];
566
+ if (operationObject) {
567
+ const code = await generateApiRequestCode({
568
+ url,
569
+ method,
570
+ operationObject,
571
+ apiConfig
572
+ });
573
+ const outputPath = path3.join(
574
+ apiConfig.output,
575
+ url,
576
+ `${method}.ts`
577
+ );
578
+ await fs5.ensureFile(outputPath);
579
+ await fs5.writeFile(outputPath, code);
580
+ }
581
+ }
582
+ );
583
+ }
584
+ });
585
+ }
586
+ log8.info("generateApi Done");
587
+ }
588
+ async function generateApiRequestCode(options) {
589
+ var _a, _b;
590
+ const { url, method, operationObject, apiConfig } = options;
591
+ const seeUrl = apiConfig.swaggerUiUrl ? `${apiConfig.swaggerUiUrl}#/${encodeURIComponent(
592
+ ((_a = operationObject.tags) == null ? void 0 : _a.join("/")) ?? ""
593
+ )}/${operationObject.operationId}` : "not found swagger ui url";
594
+ const requestBuilderName = _.camelCase(`${url}_${method}_api`);
595
+ const urlPathParams = getUrlPathParams(
596
+ operationObject.parameters ?? []
597
+ );
598
+ const urlPathParamsCode = urlPathParams.length ? `urlPathParams: ${JSON.stringify(urlPathParams)},` : "";
599
+ let code = [
600
+ "// \u8FD9\u4E2A\u6587\u4EF6\u7531 @dune2/cli \u81EA\u52A8\u751F\u6210\uFF0C\u4E0D\u8981\u624B\u52A8\u4FEE\u6539\uFF0C\u5426\u5219\u4F1A\u88AB\u8986\u76D6",
601
+ `import { RequestBuilder } from '@dune2/tools';`,
602
+ apiConfig.requestFnImportPath,
603
+ `/**
604
+ * ${operationObject.summary}
605
+ * @tags ${(_b = operationObject.tags) == null ? void 0 : _b.join(",")}
606
+ * @see ${seeUrl}
607
+ */`
608
+ ];
609
+ code.push(`export const ${requestBuilderName} = new RequestBuilder<${requestBuilderName}.Req, ${requestBuilderName}.Res>({
610
+ url: '${url}',
611
+ method: '${method}',
612
+ requestFn,
613
+ ${urlPathParamsCode}
614
+ });`);
615
+ const [requestParamsTypeCode, responseParamsTypeCode] = await Promise.all([
616
+ compileRequestParams(operationObject),
617
+ compileResponseParams(operationObject)
618
+ ]);
619
+ code.push(`
620
+ export namespace ${requestBuilderName} {
621
+ ${requestParamsTypeCode}
622
+
623
+ ${responseParamsTypeCode}
624
+ };`);
625
+ return code.join(os.EOL);
626
+ }
627
+ function getUrlPathParams(parameters) {
628
+ return parameters.filter((item) => item.in === "path").map((item) => item.name);
629
+ }
630
+ async function compileRequestParams(operationObject) {
631
+ let schema;
632
+ if (operationObject.parameters) {
633
+ const parameters = operationObject.parameters.filter(
634
+ (p) => p.in === "query" || p.in === "path"
635
+ ) ?? [];
636
+ const required = parameters.filter((p) => p.required).map((p) => p.name);
637
+ const properties = Object.fromEntries(
638
+ parameters.map((p) => {
639
+ const schema2 = p.schema;
640
+ return [
641
+ p.name,
642
+ {
643
+ type: schema2.type,
644
+ description: p.description
645
+ }
646
+ ];
647
+ })
648
+ );
649
+ schema = {
650
+ required,
651
+ type: "object",
652
+ properties
653
+ };
654
+ } else if (operationObject.requestBody) {
655
+ schema = operationObject.requestBody.content["application/json"].schema;
656
+ }
657
+ let code = "";
658
+ if (schema) {
659
+ code = await compile(schema, "Req", {
660
+ bannerComment: "",
661
+ ignoreMinAndMaxItems: true
662
+ });
663
+ }
664
+ return code ? code : "export type Req = any;";
665
+ }
666
+ async function compileResponseParams(operationObject) {
667
+ var _a;
668
+ const temp = operationObject.responses["200"];
669
+ let code = "";
670
+ if (temp == null ? void 0 : temp.content) {
671
+ const temp2 = temp.content["application/json"] || temp.content["*/*"];
672
+ const schema = temp2.schema;
673
+ const data = ((_a = schema.properties) == null ? void 0 : _a.data) ?? schema;
674
+ if (data) {
675
+ code = await compile(data, "Res", {
676
+ bannerComment: "",
677
+ ignoreMinAndMaxItems: true
678
+ });
679
+ }
680
+ }
681
+ return code ? code : "export type Res = any;";
682
+ }
683
+
684
+ // src/commands/initConfig.ts
685
+ import fs6 from "fs-extra";
686
+ import path4 from "path";
687
+ var log9 = createLogger("initConfig");
688
+ var tpl = `/**
545
689
  * @template {import('@dune2/cli').Config} T
546
690
  * @param {T} config - A generic parameter that flows through to the return type
547
691
  * @constraint {{import('@dune2/cli').Config}}
@@ -549,28 +693,28 @@ var tpl = `
549
693
  function defineConfig(config) {
550
694
  return config;
551
695
  }
552
- module.exports = defineConfig({ i18n: [] });
696
+ module.exports = defineConfig({ i18n: [], api:[] });
553
697
  `;
554
698
  var initConfig = async () => {
555
699
  const config = await getConfig();
556
- const configPath = path3.join(config[0].cwd, configName);
557
- log8.info(`config file path: ${configPath}`);
558
- if (await fs5.pathExists(configPath)) {
559
- log8.info(`config file already exists, skip`);
700
+ const configPath = path4.join(config.cwd, configName);
701
+ log9.info(`config file path: ${configPath}`);
702
+ if (await fs6.pathExists(configPath)) {
703
+ log9.info(`config file already exists, skip`);
560
704
  return;
561
705
  }
562
- await fs5.writeFile(configPath, tpl);
563
- log8.info(`config file created`);
706
+ await fs6.writeFile(configPath, tpl);
707
+ log9.info(`config file created`);
564
708
  };
565
709
 
566
710
  // src/commands/upload.ts
567
- import pMap4 from "p-map";
568
- var log9 = createLogger("upload");
711
+ import pMap5 from "p-map";
712
+ var log10 = createLogger("upload");
569
713
  async function batchUpdateKeys(config) {
570
714
  let sheetData = await resolveSheetData(config);
571
715
  const allKeys = [];
572
716
  const i18nDataMap = /* @__PURE__ */ new Map();
573
- await pMap4(config.locales ?? [], async (locale) => {
717
+ await pMap5(config.locales ?? [], async (locale) => {
574
718
  const i18nData = new I18nData(locale, config);
575
719
  i18nDataMap.set(locale, i18nData);
576
720
  const data = await i18nData.loadData();
@@ -581,7 +725,7 @@ async function batchUpdateKeys(config) {
581
725
  });
582
726
  needSyncKeys = needSyncKeys.sort(config.keySorter);
583
727
  if (needSyncKeys.length) {
584
- log9.info(`\u5373\u5C06\u540C\u6B65\u672C\u5730\u65B0\u589E\u52A0 ${needSyncKeys.length} \u4E2A i18nKey \u5230\u4E91\u7AEF`);
728
+ log10.info(`\u5373\u5C06\u540C\u6B65\u672C\u5730\u65B0\u589E\u52A0 ${needSyncKeys.length} \u4E2A i18nKey \u5230\u4E91\u7AEF`);
585
729
  await googleSheet.updateColumn(
586
730
  config.sheetId,
587
731
  config.sheetRange,
@@ -589,7 +733,7 @@ async function batchUpdateKeys(config) {
589
733
  sheetData.nextAppendRowIndex,
590
734
  needSyncKeys
591
735
  );
592
- log9.info("\u540C\u6B65\u65B0\u589E\u52A0i18nKey\u6210\u529F");
736
+ log10.info("\u540C\u6B65\u65B0\u589E\u52A0i18nKey\u6210\u529F");
593
737
  for (const lang of config.locales) {
594
738
  const langFile = i18nDataMap.get(lang);
595
739
  const needSyncValues = [];
@@ -598,7 +742,7 @@ async function batchUpdateKeys(config) {
598
742
  needSyncValues.push(langFile.data[k]);
599
743
  }
600
744
  });
601
- log9.info(
745
+ log10.info(
602
746
  `\u5373\u5C06\u540C\u6B65\u672C\u5730 ${lang} \u65B0\u589E\u52A0 ${needSyncValues.length} \u4E2A\u503C\u5230\u4E91\u7AEF`
603
747
  );
604
748
  await googleSheet.updateColumn(
@@ -611,7 +755,7 @@ async function batchUpdateKeys(config) {
611
755
  }
612
756
  await sleep(1e3);
613
757
  sheetData = await resolveSheetData(config);
614
- log9.info(`\u5DF2\u91CD\u65B0\u83B7\u53D6 ${config.sheetRange} \u7684\u6570\u636E`);
758
+ log10.info(`\u5DF2\u91CD\u65B0\u83B7\u53D6 ${config.sheetRange} \u7684\u6570\u636E`);
615
759
  }
616
760
  return { sheetData, i18nDataMap };
617
761
  }
@@ -624,20 +768,20 @@ async function upload() {
624
768
  const config = await getConfig();
625
769
  for (const configItem of config.i18n ?? []) {
626
770
  const { sheetData, i18nDataMap } = await batchUpdateKeys(configItem);
627
- await pMap4(configItem.locales ?? [], async (locale) => {
771
+ await pMap5(configItem.locales ?? [], async (locale) => {
628
772
  const i18nData = i18nDataMap.get(locale);
629
773
  await i18nData.trySaveToGoogle(sheetData);
630
774
  });
631
775
  }
632
- log9.info("\u4E0A\u4F20\u6210\u529F");
776
+ log10.info("\u4E0A\u4F20\u6210\u529F");
633
777
  }
634
778
 
635
779
  // package.json
636
- var version = "0.0.4";
780
+ var version = "0.0.5";
637
781
 
638
782
  // src/commands/interactive.ts
639
783
  import prompts from "prompts";
640
- var log10 = createLogger("interactive");
784
+ var log11 = createLogger("interactive");
641
785
  var interactive = async (args) => {
642
786
  var _a;
643
787
  const commands = cli.commands.filter((command2) => {
@@ -648,7 +792,7 @@ var interactive = async (args) => {
648
792
  });
649
793
  const commandMap = /* @__PURE__ */ new Map();
650
794
  const res = await prompts({
651
- type: "select",
795
+ type: "autocomplete",
652
796
  name: "command",
653
797
  message: "\u9009\u62E9\u8981\u6267\u884C\u7684\u547D\u4EE4\uFF1A",
654
798
  choices: commands.map((command2) => {
@@ -662,16 +806,17 @@ var interactive = async (args) => {
662
806
  });
663
807
  const command = commandMap.get(res.command);
664
808
  if (!command) {
665
- log10.error("\u672A\u627E\u5230\u547D\u4EE4 %s", res.command);
809
+ log11.error("\u672A\u627E\u5230\u547D\u4EE4 %s", res.command);
666
810
  return;
667
811
  }
668
812
  (_a = command.commandAction) == null ? void 0 : _a.apply(cli, args);
669
813
  };
670
814
 
671
815
  // src/cli.ts
672
- cli.command("generate", "\u751F\u6210\u7FFB\u8BD1\u6587\u4EF6").alias("gen").example("dune generate").example("dune gen").action(generate);
673
- cli.command("upload", "\u4E0A\u4F20\u7FFB\u8BD1\u6587\u4EF6").example("dune upload").action(upload);
816
+ cli.command("download", "\u751F\u6210\u7FFB\u8BD1\u6587\u4EF6").example("dune download").action(download);
674
817
  cli.command("extract", "\u63D0\u53D6\u4EE3\u7801\u4E2D\u7684\u6587\u6848").example("dune extract").action(extract);
818
+ cli.command("upload", "\u4E0A\u4F20\u7FFB\u8BD1\u6587\u4EF6").example("dune upload").action(upload);
819
+ cli.command("generateApi", "\u751F\u6210api\u6587\u4EF6").example("dune generateApi").action(generateApi);
675
820
  cli.command("init", "\u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6").example("dune init").action(initConfig);
676
821
  cli.command("interactive", "\u4EA4\u4E92\u5F0F\u64CD\u4F5C").example("dune interactive").alias("i").action(interactive);
677
822
  cli.command("").action(interactive);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/extract.ts","../src/shared/index.ts","../src/shared/i18nData.ts","../src/commands/generate.ts","../src/shared/google/sheet.ts","../src/shared/letters.ts","../src/shared/google/auth.ts","../src/shared/google/shared.ts","../src/shared/resolveSheetData.ts","../src/commands/initConfig.ts","../src/commands/upload.ts","../src/commands/interactive.ts","../src/cli.ts"],"sourcesContent":["import fs from \"fs-extra\";\nimport { globby } from \"globby\";\nimport pMap from \"p-map\";\nimport { createLogger } from \"../shared\";\nimport { getConfig } from \"../shared/config\";\nimport { I18nData, ExtractedMap } from \"../shared/i18nData\";\nimport pc from \"picocolors\";\n\nconst log = createLogger(\"extract\");\n\nexport async function extract() {\n const config = await getConfig();\n const { extract } = await import(\"@dune2/wasm\");\n\n // 这是使得任务串行,方便看日志\n for (const configItem of config.i18n ?? []) {\n const files = await globby(\n [`**/**.{js,jsx,ts,tsx}`, \"!**/node_modules/**\", \"!**.d.ts\"],\n {\n cwd: configItem.cwd,\n }\n );\n log.info(\"预计共解析 %s 个文件\", pc.green(files.length));\n const extractedI18nDataMap: ExtractedMap = new Map();\n await pMap(\n files,\n async (file) => {\n const content = await fs.readFile(file, \"utf-8\");\n const res: ExtractedMap = await extract(content);\n log.info(\n \"从 %s 中提取到 %s 条文案\",\n pc.dim(file),\n res.size ? pc.green(res.size) : res.size\n );\n res.forEach((value, key) => {\n const cur = extractedI18nDataMap.get(key);\n // 优先保留有 messages 的\n if (!cur?.messages) {\n extractedI18nDataMap.set(key, value);\n }\n });\n },\n { concurrency: 20 }\n );\n log.info(\n \"总统提取到 %s 条文案 %O\",\n pc.green(extractedI18nDataMap.size),\n extractedI18nDataMap\n );\n\n const statistics = await pMap(\n configItem.locales ?? [],\n async (locale, index) => {\n const i18nData = new I18nData(locale, configItem);\n await i18nData.updateByExtractedData(extractedI18nDataMap, index === 0);\n await i18nData.saveToDisk();\n return await i18nData.statistic();\n }\n );\n I18nData.printStatistic(\"提取结果: \", statistics);\n }\n}\n","import cac from \"cac\";\nimport debug from \"debug\";\nimport path from \"path\";\nimport fs from \"fs-extra\";\n\nexport const cli = cac(\"dune\");\n\nexport const createLogger = (name: string) => {\n const info = debug(`${cli.name}:${name}`);\n return {\n ...info,\n info,\n error: info.extend(\"error\"),\n };\n};\n\ndebug.enable(`${cli.name}:*`);\n\n/**\n * same with chacheDir\n * homeDir();\n * //=> '~/name'\n *\n * homeDir('bar.js')\n * //=> '~/name/bar.js'\n */\n// eslint-disable-next-line @typescript-eslint/explicit-function-return-type\nexport const homeConfigDir = (() => {\n const isWin = process.platform === \"win32\";\n const USER_HOME_DIR = isWin ? process.env.USERPROFILE : process.env.HOME;\n const configDir = path.join(USER_HOME_DIR!, \".config\", cli.name);\n\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir);\n }\n return homeConfigDir;\n\n function homeConfigDir(p?: string): string {\n if (!p) {\n return configDir;\n }\n return path.join(configDir, p);\n }\n})();\n","import pMap from \"p-map\";\nimport { I18nConfig } from \"./config\";\nimport { createLogger } from \"./index\";\nimport fs from \"fs-extra\";\nimport pc from \"picocolors\";\nimport path from \"path\";\nimport Table from \"cli-table3\";\nimport { SheetData } from \"./resolveSheetData\";\n\nconst log = createLogger(\"i18nData\");\nexport type ExtractedMap = Map<string, { id: string; messages: string }>;\n\nexport class I18nData {\n data: Record<string, string> = {};\n\n filePath: string;\n\n constructor(public locale: string, public config: I18nConfig) {\n this.filePath = path.join(\n config.cwd!,\n config.i18nDir!,\n config.i18nFileName!.replace(\"{locale}\", locale)\n );\n }\n\n async loadData() {\n try {\n if (await fs.pathExists(this.filePath)) {\n this.data = await fs.readJSON(this.filePath);\n } else {\n log.info(\"File '%s' does not exist\", pc.dim(this.filePath));\n }\n } catch (e) {\n log.error(\n \"Error loading i18n data for locale '%s' from file '%s'\",\n pc.yellow(this.locale),\n pc.yellow(this.filePath)\n );\n log.error(\"Error: %s\", e.message);\n }\n return this.data;\n }\n\n async updateByExtractedData(\n extractedData: ExtractedMap,\n shouldUseDefault = false\n ) {\n await this.loadData();\n extractedData.forEach((value, key) => {\n this.data[key] = shouldUseDefault\n ? value.messages || key\n : this.data[key] || \"\";\n });\n }\n\n async updateFromSheetData(sheetData: Record<string, string>) {\n await this.loadData();\n this.data = { ...this.data, ...sheetData };\n }\n\n private sortData() {\n //region 语言key按一定规则排序\n const keys = Object.keys(this.data).sort(this.config.keySorter);\n const newData = {};\n keys.forEach((key) => {\n newData[key] = this.data[key];\n });\n this.data = newData;\n //endregion\n }\n\n async saveToDisk() {\n this.sortData();\n log.info(\n \"Saving i18n data for locale '%s' to file '%s'\",\n pc.green(this.locale),\n pc.dim(this.filePath)\n );\n try {\n await fs.ensureFile(this.filePath);\n await fs.writeJson(this.filePath, this.data, { spaces: 2 });\n log.info(\n \"Saved i18n data for locale '%s' to file '%s'\",\n pc.green(this.locale),\n pc.dim(this.filePath)\n );\n } catch (e) {\n log.error(\n \"Error saving i18n data for locale '%s' to file '%s'\",\n pc.green(this.locale),\n pc.dim(this.filePath)\n );\n log.error(\"Error: %s\", pc.red(e.message));\n }\n }\n\n async statistic() {\n const total = Object.keys(this.data).length;\n const missing = Object.values(this.data).filter((v) => !v).length;\n\n return {\n locale: this.locale,\n total,\n missing,\n };\n }\n\n async trySaveToGoogle(sheetData: SheetData) {\n await pMap(Object.entries(this.data), async ([key, value]) => {\n if (sheetData.hasI18nItem(this.locale, key)) {\n // 更新已有的\n await sheetData.getI18nItem(this.locale, key)?.tryUpdate(value);\n } else {\n log.error(\n \"%s range %s not found key %s\",\n pc.bold(this.config.sheetRange),\n pc.bold(this.locale),\n pc.bold(key)\n );\n }\n });\n }\n\n static printStatistic(\n label: string,\n statistics: { locale: string; total: number; missing: number }[]\n ) {\n const table = new Table({\n head: [\"Language\", \"Total count\", \"Missing\"],\n colAligns: [\"left\", \"center\", \"center\"],\n style: {\n head: [\"green\"],\n border: [],\n compact: true,\n },\n });\n statistics.forEach((statistic) => {\n table.push([\n statistic.locale,\n statistic.total,\n statistic.missing > 0 ? pc.red(statistic.missing) : statistic.missing,\n ]);\n });\n console.log(pc.bold(label));\n console.log(table.toString());\n }\n}\n","import pMap from \"p-map\";\nimport { createLogger } from \"../shared\";\nimport { getConfig } from \"../shared/config\";\nimport { I18nData } from \"../shared/i18nData\";\nimport { resolveSheetData } from \"../shared/resolveSheetData\";\n\nconst log = createLogger(\"generate\");\n\nexport async function generate() {\n const config = await getConfig();\n let i = 0;\n for (const configItem of config.i18n ?? []) {\n i++;\n if (!configItem.sheetId || !configItem.sheetRange) {\n log.error(`config.${i} sheetId 或 sheetRange 为空,请检查`);\n continue;\n }\n const sheetData = await resolveSheetData(configItem);\n\n const statistics = await pMap(\n configItem.locales ?? [],\n async (locale, index) => {\n const i18nData = new I18nData(locale, configItem);\n await i18nData.updateFromSheetData(sheetData.getLangDataJSON(locale));\n await i18nData.saveToDisk();\n return await i18nData.statistic();\n }\n );\n I18nData.printStatistic(`${configItem.sheetRange} 生成结果: `, statistics);\n }\n}\n","import { GaxiosResponse } from \"gaxios\";\nimport type { sheets_v4 } from \"googleapis\";\nimport { google } from \"googleapis\";\nimport { createLogger } from \"../index\";\nimport { numberToLetter } from \"../letters\";\nimport { googleAuth } from \"./auth\";\n\nconst log = createLogger(\"google:sheets\");\n\nclass GoogleSheet {\n private sheets: sheets_v4.Sheets;\n\n private initPromise: Promise<void> | null = null;\n private async initImpl() {\n if (!this.sheets) {\n await googleAuth.initCredentials();\n this.sheets = google.sheets({\n version: \"v4\",\n auth: googleAuth.oauth2Client,\n });\n }\n }\n /**\n * 调用sheets其他方法的时候需要先初始化\n */\n async init() {\n if (!this.initPromise) {\n this.initPromise = this.initImpl();\n }\n await this.initPromise;\n }\n\n get = async (options: sheets_v4.Params$Resource$Spreadsheets$Values$Get) => {\n await this.init();\n return this.sheets.spreadsheets.values.get(options);\n };\n\n private async update(\n spreadsheetId: string,\n range: string,\n colIndex: number,\n rowIndex: number,\n values: string[][]\n ): Promise<GaxiosResponse<sheets_v4.Schema$UpdateValuesResponse>> {\n await this.init();\n\n const colLetter = numberToLetter(colIndex);\n const sheetNeedRange = `${range}!${colLetter}${rowIndex}`;\n\n const res = await this.sheets.spreadsheets.values.update({\n spreadsheetId,\n range: sheetNeedRange,\n valueInputOption: \"USER_ENTERED\",\n requestBody: { values },\n });\n\n if (res.status === 200) {\n log.info(`更新 ${sheetNeedRange} 的值成功`);\n } else {\n log.error(\"some thing wrong\", res);\n }\n return res;\n }\n\n /**\n * 更新某一个单元格\n */\n async updateCell(\n spreadsheetId: string,\n range: string,\n colIndex: number,\n rowIndex: number,\n values: string\n ) {\n return this.update(spreadsheetId, range, colIndex, rowIndex, [[values]]);\n }\n\n /**\n * 更新某一列\n */\n async updateColumn(\n spreadsheetId: string,\n range: string,\n colIndex: number,\n rowIndex: number,\n values: string[]\n ) {\n return this.update(spreadsheetId, range, colIndex, rowIndex, [\n ...values.map((v) => [v]),\n ]);\n }\n}\n\nexport const googleSheet = new GoogleSheet();\n","/**\n *\n * - input : A\n * output : 0\n * - input : Z\n * output : 25\n * - input : AA\n * output : 26\n * - input : a\n * output : 0\n */\nexport function letterToNumber(letters: string | number) {\n if (typeof letters === \"number\" || !Number.isNaN(+letters)) {\n return +letters;\n }\n\n let n = 0;\n letters = letters.toUpperCase();\n for (let p = 0; p < letters.length; p++) {\n n = letters[p].charCodeAt(0) - 64 + n * 26;\n }\n\n return Math.max(0, n - 1);\n}\n\n/**\n * fork from https://github.com/matthewmueller/number-to-letter\n */\nconst alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\nconst base = alphabet.length;\n/**\n * numberToLetter(0) // A\n * numberToLetter(25) // Z\n * numberToLetter(26) // AA\n * numberToLetter(51) // AZ\n * numberToLetter(52) // BA\n * numberToLetter(676) // ZA\n * numberToLetter(701) // ZZ\n * numberToLetter(702) // AAA\n */\nexport function numberToLetter(n: number) {\n const digits: number[] = [];\n\n do {\n const v = n % base;\n digits.push(v);\n n = Math.floor(n / base);\n } while (n-- > 0);\n\n const chars: string[] = [];\n while (digits.length) {\n chars.push(alphabet[digits.pop()!]);\n }\n\n return chars.join(\"\");\n}\n","import fs from \"fs-extra\";\nimport { OAuth2Client } from \"google-auth-library\";\nimport { google } from \"googleapis\";\nimport { homeConfigDir, createLogger } from \"../index\";\nimport { openAndWaitReturnQuery } from \"./shared\";\n\ninterface AuthOptions {\n client_id: string;\n client_secret: string;\n /**\n * @default ['spreadsheets']\n */\n scope?: string[];\n}\n\nconst log = createLogger(\"google:auth\");\n\nclass GoogleAuth {\n private tokens: any = null;\n\n private tokensCachePath = homeConfigDir(\"chromeAuthToken.json\");\n\n private readonly scope: string[];\n\n oauth2Client: OAuth2Client = new google.auth.OAuth2(\n this.options.client_id,\n this.options.client_secret,\n \"http://localhost:12345\"\n );\n\n constructor(private options: AuthOptions) {\n this.options.scope = this.options.scope ?? [\"spreadsheets\"];\n const scopePrefix = `https://www.googleapis.com/auth/`;\n this.scope = this.options.scope.map((item) => {\n if (item.startsWith(scopePrefix)) {\n return item;\n }\n return `${scopePrefix}${item}`;\n });\n\n try {\n this.tokens = fs.readJsonSync(this.tokensCachePath);\n } catch (e) {\n log.info(\"未获取到缓存的token\");\n this.tokens = null;\n }\n }\n\n private async saveTokens(tokens: any): Promise<void> {\n this.tokens = tokens;\n await fs.writeJSON(this.tokensCachePath, tokens);\n log.info(\"保存token成功\");\n }\n\n async getCode(): Promise<string> {\n log.info(\"将打开chrome浏览器进行授权\");\n const authUrl = this.oauth2Client.generateAuthUrl({\n access_type: \"offline\",\n scope: this.scope,\n });\n return await openAndWaitReturnQuery(authUrl, 12345, \"code\");\n }\n\n private async initCredentialsImpl(): Promise<void> {\n if (!this.tokens) {\n const code = await this.getCode();\n const { tokens } = await this.oauth2Client.getToken(code);\n await this.saveTokens(tokens);\n }\n this.oauth2Client.setCredentials(this.tokens);\n }\n private initCredentialsPromise: Promise<void> | null = null;\n async initCredentials() {\n if (!this.initCredentialsPromise) {\n this.initCredentialsPromise = this.initCredentialsImpl();\n }\n return await this.initCredentialsPromise;\n }\n}\n\n// see in https://console.cloud.google.com/apis/credentials?organizationId=286245507762&orgonly=true&project=dune-cli&supportedpurview=organizationId\nconst clientSecret = {\n client_id:\n \"728508473485-fgl11a3op1ucd0lef9hs49mkliuaipqa.apps.googleusercontent.com\",\n project_id: \"dune-cli\",\n auth_uri: \"https://accounts.google.com/o/oauth2/auth\",\n token_uri: \"https://oauth2.googleapis.com/token\",\n auth_provider_x509_cert_url: \"https://www.googleapis.com/oauth2/v1/certs\",\n client_secret: \"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\",\n redirect_uris: [\"http://localhost\"],\n};\nexport const googleAuth = new GoogleAuth(clientSecret);\n","import connect from \"connect\";\nimport open from \"open\";\nimport { URL } from \"url\";\nimport { createLogger } from \"../index\";\n\nconst log = createLogger(\"google:shared\");\n\nfunction openBrowser(url: string) {\n log.info(`如未自动打开浏览器,可访问以下地址:${url}`);\n open(url, { app: { name: open.apps.chrome } });\n}\n\nexport function openAndWaitReturnQuery(\n toOpenUrl: string,\n port: number,\n queryKey: string\n) {\n return new Promise<string>((resolve) => {\n openBrowser(toOpenUrl);\n\n const app = connect();\n app.use((req, res, next) => {\n const parsedUrl = new URL(req.url, `http://localhost:${port}`);\n const target = parsedUrl.searchParams.get(queryKey);\n if (target) {\n res.writeHead(200, {\n \"content-type\": \"text/html;charset=utf8\",\n });\n res.end(\n `<div>当前窗口可直接关闭</div>\n <script>\n window.open(\"\", \"_self\", \"\");\n window.close();\n </script>`\n );\n server.close();\n server.unref();\n resolve(target);\n }\n next();\n });\n\n const server = app.listen(port);\n });\n}\n","import { I18nConfig } from \"./config\";\nimport { googleSheet } from \"./google/sheet\";\nimport { createLogger } from \"./index\";\nimport { letterToNumber } from \"./letters\";\n\nconst log = createLogger(\"shared:resolveSheetData\");\n\nclass SheetI18nItem {\n value: string;\n\n colIndex: number;\n\n constructor(\n private spreadsheetId: string,\n private range: string,\n public key: string,\n colIndex: number | string,\n public row: string[],\n private rowIndex: number\n ) {\n this.colIndex = letterToNumber(colIndex);\n // TODO: 待确定 无值的key是否要生成\n // value = undefined 即不会生成\n this.value = this.row[this.colIndex] || \"\";\n }\n\n /**\n * 尝试更新 单元格的值\n * 如果一样则不更新\n */\n async tryUpdate(newValue: string) {\n if (newValue === this.value) {\n // 云端的值跟本地一样 不需要同步\n return;\n }\n\n await googleSheet.updateCell(\n this.spreadsheetId,\n this.range,\n this.colIndex,\n this.rowIndex,\n newValue\n );\n }\n}\n\nexport async function resolveSheetData(config: I18nConfig): Promise<SheetData> {\n const { position } = config;\n const parseStartIndex = config.parseStartIndex ?? 2;\n\n log.info(`读取 ${config.sheetRange} sheet数据`);\n\n const res = await googleSheet.get({\n spreadsheetId: config.sheetId,\n range: config.sheetRange,\n });\n if (!res.data.values) {\n log.error(\n `${config.sheetRange} sheet数据为空,请检查,应该最少有一行头部数据`\n );\n return new SheetData(config, 2);\n }\n\n const sheetData = new SheetData(config, res.data.values.length + 1);\n\n res.data.values.forEach((row: string[], rowIndex) => {\n // excel 的开始索引是1,所以要 +1\n const trulyRowIndex = rowIndex + 1;\n if (parseStartIndex > trulyRowIndex) {\n // 还未到可以开始解析的行数\n return;\n }\n\n const i18nKey = row[letterToNumber(position!.key)]?.trim();\n\n if (i18nKey) {\n if (sheetData.keySet.has(i18nKey)) {\n log.error(\n `表格 ${config.sheetRange} 发现重复的i18nKey: ${i18nKey}, 将覆盖前面的值`\n );\n } else {\n sheetData.keySet.add(i18nKey);\n }\n\n config.locales?.forEach((lang) => {\n sheetData.setI18nItem(\n lang,\n new SheetI18nItem(\n config.sheetId!,\n config.sheetRange!,\n i18nKey,\n position![lang],\n row,\n trulyRowIndex\n )\n );\n });\n }\n });\n\n return sheetData;\n}\n\nexport class SheetData {\n keySet = new Set<string>();\n\n constructor(\n private config: I18nConfig,\n // 当前有多少行 新增的时候往后面添加\n public nextAppendRowIndex: number\n ) {}\n\n private value = new Map(\n this.config.locales!.map((lang) => {\n return [lang, new Map<string, SheetI18nItem>()];\n })\n );\n\n getI18nItem(lang: string, i18nKey: string) {\n return this.value.get(lang)!.get(i18nKey);\n }\n\n setI18nItem(lang: string, i18nItem: SheetI18nItem) {\n this.value.get(lang)!.set(i18nItem.key, i18nItem);\n }\n\n hasI18nItem(lang: string, i18nKey: string) {\n return this.value.get(lang)!.has(i18nKey);\n }\n\n getLangDataJSON(lang: string) {\n const res = {} as Record<string, string>;\n this.value.get(lang)!.forEach((i18nItem) => {\n res[i18nItem.key] = i18nItem.value;\n });\n return res;\n }\n}\n","import { createLogger } from \"../shared\";\nimport { getConfig, configName } from \"../shared/config\";\nimport fs from \"fs-extra\";\nimport path from \"path\";\n\nconst log = createLogger(\"initConfig\");\n\nconst tpl = `\n/**\n * @template {import('@dune2/cli').Config} T\n * @param {T} config - A generic parameter that flows through to the return type\n * @constraint {{import('@dune2/cli').Config}}\n */\nfunction defineConfig(config) {\n return config;\n}\nmodule.exports = defineConfig({ i18n: [] });\n`;\n\nexport const initConfig = async () => {\n const config = await getConfig();\n const configPath = path.join(config[0].cwd, configName);\n log.info(`config file path: ${configPath}`);\n if (await fs.pathExists(configPath)) {\n log.info(`config file already exists, skip`);\n return;\n }\n await fs.writeFile(configPath, tpl);\n log.info(`config file created`);\n};\n","import pMap from \"p-map\";\nimport { createLogger } from \"../shared\";\nimport { getConfig, I18nConfig } from \"../shared/config\";\nimport { googleSheet } from \"../shared/google/sheet\";\nimport { I18nData } from \"../shared/i18nData\";\nimport { letterToNumber } from \"../shared/letters\";\nimport { resolveSheetData } from \"../shared/resolveSheetData\";\n\nconst log = createLogger(\"upload\");\n\n/**\n * 检测有本地有新增的key 则将新增的key更新到云端\n * 无则跳过\n */\nasync function batchUpdateKeys(config: I18nConfig) {\n let sheetData = await resolveSheetData(config);\n\n const allKeys: string[] = [];\n const i18nDataMap = new Map<string, I18nData>();\n await pMap(config.locales ?? [], async (locale) => {\n const i18nData = new I18nData(locale, config);\n i18nDataMap.set(locale, i18nData);\n const data = await i18nData.loadData();\n allKeys.push(...Object.keys(data));\n });\n\n let needSyncKeys = Array.from(new Set(allKeys)).filter((key) => {\n // 本地有新的key 服务器没有\n return !sheetData.keySet.has(key);\n });\n needSyncKeys = needSyncKeys.sort(config.keySorter);\n\n if (needSyncKeys.length) {\n log.info(`即将同步本地新增加 ${needSyncKeys.length} 个 i18nKey 到云端`);\n await googleSheet.updateColumn(\n config.sheetId!,\n config.sheetRange!,\n letterToNumber(config.position!.key),\n sheetData.nextAppendRowIndex,\n needSyncKeys\n );\n log.info(\"同步新增加i18nKey成功\");\n\n // 批量同步新增的key的values到云端\n for (const lang of config.locales!) {\n const langFile = i18nDataMap.get(lang);\n const needSyncValues: string[] = [];\n needSyncKeys.forEach((k) => {\n if (langFile) {\n needSyncValues.push(langFile.data[k]);\n }\n });\n log.info(\n `即将同步本地 ${lang} 新增加 ${needSyncValues.length} 个值到云端`\n );\n await googleSheet.updateColumn(\n config.sheetId!,\n config.sheetRange!,\n letterToNumber(config.position![lang]),\n sheetData.nextAppendRowIndex,\n needSyncValues\n );\n }\n\n await sleep(1000);\n sheetData = await resolveSheetData(config);\n log.info(`已重新获取 ${config.sheetRange} 的数据`);\n }\n\n return { sheetData, i18nDataMap };\n}\nfunction sleep(ms: number) {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nexport async function upload() {\n const config = await getConfig();\n\n for (const configItem of config.i18n ?? []) {\n const { sheetData, i18nDataMap } = await batchUpdateKeys(configItem);\n await pMap(configItem.locales ?? [], async (locale) => {\n const i18nData = i18nDataMap.get(locale)!;\n await i18nData.trySaveToGoogle(sheetData);\n });\n }\n\n log.info(\"上传成功\");\n}\n","import { cli, createLogger } from \"../shared\";\nimport prompts from \"prompts\";\n\nconst log = createLogger(\"interactive\");\n\nexport const interactive = async (args: any) => {\n const commands = cli.commands.filter((command) => {\n if (command.name) {\n return !command.name.startsWith(\"@@\") && command.name !== \"interactive\";\n }\n return false;\n });\n\n const commandMap = new Map<string, typeof commands[number]>();\n const res = await prompts({\n type: \"select\",\n name: \"command\",\n message: \"选择要执行的命令:\",\n choices: commands.map((command) => {\n commandMap.set(command.name, command);\n return {\n title: command.name,\n value: command.name,\n description: command.description,\n };\n }),\n });\n\n const command = commandMap.get(res.command);\n if (!command) {\n log.error(\"未找到命令 %s\", res.command);\n return;\n }\n command.commandAction?.apply(cli, args);\n};\n","#!/usr/bin/env node\nimport { extract } from \"./commands/extract\";\nimport { generate } from \"./commands/generate\";\nimport { initConfig } from \"./commands/initConfig\";\nimport { upload } from \"./commands/upload\";\nimport { cli } from \"./shared\";\nimport { version } from \"../package.json\";\nimport { interactive } from \"./commands/interactive\";\n\ncli\n .command(\"generate\", \"生成翻译文件\")\n .alias(\"gen\")\n .example(\"dune generate\")\n .example(\"dune gen\")\n .action(generate);\n\ncli.command(\"upload\", \"上传翻译文件\").example(\"dune upload\").action(upload);\n\ncli\n .command(\"extract\", \"提取代码中的文案\")\n .example(\"dune extract\")\n .action(extract);\n\ncli.command(\"init\", \"初始化配置文件\").example(\"dune init\").action(initConfig);\n\ncli\n .command(\"interactive\", \"交互式操作\")\n .example(\"dune interactive\")\n .alias(\"i\")\n .action(interactive);\n// make default command run interactive\ncli.command(\"\").action(interactive);\n\ncli.version(version);\ncli.help();\n\n(async () => {\n try {\n // Parse CLI args without running the command\n cli.parse(process.argv, { run: false });\n // Run the command yourself\n // You only need `await` when your command action returns a Promise\n await cli.runMatchedCommand();\n } catch (error) {\n // Handle error here..\n // e.g.\n // console.error(error.stack)\n // process.exit(1)\n }\n})();\n"],"mappings":"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","names":["fs","pMap","homeConfigDir","fs","path","pc","log","extract","pMap","fs","pMap","google","fs","log","log","fs","log","google","log","log","pMap","fs","path","log","pMap","log","pMap","log","command"]}
1
+ {"version":3,"sources":["../src/commands/extract.ts","../src/shared/index.ts","../src/shared/i18nData.ts","../src/commands/download.ts","../src/shared/google/sheet.ts","../src/shared/letters.ts","../src/shared/google/auth.ts","../src/shared/google/shared.ts","../src/shared/resolveSheetData.ts","../src/commands/generateApi/index.ts","../src/commands/initConfig.ts","../src/commands/upload.ts","../src/commands/interactive.ts","../src/cli.ts"],"sourcesContent":["import fs from \"fs-extra\";\nimport { globby } from \"globby\";\nimport pMap from \"p-map\";\nimport { createLogger } from \"../shared\";\nimport { getConfig } from \"../shared/config\";\nimport { I18nData, ExtractedMap } from \"../shared/i18nData\";\nimport pc from \"picocolors\";\n\nconst log = createLogger(\"extract\");\n\nexport async function extract() {\n const config = await getConfig();\n const { extract } = await import(\"@dune2/wasm\");\n\n // 这是使得任务串行,方便看日志\n for (const configItem of config.i18n ?? []) {\n const files = await globby(\n [`**/**.{js,jsx,ts,tsx}`, \"!**/node_modules/**\", \"!**.d.ts\"],\n {\n cwd: configItem.cwd,\n }\n );\n log.info(\"预计共解析 %s 个文件\", pc.green(files.length));\n const extractedI18nDataMap: ExtractedMap = new Map();\n await pMap(\n files,\n async (file) => {\n const content = await fs.readFile(file, \"utf-8\");\n const res: ExtractedMap = await extract(content);\n log.info(\n \"从 %s 中提取到 %s 条文案\",\n pc.dim(file),\n res.size ? pc.green(res.size) : res.size\n );\n res.forEach((value, key) => {\n const cur = extractedI18nDataMap.get(key);\n // 优先保留有 messages 的\n if (!cur?.messages) {\n extractedI18nDataMap.set(key, value);\n }\n });\n },\n { concurrency: 20 }\n );\n log.info(\n \"总统提取到 %s 条文案 %O\",\n pc.green(extractedI18nDataMap.size),\n extractedI18nDataMap\n );\n\n const statistics = await pMap(\n configItem.locales ?? [],\n async (locale, index) => {\n const i18nData = new I18nData(locale, configItem);\n await i18nData.updateByExtractedData(extractedI18nDataMap, index === 0);\n await i18nData.saveToDisk();\n return await i18nData.statistic();\n }\n );\n I18nData.printStatistic(\"提取结果: \", statistics);\n }\n}\n","import cac from \"cac\";\nimport debug from \"debug\";\nimport path from \"path\";\nimport fs from \"fs-extra\";\n\nexport const cli = cac(\"dune\");\n\nexport const createLogger = (name: string) => {\n const info = debug(`${cli.name}:${name}`);\n return {\n ...info,\n info,\n error: info.extend(\"error\"),\n };\n};\n\ndebug.enable(`${cli.name}:*`);\n\n/**\n * same with chacheDir\n * homeDir();\n * //=> '~/name'\n *\n * homeDir('bar.js')\n * //=> '~/name/bar.js'\n */\n// eslint-disable-next-line @typescript-eslint/explicit-function-return-type\nexport const homeConfigDir = (() => {\n const isWin = process.platform === \"win32\";\n const USER_HOME_DIR = isWin ? process.env.USERPROFILE : process.env.HOME;\n const configDir = path.join(USER_HOME_DIR!, \".config\", cli.name);\n\n if (!fs.existsSync(configDir)) {\n fs.mkdirSync(configDir);\n }\n return homeConfigDir;\n\n function homeConfigDir(p?: string): string {\n if (!p) {\n return configDir;\n }\n return path.join(configDir, p);\n }\n})();\n","import pMap from \"p-map\";\nimport { I18nConfig } from \"./config\";\nimport { createLogger } from \"./index\";\nimport fs from \"fs-extra\";\nimport pc from \"picocolors\";\nimport path from \"path\";\nimport Table from \"cli-table3\";\nimport { SheetData } from \"./resolveSheetData\";\n\nconst log = createLogger(\"i18nData\");\nexport type ExtractedMap = Map<string, { id: string; messages: string }>;\n\nexport class I18nData {\n data: Record<string, string> = {};\n\n filePath: string;\n\n constructor(public locale: string, public config: I18nConfig) {\n this.filePath = path.join(\n config.cwd!,\n config.i18nDir!,\n config.i18nFileName!.replace(\"{locale}\", locale)\n );\n }\n\n async loadData() {\n try {\n if (await fs.pathExists(this.filePath)) {\n this.data = await fs.readJSON(this.filePath);\n } else {\n log.info(\"File '%s' does not exist\", pc.dim(this.filePath));\n }\n } catch (e) {\n log.error(\n \"Error loading i18n data for locale '%s' from file '%s'\",\n pc.yellow(this.locale),\n pc.yellow(this.filePath)\n );\n log.error(\"Error: %s\", e.message);\n }\n return this.data;\n }\n\n async updateByExtractedData(\n extractedData: ExtractedMap,\n shouldUseDefault = false\n ) {\n await this.loadData();\n extractedData.forEach((value, key) => {\n this.data[key] = shouldUseDefault\n ? value.messages || key\n : this.data[key] || \"\";\n });\n }\n\n async updateFromSheetData(sheetData: Record<string, string>) {\n await this.loadData();\n this.data = { ...this.data, ...sheetData };\n }\n\n private sortData() {\n //region 语言key按一定规则排序\n const keys = Object.keys(this.data).sort(this.config.keySorter);\n const newData = {};\n keys.forEach((key) => {\n newData[key] = this.data[key];\n });\n this.data = newData;\n //endregion\n }\n\n async saveToDisk() {\n this.sortData();\n log.info(\n \"Saving i18n data for locale '%s' to file '%s'\",\n pc.green(this.locale),\n pc.dim(this.filePath)\n );\n try {\n await fs.ensureFile(this.filePath);\n await fs.writeJson(this.filePath, this.data, { spaces: 2 });\n log.info(\n \"Saved i18n data for locale '%s' to file '%s'\",\n pc.green(this.locale),\n pc.dim(this.filePath)\n );\n } catch (e) {\n log.error(\n \"Error saving i18n data for locale '%s' to file '%s'\",\n pc.green(this.locale),\n pc.dim(this.filePath)\n );\n log.error(\"Error: %s\", pc.red(e.message));\n }\n }\n\n async statistic() {\n const total = Object.keys(this.data).length;\n const missing = Object.values(this.data).filter((v) => !v).length;\n\n return {\n locale: this.locale,\n total,\n missing,\n };\n }\n\n async trySaveToGoogle(sheetData: SheetData) {\n await pMap(Object.entries(this.data), async ([key, value]) => {\n if (sheetData.hasI18nItem(this.locale, key)) {\n // 更新已有的\n await sheetData.getI18nItem(this.locale, key)?.tryUpdate(value);\n } else {\n log.error(\n \"%s range %s not found key %s\",\n pc.bold(this.config.sheetRange),\n pc.bold(this.locale),\n pc.bold(key)\n );\n }\n });\n }\n\n static printStatistic(\n label: string,\n statistics: { locale: string; total: number; missing: number }[]\n ) {\n const table = new Table({\n head: [\"Language\", \"Total count\", \"Missing\"],\n colAligns: [\"left\", \"center\", \"center\"],\n style: {\n head: [\"green\"],\n border: [],\n compact: true,\n },\n });\n statistics.forEach((statistic) => {\n table.push([\n statistic.locale,\n statistic.total,\n statistic.missing > 0 ? pc.red(statistic.missing) : statistic.missing,\n ]);\n });\n console.log(pc.bold(label));\n console.log(table.toString());\n }\n}\n","import pMap from \"p-map\";\nimport { createLogger } from \"../shared\";\nimport { getConfig } from \"../shared/config\";\nimport { I18nData } from \"../shared/i18nData\";\nimport { resolveSheetData } from \"../shared/resolveSheetData\";\n\nconst log = createLogger(\"generate\");\n\nexport async function download() {\n const config = await getConfig();\n let i = 0;\n for (const configItem of config.i18n ?? []) {\n i++;\n if (!configItem.sheetId || !configItem.sheetRange) {\n log.error(`config.${i} sheetId 或 sheetRange 为空,请检查`);\n continue;\n }\n const sheetData = await resolveSheetData(configItem);\n\n const statistics = await pMap(\n configItem.locales ?? [],\n async (locale, index) => {\n const i18nData = new I18nData(locale, configItem);\n await i18nData.updateFromSheetData(sheetData.getLangDataJSON(locale));\n await i18nData.saveToDisk();\n return await i18nData.statistic();\n }\n );\n I18nData.printStatistic(`${configItem.sheetRange} 生成结果: `, statistics);\n }\n}\n","import { GaxiosResponse } from \"gaxios\";\nimport type { sheets_v4 } from \"googleapis\";\nimport { google } from \"googleapis\";\nimport { createLogger } from \"../index\";\nimport { numberToLetter } from \"../letters\";\nimport { googleAuth } from \"./auth\";\n\nconst log = createLogger(\"google:sheets\");\n\nclass GoogleSheet {\n private sheets: sheets_v4.Sheets;\n\n private initPromise: Promise<void> | null = null;\n private async initImpl() {\n if (!this.sheets) {\n await googleAuth.initCredentials();\n this.sheets = google.sheets({\n version: \"v4\",\n auth: googleAuth.oauth2Client,\n });\n }\n }\n /**\n * 调用sheets其他方法的时候需要先初始化\n */\n async init() {\n if (!this.initPromise) {\n this.initPromise = this.initImpl();\n }\n await this.initPromise;\n }\n\n get = async (options: sheets_v4.Params$Resource$Spreadsheets$Values$Get) => {\n await this.init();\n return this.sheets.spreadsheets.values.get(options);\n };\n\n private async update(\n spreadsheetId: string,\n range: string,\n colIndex: number,\n rowIndex: number,\n values: string[][]\n ): Promise<GaxiosResponse<sheets_v4.Schema$UpdateValuesResponse>> {\n await this.init();\n\n const colLetter = numberToLetter(colIndex);\n const sheetNeedRange = `${range}!${colLetter}${rowIndex}`;\n\n const res = await this.sheets.spreadsheets.values.update({\n spreadsheetId,\n range: sheetNeedRange,\n valueInputOption: \"USER_ENTERED\",\n requestBody: { values },\n });\n\n if (res.status === 200) {\n log.info(`更新 ${sheetNeedRange} 的值成功`);\n } else {\n log.error(\"some thing wrong\", res);\n }\n return res;\n }\n\n /**\n * 更新某一个单元格\n */\n async updateCell(\n spreadsheetId: string,\n range: string,\n colIndex: number,\n rowIndex: number,\n values: string\n ) {\n return this.update(spreadsheetId, range, colIndex, rowIndex, [[values]]);\n }\n\n /**\n * 更新某一列\n */\n async updateColumn(\n spreadsheetId: string,\n range: string,\n colIndex: number,\n rowIndex: number,\n values: string[]\n ) {\n return this.update(spreadsheetId, range, colIndex, rowIndex, [\n ...values.map((v) => [v]),\n ]);\n }\n}\n\nexport const googleSheet = new GoogleSheet();\n","/**\n *\n * - input : A\n * output : 0\n * - input : Z\n * output : 25\n * - input : AA\n * output : 26\n * - input : a\n * output : 0\n */\nexport function letterToNumber(letters: string | number) {\n if (typeof letters === \"number\" || !Number.isNaN(+letters)) {\n return +letters;\n }\n\n let n = 0;\n letters = letters.toUpperCase();\n for (let p = 0; p < letters.length; p++) {\n n = letters[p].charCodeAt(0) - 64 + n * 26;\n }\n\n return Math.max(0, n - 1);\n}\n\n/**\n * fork from https://github.com/matthewmueller/number-to-letter\n */\nconst alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\nconst base = alphabet.length;\n/**\n * numberToLetter(0) // A\n * numberToLetter(25) // Z\n * numberToLetter(26) // AA\n * numberToLetter(51) // AZ\n * numberToLetter(52) // BA\n * numberToLetter(676) // ZA\n * numberToLetter(701) // ZZ\n * numberToLetter(702) // AAA\n */\nexport function numberToLetter(n: number) {\n const digits: number[] = [];\n\n do {\n const v = n % base;\n digits.push(v);\n n = Math.floor(n / base);\n } while (n-- > 0);\n\n const chars: string[] = [];\n while (digits.length) {\n chars.push(alphabet[digits.pop()!]);\n }\n\n return chars.join(\"\");\n}\n","import fs from \"fs-extra\";\nimport { OAuth2Client } from \"google-auth-library\";\nimport { google } from \"googleapis\";\nimport { homeConfigDir, createLogger } from \"../index\";\nimport { openAndWaitReturnQuery } from \"./shared\";\n\ninterface AuthOptions {\n client_id: string;\n client_secret: string;\n /**\n * @default ['spreadsheets']\n */\n scope?: string[];\n}\n\nconst log = createLogger(\"google:auth\");\n\nclass GoogleAuth {\n private tokens: any = null;\n\n private tokensCachePath = homeConfigDir(\"chromeAuthToken.json\");\n\n private readonly scope: string[];\n\n oauth2Client: OAuth2Client = new google.auth.OAuth2(\n this.options.client_id,\n this.options.client_secret,\n \"http://localhost:12345\"\n );\n\n constructor(private options: AuthOptions) {\n this.options.scope = this.options.scope ?? [\"spreadsheets\"];\n const scopePrefix = `https://www.googleapis.com/auth/`;\n this.scope = this.options.scope.map((item) => {\n if (item.startsWith(scopePrefix)) {\n return item;\n }\n return `${scopePrefix}${item}`;\n });\n\n try {\n this.tokens = fs.readJsonSync(this.tokensCachePath);\n } catch (e) {\n log.info(\"未获取到缓存的token\");\n this.tokens = null;\n }\n }\n\n private async saveTokens(tokens: any): Promise<void> {\n this.tokens = tokens;\n await fs.writeJSON(this.tokensCachePath, tokens);\n log.info(\"保存token成功\");\n }\n\n async getCode(): Promise<string> {\n log.info(\"将打开chrome浏览器进行授权\");\n const authUrl = this.oauth2Client.generateAuthUrl({\n access_type: \"offline\",\n scope: this.scope,\n });\n return await openAndWaitReturnQuery(authUrl, 12345, \"code\");\n }\n\n private async initCredentialsImpl(): Promise<void> {\n if (!this.tokens) {\n const code = await this.getCode();\n const { tokens } = await this.oauth2Client.getToken(code);\n await this.saveTokens(tokens);\n }\n this.oauth2Client.setCredentials(this.tokens);\n }\n private initCredentialsPromise: Promise<void> | null = null;\n async initCredentials() {\n if (!this.initCredentialsPromise) {\n this.initCredentialsPromise = this.initCredentialsImpl();\n }\n return await this.initCredentialsPromise;\n }\n}\n\n// see in https://console.cloud.google.com/apis/credentials?organizationId=286245507762&orgonly=true&project=dune-cli&supportedpurview=organizationId\nconst clientSecret = {\n client_id:\n \"728508473485-fgl11a3op1ucd0lef9hs49mkliuaipqa.apps.googleusercontent.com\",\n project_id: \"dune-cli\",\n auth_uri: \"https://accounts.google.com/o/oauth2/auth\",\n token_uri: \"https://oauth2.googleapis.com/token\",\n auth_provider_x509_cert_url: \"https://www.googleapis.com/oauth2/v1/certs\",\n client_secret: \"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\",\n redirect_uris: [\"http://localhost\"],\n};\nexport const googleAuth = new GoogleAuth(clientSecret);\n","import connect from \"connect\";\nimport open from \"open\";\nimport { URL } from \"url\";\nimport { createLogger } from \"../index\";\n\nconst log = createLogger(\"google:shared\");\n\nfunction openBrowser(url: string) {\n log.info(`如未自动打开浏览器,可访问以下地址:${url}`);\n open(url, { app: { name: open.apps.chrome } });\n}\n\nexport function openAndWaitReturnQuery(\n toOpenUrl: string,\n port: number,\n queryKey: string\n) {\n return new Promise<string>((resolve) => {\n openBrowser(toOpenUrl);\n\n const app = connect();\n app.use((req, res, next) => {\n const parsedUrl = new URL(req.url, `http://localhost:${port}`);\n const target = parsedUrl.searchParams.get(queryKey);\n if (target) {\n res.writeHead(200, {\n \"content-type\": \"text/html;charset=utf8\",\n });\n res.end(\n `<div>当前窗口可直接关闭</div>\n <script>\n window.open(\"\", \"_self\", \"\");\n window.close();\n </script>`\n );\n server.close();\n server.unref();\n resolve(target);\n }\n next();\n });\n\n const server = app.listen(port);\n });\n}\n","import { I18nConfig } from \"./config\";\nimport { googleSheet } from \"./google/sheet\";\nimport { createLogger } from \"./index\";\nimport { letterToNumber } from \"./letters\";\n\nconst log = createLogger(\"shared:resolveSheetData\");\n\nclass SheetI18nItem {\n value: string;\n\n colIndex: number;\n\n constructor(\n private spreadsheetId: string,\n private range: string,\n public key: string,\n colIndex: number | string,\n public row: string[],\n private rowIndex: number\n ) {\n this.colIndex = letterToNumber(colIndex);\n // TODO: 待确定 无值的key是否要生成\n // value = undefined 即不会生成\n this.value = this.row[this.colIndex] || \"\";\n }\n\n /**\n * 尝试更新 单元格的值\n * 如果一样则不更新\n */\n async tryUpdate(newValue: string) {\n if (newValue === this.value) {\n // 云端的值跟本地一样 不需要同步\n return;\n }\n\n await googleSheet.updateCell(\n this.spreadsheetId,\n this.range,\n this.colIndex,\n this.rowIndex,\n newValue\n );\n }\n}\n\nexport async function resolveSheetData(config: I18nConfig): Promise<SheetData> {\n const { position } = config;\n const parseStartIndex = config.parseStartIndex ?? 2;\n\n log.info(`读取 ${config.sheetRange} sheet数据`);\n\n const res = await googleSheet.get({\n spreadsheetId: config.sheetId,\n range: config.sheetRange,\n });\n if (!res.data.values) {\n log.error(\n `${config.sheetRange} sheet数据为空,请检查,应该最少有一行头部数据`\n );\n return new SheetData(config, 2);\n }\n\n const sheetData = new SheetData(config, res.data.values.length + 1);\n\n res.data.values.forEach((row: string[], rowIndex) => {\n // excel 的开始索引是1,所以要 +1\n const trulyRowIndex = rowIndex + 1;\n if (parseStartIndex > trulyRowIndex) {\n // 还未到可以开始解析的行数\n return;\n }\n\n const i18nKey = row[letterToNumber(position!.key)]?.trim();\n\n if (i18nKey) {\n if (sheetData.keySet.has(i18nKey)) {\n log.error(\n `表格 ${config.sheetRange} 发现重复的i18nKey: ${i18nKey}, 将覆盖前面的值`\n );\n } else {\n sheetData.keySet.add(i18nKey);\n }\n\n config.locales?.forEach((lang) => {\n sheetData.setI18nItem(\n lang,\n new SheetI18nItem(\n config.sheetId!,\n config.sheetRange!,\n i18nKey,\n position![lang],\n row,\n trulyRowIndex\n )\n );\n });\n }\n });\n\n return sheetData;\n}\n\nexport class SheetData {\n keySet = new Set<string>();\n\n constructor(\n private config: I18nConfig,\n // 当前有多少行 新增的时候往后面添加\n public nextAppendRowIndex: number\n ) {}\n\n private value = new Map(\n this.config.locales!.map((lang) => {\n return [lang, new Map<string, SheetI18nItem>()];\n })\n );\n\n getI18nItem(lang: string, i18nKey: string) {\n return this.value.get(lang)!.get(i18nKey);\n }\n\n setI18nItem(lang: string, i18nItem: SheetI18nItem) {\n this.value.get(lang)!.set(i18nItem.key, i18nItem);\n }\n\n hasI18nItem(lang: string, i18nKey: string) {\n return this.value.get(lang)!.has(i18nKey);\n }\n\n getLangDataJSON(lang: string) {\n const res = {} as Record<string, string>;\n this.value.get(lang)!.forEach((i18nItem) => {\n res[i18nItem.key] = i18nItem.value;\n });\n return res;\n }\n}\n","import _ from \"lodash\";\nimport { OpenAPIV3 } from \"openapi-types\";\nimport pMap from \"p-map\";\nimport { createLogger } from \"../../shared\";\nimport { getConfig, ApiConfig } from \"../../shared/config\";\nimport fs from \"fs-extra\";\nimport * as os from \"os\";\nimport SwaggerParser from \"@apidevtools/swagger-parser\";\nimport { compile } from \"json-schema-to-typescript\";\nimport path from \"path\";\n\nconst log = createLogger(\"generateApi\");\n\nexport async function generateApi() {\n const config = await getConfig();\n\n for (const apiConfig of config.api ?? []) {\n log.info(`清除旧的api文件: ${apiConfig.output}`);\n await fs.emptydir(apiConfig.output!);\n }\n\n for (const apiConfig of config.api ?? []) {\n log.info(\"开始解析 %s\", apiConfig.swaggerJSONPath);\n const parsed = (await SwaggerParser.dereference(\n apiConfig.swaggerJSONPath\n )) as OpenAPIV3.Document;\n\n await pMap(Object.entries(parsed.paths), async ([url, pathItemObject]) => {\n log.info(\"开始生成 %s\", url);\n if (pathItemObject) {\n await pMap(\n [\"get\", \"put\", \"post\", \"delete\", \"patch\"],\n async (method) => {\n const operationObject = pathItemObject[method];\n if (operationObject) {\n const code = await generateApiRequestCode({\n url: url,\n method: method,\n operationObject: operationObject,\n apiConfig,\n });\n const outputPath = path.join(\n apiConfig.output!,\n url,\n `${method}.ts`\n );\n await fs.ensureFile(outputPath);\n await fs.writeFile(outputPath, code);\n }\n }\n );\n }\n });\n }\n\n log.info(\"generateApi Done\");\n}\n\n/**\n * 根据请求方法,生成请求代码\n */\nexport async function generateApiRequestCode(options: {\n url: string;\n method: string;\n operationObject: OpenAPIV3.OperationObject;\n apiConfig: ApiConfig;\n}): Promise<string> {\n const { url, method, operationObject, apiConfig } = options;\n\n const seeUrl = apiConfig.swaggerUiUrl\n ? `${apiConfig.swaggerUiUrl}#/${encodeURIComponent(\n operationObject.tags?.join(\"/\") ?? \"\"\n )}/${operationObject.operationId}`\n : \"not found swagger ui url\";\n\n const requestBuilderName = _.camelCase(`${url}_${method}_api`);\n //#region url上参数 例如 /api/v1/users/{userId}\n const urlPathParams = getUrlPathParams(\n (operationObject.parameters as never) ?? []\n );\n const urlPathParamsCode = urlPathParams.length\n ? `urlPathParams: ${JSON.stringify(urlPathParams)},`\n : \"\";\n //#endregion\n\n let code: string[] = [\n \"// 这个文件由 @dune2/cli 自动生成,不要手动修改,否则会被覆盖\",\n `import { RequestBuilder } from '@dune2/tools';`,\n apiConfig.requestFnImportPath!,\n `\\\n/**\n * ${operationObject.summary}\n * @tags ${operationObject.tags?.join(\",\")}\n * @see ${seeUrl}\n */`,\n ];\n\n // builder 代码\n code.push(`\\\nexport const ${requestBuilderName} = new RequestBuilder<${requestBuilderName}.Req, ${requestBuilderName}.Res>({\n url: '${url}',\n method: '${method}',\n requestFn,\n ${urlPathParamsCode}\n});`);\n\n // 请求参数类型\n // 响应参数类型\n const [requestParamsTypeCode, responseParamsTypeCode] = await Promise.all([\n compileRequestParams(operationObject),\n compileResponseParams(operationObject),\n ]);\n\n code.push(`\nexport namespace ${requestBuilderName} {\n ${requestParamsTypeCode}\n \n ${responseParamsTypeCode}\n};`);\n\n return code.join(os.EOL);\n}\n\nfunction getUrlPathParams(parameters: OpenAPIV3.ParameterObject[]) {\n return parameters\n .filter((item) => item.in === \"path\")\n .map((item) => item.name);\n}\n\n/**\n * 这个方法还不完善,只能处理简单的请求参数\n */\nasync function compileRequestParams(\n operationObject: OpenAPIV3.OperationObject\n) {\n let schema;\n if (operationObject.parameters) {\n // FIXME: 这里还需要处理 ref 类型\n const parameters =\n (operationObject.parameters as OpenAPIV3.ParameterObject[]).filter(\n (p) => p.in === \"query\" || p.in === \"path\"\n ) ?? [];\n\n const required = parameters.filter((p) => p.required).map((p) => p.name);\n const properties = Object.fromEntries(\n parameters.map((p) => {\n //TODO: 这里也要处理 ref 类型\n const schema = p.schema as OpenAPIV3.SchemaObject;\n return [\n p.name,\n {\n type: schema.type,\n description: p.description,\n // enum: schema.enum ?? [],\n },\n ];\n })\n );\n schema = {\n required,\n type: \"object\",\n properties,\n };\n } else if (operationObject.requestBody) {\n //TODO: 这里也需要处理其他类型\n schema = (operationObject.requestBody as OpenAPIV3.RequestBodyObject)\n .content[\"application/json\"].schema;\n }\n\n let code = \"\";\n if (schema) {\n code = await compile(schema, \"Req\", {\n bannerComment: \"\",\n ignoreMinAndMaxItems: !!1,\n // format: false,\n });\n }\n\n return code ? code : \"export type Req = any;\";\n}\n\nasync function compileResponseParams(\n operationObject: OpenAPIV3.OperationObject\n) {\n const temp = operationObject.responses[\"200\"] as OpenAPIV3.ResponseObject;\n let code = \"\";\n if (temp?.content) {\n // FIXME: 可能需要处理其他的content类型\n const temp2 = temp.content[\"application/json\"] || temp.content[\"*/*\"];\n const schema = temp2.schema as OpenAPIV3.SchemaObject;\n const data = schema.properties?.data ?? schema;\n if (data) {\n code = await compile(data, \"Res\", {\n bannerComment: \"\",\n ignoreMinAndMaxItems: !!1,\n // format: false,\n });\n }\n }\n\n return code ? code : \"export type Res = any;\";\n}\n","import { createLogger } from \"../shared\";\nimport { getConfig, configName } from \"../shared/config\";\nimport fs from \"fs-extra\";\nimport path from \"path\";\n\nconst log = createLogger(\"initConfig\");\n\nconst tpl = `\\\n/**\n * @template {import('@dune2/cli').Config} T\n * @param {T} config - A generic parameter that flows through to the return type\n * @constraint {{import('@dune2/cli').Config}}\n */\nfunction defineConfig(config) {\n return config;\n}\nmodule.exports = defineConfig({ i18n: [], api:[] });\n`;\n\nexport const initConfig = async () => {\n const config = await getConfig();\n const configPath = path.join(config.cwd!, configName);\n log.info(`config file path: ${configPath}`);\n if (await fs.pathExists(configPath)) {\n log.info(`config file already exists, skip`);\n return;\n }\n await fs.writeFile(configPath, tpl);\n log.info(`config file created`);\n};\n","import pMap from \"p-map\";\nimport { createLogger } from \"../shared\";\nimport { getConfig, I18nConfig } from \"../shared/config\";\nimport { googleSheet } from \"../shared/google/sheet\";\nimport { I18nData } from \"../shared/i18nData\";\nimport { letterToNumber } from \"../shared/letters\";\nimport { resolveSheetData } from \"../shared/resolveSheetData\";\n\nconst log = createLogger(\"upload\");\n\n/**\n * 检测有本地有新增的key 则将新增的key更新到云端\n * 无则跳过\n */\nasync function batchUpdateKeys(config: I18nConfig) {\n let sheetData = await resolveSheetData(config);\n\n const allKeys: string[] = [];\n const i18nDataMap = new Map<string, I18nData>();\n await pMap(config.locales ?? [], async (locale) => {\n const i18nData = new I18nData(locale, config);\n i18nDataMap.set(locale, i18nData);\n const data = await i18nData.loadData();\n allKeys.push(...Object.keys(data));\n });\n\n let needSyncKeys = Array.from(new Set(allKeys)).filter((key) => {\n // 本地有新的key 服务器没有\n return !sheetData.keySet.has(key);\n });\n needSyncKeys = needSyncKeys.sort(config.keySorter);\n\n if (needSyncKeys.length) {\n log.info(`即将同步本地新增加 ${needSyncKeys.length} 个 i18nKey 到云端`);\n await googleSheet.updateColumn(\n config.sheetId!,\n config.sheetRange!,\n letterToNumber(config.position!.key),\n sheetData.nextAppendRowIndex,\n needSyncKeys\n );\n log.info(\"同步新增加i18nKey成功\");\n\n // 批量同步新增的key的values到云端\n for (const lang of config.locales!) {\n const langFile = i18nDataMap.get(lang);\n const needSyncValues: string[] = [];\n needSyncKeys.forEach((k) => {\n if (langFile) {\n needSyncValues.push(langFile.data[k]);\n }\n });\n log.info(\n `即将同步本地 ${lang} 新增加 ${needSyncValues.length} 个值到云端`\n );\n await googleSheet.updateColumn(\n config.sheetId!,\n config.sheetRange!,\n letterToNumber(config.position![lang]),\n sheetData.nextAppendRowIndex,\n needSyncValues\n );\n }\n\n await sleep(1000);\n sheetData = await resolveSheetData(config);\n log.info(`已重新获取 ${config.sheetRange} 的数据`);\n }\n\n return { sheetData, i18nDataMap };\n}\nfunction sleep(ms: number) {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nexport async function upload() {\n const config = await getConfig();\n\n for (const configItem of config.i18n ?? []) {\n const { sheetData, i18nDataMap } = await batchUpdateKeys(configItem);\n await pMap(configItem.locales ?? [], async (locale) => {\n const i18nData = i18nDataMap.get(locale)!;\n await i18nData.trySaveToGoogle(sheetData);\n });\n }\n\n log.info(\"上传成功\");\n}\n","import { cli, createLogger } from \"../shared\";\nimport prompts from \"prompts\";\n\nconst log = createLogger(\"interactive\");\n\nexport const interactive = async (args: any) => {\n const commands = cli.commands.filter((command) => {\n if (command.name) {\n return !command.name.startsWith(\"@@\") && command.name !== \"interactive\";\n }\n return false;\n });\n\n const commandMap = new Map<string, typeof commands[number]>();\n const res = await prompts({\n type: \"autocomplete\",\n name: \"command\",\n message: \"选择要执行的命令:\",\n choices: commands.map((command) => {\n commandMap.set(command.name, command);\n return {\n title: command.name,\n value: command.name,\n description: command.description,\n };\n }),\n });\n const command = commandMap.get(res.command);\n if (!command) {\n log.error(\"未找到命令 %s\", res.command);\n return;\n }\n command.commandAction?.apply(cli, args);\n};\n","#!/usr/bin/env node\nimport { extract } from \"./commands/extract\";\nimport { download } from \"./commands/download\";\nimport { generateApi } from \"./commands/generateApi\";\nimport { initConfig } from \"./commands/initConfig\";\nimport { upload } from \"./commands/upload\";\nimport { cli } from \"./shared\";\nimport { version } from \"../package.json\";\nimport { interactive } from \"./commands/interactive\";\n//#region 翻译相关\n\ncli\n .command(\"download\", \"生成翻译文件\")\n .example(\"dune download\")\n .action(download);\ncli\n .command(\"extract\", \"提取代码中的文案\")\n .example(\"dune extract\")\n .action(extract);\ncli.command(\"upload\", \"上传翻译文件\").example(\"dune upload\").action(upload);\n//#endregion\n\n//#region api 相关\ncli\n .command(\"generateApi\", \"生成api文件\")\n .example(\"dune generateApi\")\n .action(generateApi);\n//#endregion\n\ncli.command(\"init\", \"初始化配置文件\").example(\"dune init\").action(initConfig);\n\ncli\n .command(\"interactive\", \"交互式操作\")\n .example(\"dune interactive\")\n .alias(\"i\")\n .action(interactive);\n\n// make default command run interactive\ncli.command(\"\").action(interactive);\n\ncli.version(version);\ncli.help();\n\n(async () => {\n try {\n // Parse CLI args without running the command\n cli.parse(process.argv, { run: false });\n // Run the command yourself\n // You only need `await` when your command action returns a Promise\n await cli.runMatchedCommand();\n } catch (error) {\n // Handle error here..\n // e.g.\n // console.error(error.stack)\n // process.exit(1)\n }\n})();\n"],"mappings":"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","names":["fs","pMap","homeConfigDir","fs","path","pc","log","extract","pMap","fs","pMap","google","fs","log","log","fs","log","google","log","log","pMap","pMap","fs","path","log","pMap","schema","fs","path","log","pMap","log","pMap","log","command"]}
package/dist/index.d.ts CHANGED
@@ -40,8 +40,42 @@ interface I18nConfig {
40
40
  */
41
41
  keySorter?: (a: string, b: string) => number;
42
42
  }
43
+ interface ApiConfig {
44
+ /**
45
+ * swagger JSON 的路径 可以是 本地 可以是远程
46
+ */
47
+ swaggerJSONPath: string;
48
+ /**
49
+ * 用在生成代码里快捷连接跳转到 swagger ui
50
+ * 必须携带 urls.primaryName 参数
51
+ * @example http://192.168.104.10:31082/swagger/?urls.primaryName=%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%E7%9B%B8%E5%85%B3API
52
+ */
53
+ swaggerUiUrl?: string;
54
+ /**
55
+ * 输出的文件夹路径
56
+ * @default "./src/apis"
57
+ */
58
+ output?: string;
59
+ /**
60
+ * 配置`requestFn` 方法引入的路径,生成的代码里会用到,生成代码如下
61
+ * 引入的名称必须是`requestFn`
62
+ * ```ts
63
+ * // 需要命名为 requestFn
64
+ * import requestFn from '@/utils/request'
65
+ * import { foo as requestFn } from '@/utils/request'
66
+ * ```
67
+ * @default `import requestFn from '@/utils/request'`
68
+ */
69
+ requestFnImportPath?: string;
70
+ }
43
71
  interface Config {
44
72
  i18n?: I18nConfig[];
73
+ api?: ApiConfig[];
74
+ /**
75
+ * 默认是 命令运行的目录,一般是项目根目录
76
+ * @internal
77
+ */
78
+ cwd?: string;
45
79
  }
46
80
 
47
81
  export { Config };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import "./chunk-BEIVJU3S.js";
1
+ import "./chunk-UPESJ46X.js";
2
2
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dune2/cli",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -19,7 +19,7 @@
19
19
  "dist"
20
20
  ],
21
21
  "dependencies": {
22
- "@dune2/wasm": "0.0.5",
22
+ "@dune2/wasm": "0.0.6",
23
23
  "cac": "^6.7.14",
24
24
  "cli-table3": "^0.6.3",
25
25
  "connect": "^3.7.0",
@@ -32,10 +32,15 @@
32
32
  "p-map": "^5.5.0",
33
33
  "path-to-regexp": "^6.2.1",
34
34
  "picocolors": "^1.0.0",
35
- "prompts": "^2.4.2"
35
+ "prompts": "^2.4.2",
36
+ "json-schema-to-typescript": "^11.0.2",
37
+ "@apidevtools/swagger-parser": "^10.1.0",
38
+ "lodash": "^4.17.21"
36
39
  },
37
40
  "devDependencies": {
41
+ "openapi-types": "^12.0.2",
38
42
  "@types/debug": "^4.1.7",
43
+ "@types/lodash": "^4.14.186",
39
44
  "@types/prompts": "^2.4.0",
40
45
  "gaxios": "*",
41
46
  "google-auth-library": "*",
package/readme.md CHANGED
@@ -3,15 +3,17 @@
3
3
  - 提取翻译文案到`json`文件, 运行 `dune extract`
4
4
  - 同步文件到`google sheet`(必须要有配置文件), 运行 `dune sync`
5
5
  - 从`google sheet`生成文件(必须要有配置文件), 运行 `dune generate`
6
+ - 从`swagger` 生成`api`请求文件, 运行 `dune generateApi`
6
7
  - 初始化配置文件, 运行 `dune init`
7
8
 
8
9
  ## 支持的命令
9
10
 
10
11
  ```shell
11
12
  Commands:
12
- generate 生成翻译文件
13
- upload 上传翻译文件
13
+ download 生成翻译文件
14
14
  extract 提取代码中的文案
15
+ upload 上传翻译文件
16
+ generateApi 生成api文件
15
17
  init 初始化配置文件
16
18
  interactive 交互式操作
17
19
  ```
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shared/config.ts","../src/shared/resolveSheetId.ts"],"sourcesContent":["import JoyCon from \"joycon\";\nimport { resolveSheetId } from \"./resolveSheetId\";\n\nconst joycon = new JoyCon();\n\nexport interface I18nConfig {\n // 语言文件存放目录,默认为项目根目录的 \"./src/i18n\"\n i18nDir?: string;\n // 语言文件名,默认为 \"{locale}.i18n.json\",其中 {locale} 会被替换为 locales 中的语言\n i18nFileName?: string;\n // 默认语言,默认为 \"zh\",影响到提出出来的文案的默认值\n defaultLocale?: string;\n /**\n * 可以是 id 或者 url,如果是 url 则会自动解析出 id\n * id: 1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk\n * url: https://docs.google.com/spreadsheets/d/1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk/edit#gid=1740568548\n */\n sheetId?: string;\n // google sheets 工作表名称\n sheetRange?: string;\n /**\n * 对应 语言、i18nKey 所在的列标识符 如(A,B,C...)\n * 传入时将忽略默认的配置\n * @default {key: \"B\", zh: \"C\", en: \"D\", in: \"E\",}\n */\n position?: {\n key: string;\n [key: string]: string;\n };\n\n /**\n * 从 Google Sheets 哪一行开始解析\n * 默认从第2行开始,模板文件第一行是表头\n * @default 2\n */\n parseStartIndex?: number;\n\n /**\n * 默认是 命令运行的目录,一般是项目根目录\n * @internal\n */\n cwd?: string;\n /**\n * 支持的语言列表,从 position 中解析出来\n * @internal\n */\n locales?: string[];\n\n /**\n * 翻译key的排序\n * @internal\n */\n keySorter?: (a: string, b: string) => number;\n}\n\nexport interface Config {\n i18n?: I18nConfig[];\n}\nexport const configName = \"dune.config.js\";\n\nexport async function getConfig(): Promise<Config> {\n const res = await joycon.load([configName]);\n\n const defaultI18nConfig: I18nConfig = {\n i18nDir: \"./src/i18n\",\n i18nFileName: \"{locale}.i18n.json\",\n cwd: process.cwd(),\n position: { key: \"B\", zh: \"C\", en: \"D\", in: \"E\" },\n parseStartIndex: 2,\n };\n\n let config = (res.data ?? {}) as Config;\n if (!config.i18n || !config.i18n?.length) {\n config.i18n = [defaultI18nConfig];\n }\n\n // 格式化 i18n 配置\n config.i18n = config.i18n.map((item) => {\n item = Object.assign({}, defaultI18nConfig, item);\n item.locales = Object.keys(item.position!).filter((item) => item !== \"key\");\n item.sheetId = resolveSheetId(item.sheetId);\n item.keySorter = (a, b) => {\n return a.localeCompare(b);\n };\n return item;\n });\n\n return config;\n}\n","/**\n * input:\n * \"https://docs.google.com/spreadsheets/d/1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk/edit#gid=1740568548\"\n * output:\n * \"1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk\"\n * --\n * input: \"1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk\"\n * output: \"1C9-Dol3oO20W9_FhiVlxNsDhOaaejJIgAZYRkonGmfk\"\n */\nimport { match } from \"path-to-regexp\";\n\nexport function resolveSheetId(str?: string) {\n if (!str || !str?.startsWith(\"http\")) {\n return str;\n }\n const url = new URL(str);\n const matched = match<{ id: string[] }>(\"/spreadsheets/d/:id*\", {\n decode: decodeURIComponent,\n })(url.pathname);\n return matched ? matched.params.id?.[0] : str;\n}\n"],"mappings":";AAAA,OAAO,YAAY;;;ACSnB,SAAS,aAAa;AAEf,SAAS,eAAe,KAAc;AAX7C;AAYE,MAAI,CAAC,OAAO,EAAC,2BAAK,WAAW,UAAS;AACpC,WAAO;AAAA,EACT;AACA,QAAM,MAAM,IAAI,IAAI,GAAG;AACvB,QAAM,UAAU,MAAwB,wBAAwB;AAAA,IAC9D,QAAQ;AAAA,EACV,CAAC,EAAE,IAAI,QAAQ;AACf,SAAO,WAAU,aAAQ,OAAO,OAAf,mBAAoB,KAAK;AAC5C;;;ADjBA,IAAM,SAAS,IAAI,OAAO;AAuDnB,IAAM,aAAa;AAE1B,eAAsB,YAA6B;AA5DnD;AA6DE,QAAM,MAAM,MAAM,OAAO,KAAK,CAAC,UAAU,CAAC;AAE1C,QAAM,oBAAgC;AAAA,IACpC,SAAS;AAAA,IACT,cAAc;AAAA,IACd,KAAK,QAAQ,IAAI;AAAA,IACjB,UAAU,EAAE,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI;AAAA,IAChD,iBAAiB;AAAA,EACnB;AAEA,MAAI,SAAU,IAAI,QAAQ,CAAC;AAC3B,MAAI,CAAC,OAAO,QAAQ,GAAC,YAAO,SAAP,mBAAa,SAAQ;AACxC,WAAO,OAAO,CAAC,iBAAiB;AAAA,EAClC;AAGA,SAAO,OAAO,OAAO,KAAK,IAAI,CAAC,SAAS;AACtC,WAAO,OAAO,OAAO,CAAC,GAAG,mBAAmB,IAAI;AAChD,SAAK,UAAU,OAAO,KAAK,KAAK,QAAS,EAAE,OAAO,CAACA,UAASA,UAAS,KAAK;AAC1E,SAAK,UAAU,eAAe,KAAK,OAAO;AAC1C,SAAK,YAAY,CAAC,GAAG,MAAM;AACzB,aAAO,EAAE,cAAc,CAAC;AAAA,IAC1B;AACA,WAAO;AAAA,EACT,CAAC;AAED,SAAO;AACT;","names":["item"]}