create-shopify-firebase-app 1.1.2 → 1.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.
Files changed (3) hide show
  1. package/README.md +73 -31
  2. package/lib/index.js +283 -81
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -18,6 +18,25 @@ npx create-shopify-firebase-app my-app
18
18
 
19
19
  ---
20
20
 
21
+ ## Table of Contents
22
+
23
+ - [What is this?](#what-is-this)
24
+ - [Quick Start](#quick-start)
25
+ - [Why Firebase?](#why-firebase)
26
+ - [What's Inside](#whats-inside)
27
+ - [Architecture](#architecture)
28
+ - [CLI Usage](#cli-usage)
29
+ - [Development](#development)
30
+ - [Extending Your App](#extending-your-app)
31
+ - [How Many Stores Can You Run for Free?](#how-many-stores-can-you-run-for-free)
32
+ - [GDPR Compliance](#gdpr-compliance)
33
+ - [Troubleshooting](#troubleshooting)
34
+ - [Contributing](#contributing)
35
+ - [Related](#related)
36
+ - [License](#license)
37
+
38
+ ---
39
+
21
40
  ## What is this?
22
41
 
23
42
  The **Firebase alternative** to `shopify app init`. Instead of Remix + Prisma + Vercel, you get:
@@ -44,53 +63,74 @@ One `npx` command scaffolds everything, installs dependencies, wires up Firebase
44
63
  | Firebase CLI | `npm i -g firebase-tools` | Yes, installed automatically if missing |
45
64
  | Shopify CLI | `npm i -g @shopify/cli` | Yes, installed automatically if missing |
46
65
 
47
- ### 1. Create your Shopify app
48
-
49
- Go to [partners.shopify.com](https://partners.shopify.com/) → **Apps** → **Create app** → **Create app manually**.
50
- Copy the **Client ID** (API Key) and **Client Secret** (API Secret).
51
-
52
- ### 2. Create your Firebase project
53
-
54
- Go to [console.firebase.google.com](https://console.firebase.google.com/) → **Add project**.
55
- Enable **Cloud Firestore** (production mode). Note the **Project ID**.
56
-
57
- ### 3. Run the scaffold
66
+ ### 1. Run the scaffold
58
67
 
59
68
  ```bash
60
69
  npx create-shopify-firebase-app my-app
61
70
  ```
62
71
 
63
- The interactive CLI asks for your credentials and does the rest:
72
+ The interactive CLI guides you through everything — creating your Shopify app, setting up Firebase, and wiring it all together:
64
73
 
65
74
  ```
66
- SHOPIFY + FIREBASE Create a new Shopify app
75
+ 🛍️ + 🔥 create-shopify-firebase-app
76
+ Serverless Shopify apps — free until you scale
67
77
 
68
- Project directory name: my-app
69
- ✔ App name: My App
70
- ✔ Shopify API Key: abc123...
71
- ✔ Shopify API Secret: ********
72
- ✔ API Scopes: read_products
73
- ✔ Firebase Project ID: my-app-12345
78
+ === App Configuration ===
74
79
 
75
- [1/6] Scaffolding project...
76
- Created 27 files in my-app/
80
+ ? Project directory name: my-app
81
+ ? App name (shown in Shopify admin): My App
82
+ ? What kind of app are you building?
83
+ ❯ Public app — list on the Shopify App Store
84
+ Custom app — built for a single store
77
85
 
78
- [2/6] Installing dependencies...
79
- ✔ Dependencies installed
86
+ === Shopify Setup ===
80
87
 
81
- [3/6] Building TypeScript...
82
- TypeScript compiled successfully
88
+ ? How would you like to connect your Shopify app?
89
+ Create a new app — we'll guide you through it
90
+ I already have an app — enter my credentials
83
91
 
84
- [4/6] Setting up Firebase...
85
- ✔ Firebase project linked: my-app-12345
92
+ Opening Shopify Partner Dashboard...
86
93
 
87
- [5/6] Checking Shopify CLI...
88
- ✔ Shopify CLI detected
94
+ Follow these steps:
89
95
 
90
- [6/6] Initializing git...
96
+ 1. Sign in to your Partner account
97
+ 2. Go to Apps → Create app → Create app manually
98
+ 3. Enter app name: My App
99
+ 4. Copy the Client ID and Client Secret
100
+
101
+ ? Paste your Client ID (API Key): abc123...
102
+ ? Paste your Client Secret: ********
103
+ ? What API access does your app need?
104
+ ❯ Read products read_products
105
+ Read + write products read_products,write_products
106
+ Orders + products read_products,write_products,read_orders,write_orders
107
+ Custom scopes — enter manually
108
+
109
+ === Firebase Setup ===
110
+
111
+ ℹ Fetching your Firebase projects...
112
+ ? Select a Firebase project
113
+ ❯ [create a new project]
114
+ My Project (my-project-123)
115
+ Another Project (another-456)
116
+ [enter project ID manually]
117
+
118
+ === Setting Up ===
119
+
120
+ ℹ Scaffolding project...
121
+ ✔ Created 27 files in my-app/
122
+ ℹ Installing dependencies...
123
+ ✔ Dependencies installed
124
+ ℹ Building TypeScript...
125
+ ✔ TypeScript compiled successfully
126
+ ℹ Setting up Firebase...
127
+ ✔ Firebase project linked + Firestore provisioned
128
+ ℹ Checking Shopify CLI...
129
+ ✔ Shopify CLI detected
130
+ ℹ Initializing git...
91
131
  ✔ Git repository initialized with first commit
92
132
 
93
- SUCCESS Your Shopify + Firebase app is ready!
133
+ ✔ All done! Your Shopify + Firebase app is ready.
94
134
 
95
135
  Next steps:
96
136
 
@@ -98,6 +138,8 @@ The interactive CLI asks for your credentials and does the rest:
98
138
  firebase deploy
99
139
  ```
100
140
 
141
+ No need to visit Firebase Console or Shopify Partner Dashboard beforehand — the CLI guides you through everything.
142
+
101
143
  ### 4. Deploy & Install
102
144
 
103
145
  ```bash
package/lib/index.js CHANGED
@@ -37,10 +37,11 @@ const c = {
37
37
  const ok = (msg) => console.log(` ${c.green}✔${c.reset} ${msg}`);
38
38
  const warn = (msg) => console.log(` ${c.yellow}⚠${c.reset} ${msg}`);
39
39
  const info = (msg) => console.log(` ${c.cyan}ℹ${c.reset} ${msg}`);
40
- const step = (n, total, msg) =>
41
- console.log(
42
- `\n ${c.dim}[${n}/${total}]${c.reset} ${c.bold}${msg}${c.reset}`,
43
- );
40
+ const section = (title) => {
41
+ console.log();
42
+ console.log(` ${c.cyan}===${c.reset} ${c.bold}${title}${c.reset} ${c.cyan}===${c.reset}`);
43
+ console.log();
44
+ };
44
45
 
45
46
  // ─── Check if a CLI tool is available ────────────────────────────────────
46
47
  function hasCommand(cmd) {
@@ -95,6 +96,48 @@ function parseArgs(argv) {
95
96
  return { projectName, ...args };
96
97
  }
97
98
 
99
+ // ─── Open URL in browser (cross-platform) ───────────────────────────────
100
+ function openBrowser(url) {
101
+ const platform = process.platform;
102
+ try {
103
+ if (platform === "win32") execSync(`start "" "${url}"`, { stdio: "ignore" });
104
+ else if (platform === "darwin") execSync(`open "${url}"`, { stdio: "ignore" });
105
+ else execSync(`xdg-open "${url}"`, { stdio: "ignore" });
106
+ } catch {
107
+ info(`Open this URL in your browser: ${url}`);
108
+ }
109
+ }
110
+
111
+ // ─── List Firebase projects ──────────────────────────────────────────────
112
+ function listFirebaseProjects() {
113
+ try {
114
+ const output = execSync("firebase projects:list --json", {
115
+ encoding: "utf8",
116
+ stdio: ["ignore", "pipe", "ignore"],
117
+ });
118
+ const data = JSON.parse(output);
119
+ if (data.status === "success" && Array.isArray(data.result)) {
120
+ return data.result
121
+ .filter((p) => p.projectId)
122
+ .map((p) => ({
123
+ projectId: p.projectId,
124
+ displayName: p.displayName || p.projectId,
125
+ }));
126
+ }
127
+ } catch {}
128
+ return [];
129
+ }
130
+
131
+ // ─── Create Firebase project ─────────────────────────────────────────────
132
+ async function createFirebaseProject(projectId, displayName) {
133
+ try {
134
+ await exec(`firebase projects:create "${projectId}" --display-name "${displayName}"`);
135
+ return true;
136
+ } catch {
137
+ return false;
138
+ }
139
+ }
140
+
98
141
  // ─── Interactive prompts ─────────────────────────────────────────────────
99
142
  async function getConfig(args) {
100
143
  // Check if running non-interactively
@@ -113,16 +156,22 @@ async function getConfig(args) {
113
156
  };
114
157
  }
115
158
 
159
+ const onCancel = () => {
160
+ console.log("\n Cancelled.\n");
161
+ process.exit(0);
162
+ };
163
+
164
+ // ── Banner ────────────────────────────────────────────────────────
116
165
  console.log();
117
- console.log(
118
- ` ${c.bgGreen}${c.white}${c.bold} SHOPIFY + FIREBASE ${c.reset} Create a new Shopify app`,
119
- );
120
- console.log();
166
+ console.log(` ${c.green}${c.bold}🛍️ + 🔥${c.reset} ${c.bold}create-shopify-firebase-app${c.reset}`);
167
+ console.log(` ${c.dim}Serverless Shopify apps free until you scale${c.reset}`);
121
168
 
122
- const questions = [];
169
+ // ═══════════════════════════════════════════════════════════════════
170
+ section("App Configuration");
123
171
 
124
- if (!args.projectName) {
125
- questions.push({
172
+ let projectName = args.projectName;
173
+ if (!projectName) {
174
+ const res = await prompts({
126
175
  type: "text",
127
176
  name: "projectName",
128
177
  message: "Project directory name",
@@ -132,58 +181,225 @@ async function getConfig(args) {
132
181
  if (/[^a-zA-Z0-9._-]/.test(v)) return "Use only letters, numbers, dots, hyphens, underscores";
133
182
  return true;
134
183
  },
135
- });
184
+ }, { onCancel });
185
+ projectName = res.projectName;
136
186
  }
137
187
 
138
- questions.push(
139
- {
140
- type: "text",
141
- name: "appName",
142
- message: "App name (shown in Shopify admin)",
143
- initial: args.projectName || "My Shopify App",
144
- },
145
- {
146
- type: "text",
147
- name: "apiKey",
148
- message: `Shopify API Key ${c.dim}(Partner Dashboard App → Client ID)${c.reset}`,
149
- validate: (v) => (v.trim() ? true : "Required — get it from partners.shopify.com"),
150
- },
151
- {
152
- type: "password",
153
- name: "apiSecret",
154
- message: "Shopify API Secret",
155
- validate: (v) => (v.trim() ? true : "Required"),
156
- },
157
- {
188
+ const { appName } = await prompts({
189
+ type: "text",
190
+ name: "appName",
191
+ message: "App name (shown in Shopify admin)",
192
+ initial: projectName || "My Shopify App",
193
+ }, { onCancel });
194
+
195
+ const { appType } = await prompts({
196
+ type: "select",
197
+ name: "appType",
198
+ message: "What kind of app are you building?",
199
+ choices: [
200
+ { title: "Public app — list on the Shopify App Store", value: "public" },
201
+ { title: "Custom app — built for a single store", value: "custom" },
202
+ ],
203
+ }, { onCancel });
204
+
205
+ // ═══════════════════════════════════════════════════════════════════
206
+ section("Shopify Setup");
207
+
208
+ const { shopifySetup } = await prompts({
209
+ type: "select",
210
+ name: "shopifySetup",
211
+ message: "How would you like to connect your Shopify app?",
212
+ choices: [
213
+ { title: "Create a new app — we'll guide you through it", value: "create" },
214
+ { title: "I already have an app — enter my credentials", value: "existing" },
215
+ ],
216
+ }, { onCancel });
217
+
218
+ let apiKey, apiSecret;
219
+
220
+ if (shopifySetup === "create") {
221
+ console.log();
222
+ info("Opening Shopify Partner Dashboard...");
223
+ console.log();
224
+ console.log(` ${c.dim}Follow these steps:${c.reset}`);
225
+ console.log();
226
+ console.log(` ${c.cyan}1.${c.reset} Sign in to your Partner account`);
227
+ console.log(` ${c.cyan}2.${c.reset} Go to ${c.bold}Apps${c.reset} → ${c.bold}Create app${c.reset} → ${c.bold}Create app manually${c.reset}`);
228
+ console.log(` ${c.cyan}3.${c.reset} Enter app name: ${c.cyan}${appName}${c.reset}`);
229
+ console.log(` ${c.cyan}4.${c.reset} Copy the ${c.bold}Client ID${c.reset} and ${c.bold}Client Secret${c.reset}`);
230
+ console.log();
231
+ openBrowser("https://partners.shopify.com");
232
+
233
+ const creds = await prompts([
234
+ {
235
+ type: "text",
236
+ name: "apiKey",
237
+ message: `Paste your Client ID (API Key)`,
238
+ validate: (v) => (v.trim() ? true : "Required — copy from the app you just created"),
239
+ },
240
+ {
241
+ type: "password",
242
+ name: "apiSecret",
243
+ message: "Paste your Client Secret (API Secret)",
244
+ validate: (v) => (v.trim() ? true : "Required"),
245
+ },
246
+ ], { onCancel });
247
+ apiKey = creds.apiKey;
248
+ apiSecret = creds.apiSecret;
249
+ } else {
250
+ const creds = await prompts([
251
+ {
252
+ type: "text",
253
+ name: "apiKey",
254
+ message: `Shopify API Key ${c.dim}(Client ID)${c.reset}`,
255
+ validate: (v) => (v.trim() ? true : "Required"),
256
+ },
257
+ {
258
+ type: "password",
259
+ name: "apiSecret",
260
+ message: "Shopify API Secret",
261
+ validate: (v) => (v.trim() ? true : "Required"),
262
+ },
263
+ ], { onCancel });
264
+ apiKey = creds.apiKey;
265
+ apiSecret = creds.apiSecret;
266
+ }
267
+
268
+ // Scope presets (like Shopify CLI template selection)
269
+ const { scopeChoice } = await prompts({
270
+ type: "select",
271
+ name: "scopeChoice",
272
+ message: "What API access does your app need?",
273
+ choices: [
274
+ { title: `Read products ${c.dim}read_products${c.reset}`, value: "read_products" },
275
+ { title: `Read + write products ${c.dim}read_products,write_products${c.reset}`, value: "read_products,write_products" },
276
+ { title: `Orders + products ${c.dim}read_products,write_products,read_orders,write_orders${c.reset}`, value: "read_products,write_products,read_orders,write_orders" },
277
+ { title: "Custom scopes — enter manually", value: "__custom__" },
278
+ ],
279
+ }, { onCancel });
280
+
281
+ let scopes;
282
+ if (scopeChoice === "__custom__") {
283
+ const res = await prompts({
158
284
  type: "text",
159
285
  name: "scopes",
160
- message: "API Scopes",
286
+ message: "Enter scopes (comma-separated)",
161
287
  initial: "read_products",
162
- },
163
- {
164
- type: "text",
165
- name: "projectId",
166
- message: `Firebase Project ID ${c.dim}(Firebase Console → Project Settings)${c.reset}`,
167
- validate: (v) => (v.trim() ? true : "Required — create one at console.firebase.google.com"),
168
- },
169
- );
170
-
171
- const onCancel = () => {
172
- console.log("\n Cancelled.\n");
173
- process.exit(0);
174
- };
288
+ validate: (v) => (v.trim() ? true : "At least one scope is required"),
289
+ }, { onCancel });
290
+ scopes = res.scopes;
291
+ } else {
292
+ scopes = scopeChoice;
293
+ }
175
294
 
176
- const answers = await prompts(questions, { onCancel });
295
+ // ═══════════════════════════════════════════════════════════════════
296
+ section("Firebase Setup");
297
+
298
+ let projectId;
299
+ const hasFirebase = hasCommand("firebase");
300
+
301
+ if (hasFirebase) {
302
+ info("Fetching your Firebase projects...");
303
+
304
+ const choices = [
305
+ { title: `${c.cyan}[create a new project]${c.reset}`, value: "__create__" },
306
+ ];
307
+ const projects = listFirebaseProjects();
308
+ if (projects.length > 0) {
309
+ for (const p of projects) {
310
+ choices.push({
311
+ title: `${p.displayName} ${c.dim}(${p.projectId})${c.reset}`,
312
+ value: p.projectId,
313
+ });
314
+ }
315
+ }
316
+ choices.push({ title: `${c.dim}[enter project ID manually]${c.reset}`, value: "__manual__" });
317
+
318
+ const { firebaseChoice } = await prompts({
319
+ type: "select",
320
+ name: "firebaseChoice",
321
+ message: "Select a Firebase project",
322
+ choices,
323
+ }, { onCancel });
324
+
325
+ if (firebaseChoice === "__create__") {
326
+ const { newProjectId } = await prompts({
327
+ type: "text",
328
+ name: "newProjectId",
329
+ message: "New project ID",
330
+ initial: projectName,
331
+ validate: (v) => {
332
+ if (!v.trim()) return "Required";
333
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(v)) return "Only lowercase letters, numbers, and hyphens";
334
+ if (v.length < 6 || v.length > 30) return "Must be 6-30 characters";
335
+ return true;
336
+ },
337
+ }, { onCancel });
338
+
339
+ info(`Creating Firebase project: ${c.cyan}${newProjectId}${c.reset}...`);
340
+ const created = await createFirebaseProject(newProjectId, appName);
341
+ if (created) {
342
+ ok(`Project created: ${c.cyan}${newProjectId}${c.reset}`);
343
+ projectId = newProjectId;
344
+ } else {
345
+ warn("Could not create project automatically");
346
+ info("Create one at https://console.firebase.google.com");
347
+ const { manualId } = await prompts({
348
+ type: "text",
349
+ name: "manualId",
350
+ message: "Firebase Project ID",
351
+ validate: (v) => (v.trim() ? true : "Required"),
352
+ }, { onCancel });
353
+ projectId = manualId;
354
+ }
355
+ } else if (firebaseChoice === "__manual__") {
356
+ const { manualId } = await prompts({
357
+ type: "text",
358
+ name: "manualId",
359
+ message: "Firebase Project ID",
360
+ validate: (v) => (v.trim() ? true : "Required"),
361
+ }, { onCancel });
362
+ projectId = manualId;
363
+ } else {
364
+ projectId = firebaseChoice;
365
+ ok(`Using project: ${c.cyan}${projectId}${c.reset}`);
366
+ }
367
+ } else {
368
+ // No Firebase CLI — manual setup
369
+ const { firebaseSetup } = await prompts({
370
+ type: "select",
371
+ name: "firebaseSetup",
372
+ message: "How would you like to set up Firebase?",
373
+ choices: [
374
+ { title: "Create a new project — opens Firebase Console", value: "create" },
375
+ { title: "Enter project ID manually", value: "manual" },
376
+ ],
377
+ }, { onCancel });
378
+
379
+ if (firebaseSetup === "create") {
380
+ console.log();
381
+ info("Opening Firebase Console...");
382
+ info("Create a new project and note the Project ID");
383
+ console.log();
384
+ openBrowser("https://console.firebase.google.com");
385
+ }
177
386
 
178
- const projectName = args.projectName || answers.projectName;
179
- const projectId = answers.projectId;
387
+ const { manualId } = await prompts({
388
+ type: "text",
389
+ name: "manualId",
390
+ message: "Firebase Project ID",
391
+ validate: (v) => (v.trim() ? true : "Required"),
392
+ }, { onCancel });
393
+ projectId = manualId;
394
+ }
180
395
 
181
396
  return {
182
397
  projectName,
183
- appName: answers.appName || projectName,
184
- apiKey: answers.apiKey,
185
- apiSecret: answers.apiSecret,
186
- scopes: answers.scopes || "read_products",
398
+ appName: appName || projectName,
399
+ appType,
400
+ apiKey,
401
+ apiSecret,
402
+ scopes: scopes || "read_products",
187
403
  projectId,
188
404
  appUrl: `https://${projectId}.web.app`,
189
405
  };
@@ -326,15 +542,14 @@ export async function run(argv) {
326
542
  fs.rmSync(outputDir, { recursive: true, force: true });
327
543
  }
328
544
 
329
- const totalSteps = 6;
545
+ // ═══════════════════════════════════════════════════════════════════
546
+ section("Setting Up");
330
547
 
331
- // ── Step 1: Scaffold ──────────────────────────────────────────────
332
- step(1, totalSteps, "Scaffolding project...");
548
+ info("Scaffolding project...");
333
549
  const fileCount = scaffold(outputDir, config);
334
550
  ok(`Created ${fileCount} files in ${c.cyan}${config.projectName}/${c.reset}`);
335
551
 
336
- // ── Step 2: Install dependencies ──────────────────────────────────
337
- step(2, totalSteps, "Installing dependencies...");
552
+ info("Installing dependencies...");
338
553
  const functionsDir = path.join(outputDir, "functions");
339
554
  try {
340
555
  await exec("npm install", functionsDir);
@@ -343,8 +558,7 @@ export async function run(argv) {
343
558
  warn(`npm install failed — run manually: cd ${config.projectName}/functions && npm install`);
344
559
  }
345
560
 
346
- // ── Step 3: Build TypeScript ──────────────────────────────────────
347
- step(3, totalSteps, "Building TypeScript...");
561
+ info("Building TypeScript...");
348
562
  try {
349
563
  await exec("npm run build", functionsDir);
350
564
  ok("TypeScript compiled successfully");
@@ -352,8 +566,7 @@ export async function run(argv) {
352
566
  warn("Build failed — run manually: cd functions && npm run build");
353
567
  }
354
568
 
355
- // ── Step 4: Firebase CLI ─────────────────────────────────────────────
356
- step(4, totalSteps, "Setting up Firebase...");
569
+ info("Setting up Firebase...");
357
570
  if (!hasCommand("firebase")) {
358
571
  info("Firebase CLI not found — installing globally...");
359
572
  try {
@@ -375,10 +588,9 @@ export async function run(argv) {
375
588
  });
376
589
  }
377
590
 
378
- // ── Step 5: Shopify CLI ─────────────────────────────────────────────
379
- step(5, totalSteps, "Checking Shopify CLI...");
591
+ info("Checking Shopify CLI...");
380
592
  if (hasCommand("shopify")) {
381
- ok("Shopify CLI detected — you can use `shopify app dev` for local development");
593
+ ok("Shopify CLI detected");
382
594
  } else {
383
595
  info("Shopify CLI not found — installing globally...");
384
596
  try {
@@ -387,12 +599,10 @@ export async function run(argv) {
387
599
  } catch (e) {
388
600
  warn("Could not install Shopify CLI automatically");
389
601
  info("Install manually: npm i -g @shopify/cli");
390
- info("Optional — you can also develop with Firebase emulators");
391
602
  }
392
603
  }
393
604
 
394
- // ── Step 6: Initialize git ────────────────────────────────────────
395
- step(6, totalSteps, "Initializing git...");
605
+ info("Initializing git...");
396
606
  if (hasCommand("git")) {
397
607
  try {
398
608
  await exec("git init", outputDir);
@@ -403,43 +613,35 @@ export async function run(argv) {
403
613
  warn("Git init failed — initialize manually if needed");
404
614
  }
405
615
  } else {
406
- warn("Git not found — skipping repository initialization");
616
+ warn("Git not found — skipping");
407
617
  }
408
618
 
409
- // ── Done! ─────────────────────────────────────────────────────────
410
619
  printSuccess(config);
411
620
  }
412
621
 
413
622
  // ─── Success output ──────────────────────────────────────────────────────
414
623
  function printSuccess(config) {
415
624
  console.log();
416
- console.log(
417
- ` ${c.bgGreen}${c.white}${c.bold} SUCCESS ${c.reset} Your Shopify + Firebase app is ready!`,
418
- );
625
+ console.log(` ${c.green}${c.bold}✔ All done!${c.reset} Your Shopify + Firebase app is ready.`);
419
626
  console.log();
420
627
  console.log(` ${c.bold}Next steps:${c.reset}`);
421
628
  console.log();
422
629
  console.log(` ${c.cyan}cd ${config.projectName}${c.reset}`);
423
630
  console.log(` ${c.cyan}firebase deploy${c.reset}`);
424
631
  console.log();
425
- console.log(` ${c.bold}Then install on your dev store:${c.reset}`);
632
+ console.log(` ${c.bold}Install on your dev store:${c.reset}`);
426
633
  console.log();
427
- console.log(
428
- ` ${c.cyan}${config.appUrl}/auth?shop=YOUR-STORE.myshopify.com${c.reset}`,
429
- );
634
+ console.log(` ${c.cyan}${config.appUrl}/auth?shop=YOUR-STORE.myshopify.com${c.reset}`);
430
635
  console.log();
431
- console.log(` ${c.bold}Or use Shopify CLI for local development:${c.reset}`);
636
+ console.log(` ${c.bold}Or develop locally:${c.reset}`);
432
637
  console.log();
433
638
  console.log(` ${c.cyan}shopify app dev${c.reset}`);
434
639
  console.log();
435
640
  console.log(` ${c.dim}─────────────────────────────────────────${c.reset}`);
436
- console.log();
437
641
  console.log(` ${c.dim}App URL: ${config.appUrl}${c.reset}`);
438
642
  console.log(` ${c.dim}Firebase: ${config.projectId}${c.reset}`);
439
643
  console.log(` ${c.dim}Scopes: ${config.scopes}${c.reset}`);
440
644
  console.log();
441
- console.log(` ${c.dim}Docs: https://github.com/mksd0398/create-shopify-firebase-app${c.reset}`);
442
- console.log();
443
645
  }
444
646
 
445
647
  // ─── Help output ─────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-shopify-firebase-app",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "Create Shopify apps powered by Firebase — serverless, lightweight, zero-framework. The official alternative to Remix for Shopify + Firebase developers.",
5
5
  "keywords": [
6
6
  "shopify",