create-0to1-monorepo 0.1.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.
Files changed (110) hide show
  1. package/README.md +19 -0
  2. package/bin/create-0to1-monorepo.js +236 -0
  3. package/lib/template.js +132 -0
  4. package/package.json +24 -0
  5. package/scripts/prepare-template.mjs +14 -0
  6. package/template/.changeset/README.md +8 -0
  7. package/template/.changeset/config.json +11 -0
  8. package/template/.dockerignore +38 -0
  9. package/template/.github/workflows/workflow.yml +42 -0
  10. package/template/README.md +422 -0
  11. package/template/apps/project/Dockerfile +32 -0
  12. package/template/apps/project/eslint.config.mjs +4 -0
  13. package/template/apps/project/index.html +14 -0
  14. package/template/apps/project/index.ts +15 -0
  15. package/template/apps/project/package.json +21 -0
  16. package/template/apps/project/tsconfig.json +9 -0
  17. package/template/apps/project/vite.config.ts +6 -0
  18. package/template/apps/web/CHANGELOG.md +13 -0
  19. package/template/apps/web/Dockerfile +43 -0
  20. package/template/apps/web/README.md +111 -0
  21. package/template/apps/web/app/favicon.ico +0 -0
  22. package/template/apps/web/app/global-error.tsx +12 -0
  23. package/template/apps/web/app/globals.css +0 -0
  24. package/template/apps/web/app/layout.tsx +28 -0
  25. package/template/apps/web/app/page.tsx +7 -0
  26. package/template/apps/web/app/providers.tsx +25 -0
  27. package/template/apps/web/eslint.config.js +4 -0
  28. package/template/apps/web/next.config.js +4 -0
  29. package/template/apps/web/package.json +31 -0
  30. package/template/apps/web/public/file-text.svg +3 -0
  31. package/template/apps/web/public/globe.svg +10 -0
  32. package/template/apps/web/public/next.svg +1 -0
  33. package/template/apps/web/public/turborepo-dark.svg +19 -0
  34. package/template/apps/web/public/turborepo-light.svg +19 -0
  35. package/template/apps/web/public/vercel.svg +10 -0
  36. package/template/apps/web/public/window.svg +3 -0
  37. package/template/apps/web/tsconfig.json +20 -0
  38. package/template/package.json +23 -0
  39. package/template/packages/core/CHANGELOG.md +7 -0
  40. package/template/packages/core/eslint.config.mjs +4 -0
  41. package/template/packages/core/package.json +31 -0
  42. package/template/packages/core/src/api/test.ts +26 -0
  43. package/template/packages/core/src/api.ts +23 -0
  44. package/template/packages/core/src/app-error.ts +84 -0
  45. package/template/packages/core/src/contracts/article.ts +20 -0
  46. package/template/packages/core/src/index.ts +4 -0
  47. package/template/packages/core/src/internal.ts +21 -0
  48. package/template/packages/core/src/request.ts +235 -0
  49. package/template/packages/core/src/schema-utils.ts +24 -0
  50. package/template/packages/core/tsconfig.json +8 -0
  51. package/template/packages/core-next/CHANGELOG.md +12 -0
  52. package/template/packages/core-next/eslint.config.mjs +13 -0
  53. package/template/packages/core-next/package.json +43 -0
  54. package/template/packages/core-next/src/auth.client.ts +1 -0
  55. package/template/packages/core-next/src/auth.server.ts +91 -0
  56. package/template/packages/core-next/src/bootstrap.client.tsx +17 -0
  57. package/template/packages/core-next/src/bootstrap.tsx +18 -0
  58. package/template/packages/core-next/src/index.ts +1 -0
  59. package/template/packages/core-next/tsconfig.json +8 -0
  60. package/template/packages/core-react/eslint.config.mjs +4 -0
  61. package/template/packages/core-react/package.json +34 -0
  62. package/template/packages/core-react/src/app-providers.tsx +36 -0
  63. package/template/packages/core-react/src/auth.ts +39 -0
  64. package/template/packages/core-react/src/hydration.tsx +26 -0
  65. package/template/packages/core-react/src/index.ts +7 -0
  66. package/template/packages/core-react/src/provider.tsx +39 -0
  67. package/template/packages/core-react/src/query-client.ts +71 -0
  68. package/template/packages/core-react/src/query-error-handler.ts +59 -0
  69. package/template/packages/core-react/src/query-keys.ts +22 -0
  70. package/template/packages/core-react/tsconfig.json +8 -0
  71. package/template/packages/eslint-config/CHANGELOG.md +7 -0
  72. package/template/packages/eslint-config/README.md +3 -0
  73. package/template/packages/eslint-config/base.js +57 -0
  74. package/template/packages/eslint-config/next.js +22 -0
  75. package/template/packages/eslint-config/package.json +25 -0
  76. package/template/packages/eslint-config/react-internal.js +33 -0
  77. package/template/packages/typescript-config/CHANGELOG.md +7 -0
  78. package/template/packages/typescript-config/base.json +19 -0
  79. package/template/packages/typescript-config/nextjs.json +12 -0
  80. package/template/packages/typescript-config/package.json +9 -0
  81. package/template/packages/typescript-config/react-library.json +7 -0
  82. package/template/packages/ui/CHANGELOG.md +7 -0
  83. package/template/packages/ui/eslint.config.mjs +4 -0
  84. package/template/packages/ui/package.json +26 -0
  85. package/template/packages/ui/src/button.tsx +20 -0
  86. package/template/packages/ui/src/card.tsx +27 -0
  87. package/template/packages/ui/src/code.tsx +11 -0
  88. package/template/packages/ui/tsconfig.json +8 -0
  89. package/template/pnpm-lock.yaml +9313 -0
  90. package/template/pnpm-workspace.yaml +9 -0
  91. package/template/turbo/generators/config.js +1217 -0
  92. package/template/turbo/generators/templates/next-app/Dockerfile.tpl +30 -0
  93. package/template/turbo/generators/templates/next-app/README.md.tpl +118 -0
  94. package/template/turbo/generators/templates/next-app/app/global-error.tsx.tpl +12 -0
  95. package/template/turbo/generators/templates/next-app/app/globals.css.tpl +1 -0
  96. package/template/turbo/generators/templates/next-app/app/layout.tsx.tpl +29 -0
  97. package/template/turbo/generators/templates/next-app/app/page.tsx.tpl +7 -0
  98. package/template/turbo/generators/templates/next-app/app/providers.tsx.tpl +25 -0
  99. package/template/turbo/generators/templates/next-app/eslint.config.js.tpl +4 -0
  100. package/template/turbo/generators/templates/next-app/next.config.js.tpl +6 -0
  101. package/template/turbo/generators/templates/next-app/tsconfig.json.tpl +18 -0
  102. package/template/turbo/generators/templates/vite-app/Dockerfile.tpl +22 -0
  103. package/template/turbo/generators/templates/vite-app/README.plain.md.tpl +90 -0
  104. package/template/turbo/generators/templates/vite-app/README.react.md.tpl +107 -0
  105. package/template/turbo/generators/templates/vite-app/eslint.config.mjs.tpl +4 -0
  106. package/template/turbo/generators/templates/vite-app/index.html.tpl +12 -0
  107. package/template/turbo/generators/templates/vite-app/index.ts.tpl +22 -0
  108. package/template/turbo/generators/templates/vite-app/tsconfig.json.tpl +9 -0
  109. package/template/turbo/generators/templates/vite-app/vite.config.ts.tpl +6 -0
  110. package/template/turbo.json +28 -0
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # create-0to1-monorepo
2
+
3
+ `pnpm create 0to1-monorepo`로 현재 모노레포 템플릿을 새 디렉터리에 복사하는 create package입니다.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ pnpm create 0to1-monorepo my-monorepo
9
+ ```
10
+
11
+ 의존성 설치를 생략하려면:
12
+
13
+ ```bash
14
+ pnpm create 0to1-monorepo my-monorepo --no-install
15
+ ```
16
+
17
+ ## Publish flow
18
+
19
+ 이 패키지는 `prepack` 시점에 현재 워크스페이스를 `template/`으로 복사해서 publish 가능한 상태를 만듭니다.
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from "node:child_process";
4
+ import * as fs from "node:fs";
5
+ import path from "node:path";
6
+ import process from "node:process";
7
+ import { createInterface } from "node:readline/promises";
8
+ import { fileURLToPath } from "node:url";
9
+
10
+ import {
11
+ copyDirectoryContents,
12
+ copyRepositoryTemplate,
13
+ } from "../lib/template.js";
14
+
15
+ function detectPackageManager() {
16
+ const userAgent = process.env.npm_config_user_agent ?? "";
17
+
18
+ if (userAgent.includes("pnpm/")) {
19
+ return "pnpm";
20
+ }
21
+
22
+ if (userAgent.includes("yarn/")) {
23
+ return "yarn";
24
+ }
25
+
26
+ if (userAgent.includes("bun/")) {
27
+ return "bun";
28
+ }
29
+
30
+ return "npm";
31
+ }
32
+
33
+ function getInstallCommand(packageManager) {
34
+ switch (packageManager) {
35
+ case "pnpm":
36
+ return "pnpm install";
37
+ case "yarn":
38
+ return "yarn";
39
+ case "bun":
40
+ return "bun install";
41
+ default:
42
+ return "npm install";
43
+ }
44
+ }
45
+
46
+ function getDevCommand(packageManager) {
47
+ switch (packageManager) {
48
+ case "npm":
49
+ return "npm run dev";
50
+ case "bun":
51
+ return "bun run dev";
52
+ default:
53
+ return `${packageManager} dev`;
54
+ }
55
+ }
56
+
57
+ function sanitizePackageName(name) {
58
+ return name
59
+ .trim()
60
+ .toLowerCase()
61
+ .replace(/[^a-z0-9-_]+/g, "-")
62
+ .replace(/^-+|-+$/g, "");
63
+ }
64
+
65
+ function parseArguments(argv) {
66
+ const args = [...argv];
67
+ const options = {
68
+ install: true,
69
+ targetDirectory: "",
70
+ };
71
+
72
+ while (args.length > 0) {
73
+ const currentArg = args.shift();
74
+
75
+ if (!currentArg) {
76
+ continue;
77
+ }
78
+
79
+ if (currentArg === "--no-install") {
80
+ options.install = false;
81
+ continue;
82
+ }
83
+
84
+ if (currentArg.startsWith("-")) {
85
+ throw new Error(`지원하지 않는 옵션입니다: ${currentArg}`);
86
+ }
87
+
88
+ if (!options.targetDirectory) {
89
+ options.targetDirectory = currentArg;
90
+ continue;
91
+ }
92
+
93
+ throw new Error(`대상 디렉터리는 하나만 지정할 수 있습니다: ${currentArg}`);
94
+ }
95
+
96
+ return options;
97
+ }
98
+
99
+ async function promptForTargetDirectory() {
100
+ const readline = createInterface({
101
+ input: process.stdin,
102
+ output: process.stdout,
103
+ });
104
+
105
+ try {
106
+ const answer = await readline.question(
107
+ "생성할 디렉터리 이름을 입력하세요: ",
108
+ );
109
+
110
+ return answer.trim();
111
+ } finally {
112
+ readline.close();
113
+ }
114
+ }
115
+
116
+ function ensureCreatableDirectory(directoryPath) {
117
+ if (!fs.existsSync(directoryPath)) {
118
+ return;
119
+ }
120
+
121
+ const entries = fs.readdirSync(directoryPath);
122
+
123
+ if (entries.length > 0) {
124
+ throw new Error(`대상 디렉터리가 비어 있지 않습니다: ${directoryPath}`);
125
+ }
126
+ }
127
+
128
+ function updateRootPackageJson(targetDirectory, projectName) {
129
+ const packageJsonPath = path.join(targetDirectory, "package.json");
130
+
131
+ if (!fs.existsSync(packageJsonPath)) {
132
+ return;
133
+ }
134
+
135
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
136
+ const sanitizedName = sanitizePackageName(projectName);
137
+
138
+ if (sanitizedName) {
139
+ packageJson.name = sanitizedName;
140
+ }
141
+
142
+ packageJson.private = true;
143
+
144
+ fs.writeFileSync(
145
+ packageJsonPath,
146
+ `${JSON.stringify(packageJson, null, 2)}\n`,
147
+ "utf-8",
148
+ );
149
+ }
150
+
151
+ function resolveTemplateSourceDirectory() {
152
+ const packageDirectory = path.resolve(
153
+ path.dirname(fileURLToPath(import.meta.url)),
154
+ "..",
155
+ );
156
+ const bundledTemplateDirectory = path.join(packageDirectory, "template");
157
+
158
+ if (fs.existsSync(bundledTemplateDirectory)) {
159
+ return {
160
+ mode: "bundled",
161
+ path: bundledTemplateDirectory,
162
+ };
163
+ }
164
+
165
+ return {
166
+ mode: "workspace",
167
+ path: path.resolve(packageDirectory, "..", ".."),
168
+ };
169
+ }
170
+
171
+ function copyTemplateIntoDirectory(targetDirectory) {
172
+ const templateSource = resolveTemplateSourceDirectory();
173
+
174
+ if (templateSource.mode === "bundled") {
175
+ copyDirectoryContents(templateSource.path, targetDirectory);
176
+ return;
177
+ }
178
+
179
+ copyRepositoryTemplate(templateSource.path, targetDirectory);
180
+ }
181
+
182
+ function printNextSteps(targetDirectory, packageManager, install) {
183
+ const relativeDirectory =
184
+ path.relative(process.cwd(), targetDirectory) || ".";
185
+ const directoryLabel = relativeDirectory.startsWith("..")
186
+ ? targetDirectory
187
+ : relativeDirectory;
188
+
189
+ console.log("");
190
+ console.log("완료되었습니다 🎉");
191
+ console.log("");
192
+ console.log("다음 단계:");
193
+ console.log(` cd ${directoryLabel}`);
194
+
195
+ if (!install) {
196
+ console.log(` ${getInstallCommand(packageManager)}`);
197
+ }
198
+
199
+ console.log(` ${getDevCommand(packageManager)}`);
200
+ }
201
+
202
+ async function main() {
203
+ const options = parseArguments(process.argv.slice(2));
204
+ const targetInput =
205
+ options.targetDirectory || (await promptForTargetDirectory());
206
+
207
+ if (!targetInput) {
208
+ throw new Error("대상 디렉터리 이름이 필요합니다.");
209
+ }
210
+
211
+ const targetDirectory = path.resolve(process.cwd(), targetInput);
212
+ const packageManager = detectPackageManager();
213
+
214
+ ensureCreatableDirectory(targetDirectory);
215
+ fs.mkdirSync(targetDirectory, { recursive: true });
216
+
217
+ copyTemplateIntoDirectory(targetDirectory);
218
+ updateRootPackageJson(targetDirectory, path.basename(targetDirectory));
219
+
220
+ if (options.install) {
221
+ execSync(getInstallCommand(packageManager), {
222
+ cwd: targetDirectory,
223
+ stdio: "inherit",
224
+ });
225
+ }
226
+
227
+ printNextSteps(targetDirectory, packageManager, options.install);
228
+ }
229
+
230
+ main().catch((error) => {
231
+ console.error("");
232
+ console.error(
233
+ error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다.",
234
+ );
235
+ process.exit(1);
236
+ });
@@ -0,0 +1,132 @@
1
+ import * as fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ export const ROOT_TEMPLATE_ENTRIES = [
6
+ ".changeset",
7
+ ".dockerignore",
8
+ ".github",
9
+ ".gitignore",
10
+ ".npmrc",
11
+ "README.md",
12
+ "apps",
13
+ "package.json",
14
+ "packages",
15
+ "pnpm-lock.yaml",
16
+ "pnpm-workspace.yaml",
17
+ "turbo",
18
+ "turbo.json",
19
+ ];
20
+
21
+ const EXCLUDED_BASENAMES = new Set([
22
+ ".DS_Store",
23
+ ".git",
24
+ ".idea",
25
+ ".turbo",
26
+ ".vscode",
27
+ "node_modules",
28
+ ]);
29
+ const EXCLUDED_ENV_FILE_NAMES = new Set([
30
+ ".env",
31
+ ".env.local",
32
+ ".env.development.local",
33
+ ".env.production.local",
34
+ ".env.test.local",
35
+ ]);
36
+ const CREATE_PACKAGE_RELATIVE_PATH = path.join(
37
+ "packages",
38
+ "create-0to1-monorepo",
39
+ );
40
+
41
+ function normalizePath(filePath) {
42
+ return filePath.split(path.sep).join("/");
43
+ }
44
+
45
+ function shouldSkipPath(sourceRoot, currentPath) {
46
+ const relativePath = normalizePath(path.relative(sourceRoot, currentPath));
47
+
48
+ if (!relativePath || relativePath === ".") {
49
+ return false;
50
+ }
51
+
52
+ const segments = relativePath.split("/");
53
+
54
+ if (segments.some((segment) => EXCLUDED_BASENAMES.has(segment))) {
55
+ return true;
56
+ }
57
+
58
+ if (EXCLUDED_ENV_FILE_NAMES.has(path.basename(currentPath))) {
59
+ return true;
60
+ }
61
+
62
+ return relativePath === normalizePath(CREATE_PACKAGE_RELATIVE_PATH);
63
+ }
64
+
65
+ export function ensureEmptyDirectory(directoryPath) {
66
+ if (fs.existsSync(directoryPath)) {
67
+ fs.readdirSync(directoryPath).forEach((entryName) => {
68
+ fs.rmSync(path.join(directoryPath, entryName), {
69
+ force: true,
70
+ recursive: true,
71
+ });
72
+ });
73
+ }
74
+
75
+ fs.mkdirSync(directoryPath, { recursive: true });
76
+ }
77
+
78
+ export function copyDirectoryContents(sourceDirectory, destinationDirectory) {
79
+ ensureEmptyDirectory(destinationDirectory);
80
+
81
+ fs.readdirSync(sourceDirectory).forEach((entryName) => {
82
+ const sourcePath = path.join(sourceDirectory, entryName);
83
+ const destinationPath = path.join(destinationDirectory, entryName);
84
+
85
+ fs.cpSync(sourcePath, destinationPath, {
86
+ recursive: true,
87
+ });
88
+ });
89
+ }
90
+
91
+ export function copyRepositoryTemplate(sourceRoot, destinationDirectory) {
92
+ ensureEmptyDirectory(destinationDirectory);
93
+
94
+ ROOT_TEMPLATE_ENTRIES.forEach((entryName) => {
95
+ const sourcePath = path.join(sourceRoot, entryName);
96
+
97
+ if (!fs.existsSync(sourcePath)) {
98
+ return;
99
+ }
100
+
101
+ const destinationPath = path.join(destinationDirectory, entryName);
102
+
103
+ if (entryName === "packages") {
104
+ fs.mkdirSync(destinationPath, { recursive: true });
105
+
106
+ fs.readdirSync(sourcePath).forEach((packageName) => {
107
+ if (packageName === "create-0to1-monorepo") {
108
+ return;
109
+ }
110
+
111
+ const sourcePackagePath = path.join(sourcePath, packageName);
112
+ const destinationPackagePath = path.join(destinationPath, packageName);
113
+
114
+ fs.cpSync(sourcePackagePath, destinationPackagePath, {
115
+ filter: (currentPath) => !shouldSkipPath(sourceRoot, currentPath),
116
+ recursive: true,
117
+ });
118
+ });
119
+
120
+ return;
121
+ }
122
+
123
+ fs.cpSync(sourcePath, destinationPath, {
124
+ filter: (currentPath) => !shouldSkipPath(sourceRoot, currentPath),
125
+ recursive: true,
126
+ });
127
+ });
128
+ }
129
+
130
+ export function getPackageDirectory(metaUrl) {
131
+ return path.resolve(path.dirname(fileURLToPath(metaUrl)), "..");
132
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "create-0to1-monorepo",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "create-0to1-monorepo": "./bin/create-0to1-monorepo.js"
7
+ },
8
+ "files": [
9
+ "README.md",
10
+ "bin",
11
+ "lib",
12
+ "scripts",
13
+ "template"
14
+ ],
15
+ "scripts": {
16
+ "prepack": "node ./scripts/prepare-template.mjs"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "engines": {
22
+ "node": ">=18"
23
+ }
24
+ }
@@ -0,0 +1,14 @@
1
+ import path from "node:path";
2
+
3
+ import {
4
+ copyRepositoryTemplate,
5
+ getPackageDirectory,
6
+ } from "../lib/template.js";
7
+
8
+ const packageDirectory = getPackageDirectory(import.meta.url);
9
+ const repositoryRoot = path.resolve(packageDirectory, "..", "..");
10
+ const templateDirectory = path.join(packageDirectory, "template");
11
+
12
+ copyRepositoryTemplate(repositoryRoot, templateDirectory);
13
+
14
+ console.log(`Template prepared at ${templateDirectory}`);
@@ -0,0 +1,8 @@
1
+ # Changesets
2
+
3
+ Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4
+ with multi-package repos, or single-package repos to help you version and publish your code. You can
5
+ find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6
+
7
+ We have a quick list of common questions to get you started engaging with this project in
8
+ [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
3
+ "changelog": "@changesets/cli/changelog",
4
+ "commit": false,
5
+ "fixed": [],
6
+ "linked": [],
7
+ "access": "restricted",
8
+ "baseBranch": "main",
9
+ "updateInternalDependencies": "patch",
10
+ "ignore": []
11
+ }
@@ -0,0 +1,38 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # Dependencies
4
+ node_modules
5
+ .pnp
6
+ .pnp.js
7
+
8
+ # Local env files
9
+ .env
10
+ .env.local
11
+ .env.development.local
12
+ .env.test.local
13
+ .env.production.local
14
+
15
+ # Testing
16
+ coverage
17
+
18
+ # Turbo
19
+ .turbo
20
+
21
+ # Vercel
22
+ .vercel
23
+
24
+ # Build Outputs
25
+ .next/
26
+ out/
27
+ build
28
+ dist
29
+
30
+
31
+ # Debug
32
+ npm-debug.log*
33
+ yarn-debug.log*
34
+ yarn-error.log*
35
+
36
+ # Misc
37
+ .DS_Store
38
+ *.pem
@@ -0,0 +1,42 @@
1
+ name: workflow.yml
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ paths:
7
+ - "apps/project/**"
8
+ - "packages/**"
9
+ - 'pnpm-lock.yaml'
10
+
11
+ jobs:
12
+ deploy:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Checkout repository
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Install pnpm
19
+ uses: pnpm/action-setup@v2
20
+ with:
21
+ version: 8
22
+
23
+ - name: Set Node.js
24
+ uses: actions/setup-node@v4
25
+ with:
26
+ node-version: 20
27
+ cache: 'pnpm'
28
+
29
+ - name: Install dependencies
30
+ run: pnpm install
31
+
32
+ - name: Build
33
+ # 모노레포의 특정 앱만 빌드 (출력 경로가 apps/web/dist라고 가정)
34
+ run: pnpm --filter ./apps/project build
35
+
36
+ - name: Publish to Cloudflare Pages
37
+ uses: cloudflare/wrangler-action@v3
38
+ with:
39
+ apiToken: S_KZq6yVZIxowvPeXvOUbLfnGNCn2kTN33LlxH8e
40
+ accountId: 97f7cfcd2a80803fd63e1434b9e58ea1
41
+ packageManager: pnpm
42
+ command: pages deploy apps/project/dist --project-name=test