@ollie-shop/cli 0.1.2 → 0.2.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.
- package/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +12 -0
- package/dist/index.js +267 -14
- package/package.json +8 -4
- package/src/commands/docs.ts +24 -0
- package/src/commands/help.ts +28 -0
- package/src/commands/index.ts +10 -3
- package/src/commands/login.ts +4 -17
- package/src/commands/validate.ts +62 -0
- package/src/commands/version.ts +14 -0
- package/src/commands/whoami.ts +51 -0
- package/src/index.ts +27 -6
- package/src/utils/auth.ts +42 -0
- package/src/utils/core.ts +105 -0
- package/src/utils/store.ts +23 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @ollie-shop/cli@0.
|
|
2
|
+
> @ollie-shop/cli@0.2.0 build /home/runner/work/ollie-shop/ollie-shop/packages/cli
|
|
3
3
|
> tsup
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
[34mCLI[39m Target: esnext
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mCJS[39m Build start
|
|
12
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
13
|
-
[32mCJS[39m ⚡️ Build success in
|
|
12
|
+
[32mCJS[39m [1mdist/index.js [22m[32m16.06 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 543ms
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @ollie-shop/cli
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8f99b17: new CLI commands—docs, help, whoami, and validate—to the Ollie Shop CLI, along with supporting utility modules
|
|
8
|
+
|
|
9
|
+
## 0.1.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 8d43054: Change browser opening to use open package
|
|
14
|
+
|
|
3
15
|
## 0.1.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -2,17 +2,25 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var commander = require('commander');
|
|
5
|
-
var
|
|
5
|
+
var chalk3 = require('chalk');
|
|
6
6
|
var crypto = require('crypto');
|
|
7
7
|
var fs = require('fs/promises');
|
|
8
8
|
var http = require('http');
|
|
9
9
|
var os = require('os');
|
|
10
|
-
var
|
|
10
|
+
var path2 = require('path');
|
|
11
|
+
var fs3 = require('fs');
|
|
12
|
+
var module$1 = require('module');
|
|
13
|
+
var latestVersion = require('latest-version');
|
|
14
|
+
var jwtDecode = require('jwt-decode');
|
|
11
15
|
|
|
16
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
12
17
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
18
|
|
|
19
|
+
var chalk3__default = /*#__PURE__*/_interopDefault(chalk3);
|
|
14
20
|
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
15
|
-
var
|
|
21
|
+
var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
22
|
+
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
23
|
+
var latestVersion__default = /*#__PURE__*/_interopDefault(latestVersion);
|
|
16
24
|
|
|
17
25
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
18
26
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
@@ -20,6 +28,48 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
20
28
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
21
29
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
22
30
|
});
|
|
31
|
+
function configureDocsCommand(program) {
|
|
32
|
+
program.command("docs").description("Show documentation links for Ollie Shop").action(() => {
|
|
33
|
+
console.log(chalk3__default.default.cyanBright("\nOllie Shop Documentation\n"));
|
|
34
|
+
console.log(
|
|
35
|
+
`${chalk3__default.default.green("Main Docs:".padEnd(20))} https://docs.ollie.shop/ollie-shop`
|
|
36
|
+
);
|
|
37
|
+
console.log(
|
|
38
|
+
`${chalk3__default.default.green("Components:".padEnd(20))} https://docs.ollie.shop/ollie-shop/concepts/component`
|
|
39
|
+
);
|
|
40
|
+
console.log(
|
|
41
|
+
`${chalk3__default.default.green("Functions:".padEnd(20))} https://docs.ollie.shop/ollie-shop/concepts/function`
|
|
42
|
+
);
|
|
43
|
+
console.log(
|
|
44
|
+
`${chalk3__default.default.green("Versions:".padEnd(20))} https://docs.ollie.shop/ollie-shop/concepts/version
|
|
45
|
+
`
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function configureHelpCommand(program) {
|
|
50
|
+
program.command("help [command]").description("Display help for a specific command or list all commands").action((cmdName) => {
|
|
51
|
+
if (cmdName) {
|
|
52
|
+
const cmd = program.commands.find((c) => c.name() === cmdName);
|
|
53
|
+
if (cmd) {
|
|
54
|
+
cmd.help();
|
|
55
|
+
} else {
|
|
56
|
+
console.log(chalk3__default.default.red(`Command '${cmdName}' not found.`));
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
console.log(chalk3__default.default.cyanBright("\nAvailable commands:\n"));
|
|
60
|
+
for (const cmd of program.commands) {
|
|
61
|
+
console.log(
|
|
62
|
+
` ${chalk3__default.default.green(cmd.name().padEnd(20))} ${cmd.description()}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
console.log(
|
|
66
|
+
`
|
|
67
|
+
Use ${chalk3__default.default.yellow("ollieshop help <command>")} to get more info on a command.
|
|
68
|
+
`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
23
73
|
var DEFAULT_CALLBACK_PORT = 7777;
|
|
24
74
|
var AUTH_ENDPOINT = "https://admin.ollie.shop/auth/login";
|
|
25
75
|
function configureLoginCommand(program) {
|
|
@@ -160,7 +210,7 @@ async function startWebAuthFlow(options) {
|
|
|
160
210
|
});
|
|
161
211
|
}
|
|
162
212
|
});
|
|
163
|
-
server.listen(port, () => {
|
|
213
|
+
server.listen(port, async () => {
|
|
164
214
|
const redirectUrl = `http://localhost:${port}/callback`;
|
|
165
215
|
const authUrl = new URL(baseUrl);
|
|
166
216
|
authUrl.searchParams.set("flow", "cli");
|
|
@@ -169,7 +219,8 @@ async function startWebAuthFlow(options) {
|
|
|
169
219
|
console.log("\n\u{1F512} Please authenticate in your browser...\n");
|
|
170
220
|
console.log(`Opening: ${authUrl}
|
|
171
221
|
`);
|
|
172
|
-
|
|
222
|
+
const open = (await import('open')).default;
|
|
223
|
+
open(authUrl.toString());
|
|
173
224
|
});
|
|
174
225
|
server.on("error", (err) => {
|
|
175
226
|
if (err.code === "EADDRINUSE") {
|
|
@@ -197,14 +248,10 @@ async function startWebAuthFlow(options) {
|
|
|
197
248
|
});
|
|
198
249
|
});
|
|
199
250
|
}
|
|
200
|
-
function openBrowser(url) {
|
|
201
|
-
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
202
|
-
child_process.spawn(command, [url], { detached: true }).unref();
|
|
203
|
-
}
|
|
204
251
|
async function saveCredentials(token) {
|
|
205
252
|
console.log("Saving credentials...");
|
|
206
|
-
const configDir =
|
|
207
|
-
const credentialsPath =
|
|
253
|
+
const configDir = path2__default.default.join(os.homedir(), ".ollie-shop");
|
|
254
|
+
const credentialsPath = path2__default.default.join(configDir, "credentials.json");
|
|
208
255
|
try {
|
|
209
256
|
await fs__default.default.mkdir(configDir, { recursive: true });
|
|
210
257
|
} catch (error) {
|
|
@@ -215,22 +262,228 @@ async function saveCredentials(token) {
|
|
|
215
262
|
await fs__default.default.writeFile(credentialsPath, JSON.stringify(token, null, 2));
|
|
216
263
|
return true;
|
|
217
264
|
}
|
|
265
|
+
function configureValidateCommand(program) {
|
|
266
|
+
program.command("validate [componentPath]").description("Validate the structure of your custom component").action(async (componentPath) => {
|
|
267
|
+
if (!componentPath) {
|
|
268
|
+
console.error(
|
|
269
|
+
`${chalk3__default.default.red("\u274C Missing required argument:")} component path
|
|
270
|
+
|
|
271
|
+
Example usage:
|
|
272
|
+
${chalk3__default.default.cyan("ollieshop validate")} ./path/to/your/component
|
|
273
|
+
`
|
|
274
|
+
);
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
const resolvedPath = path2__default.default.resolve(componentPath);
|
|
278
|
+
const requiredFiles = ["index.tsx", "package.json", "meta.json"];
|
|
279
|
+
const optionalFile = "styles.module.css";
|
|
280
|
+
let isValid = true;
|
|
281
|
+
for (const file of requiredFiles) {
|
|
282
|
+
try {
|
|
283
|
+
await fs__default.default.access(path2__default.default.join(resolvedPath, file));
|
|
284
|
+
console.log(`${chalk3__default.default.green("\u2714")} Found: ${chalk3__default.default.cyan(file)}`);
|
|
285
|
+
} catch {
|
|
286
|
+
console.log(`${chalk3__default.default.red("\u2716")} Missing: ${chalk3__default.default.cyan(file)}`);
|
|
287
|
+
isValid = false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
await fs__default.default.access(path2__default.default.join(resolvedPath, optionalFile));
|
|
292
|
+
console.log(
|
|
293
|
+
`${chalk3__default.default.yellow("\u2139")} Optional file found: ${chalk3__default.default.cyan(optionalFile)}`
|
|
294
|
+
);
|
|
295
|
+
} catch {
|
|
296
|
+
console.log(
|
|
297
|
+
`${chalk3__default.default.dim("\u2139")} Optional file not found: ${chalk3__default.default.cyan(optionalFile)}`
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
if (isValid) {
|
|
301
|
+
console.log(`
|
|
302
|
+
${chalk3__default.default.green("\u2714")} Component structure looks valid!`);
|
|
303
|
+
} else {
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.js', document.baseURI).href)));
|
|
309
|
+
var pkg = require2("../package.json");
|
|
310
|
+
async function checkForUpdates() {
|
|
311
|
+
try {
|
|
312
|
+
const latest = await latestVersion__default.default(pkg.name);
|
|
313
|
+
if (latest !== pkg.version) {
|
|
314
|
+
console.log(
|
|
315
|
+
chalk3__default.default.yellow(
|
|
316
|
+
`
|
|
317
|
+
\u26A0\uFE0F A new version ${chalk3__default.default.bold(latest)} is available!
|
|
318
|
+
Update now: ${chalk3__default.default.bold(
|
|
319
|
+
"npm install -g @ollie-shop/cli"
|
|
320
|
+
)}
|
|
321
|
+
`
|
|
322
|
+
)
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
} catch {
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function showWelcomeMessage() {
|
|
329
|
+
console.log(
|
|
330
|
+
chalk3__default.default.cyanBright.bold(
|
|
331
|
+
`
|
|
332
|
+
\u2728 Welcome to the Ollie Shop CLI - ${pkg.version} \u2728
|
|
333
|
+
`
|
|
334
|
+
)
|
|
335
|
+
);
|
|
336
|
+
console.log("Build, customize and deploy your e-commerce with ease.\n");
|
|
337
|
+
console.log("Usage:");
|
|
338
|
+
console.log(" ollieshop <command> [options]\n");
|
|
339
|
+
console.log("Available commands:");
|
|
340
|
+
const stableCommands = [
|
|
341
|
+
{ cmd: "login", desc: "Log in to your Ollie Shop account" },
|
|
342
|
+
{ cmd: "validate", desc: "Validate your component files" },
|
|
343
|
+
{ cmd: "whoami", desc: "Show logged-in user and current store" }
|
|
344
|
+
];
|
|
345
|
+
for (const { cmd, desc } of stableCommands) {
|
|
346
|
+
console.log(` ${chalk3__default.default.green(cmd.padEnd(20))} ${desc}`);
|
|
347
|
+
}
|
|
348
|
+
console.log("\nComing soon:");
|
|
349
|
+
const comingSoonCommands = [
|
|
350
|
+
{ cmd: "build", desc: "Compile components for deployment" },
|
|
351
|
+
{ cmd: "deploy", desc: "Deploy a component or function" },
|
|
352
|
+
{ cmd: "dev", desc: "Start local preview server" },
|
|
353
|
+
{ cmd: "status", desc: "Show store status" }
|
|
354
|
+
];
|
|
355
|
+
for (const { cmd, desc } of comingSoonCommands) {
|
|
356
|
+
console.log(` ${chalk3__default.default.yellow(cmd.padEnd(20))} ${desc}`);
|
|
357
|
+
}
|
|
358
|
+
console.log(
|
|
359
|
+
`
|
|
360
|
+
For a full list of commands, run: ${chalk3__default.default.yellow.bold("ollieshop help")}
|
|
361
|
+
`
|
|
362
|
+
);
|
|
363
|
+
console.log(
|
|
364
|
+
"Documentation:",
|
|
365
|
+
chalk3__default.default.underline.blue("https://docs.ollie.shop/ollie-shop")
|
|
366
|
+
);
|
|
367
|
+
console.log();
|
|
368
|
+
}
|
|
369
|
+
function validateProjectLocation() {
|
|
370
|
+
const command = process.argv[2] ?? "";
|
|
371
|
+
const allowedOutside = ["help", "docs", "version"];
|
|
372
|
+
if (allowedOutside.includes(command)) return;
|
|
373
|
+
const configPath = path2__default.default.join(process.cwd(), "ollie.json");
|
|
374
|
+
const isValidProject = fs3__default.default.existsSync(configPath);
|
|
375
|
+
if (!isValidProject) {
|
|
376
|
+
console.log(`
|
|
377
|
+
${chalk3__default.default.red("\u274C")} This command must be run inside an ${chalk3__default.default.bold("Ollie Shop project")}.
|
|
378
|
+
Please navigate to your project directory and try again.
|
|
379
|
+
`);
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/commands/version.ts
|
|
385
|
+
function configureVersionCommand(program) {
|
|
386
|
+
program.command("version").description("Display the current CLI version").action(() => {
|
|
387
|
+
console.log(
|
|
388
|
+
`${chalk3__default.default.bold("Ollie Shop CLI version:")} ${chalk3__default.default.cyan(pkg.version)}
|
|
389
|
+
`
|
|
390
|
+
);
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
var CREDENTIALS_PATH = path2__default.default.join(
|
|
394
|
+
os.homedir(),
|
|
395
|
+
".ollie-shop",
|
|
396
|
+
"credentials.json"
|
|
397
|
+
);
|
|
398
|
+
async function getCurrentUser() {
|
|
399
|
+
try {
|
|
400
|
+
const raw = await fs__default.default.readFile(CREDENTIALS_PATH, "utf-8");
|
|
401
|
+
const token = JSON.parse(raw);
|
|
402
|
+
if (!token.accessToken) return null;
|
|
403
|
+
const decoded = jwtDecode.jwtDecode(token.accessToken);
|
|
404
|
+
return {
|
|
405
|
+
email: decoded.email
|
|
406
|
+
};
|
|
407
|
+
} catch {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async function getOllieConfig() {
|
|
412
|
+
try {
|
|
413
|
+
const configPath = path2__default.default.join(process.cwd(), "ollie.json");
|
|
414
|
+
const raw = await fs__default.default.readFile(configPath, "utf-8");
|
|
415
|
+
const data = JSON.parse(raw);
|
|
416
|
+
return data;
|
|
417
|
+
} catch {
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/commands/whoami.ts
|
|
423
|
+
async function getUserInfo() {
|
|
424
|
+
try {
|
|
425
|
+
const user = await getCurrentUser();
|
|
426
|
+
const store = await getOllieConfig();
|
|
427
|
+
if (!user || !user.email) {
|
|
428
|
+
console.log("\u274C No user authenticated");
|
|
429
|
+
return {};
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
email: user.email,
|
|
433
|
+
store: store ?? void 0
|
|
434
|
+
};
|
|
435
|
+
} catch {
|
|
436
|
+
return {};
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function configureWhoamiCommand(program) {
|
|
440
|
+
program.command("whoami").description("Show logged-in user and current store").action(async () => {
|
|
441
|
+
const { email, store } = await getUserInfo();
|
|
442
|
+
if (!email) {
|
|
443
|
+
console.log(chalk3__default.default.red("You are not authenticated"));
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
console.log();
|
|
447
|
+
console.log(
|
|
448
|
+
`${chalk3__default.default.bold("You are logged in as:")} ${chalk3__default.default.cyan(email)}`
|
|
449
|
+
);
|
|
450
|
+
if (store) {
|
|
451
|
+
console.log(
|
|
452
|
+
`${chalk3__default.default.bold("Current store:")} ${chalk3__default.default.cyan(store.platformStoreId ?? "unknown")}`
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
console.log();
|
|
456
|
+
});
|
|
457
|
+
}
|
|
218
458
|
|
|
219
459
|
// src/commands/index.ts
|
|
220
460
|
function registerCommands(program) {
|
|
461
|
+
configureDocsCommand(program);
|
|
462
|
+
configureHelpCommand(program);
|
|
221
463
|
configureLoginCommand(program);
|
|
464
|
+
configureWhoamiCommand(program);
|
|
465
|
+
configureValidateCommand(program);
|
|
466
|
+
configureVersionCommand(program);
|
|
222
467
|
}
|
|
223
468
|
|
|
224
469
|
// src/index.ts
|
|
225
470
|
function createProgram() {
|
|
226
471
|
const program = new commander.Command();
|
|
227
|
-
program.name("
|
|
472
|
+
program.name("ollieshop").description("Ollie Shop CLI tools").version(pkg.version);
|
|
228
473
|
registerCommands(program);
|
|
229
474
|
return program;
|
|
230
475
|
}
|
|
231
476
|
if (__require.main === module) {
|
|
232
|
-
|
|
233
|
-
|
|
477
|
+
(async () => {
|
|
478
|
+
const program = createProgram();
|
|
479
|
+
if (process.argv.length <= 2) {
|
|
480
|
+
await checkForUpdates();
|
|
481
|
+
showWelcomeMessage();
|
|
482
|
+
process.exit(0);
|
|
483
|
+
}
|
|
484
|
+
validateProjectLocation();
|
|
485
|
+
program.parse();
|
|
486
|
+
})();
|
|
234
487
|
}
|
|
235
488
|
|
|
236
489
|
exports.createProgram = createProgram;
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ollie-shop/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Command-line tools for developing with Ollie Shop",
|
|
5
5
|
"bin": {
|
|
6
|
-
"
|
|
6
|
+
"ollieshop": "./dist/index.js"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"
|
|
9
|
+
"chalk": "^5.4.1",
|
|
10
|
+
"commander": "^11.1.0",
|
|
11
|
+
"jwt-decode": "^4.0.0",
|
|
12
|
+
"latest-version": "^9.0.0",
|
|
13
|
+
"open": "^10.1.2"
|
|
10
14
|
},
|
|
11
15
|
"devDependencies": {
|
|
12
16
|
"@types/node": "^22.15.23",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
|
|
4
|
+
export function configureDocsCommand(program: Command): void {
|
|
5
|
+
program
|
|
6
|
+
.command("docs")
|
|
7
|
+
.description("Show documentation links for Ollie Shop")
|
|
8
|
+
.action(() => {
|
|
9
|
+
console.log(chalk.cyanBright("\nOllie Shop Documentation\n"));
|
|
10
|
+
|
|
11
|
+
console.log(
|
|
12
|
+
`${chalk.green("Main Docs:".padEnd(20))} https://docs.ollie.shop/ollie-shop`,
|
|
13
|
+
);
|
|
14
|
+
console.log(
|
|
15
|
+
`${chalk.green("Components:".padEnd(20))} https://docs.ollie.shop/ollie-shop/concepts/component`,
|
|
16
|
+
);
|
|
17
|
+
console.log(
|
|
18
|
+
`${chalk.green("Functions:".padEnd(20))} https://docs.ollie.shop/ollie-shop/concepts/function`,
|
|
19
|
+
);
|
|
20
|
+
console.log(
|
|
21
|
+
`${chalk.green("Versions:".padEnd(20))} https://docs.ollie.shop/ollie-shop/concepts/version\n`,
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
|
|
4
|
+
export function configureHelpCommand(program: Command): void {
|
|
5
|
+
program
|
|
6
|
+
.command("help [command]")
|
|
7
|
+
.description("Display help for a specific command or list all commands")
|
|
8
|
+
.action((cmdName) => {
|
|
9
|
+
if (cmdName) {
|
|
10
|
+
const cmd = program.commands.find((c) => c.name() === cmdName);
|
|
11
|
+
if (cmd) {
|
|
12
|
+
cmd.help();
|
|
13
|
+
} else {
|
|
14
|
+
console.log(chalk.red(`Command '${cmdName}' not found.`));
|
|
15
|
+
}
|
|
16
|
+
} else {
|
|
17
|
+
console.log(chalk.cyanBright("\nAvailable commands:\n"));
|
|
18
|
+
for (const cmd of program.commands) {
|
|
19
|
+
console.log(
|
|
20
|
+
` ${chalk.green(cmd.name().padEnd(20))} ${cmd.description()}`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
console.log(
|
|
24
|
+
`\nUse ${chalk.yellow("ollieshop help <command>")} to get more info on a command.\n`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
package/src/commands/index.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
+
import { configureDocsCommand } from "./docs";
|
|
3
|
+
import { configureHelpCommand } from "./help";
|
|
2
4
|
import { configureLoginCommand } from "./login";
|
|
5
|
+
import { configureValidateCommand } from "./validate";
|
|
6
|
+
import { configureVersionCommand } from "./version";
|
|
7
|
+
import { configureWhoamiCommand } from "./whoami";
|
|
3
8
|
|
|
4
9
|
/**
|
|
5
10
|
* Register all CLI commands with the program
|
|
6
11
|
* @param program The commander program instance
|
|
7
12
|
*/
|
|
8
13
|
export function registerCommands(program: Command): void {
|
|
9
|
-
|
|
14
|
+
configureDocsCommand(program);
|
|
15
|
+
configureHelpCommand(program);
|
|
10
16
|
configureLoginCommand(program);
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
configureWhoamiCommand(program);
|
|
18
|
+
configureValidateCommand(program);
|
|
19
|
+
configureVersionCommand(program);
|
|
13
20
|
}
|
package/src/commands/login.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
1
|
import { randomBytes } from "node:crypto";
|
|
3
2
|
import fs from "node:fs/promises";
|
|
4
3
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
@@ -244,7 +243,7 @@ async function startWebAuthFlow(options: {
|
|
|
244
243
|
});
|
|
245
244
|
|
|
246
245
|
// Start the server
|
|
247
|
-
server.listen(port, () => {
|
|
246
|
+
server.listen(port, async () => {
|
|
248
247
|
// Build the URL to your Next.js app with necessary parameters
|
|
249
248
|
const redirectUrl = `http://localhost:${port}/callback`;
|
|
250
249
|
|
|
@@ -257,8 +256,10 @@ async function startWebAuthFlow(options: {
|
|
|
257
256
|
console.log("\n🔒 Please authenticate in your browser...\n");
|
|
258
257
|
console.log(`Opening: ${authUrl}\n`);
|
|
259
258
|
|
|
259
|
+
const open = (await import("open")).default;
|
|
260
|
+
|
|
260
261
|
// Open the browser with the authorization URL
|
|
261
|
-
|
|
262
|
+
open(authUrl.toString());
|
|
262
263
|
});
|
|
263
264
|
|
|
264
265
|
// Handle server errors
|
|
@@ -293,20 +294,6 @@ async function startWebAuthFlow(options: {
|
|
|
293
294
|
});
|
|
294
295
|
}
|
|
295
296
|
|
|
296
|
-
/**
|
|
297
|
-
* Open the default browser with the given URL
|
|
298
|
-
*/
|
|
299
|
-
function openBrowser(url: string) {
|
|
300
|
-
const command =
|
|
301
|
-
process.platform === "darwin"
|
|
302
|
-
? "open"
|
|
303
|
-
: process.platform === "win32"
|
|
304
|
-
? "start"
|
|
305
|
-
: "xdg-open";
|
|
306
|
-
|
|
307
|
-
spawn(command, [url], { detached: true }).unref();
|
|
308
|
-
}
|
|
309
|
-
|
|
310
297
|
/**
|
|
311
298
|
* Save authentication credentials locally
|
|
312
299
|
*
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import type { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register the `validate` command
|
|
8
|
+
*/
|
|
9
|
+
export function configureValidateCommand(program: Command): void {
|
|
10
|
+
program
|
|
11
|
+
// Make componentPath optional here to suppress Commander error
|
|
12
|
+
.command("validate [componentPath]")
|
|
13
|
+
.description("Validate the structure of your custom component")
|
|
14
|
+
.action(async (componentPath: string | undefined) => {
|
|
15
|
+
if (!componentPath) {
|
|
16
|
+
console.error(
|
|
17
|
+
`${chalk.red("❌ Missing required argument:")} component path\n\n` +
|
|
18
|
+
`Example usage:\n ${chalk.cyan("ollieshop validate")} ./path/to/your/component\n`,
|
|
19
|
+
);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const resolvedPath = path.resolve(componentPath);
|
|
24
|
+
const requiredFiles = ["index.tsx", "package.json", "meta.json"];
|
|
25
|
+
const optionalFile = "styles.module.css";
|
|
26
|
+
|
|
27
|
+
let isValid = true;
|
|
28
|
+
|
|
29
|
+
// Track missing required files
|
|
30
|
+
const missingFiles: string[] = [];
|
|
31
|
+
|
|
32
|
+
// Check required files
|
|
33
|
+
for (const file of requiredFiles) {
|
|
34
|
+
try {
|
|
35
|
+
await fs.access(path.join(resolvedPath, file));
|
|
36
|
+
console.log(`${chalk.green("✔")} Found: ${chalk.cyan(file)}`);
|
|
37
|
+
} catch {
|
|
38
|
+
console.log(`${chalk.red("✖")} Missing: ${chalk.cyan(file)}`);
|
|
39
|
+
missingFiles.push(file);
|
|
40
|
+
isValid = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check optional file presence
|
|
45
|
+
try {
|
|
46
|
+
await fs.access(path.join(resolvedPath, optionalFile));
|
|
47
|
+
console.log(
|
|
48
|
+
`${chalk.yellow("ℹ")} Optional file found: ${chalk.cyan(optionalFile)}`,
|
|
49
|
+
);
|
|
50
|
+
} catch {
|
|
51
|
+
console.log(
|
|
52
|
+
`${chalk.dim("ℹ")} Optional file not found: ${chalk.cyan(optionalFile)}`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (isValid) {
|
|
57
|
+
console.log(`\n${chalk.green("✔")} Component structure looks valid!`);
|
|
58
|
+
} else {
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import { pkg } from "../utils/core";
|
|
4
|
+
|
|
5
|
+
export function configureVersionCommand(program: Command): void {
|
|
6
|
+
program
|
|
7
|
+
.command("version")
|
|
8
|
+
.description("Display the current CLI version")
|
|
9
|
+
.action(() => {
|
|
10
|
+
console.log(
|
|
11
|
+
`${chalk.bold("Ollie Shop CLI version:")} ${chalk.cyan(pkg.version)}\n`,
|
|
12
|
+
);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import { getCurrentUser } from "../utils/auth";
|
|
4
|
+
import { type OllieConfig, getOllieConfig } from "../utils/store";
|
|
5
|
+
|
|
6
|
+
// Reads credentials from ~/.ollie-shop/credentials.json
|
|
7
|
+
async function getUserInfo(): Promise<{ email?: string; store?: OllieConfig }> {
|
|
8
|
+
try {
|
|
9
|
+
const user = await getCurrentUser();
|
|
10
|
+
const store = await getOllieConfig();
|
|
11
|
+
|
|
12
|
+
if (!user || !user.email) {
|
|
13
|
+
console.log("❌ No user authenticated");
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
email: user.email,
|
|
19
|
+
store: store ?? undefined,
|
|
20
|
+
};
|
|
21
|
+
} catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function configureWhoamiCommand(program: Command) {
|
|
27
|
+
program
|
|
28
|
+
.command("whoami")
|
|
29
|
+
.description("Show logged-in user and current store")
|
|
30
|
+
.action(async () => {
|
|
31
|
+
const { email, store } = await getUserInfo();
|
|
32
|
+
|
|
33
|
+
if (!email) {
|
|
34
|
+
console.log(chalk.red("You are not authenticated"));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log();
|
|
39
|
+
console.log(
|
|
40
|
+
`${chalk.bold("You are logged in as:")} ${chalk.cyan(email)}`,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (store) {
|
|
44
|
+
console.log(
|
|
45
|
+
`${chalk.bold("Current store:")} ${chalk.cyan(store.platformStoreId ?? "unknown")}`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log();
|
|
50
|
+
});
|
|
51
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,21 +1,42 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { registerCommands } from "./commands";
|
|
3
|
+
import {
|
|
4
|
+
checkForUpdates,
|
|
5
|
+
pkg,
|
|
6
|
+
showWelcomeMessage,
|
|
7
|
+
validateProjectLocation,
|
|
8
|
+
} from "./utils/core";
|
|
3
9
|
|
|
4
10
|
/**
|
|
5
|
-
*
|
|
11
|
+
* Creates and configures the CLI program instance
|
|
6
12
|
*/
|
|
7
13
|
export function createProgram() {
|
|
8
14
|
const program = new Command();
|
|
9
|
-
program.name("ollie").description("Ollie Shop CLI tools").version("0.1.0");
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
program
|
|
17
|
+
.name("ollieshop")
|
|
18
|
+
.description("Ollie Shop CLI tools")
|
|
19
|
+
.version(pkg.version);
|
|
20
|
+
|
|
21
|
+
// Register CLI commands from external modules
|
|
12
22
|
registerCommands(program);
|
|
13
23
|
|
|
14
24
|
return program;
|
|
15
25
|
}
|
|
16
26
|
|
|
17
|
-
// If this file is
|
|
27
|
+
// If this file is executed directly, initialize the CLI
|
|
18
28
|
if (require.main === module) {
|
|
19
|
-
|
|
20
|
-
|
|
29
|
+
(async () => {
|
|
30
|
+
const program = createProgram();
|
|
31
|
+
|
|
32
|
+
if (process.argv.length <= 2) {
|
|
33
|
+
await checkForUpdates();
|
|
34
|
+
showWelcomeMessage();
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
validateProjectLocation();
|
|
39
|
+
|
|
40
|
+
program.parse();
|
|
41
|
+
})();
|
|
21
42
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// lib/auth.ts
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { jwtDecode } from "jwt-decode";
|
|
6
|
+
|
|
7
|
+
const CREDENTIALS_PATH = path.join(
|
|
8
|
+
homedir(),
|
|
9
|
+
".ollie-shop",
|
|
10
|
+
"credentials.json",
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
type Token = {
|
|
14
|
+
accessToken: string;
|
|
15
|
+
refreshToken?: string;
|
|
16
|
+
expiresAt?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type DecodedToken = {
|
|
20
|
+
email?: string;
|
|
21
|
+
exp?: number;
|
|
22
|
+
sub?: string;
|
|
23
|
+
iat?: number;
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export async function getCurrentUser(): Promise<{ email?: string } | null> {
|
|
28
|
+
try {
|
|
29
|
+
const raw = await fs.readFile(CREDENTIALS_PATH, "utf-8");
|
|
30
|
+
const token: Token = JSON.parse(raw);
|
|
31
|
+
|
|
32
|
+
if (!token.accessToken) return null;
|
|
33
|
+
|
|
34
|
+
const decoded = jwtDecode<DecodedToken>(token.accessToken);
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
email: decoded.email,
|
|
38
|
+
};
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import latestVersion from "latest-version";
|
|
6
|
+
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
export const pkg = require("../package.json");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Checks if there is a newer version of the CLI available
|
|
12
|
+
* and notifies the user to update if so.
|
|
13
|
+
*/
|
|
14
|
+
export async function checkForUpdates() {
|
|
15
|
+
try {
|
|
16
|
+
const latest = await latestVersion(pkg.name);
|
|
17
|
+
if (latest !== pkg.version) {
|
|
18
|
+
console.log(
|
|
19
|
+
chalk.yellow(
|
|
20
|
+
`\n⚠️ A new version ${chalk.bold(latest)} is available!\n Update now: ${chalk.bold(
|
|
21
|
+
"npm install -g @ollie-shop/cli",
|
|
22
|
+
)}\n`,
|
|
23
|
+
),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
} catch {
|
|
27
|
+
// Silently ignore errors if offline or failed
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Displays a custom welcome message when no arguments are passed
|
|
33
|
+
*/
|
|
34
|
+
export function showWelcomeMessage() {
|
|
35
|
+
console.log(
|
|
36
|
+
chalk.cyanBright.bold(
|
|
37
|
+
`\n✨ Welcome to the Ollie Shop CLI - ${pkg.version} ✨\n`,
|
|
38
|
+
),
|
|
39
|
+
);
|
|
40
|
+
console.log("Build, customize and deploy your e-commerce with ease.\n");
|
|
41
|
+
|
|
42
|
+
console.log("Usage:");
|
|
43
|
+
console.log(" ollieshop <command> [options]\n");
|
|
44
|
+
|
|
45
|
+
console.log("Available commands:");
|
|
46
|
+
|
|
47
|
+
// Stable commands
|
|
48
|
+
const stableCommands = [
|
|
49
|
+
{ cmd: "login", desc: "Log in to your Ollie Shop account" },
|
|
50
|
+
{ cmd: "validate", desc: "Validate your component files" },
|
|
51
|
+
{ cmd: "whoami", desc: "Show logged-in user and current store" },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
for (const { cmd, desc } of stableCommands) {
|
|
55
|
+
console.log(` ${chalk.green(cmd.padEnd(20))} ${desc}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log("\nComing soon:");
|
|
59
|
+
|
|
60
|
+
// Coming soon
|
|
61
|
+
const comingSoonCommands = [
|
|
62
|
+
{ cmd: "build", desc: "Compile components for deployment" },
|
|
63
|
+
{ cmd: "deploy", desc: "Deploy a component or function" },
|
|
64
|
+
{ cmd: "dev", desc: "Start local preview server" },
|
|
65
|
+
{ cmd: "status", desc: "Show store status" },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
for (const { cmd, desc } of comingSoonCommands) {
|
|
69
|
+
console.log(` ${chalk.yellow(cmd.padEnd(20))} ${desc}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(
|
|
73
|
+
`\nFor a full list of commands, run: ${chalk.yellow.bold("ollieshop help")}\n`,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
console.log(
|
|
77
|
+
"Documentation:",
|
|
78
|
+
chalk.underline.blue("https://docs.ollie.shop/ollie-shop"),
|
|
79
|
+
);
|
|
80
|
+
console.log();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Ensures the current working directory is a valid Ollie Shop project.
|
|
85
|
+
* If not, it shows an error and exits the process.
|
|
86
|
+
*/
|
|
87
|
+
export function validateProjectLocation() {
|
|
88
|
+
const command = process.argv[2] ?? "";
|
|
89
|
+
|
|
90
|
+
// Commands allowed outside a valid Ollie Shop project
|
|
91
|
+
const allowedOutside = ["help", "docs", "version"];
|
|
92
|
+
|
|
93
|
+
if (allowedOutside.includes(command)) return;
|
|
94
|
+
|
|
95
|
+
const configPath = path.join(process.cwd(), "ollie.json");
|
|
96
|
+
const isValidProject = fs.existsSync(configPath);
|
|
97
|
+
|
|
98
|
+
if (!isValidProject) {
|
|
99
|
+
console.log(`
|
|
100
|
+
${chalk.red("❌")} This command must be run inside an ${chalk.bold("Ollie Shop project")}.
|
|
101
|
+
Please navigate to your project directory and try again.\n`);
|
|
102
|
+
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export type OllieConfig = {
|
|
5
|
+
storeId: string;
|
|
6
|
+
versionId: string;
|
|
7
|
+
platform: string;
|
|
8
|
+
platformStoreId: string;
|
|
9
|
+
sessionId: string;
|
|
10
|
+
props: unknown;
|
|
11
|
+
theme: Record<string, unknown>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export async function getOllieConfig(): Promise<OllieConfig | null> {
|
|
15
|
+
try {
|
|
16
|
+
const configPath = path.join(process.cwd(), "ollie.json");
|
|
17
|
+
const raw = await fs.readFile(configPath, "utf-8");
|
|
18
|
+
const data: OllieConfig = JSON.parse(raw);
|
|
19
|
+
return data;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|