@unifiedcommerce/cli 0.0.1 → 0.2.1
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.
- package/dist/commands/init.js +1 -1
- package/dist/index.js +6 -6
- package/package.json +1 -2
- package/src/commands/deploy.ts +0 -49
- package/src/commands/dev.ts +0 -37
- package/src/commands/generate-migration.ts +0 -23
- package/src/commands/import.ts +0 -324
- package/src/commands/init.ts +0 -63
- package/src/commands/migrate.ts +0 -23
- package/src/index.ts +0 -30
- package/src/utils.ts +0 -21
package/dist/commands/init.js
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import consola from "consola";
|
|
6
6
|
import { downloadTemplate } from "giget";
|
|
7
|
-
import { copyDir, readJson, writeJson } from "../utils";
|
|
7
|
+
import { copyDir, readJson, writeJson } from "../utils.js";
|
|
8
8
|
const currentDir = fileURLToPath(new URL(".", import.meta.url));
|
|
9
9
|
export const initCommand = defineCommand({
|
|
10
10
|
meta: {
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { defineCommand, runMain } from "citty";
|
|
3
|
-
import { initCommand } from "./commands/init";
|
|
4
|
-
import { devCommand } from "./commands/dev";
|
|
5
|
-
import { generateMigrationCommand } from "./commands/generate-migration";
|
|
6
|
-
import { migrateCommand } from "./commands/migrate";
|
|
7
|
-
import { deployCommand } from "./commands/deploy";
|
|
8
|
-
import { importCommand } from "./commands/import";
|
|
3
|
+
import { initCommand } from "./commands/init.js";
|
|
4
|
+
import { devCommand } from "./commands/dev.js";
|
|
5
|
+
import { generateMigrationCommand } from "./commands/generate-migration.js";
|
|
6
|
+
import { migrateCommand } from "./commands/migrate.js";
|
|
7
|
+
import { deployCommand } from "./commands/deploy.js";
|
|
8
|
+
import { importCommand } from "./commands/import.js";
|
|
9
9
|
const main = defineCommand({
|
|
10
10
|
meta: {
|
|
11
11
|
name: "@unifiedcommerce/cli",
|
package/package.json
CHANGED
package/src/commands/deploy.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { defineCommand } from "citty";
|
|
3
|
-
import consola from "consola";
|
|
4
|
-
|
|
5
|
-
export const deployCommand = defineCommand({
|
|
6
|
-
meta: {
|
|
7
|
-
name: "deploy",
|
|
8
|
-
description: "Deploy UnifiedCommerce app to a target environment.",
|
|
9
|
-
},
|
|
10
|
-
args: {
|
|
11
|
-
target: {
|
|
12
|
-
type: "string",
|
|
13
|
-
required: true,
|
|
14
|
-
description: "Deployment target. Supported: vercel",
|
|
15
|
-
},
|
|
16
|
-
prod: {
|
|
17
|
-
type: "boolean",
|
|
18
|
-
default: false,
|
|
19
|
-
description: "Deploy production build",
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
async run({ args }) {
|
|
23
|
-
const target = String(args.target).toLowerCase();
|
|
24
|
-
|
|
25
|
-
if (target !== "vercel") {
|
|
26
|
-
throw new Error(`Unsupported deploy target: ${target}. Currently supported: vercel.`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const command = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
30
|
-
const deployArgs = ["vercel", "deploy"];
|
|
31
|
-
if (args.prod) deployArgs.push("--prod");
|
|
32
|
-
|
|
33
|
-
consola.info(`Running deploy for target=${target} (${deployArgs.join(" ")})`);
|
|
34
|
-
|
|
35
|
-
await new Promise<void>((resolvePromise, rejectPromise) => {
|
|
36
|
-
const proc = spawn(command, deployArgs, {
|
|
37
|
-
stdio: "inherit",
|
|
38
|
-
shell: false,
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
proc.on("exit", (code) => {
|
|
42
|
-
if (code === 0) resolvePromise();
|
|
43
|
-
else rejectPromise(new Error(`Deploy command failed with code ${code}`));
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
proc.on("error", rejectPromise);
|
|
47
|
-
});
|
|
48
|
-
},
|
|
49
|
-
});
|
package/src/commands/dev.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { defineCommand } from "citty";
|
|
3
|
-
import consola from "consola";
|
|
4
|
-
|
|
5
|
-
export const devCommand = defineCommand({
|
|
6
|
-
meta: {
|
|
7
|
-
name: "dev",
|
|
8
|
-
description: "Run local dev server with file watching",
|
|
9
|
-
},
|
|
10
|
-
args: {
|
|
11
|
-
port: {
|
|
12
|
-
type: "string",
|
|
13
|
-
default: "3000",
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
async run({ args }) {
|
|
17
|
-
const port = String(args.port);
|
|
18
|
-
consola.info(`Starting dev server on port ${port}`);
|
|
19
|
-
|
|
20
|
-
const proc = spawn(
|
|
21
|
-
process.platform === "win32" ? "npx.cmd" : "npx",
|
|
22
|
-
["tsx", "watch", "src/dev-server.ts", "--port", port],
|
|
23
|
-
{
|
|
24
|
-
stdio: "inherit",
|
|
25
|
-
shell: false,
|
|
26
|
-
},
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
await new Promise<void>((resolvePromise, rejectPromise) => {
|
|
30
|
-
proc.on("exit", (code) => {
|
|
31
|
-
if (code === 0) resolvePromise();
|
|
32
|
-
else rejectPromise(new Error(`dev exited with code ${code}`));
|
|
33
|
-
});
|
|
34
|
-
proc.on("error", rejectPromise);
|
|
35
|
-
});
|
|
36
|
-
},
|
|
37
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { defineCommand } from "citty";
|
|
3
|
-
|
|
4
|
-
export const generateMigrationCommand = defineCommand({
|
|
5
|
-
meta: {
|
|
6
|
-
name: "generate migration",
|
|
7
|
-
description: "Generate Drizzle migration",
|
|
8
|
-
},
|
|
9
|
-
async run() {
|
|
10
|
-
await new Promise<void>((resolvePromise, rejectPromise) => {
|
|
11
|
-
const proc = spawn(
|
|
12
|
-
process.platform === "win32" ? "npx.cmd" : "npx",
|
|
13
|
-
["drizzle-kit", "generate"],
|
|
14
|
-
{ stdio: "inherit" },
|
|
15
|
-
);
|
|
16
|
-
proc.on("exit", (code) => {
|
|
17
|
-
if (code === 0) resolvePromise();
|
|
18
|
-
else rejectPromise(new Error(`drizzle-kit generate failed with code ${code}`));
|
|
19
|
-
});
|
|
20
|
-
proc.on("error", rejectPromise);
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
});
|
package/src/commands/import.ts
DELETED
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { extname, resolve } from "node:path";
|
|
3
|
-
import { defineCommand } from "citty";
|
|
4
|
-
import consola from "consola";
|
|
5
|
-
// @ts-ignore — missing type declarations
|
|
6
|
-
import { importFlat } from "@unifiedcommerce/import-flat";
|
|
7
|
-
// @ts-ignore — missing type declarations
|
|
8
|
-
import { importShopifyCatalog } from "@unifiedcommerce/import-shopify";
|
|
9
|
-
// @ts-ignore — missing type declarations
|
|
10
|
-
import { importWooCommerceCatalog } from "@unifiedcommerce/import-woocommerce";
|
|
11
|
-
|
|
12
|
-
type JsonRecord = Record<string, unknown>;
|
|
13
|
-
|
|
14
|
-
function toBaseUrl(raw: string | undefined): string {
|
|
15
|
-
return (raw ?? "http://localhost:3000").replace(/\/$/, "");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function toHeaders(token?: string): Record<string, string> {
|
|
19
|
-
if (!token) return {};
|
|
20
|
-
return { authorization: `Bearer ${token}` };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function requestJson<T>(
|
|
24
|
-
baseUrl: string,
|
|
25
|
-
path: string,
|
|
26
|
-
method: "GET" | "POST" | "PATCH" | "DELETE",
|
|
27
|
-
body: unknown,
|
|
28
|
-
token?: string,
|
|
29
|
-
): Promise<T> {
|
|
30
|
-
const response = await fetch(`${baseUrl}${path}`, {
|
|
31
|
-
method,
|
|
32
|
-
headers: {
|
|
33
|
-
"content-type": "application/json",
|
|
34
|
-
...toHeaders(token),
|
|
35
|
-
},
|
|
36
|
-
body: JSON.stringify(body),
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const payload = (await response.json().catch(() => ({}))) as { data?: T; error?: { message?: string } };
|
|
40
|
-
if (!response.ok) {
|
|
41
|
-
const message = payload.error?.message ?? `HTTP ${response.status}`;
|
|
42
|
-
throw new Error(`Request failed for ${method} ${path}: ${message}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (payload.data === undefined) {
|
|
46
|
-
throw new Error(`Expected data payload for ${method} ${path}.`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return payload.data;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function createRestImportTarget(baseUrl: string, token?: string) {
|
|
53
|
-
return {
|
|
54
|
-
async createEntity(input: {
|
|
55
|
-
type: string;
|
|
56
|
-
slug: string;
|
|
57
|
-
attributes: { title: string; description?: string; subtitle?: string; locale?: string };
|
|
58
|
-
metadata?: Record<string, unknown>;
|
|
59
|
-
customFields?: Record<string, unknown>;
|
|
60
|
-
}) {
|
|
61
|
-
return requestJson<{ id: string }>(baseUrl, "/api/catalog/entities", "POST", input, token);
|
|
62
|
-
},
|
|
63
|
-
async createOptionType(input: {
|
|
64
|
-
entityId: string;
|
|
65
|
-
name: string;
|
|
66
|
-
displayName: string;
|
|
67
|
-
sortOrder?: number;
|
|
68
|
-
}) {
|
|
69
|
-
return requestJson<{ id: string }>(
|
|
70
|
-
baseUrl,
|
|
71
|
-
`/api/catalog/entities/${input.entityId}/options`,
|
|
72
|
-
"POST",
|
|
73
|
-
input,
|
|
74
|
-
token,
|
|
75
|
-
);
|
|
76
|
-
},
|
|
77
|
-
async createOptionValue(input: {
|
|
78
|
-
optionTypeId: string;
|
|
79
|
-
value: string;
|
|
80
|
-
displayValue: string;
|
|
81
|
-
sortOrder?: number;
|
|
82
|
-
}) {
|
|
83
|
-
return requestJson<{ id: string }>(
|
|
84
|
-
baseUrl,
|
|
85
|
-
`/api/catalog/options/${input.optionTypeId}/values`,
|
|
86
|
-
"POST",
|
|
87
|
-
input,
|
|
88
|
-
token,
|
|
89
|
-
);
|
|
90
|
-
},
|
|
91
|
-
async createVariant(input: {
|
|
92
|
-
entityId: string;
|
|
93
|
-
optionValueIds: string[];
|
|
94
|
-
sku?: string;
|
|
95
|
-
barcode?: string;
|
|
96
|
-
metadata?: Record<string, unknown>;
|
|
97
|
-
}) {
|
|
98
|
-
return requestJson<{ id: string }>(
|
|
99
|
-
baseUrl,
|
|
100
|
-
`/api/catalog/entities/${input.entityId}/variants`,
|
|
101
|
-
"POST",
|
|
102
|
-
input,
|
|
103
|
-
token,
|
|
104
|
-
);
|
|
105
|
-
},
|
|
106
|
-
async uploadMedia(input: {
|
|
107
|
-
filename: string;
|
|
108
|
-
contentType: string;
|
|
109
|
-
data: ArrayBuffer;
|
|
110
|
-
alt?: string;
|
|
111
|
-
metadata?: Record<string, unknown>;
|
|
112
|
-
}) {
|
|
113
|
-
const form = new FormData();
|
|
114
|
-
form.set(
|
|
115
|
-
"file",
|
|
116
|
-
new File([input.data], input.filename, {
|
|
117
|
-
type: input.contentType,
|
|
118
|
-
}),
|
|
119
|
-
);
|
|
120
|
-
if (input.alt) form.set("alt", input.alt);
|
|
121
|
-
|
|
122
|
-
const response = await fetch(`${baseUrl}/api/media/upload`, {
|
|
123
|
-
method: "POST",
|
|
124
|
-
headers: {
|
|
125
|
-
...toHeaders(token),
|
|
126
|
-
},
|
|
127
|
-
body: form,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const payload = (await response.json().catch(() => ({}))) as {
|
|
131
|
-
data?: { id: string; url: string };
|
|
132
|
-
error?: { message?: string };
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
if (!response.ok || !payload.data) {
|
|
136
|
-
throw new Error(payload.error?.message ?? `Media upload failed (${response.status}).`);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return payload.data;
|
|
140
|
-
},
|
|
141
|
-
async attachMedia(input: { entityId: string; mediaAssetId: string; role: "primary" | "gallery" }) {
|
|
142
|
-
await requestJson<{ attached: boolean }>(baseUrl, "/api/media/attach", "POST", input, token);
|
|
143
|
-
},
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async function loadJsonInput(path: string): Promise<unknown> {
|
|
148
|
-
const absolute = resolve(process.cwd(), path);
|
|
149
|
-
const raw = await readFile(absolute, "utf8");
|
|
150
|
-
return JSON.parse(raw) as unknown;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export const importCommand = defineCommand({
|
|
154
|
-
meta: {
|
|
155
|
-
name: "import",
|
|
156
|
-
description: "Import catalog data from Shopify, WooCommerce, or flat CSV/JSON into UnifiedCommerce.",
|
|
157
|
-
},
|
|
158
|
-
args: {
|
|
159
|
-
source: {
|
|
160
|
-
type: "string",
|
|
161
|
-
required: true,
|
|
162
|
-
description: "shopify | woocommerce | flat",
|
|
163
|
-
},
|
|
164
|
-
input: {
|
|
165
|
-
type: "string",
|
|
166
|
-
description: "Path to input JSON/CSV for offline imports",
|
|
167
|
-
},
|
|
168
|
-
targetUrl: {
|
|
169
|
-
type: "string",
|
|
170
|
-
default: "http://localhost:3000",
|
|
171
|
-
description: "UnifiedCommerce API base URL",
|
|
172
|
-
},
|
|
173
|
-
authToken: {
|
|
174
|
-
type: "string",
|
|
175
|
-
description: "Optional bearer token for target API",
|
|
176
|
-
},
|
|
177
|
-
apiKey: {
|
|
178
|
-
type: "string",
|
|
179
|
-
description: "Shopify admin API key/token",
|
|
180
|
-
},
|
|
181
|
-
storeUrl: {
|
|
182
|
-
type: "string",
|
|
183
|
-
description: "Shopify or WooCommerce store URL",
|
|
184
|
-
},
|
|
185
|
-
consumerKey: {
|
|
186
|
-
type: "string",
|
|
187
|
-
description: "WooCommerce consumer key",
|
|
188
|
-
},
|
|
189
|
-
consumerSecret: {
|
|
190
|
-
type: "string",
|
|
191
|
-
description: "WooCommerce consumer secret",
|
|
192
|
-
},
|
|
193
|
-
mapping: {
|
|
194
|
-
type: "string",
|
|
195
|
-
description: "Path to flat-import mapping JSON (required for source=flat)",
|
|
196
|
-
},
|
|
197
|
-
entityType: {
|
|
198
|
-
type: "string",
|
|
199
|
-
default: "product",
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
|
-
async run({ args }) {
|
|
203
|
-
const source = String(args.source).toLowerCase();
|
|
204
|
-
const baseUrl = toBaseUrl(args.targetUrl ? String(args.targetUrl) : undefined);
|
|
205
|
-
const token = args.authToken ? String(args.authToken) : undefined;
|
|
206
|
-
const target = createRestImportTarget(baseUrl, token);
|
|
207
|
-
|
|
208
|
-
if (source === "shopify") {
|
|
209
|
-
let products: unknown;
|
|
210
|
-
let customers: unknown;
|
|
211
|
-
|
|
212
|
-
if (args.input) {
|
|
213
|
-
const parsed = await loadJsonInput(String(args.input));
|
|
214
|
-
if (Array.isArray(parsed)) {
|
|
215
|
-
products = parsed;
|
|
216
|
-
} else if (parsed && typeof parsed === "object") {
|
|
217
|
-
products = (parsed as JsonRecord).products;
|
|
218
|
-
customers = (parsed as JsonRecord).customers;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const imported = await importShopifyCatalog({
|
|
223
|
-
target,
|
|
224
|
-
...(args.storeUrl ? { storeUrl: String(args.storeUrl) } : {}),
|
|
225
|
-
...(args.apiKey ? { apiKey: String(args.apiKey) } : {}),
|
|
226
|
-
entityType: String(args.entityType),
|
|
227
|
-
...(Array.isArray(products) ? { products: products as any[] } : {}),
|
|
228
|
-
...(Array.isArray(customers) ? { customers: customers as any[] } : {}),
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
if (!imported.ok) {
|
|
232
|
-
throw new Error(imported.error.message);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
consola.success(`Shopify import completed: ${imported.value.entitiesImported} entities, ${imported.value.variantsImported} variants, ${imported.value.mediaImported} media.`);
|
|
236
|
-
if (imported.value.customersImported > 0) {
|
|
237
|
-
consola.info(`Imported customers: ${imported.value.customersImported}`);
|
|
238
|
-
}
|
|
239
|
-
if (imported.value.errors.length > 0) {
|
|
240
|
-
consola.warn(`Import completed with ${imported.value.errors.length} warnings.`);
|
|
241
|
-
}
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (source === "woocommerce") {
|
|
246
|
-
let products: unknown;
|
|
247
|
-
let customers: unknown;
|
|
248
|
-
|
|
249
|
-
if (args.input) {
|
|
250
|
-
const parsed = await loadJsonInput(String(args.input));
|
|
251
|
-
if (Array.isArray(parsed)) {
|
|
252
|
-
products = parsed;
|
|
253
|
-
} else if (parsed && typeof parsed === "object") {
|
|
254
|
-
products = (parsed as JsonRecord).products;
|
|
255
|
-
customers = (parsed as JsonRecord).customers;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const imported = await importWooCommerceCatalog({
|
|
260
|
-
target,
|
|
261
|
-
...(args.storeUrl ? { storeUrl: String(args.storeUrl) } : {}),
|
|
262
|
-
...(args.consumerKey ? { consumerKey: String(args.consumerKey) } : {}),
|
|
263
|
-
...(args.consumerSecret ? { consumerSecret: String(args.consumerSecret) } : {}),
|
|
264
|
-
entityType: String(args.entityType),
|
|
265
|
-
...(Array.isArray(products) ? { products: products as any[] } : {}),
|
|
266
|
-
...(Array.isArray(customers) ? { customers: customers as any[] } : {}),
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
if (!imported.ok) {
|
|
270
|
-
throw new Error(imported.error.message);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
consola.success(`WooCommerce import completed: ${imported.value.entitiesImported} entities, ${imported.value.variantsImported} variants, ${imported.value.mediaImported} media.`);
|
|
274
|
-
if (imported.value.customersImported > 0) {
|
|
275
|
-
consola.info(`Imported customers: ${imported.value.customersImported}`);
|
|
276
|
-
}
|
|
277
|
-
if (imported.value.errors.length > 0) {
|
|
278
|
-
consola.warn(`Import completed with ${imported.value.errors.length} warnings.`);
|
|
279
|
-
}
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (source === "flat") {
|
|
284
|
-
if (!args.mapping) {
|
|
285
|
-
throw new Error("--mapping is required for source=flat");
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const mappingParsed = await loadJsonInput(String(args.mapping));
|
|
289
|
-
if (!mappingParsed || typeof mappingParsed !== "object") {
|
|
290
|
-
throw new Error("Flat import mapping file must be a JSON object.");
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (!args.input) {
|
|
294
|
-
throw new Error("--input is required for source=flat");
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const inputPath = resolve(process.cwd(), String(args.input));
|
|
298
|
-
const raw = await readFile(inputPath, "utf8");
|
|
299
|
-
const extension = extname(inputPath).toLowerCase();
|
|
300
|
-
|
|
301
|
-
const imported = await importFlat({
|
|
302
|
-
mapping: mappingParsed as any,
|
|
303
|
-
target: {
|
|
304
|
-
async createEntity(input: unknown) {
|
|
305
|
-
return target.createEntity(input as Parameters<typeof target.createEntity>[0]);
|
|
306
|
-
},
|
|
307
|
-
},
|
|
308
|
-
...(extension === ".csv" ? { csv: raw } : { json: raw }),
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
if (!imported.ok) {
|
|
312
|
-
throw new Error(imported.error.message);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
consola.success(`Flat import completed: ${imported.value.imported} entities imported.`);
|
|
316
|
-
if (imported.value.failed > 0) {
|
|
317
|
-
consola.warn(`Failed rows: ${imported.value.failed}`);
|
|
318
|
-
}
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
throw new Error(`Unsupported source: ${source}. Expected shopify, woocommerce, or flat.`);
|
|
323
|
-
},
|
|
324
|
-
});
|
package/src/commands/init.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { join, resolve } from "node:path";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { defineCommand } from "citty";
|
|
5
|
-
import consola from "consola";
|
|
6
|
-
import { downloadTemplate } from "giget";
|
|
7
|
-
import { copyDir, readJson, writeJson } from "../utils";
|
|
8
|
-
|
|
9
|
-
const currentDir = fileURLToPath(new URL(".", import.meta.url));
|
|
10
|
-
|
|
11
|
-
interface PackageJsonShape {
|
|
12
|
-
name?: string;
|
|
13
|
-
[key: string]: unknown;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const initCommand = defineCommand({
|
|
17
|
-
meta: {
|
|
18
|
-
name: "init",
|
|
19
|
-
description: "Scaffold a UnifiedCommerce project",
|
|
20
|
-
},
|
|
21
|
-
args: {
|
|
22
|
-
projectName: {
|
|
23
|
-
type: "positional",
|
|
24
|
-
description: "Project directory name",
|
|
25
|
-
required: true,
|
|
26
|
-
},
|
|
27
|
-
template: {
|
|
28
|
-
type: "string",
|
|
29
|
-
default: "starter",
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
async run({ args }) {
|
|
33
|
-
const projectName = String(args.projectName);
|
|
34
|
-
const destination = resolve(process.cwd(), projectName);
|
|
35
|
-
|
|
36
|
-
if (existsSync(destination)) {
|
|
37
|
-
throw new Error(`Directory ${destination} already exists.`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const localTemplatePath = resolve(currentDir, "../../templates", String(args.template));
|
|
41
|
-
|
|
42
|
-
if (existsSync(localTemplatePath)) {
|
|
43
|
-
await copyDir(localTemplatePath, destination);
|
|
44
|
-
} else {
|
|
45
|
-
await downloadTemplate(`gh:unifiedcommerce/templates/${String(args.template)}`, {
|
|
46
|
-
dir: destination,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const packageJsonPath = join(destination, "package.json");
|
|
51
|
-
if (existsSync(packageJsonPath)) {
|
|
52
|
-
const pkg = await readJson<PackageJsonShape>(packageJsonPath);
|
|
53
|
-
pkg.name = projectName;
|
|
54
|
-
await writeJson(packageJsonPath, pkg);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
consola.success(`Project created at ${destination}`);
|
|
58
|
-
consola.info(`Next steps:`);
|
|
59
|
-
consola.info(` cd ${projectName}`);
|
|
60
|
-
consola.info(` bun install`);
|
|
61
|
-
consola.info(` bun run dev`);
|
|
62
|
-
},
|
|
63
|
-
});
|
package/src/commands/migrate.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { defineCommand } from "citty";
|
|
3
|
-
|
|
4
|
-
export const migrateCommand = defineCommand({
|
|
5
|
-
meta: {
|
|
6
|
-
name: "migrate",
|
|
7
|
-
description: "Apply Drizzle migrations",
|
|
8
|
-
},
|
|
9
|
-
async run() {
|
|
10
|
-
await new Promise<void>((resolvePromise, rejectPromise) => {
|
|
11
|
-
const proc = spawn(
|
|
12
|
-
process.platform === "win32" ? "npx.cmd" : "npx",
|
|
13
|
-
["drizzle-kit", "migrate"],
|
|
14
|
-
{ stdio: "inherit" },
|
|
15
|
-
);
|
|
16
|
-
proc.on("exit", (code) => {
|
|
17
|
-
if (code === 0) resolvePromise();
|
|
18
|
-
else rejectPromise(new Error(`drizzle-kit migrate failed with code ${code}`));
|
|
19
|
-
});
|
|
20
|
-
proc.on("error", rejectPromise);
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { defineCommand, runMain } from "citty";
|
|
3
|
-
import { initCommand } from "./commands/init";
|
|
4
|
-
import { devCommand } from "./commands/dev";
|
|
5
|
-
import { generateMigrationCommand } from "./commands/generate-migration";
|
|
6
|
-
import { migrateCommand } from "./commands/migrate";
|
|
7
|
-
import { deployCommand } from "./commands/deploy";
|
|
8
|
-
import { importCommand } from "./commands/import";
|
|
9
|
-
|
|
10
|
-
const main = defineCommand({
|
|
11
|
-
meta: {
|
|
12
|
-
name: "@unifiedcommerce/cli",
|
|
13
|
-
version: "0.0.1",
|
|
14
|
-
description: "UnifiedCommerce Engine CLI",
|
|
15
|
-
},
|
|
16
|
-
subCommands: {
|
|
17
|
-
init: initCommand,
|
|
18
|
-
dev: devCommand,
|
|
19
|
-
migrate: migrateCommand,
|
|
20
|
-
deploy: deployCommand,
|
|
21
|
-
import: importCommand,
|
|
22
|
-
generate: defineCommand({
|
|
23
|
-
subCommands: {
|
|
24
|
-
migration: generateMigrationCommand,
|
|
25
|
-
},
|
|
26
|
-
}),
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
await runMain(main);
|
package/src/utils.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { cp, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
-
|
|
4
|
-
export async function copyDir(src: string, dest: string): Promise<void> {
|
|
5
|
-
await mkdir(dest, { recursive: true });
|
|
6
|
-
await cp(src, dest, { recursive: true, force: true });
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export async function writeJson(path: string, value: unknown): Promise<void> {
|
|
10
|
-
await mkdir(dirname(path), { recursive: true });
|
|
11
|
-
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export async function readJson<T>(path: string): Promise<T> {
|
|
15
|
-
const raw = await readFile(path, "utf8");
|
|
16
|
-
return JSON.parse(raw) as T;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function resolveFromCwd(path: string): string {
|
|
20
|
-
return resolve(process.cwd(), path);
|
|
21
|
-
}
|