@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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @ollie-shop/cli@0.1.2 build /home/runner/work/ollie-shop/ollie-shop/packages/cli
2
+ > @ollie-shop/cli@0.2.0 build /home/runner/work/ollie-shop/ollie-shop/packages/cli
3
3
  > tsup
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -9,5 +9,5 @@
9
9
  CLI Target: esnext
10
10
  CLI Cleaning output folder
11
11
  CJS Build start
12
- CJS dist/index.js 7.47 KB
13
- CJS ⚡️ Build success in 699ms
12
+ CJS dist/index.js 16.06 KB
13
+ CJS ⚡️ 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 child_process = require('child_process');
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 path = require('path');
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 path__default = /*#__PURE__*/_interopDefault(path);
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
- openBrowser(authUrl.toString());
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 = path__default.default.join(os.homedir(), ".ollie-shop");
207
- const credentialsPath = path__default.default.join(configDir, "credentials.json");
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("ollie").description("Ollie Shop CLI tools").version("0.1.0");
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
- const program = createProgram();
233
- program.parse();
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.1.2",
4
- "description": "CLI tools for Ollie Shop",
3
+ "version": "0.2.0",
4
+ "description": "Command-line tools for developing with Ollie Shop",
5
5
  "bin": {
6
- "ollie-shop": "./dist/index.js"
6
+ "ollieshop": "./dist/index.js"
7
7
  },
8
8
  "dependencies": {
9
- "commander": "^11.1.0"
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
+ }
@@ -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
- // Register individual commands
14
+ configureDocsCommand(program);
15
+ configureHelpCommand(program);
10
16
  configureLoginCommand(program);
11
-
12
- // Add more commands here as they are implemented
17
+ configureWhoamiCommand(program);
18
+ configureValidateCommand(program);
19
+ configureVersionCommand(program);
13
20
  }
@@ -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
- openBrowser(authUrl.toString());
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
- * Create and configure the CLI program
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
- // Register all commands
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 run directly, execute the CLI
27
+ // If this file is executed directly, initialize the CLI
18
28
  if (require.main === module) {
19
- const program = createProgram();
20
- program.parse();
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
+ }