@ruan-cat/utils 4.20.0 → 4.21.0

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.
@@ -159,6 +159,16 @@ type Prettify<T> = {
159
159
  */
160
160
  type ToNumberLike<T extends number> = T | `${T}`;
161
161
 
162
+ /**
163
+ * 从给定目录开始,向上查找 monorepo 根目录。
164
+ * @description
165
+ * 通过查找 `pnpm-workspace.yaml` 文件来定位 monorepo 根目录。
166
+ * 该函数适用于脚本在 monorepo 子包内运行的场景。
167
+ *
168
+ * @param startDir 起始查找目录,默认值为 `process.cwd()`
169
+ * @returns 找到的 monorepo 根目录绝对路径;若未找到则返回 `null`
170
+ */
171
+ declare function findMonorepoRoot(startDir?: string): string | null;
162
172
  /**
163
173
  * 判断目标项目是否是 monorepo 格式的项目
164
174
  * @description
@@ -172,4 +182,4 @@ type ToNumberLike<T extends number> = T | `${T}`;
172
182
  */
173
183
  declare function isMonorepoProject(): boolean;
174
184
 
175
- export { type BaseTask, type Condition, type Conditions, type ParallelTasks, type PnpmWorkspace, type Prettify, type PrintListParams, type PromiseTasksConfig, type QueueTasks, type SimpleAsyncTask, type SingleTasks, type SpawnSyncSimpleParams, type Task, type TaskType, type TasksConfig, type ToNumberLike, defPrintCurrentCommand, definePromiseTasks, executePromiseTasks, generateSimpleAsyncTask, generateSpawnSync, initFlag, isConditionsEvery, isConditionsSome, isMonorepoProject, pathChange, printFormat, printList, runPromiseByConcurrency, runPromiseByQueue, taskTypes };
185
+ export { type BaseTask, type Condition, type Conditions, type ParallelTasks, type PnpmWorkspace, type Prettify, type PrintListParams, type PromiseTasksConfig, type QueueTasks, type SimpleAsyncTask, type SingleTasks, type SpawnSyncSimpleParams, type Task, type TaskType, type TasksConfig, type ToNumberLike, defPrintCurrentCommand, definePromiseTasks, executePromiseTasks, findMonorepoRoot, generateSimpleAsyncTask, generateSpawnSync, initFlag, isConditionsEvery, isConditionsSome, isMonorepoProject, pathChange, printFormat, printList, runPromiseByConcurrency, runPromiseByQueue, taskTypes };
@@ -30,6 +30,83 @@ declare function clean(
30
30
  */
31
31
  targets?: string[]): Promise<void>;
32
32
 
33
+ interface MoveVercelOutputToRootOptions {
34
+ /**
35
+ * 运行目录。
36
+ * 默认值为 `process.cwd()`。
37
+ */
38
+ cwd?: string;
39
+ /**
40
+ * monorepo 根目录。
41
+ * 不传时会从 `cwd` 开始,向上查找 `pnpm-workspace.yaml`。
42
+ */
43
+ rootDir?: string;
44
+ /**
45
+ * 子包内构建产物目录。
46
+ * 相对路径基于 `cwd` 解析。
47
+ *
48
+ * @default ".vercel/output"
49
+ */
50
+ sourceDir?: string;
51
+ /**
52
+ * monorepo 根目录内的目标输出目录。
53
+ * 相对路径基于 monorepo 根目录解析。
54
+ *
55
+ * @default ".vercel/output"
56
+ */
57
+ targetDir?: string;
58
+ /**
59
+ * 是否跳过目标目录清理。
60
+ *
61
+ * @default false
62
+ */
63
+ skipClean?: boolean;
64
+ /**
65
+ * 仅打印解析结果,不实际复制文件。
66
+ *
67
+ * @default false
68
+ */
69
+ dryRun?: boolean;
70
+ }
71
+ interface ResolvedMoveVercelOutputToRootOptions {
72
+ cwd: string;
73
+ monorepoRoot: string;
74
+ sourceDir: string;
75
+ targetDir: string;
76
+ skipClean: boolean;
77
+ dryRun: boolean;
78
+ }
79
+ interface MoveVercelOutputToRootResult extends ResolvedMoveVercelOutputToRootOptions {
80
+ copied: boolean;
81
+ }
82
+ /**
83
+ * 解析脚本所需的全部路径。
84
+ */
85
+ declare function resolveMoveVercelOutputToRootOptions(options?: MoveVercelOutputToRootOptions): ResolvedMoveVercelOutputToRootOptions;
86
+ /**
87
+ * 将当前子包内的 `.vercel/output` 移动到 monorepo 根目录。
88
+ * @description
89
+ * 该函数服务于 Vercel 在 monorepo 场景下的部署:
90
+ * 1. 构建仍在子包目录内执行
91
+ * 2. 产物默认出现在子包的 `.vercel/output`
92
+ * 3. Vercel 却要求在 monorepo 根目录下读取 `.vercel/output`
93
+ */
94
+ declare function moveVercelOutputToRoot(options?: MoveVercelOutputToRootOptions): MoveVercelOutputToRootResult;
95
+ /**
96
+ * 解析命令行参数。
97
+ */
98
+ declare function parseMoveVercelOutputToRootCliArgs(args: string[]): MoveVercelOutputToRootOptions & {
99
+ help?: boolean;
100
+ };
101
+ /**
102
+ * CLI 帮助信息。
103
+ */
104
+ declare function getMoveVercelOutputToRootHelpText(): string;
105
+ /**
106
+ * 执行 CLI。
107
+ */
108
+ declare function runMoveVercelOutputToRootCli(args?: string[]): void;
109
+
33
110
  interface WriteYaml2mdParams<T = Record<string, any>> {
34
111
  /** 目标md文件地址 */
35
112
  mdPath: string;
@@ -43,6 +120,16 @@ interface WriteYaml2mdParams<T = Record<string, any>> {
43
120
  */
44
121
  declare function writeYaml2md<T>(params: WriteYaml2mdParams<T>): void;
45
122
 
123
+ /**
124
+ * 从给定目录开始,向上查找 monorepo 根目录。
125
+ * @description
126
+ * 通过查找 `pnpm-workspace.yaml` 文件来定位 monorepo 根目录。
127
+ * 该函数适用于脚本在 monorepo 子包内运行的场景。
128
+ *
129
+ * @param startDir 起始查找目录,默认值为 `process.cwd()`
130
+ * @returns 找到的 monorepo 根目录绝对路径;若未找到则返回 `null`
131
+ */
132
+ declare function findMonorepoRoot(startDir?: string): string | null;
46
133
  /**
47
134
  * 判断目标项目是否是 monorepo 格式的项目
48
135
  * @description
@@ -56,4 +143,4 @@ declare function writeYaml2md<T>(params: WriteYaml2mdParams<T>): void;
56
143
  */
57
144
  declare function isMonorepoProject(): boolean;
58
145
 
59
- export { type PackageInfo, type WriteYaml2mdParams, clean, defaultCleanTargets, getRuanCatPkgInfo, isMonorepoProject, writeYaml2md };
146
+ export { type MoveVercelOutputToRootOptions, type MoveVercelOutputToRootResult, type PackageInfo, type ResolvedMoveVercelOutputToRootOptions, type WriteYaml2mdParams, clean, defaultCleanTargets, findMonorepoRoot, getMoveVercelOutputToRootHelpText, getRuanCatPkgInfo, isMonorepoProject, moveVercelOutputToRoot, parseMoveVercelOutputToRootCliArgs, resolveMoveVercelOutputToRootOptions, runMoveVercelOutputToRootCli, writeYaml2md };
@@ -1,7 +1,7 @@
1
1
  // src/node-esm/ruan-cat-pkg-info.ts
2
2
  import { spawnSync } from "child_process";
3
3
  async function getRuanCatPkgInfo() {
4
- return new Promise((resolve, reject) => {
4
+ return new Promise((resolve2, reject) => {
5
5
  const result = spawnSync("pnpm", ["s", "@ruan-cat/*", "--registry", "https://registry.npmmirror.com/", "--json"], {
6
6
  encoding: "utf-8"
7
7
  });
@@ -23,7 +23,7 @@ async function getRuanCatPkgInfo() {
23
23
  url: `https://npm.im/${pkg.name}`
24
24
  })
25
25
  );
26
- resolve(res);
26
+ resolve2(res);
27
27
  });
28
28
  }
29
29
 
@@ -33,8 +33,8 @@ import { spawnSync as spawnSync2 } from "child_process";
33
33
  // src/simple-promise-tools.ts
34
34
  function generateSimpleAsyncTask(func) {
35
35
  return function(...args) {
36
- return new Promise((resolve, reject) => {
37
- resolve(func(...args));
36
+ return new Promise((resolve2, reject) => {
37
+ resolve2(func(...args));
38
38
  });
39
39
  };
40
40
  }
@@ -85,7 +85,7 @@ import { isPlainObject, isArray } from "lodash-es";
85
85
  import consola2 from "consola";
86
86
 
87
87
  // src/monorepo/index.ts
88
- import { join } from "path";
88
+ import { dirname, join, parse, resolve } from "path";
89
89
  import * as fs from "fs";
90
90
  import { globSync } from "tinyglobby";
91
91
 
@@ -2715,8 +2715,22 @@ var jsYaml = {
2715
2715
 
2716
2716
  // src/monorepo/index.ts
2717
2717
  import { isUndefined } from "lodash-es";
2718
- function pathChange(path) {
2719
- return path.replace(/\\/g, "/");
2718
+ function pathChange(path2) {
2719
+ return path2.replace(/\\/g, "/");
2720
+ }
2721
+ function findMonorepoRoot(startDir = process.cwd()) {
2722
+ let currentDir = resolve(startDir);
2723
+ const fileSystemRoot = parse(currentDir).root;
2724
+ while (true) {
2725
+ const workspaceConfigPath = join(currentDir, "pnpm-workspace.yaml");
2726
+ if (fs.existsSync(workspaceConfigPath)) {
2727
+ return currentDir;
2728
+ }
2729
+ if (currentDir === fileSystemRoot) {
2730
+ return null;
2731
+ }
2732
+ currentDir = dirname(currentDir);
2733
+ }
2720
2734
  }
2721
2735
  function isMonorepoProject() {
2722
2736
  const workspaceConfigPath = join(process.cwd(), "pnpm-workspace.yaml");
@@ -2775,21 +2789,188 @@ async function clean(targets) {
2775
2789
  await doClean();
2776
2790
  }
2777
2791
 
2792
+ // src/node-esm/scripts/move-vercel-output-to-root/index.ts
2793
+ import * as fs2 from "fs";
2794
+ import path from "path";
2795
+ import { fileURLToPath } from "url";
2796
+ import consola3 from "consola";
2797
+ function resolvePathFromBase(baseDir, inputPath) {
2798
+ if (path.isAbsolute(inputPath)) {
2799
+ return path.normalize(inputPath);
2800
+ }
2801
+ return path.resolve(baseDir, inputPath);
2802
+ }
2803
+ function resolveMonorepoRoot(cwd, rootDir) {
2804
+ if (rootDir) {
2805
+ const resolvedRoot = resolvePathFromBase(cwd, rootDir);
2806
+ const workspaceConfigPath = path.join(resolvedRoot, "pnpm-workspace.yaml");
2807
+ if (!fs2.existsSync(workspaceConfigPath)) {
2808
+ throw new Error(`\u6307\u5B9A\u7684 rootDir \u4E0D\u662F\u6709\u6548\u7684 monorepo \u6839\u76EE\u5F55\uFF1A${resolvedRoot}\u3002\u7F3A\u5C11 pnpm-workspace.yaml\u3002`);
2809
+ }
2810
+ return resolvedRoot;
2811
+ }
2812
+ const detectedRoot = findMonorepoRoot(cwd);
2813
+ if (detectedRoot) {
2814
+ return detectedRoot;
2815
+ }
2816
+ throw new Error(`\u65E0\u6CD5\u4ECE\u5F53\u524D\u76EE\u5F55\u5411\u4E0A\u5B9A\u4F4D monorepo \u6839\u76EE\u5F55\uFF1A${cwd}\u3002\u8BF7\u663E\u5F0F\u4F20\u5165 --root-dir \u53C2\u6570\u3002`);
2817
+ }
2818
+ function resolveMoveVercelOutputToRootOptions(options = {}) {
2819
+ const cwd = path.resolve(options.cwd ?? process.cwd());
2820
+ const monorepoRoot = resolveMonorepoRoot(cwd, options.rootDir);
2821
+ const sourceDir = resolvePathFromBase(cwd, options.sourceDir ?? ".vercel/output");
2822
+ const targetDir = resolvePathFromBase(monorepoRoot, options.targetDir ?? ".vercel/output");
2823
+ if (sourceDir === targetDir) {
2824
+ throw new Error(`\u6E90\u76EE\u5F55\u548C\u76EE\u6807\u76EE\u5F55\u89E3\u6790\u5230\u4E86\u540C\u4E00\u8DEF\u5F84\uFF1A${sourceDir}\u3002\u8BF7\u8C03\u6574 sourceDir \u6216 targetDir\u3002`);
2825
+ }
2826
+ return {
2827
+ cwd,
2828
+ monorepoRoot,
2829
+ sourceDir,
2830
+ targetDir,
2831
+ skipClean: options.skipClean ?? false,
2832
+ dryRun: options.dryRun ?? false
2833
+ };
2834
+ }
2835
+ function copyDirectoryContents(sourceDir, targetDir) {
2836
+ fs2.mkdirSync(targetDir, { recursive: true });
2837
+ for (const entryName of fs2.readdirSync(sourceDir)) {
2838
+ const sourceEntry = path.join(sourceDir, entryName);
2839
+ const targetEntry = path.join(targetDir, entryName);
2840
+ fs2.cpSync(sourceEntry, targetEntry, {
2841
+ force: true,
2842
+ recursive: true
2843
+ });
2844
+ }
2845
+ }
2846
+ function moveVercelOutputToRoot(options = {}) {
2847
+ const resolvedOptions = resolveMoveVercelOutputToRootOptions(options);
2848
+ if (!fs2.existsSync(resolvedOptions.sourceDir)) {
2849
+ throw new Error(`\u6E90\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u65E0\u6CD5\u642C\u8FD0 Vercel \u6784\u5EFA\u4EA7\u7269\uFF1A${resolvedOptions.sourceDir}\u3002\u8BF7\u5148\u5728\u5B50\u5305\u5185\u5B8C\u6210\u6784\u5EFA\u3002`);
2850
+ }
2851
+ if (!fs2.statSync(resolvedOptions.sourceDir).isDirectory()) {
2852
+ throw new Error(`\u6E90\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${resolvedOptions.sourceDir}`);
2853
+ }
2854
+ consola3.info("move-vercel-output-to-root \u89E3\u6790\u7ED3\u679C");
2855
+ consola3.log(`- cwd: ${resolvedOptions.cwd}`);
2856
+ consola3.log(`- monorepoRoot: ${resolvedOptions.monorepoRoot}`);
2857
+ consola3.log(`- sourceDir: ${resolvedOptions.sourceDir}`);
2858
+ consola3.log(`- targetDir: ${resolvedOptions.targetDir}`);
2859
+ consola3.log(`- skipClean: ${resolvedOptions.skipClean}`);
2860
+ consola3.log(`- dryRun: ${resolvedOptions.dryRun}`);
2861
+ if (resolvedOptions.dryRun) {
2862
+ consola3.info("dry-run \u6A21\u5F0F\uFF1A\u4EC5\u8F93\u51FA\u8DEF\u5F84\u4FE1\u606F\uFF0C\u4E0D\u6267\u884C\u590D\u5236\u3002");
2863
+ return {
2864
+ ...resolvedOptions,
2865
+ copied: false
2866
+ };
2867
+ }
2868
+ if (!resolvedOptions.skipClean) {
2869
+ fs2.rmSync(resolvedOptions.targetDir, {
2870
+ force: true,
2871
+ recursive: true
2872
+ });
2873
+ }
2874
+ copyDirectoryContents(resolvedOptions.sourceDir, resolvedOptions.targetDir);
2875
+ consola3.success(`\u5DF2\u5C06 ${resolvedOptions.sourceDir} \u642C\u8FD0\u5230 ${resolvedOptions.targetDir}`);
2876
+ return {
2877
+ ...resolvedOptions,
2878
+ copied: true
2879
+ };
2880
+ }
2881
+ function parseMoveVercelOutputToRootCliArgs(args) {
2882
+ const options = {};
2883
+ const readFlagValue = (flagName, currentIndex) => {
2884
+ const value = args[currentIndex + 1];
2885
+ if (!value || value.startsWith("--")) {
2886
+ throw new Error(`\u53C2\u6570 ${flagName} \u7F3A\u5C11\u5BF9\u5E94\u7684\u503C\u3002`);
2887
+ }
2888
+ return value;
2889
+ };
2890
+ for (let index = 0; index < args.length; index += 1) {
2891
+ const currentArg = args[index];
2892
+ switch (currentArg) {
2893
+ case "--root-dir":
2894
+ options.rootDir = readFlagValue(currentArg, index);
2895
+ index += 1;
2896
+ break;
2897
+ case "--source-dir":
2898
+ options.sourceDir = readFlagValue(currentArg, index);
2899
+ index += 1;
2900
+ break;
2901
+ case "--target-dir":
2902
+ options.targetDir = readFlagValue(currentArg, index);
2903
+ index += 1;
2904
+ break;
2905
+ case "--skip-clean":
2906
+ options.skipClean = true;
2907
+ break;
2908
+ case "--dry-run":
2909
+ options.dryRun = true;
2910
+ break;
2911
+ case "--help":
2912
+ case "-h":
2913
+ options.help = true;
2914
+ break;
2915
+ default:
2916
+ throw new Error(`\u4E0D\u652F\u6301\u7684\u53C2\u6570\uFF1A${currentArg}`);
2917
+ }
2918
+ }
2919
+ return options;
2920
+ }
2921
+ function getMoveVercelOutputToRootHelpText() {
2922
+ return [
2923
+ "tsx @ruan-cat/utils/move-vercel-output-to-root [options]",
2924
+ "",
2925
+ "\u9009\u9879\uFF1A",
2926
+ " --root-dir <path> \u663E\u5F0F\u6307\u5B9A monorepo \u6839\u76EE\u5F55",
2927
+ " --source-dir <path> \u6307\u5B9A\u5B50\u5305\u5185\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\uFF0C\u9ED8\u8BA4 .vercel/output",
2928
+ " --target-dir <path> \u6307\u5B9A\u6839\u76EE\u5F55\u5185\u76EE\u6807\u76EE\u5F55\uFF0C\u9ED8\u8BA4 .vercel/output",
2929
+ " --skip-clean \u8DF3\u8FC7\u76EE\u6807\u76EE\u5F55\u6E05\u7406",
2930
+ " --dry-run \u4EC5\u6253\u5370\u8DEF\u5F84\u89E3\u6790\u7ED3\u679C\uFF0C\u4E0D\u6267\u884C\u590D\u5236",
2931
+ " -h, --help \u67E5\u770B\u5E2E\u52A9\u4FE1\u606F"
2932
+ ].join("\n");
2933
+ }
2934
+ function runMoveVercelOutputToRootCli(args = process.argv.slice(2)) {
2935
+ const cliOptions = parseMoveVercelOutputToRootCliArgs(args);
2936
+ if (cliOptions.help) {
2937
+ console.log(getMoveVercelOutputToRootHelpText());
2938
+ return;
2939
+ }
2940
+ moveVercelOutputToRoot(cliOptions);
2941
+ }
2942
+ function isRunningAsCli() {
2943
+ const currentFilePath = fileURLToPath(import.meta.url);
2944
+ const entryPath = process.argv[1];
2945
+ if (!entryPath) {
2946
+ return false;
2947
+ }
2948
+ return path.resolve(entryPath) === currentFilePath;
2949
+ }
2950
+ if (isRunningAsCli()) {
2951
+ try {
2952
+ runMoveVercelOutputToRootCli();
2953
+ } catch (error) {
2954
+ consola3.error(error instanceof Error ? error.message : String(error));
2955
+ process.exitCode = 1;
2956
+ }
2957
+ }
2958
+
2778
2959
  // src/node-esm/yaml-to-md.ts
2779
2960
  import { readFileSync as readFileSync2, writeFileSync } from "fs";
2780
- import { consola as consola3 } from "consola";
2961
+ import { consola as consola4 } from "consola";
2781
2962
  import { isUndefined as isUndefined2 } from "lodash-es";
2782
2963
  function writeYaml2md(params) {
2783
- consola3.info(` \u5F53\u524D\u8FD0\u884C\u7684\u5730\u5740\u4E3A\uFF1A ${process.cwd()} `);
2964
+ consola4.info(` \u5F53\u524D\u8FD0\u884C\u7684\u5730\u5740\u4E3A\uFF1A ${process.cwd()} `);
2784
2965
  const { mdPath, data } = params;
2785
2966
  if (isUndefined2(mdPath)) {
2786
- consola3.error(" \u8BF7\u63D0\u4F9Bmd\u6587\u4EF6\u7684\u5730\u5740 ");
2967
+ consola4.error(" \u8BF7\u63D0\u4F9Bmd\u6587\u4EF6\u7684\u5730\u5740 ");
2787
2968
  process.exit(1);
2788
2969
  }
2789
2970
  try {
2790
2971
  readFileSync2(mdPath, "utf-8");
2791
2972
  } catch (error) {
2792
- consola3.error(` \u6587\u4EF6 ${mdPath} \u4E0D\u5B58\u5728 `);
2973
+ consola4.error(` \u6587\u4EF6 ${mdPath} \u4E0D\u5B58\u5728 `);
2793
2974
  process.exit(1);
2794
2975
  }
2795
2976
  const mdContent = readFileSync2(mdPath, "utf-8");
@@ -2799,13 +2980,19 @@ ${yamlContent}---
2799
2980
 
2800
2981
  ${mdContent}`;
2801
2982
  writeFileSync(mdPath, newContent, "utf-8");
2802
- consola3.success(` \u5DF2\u5C06YAML\u6570\u636E\u5199\u5165\u5230 ${mdPath} `);
2983
+ consola4.success(` \u5DF2\u5C06YAML\u6570\u636E\u5199\u5165\u5230 ${mdPath} `);
2803
2984
  }
2804
2985
  export {
2805
2986
  clean,
2806
2987
  defaultCleanTargets,
2988
+ findMonorepoRoot,
2989
+ getMoveVercelOutputToRootHelpText,
2807
2990
  getRuanCatPkgInfo,
2808
2991
  isMonorepoProject,
2992
+ moveVercelOutputToRoot,
2993
+ parseMoveVercelOutputToRootCliArgs,
2994
+ resolveMoveVercelOutputToRootOptions,
2995
+ runMoveVercelOutputToRootCli,
2809
2996
  writeYaml2md
2810
2997
  };
2811
2998
  /*! Bundled license information: