create-shopify-firebase-app 1.2.0 → 1.3.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/README.md +9 -13
- package/lib/index.js +120 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,19 +86,15 @@ The interactive CLI guides you through everything — creating your Shopify app,
|
|
|
86
86
|
=== Shopify Setup ===
|
|
87
87
|
|
|
88
88
|
? How would you like to connect your Shopify app?
|
|
89
|
-
❯ Create a new app
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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...
|
|
89
|
+
❯ Create a new app via Shopify CLI
|
|
90
|
+
Link an existing app via Shopify CLI
|
|
91
|
+
Enter credentials manually (Client ID + Secret)
|
|
92
|
+
|
|
93
|
+
ℹ Logging into Shopify CLI...
|
|
94
|
+
✔ Logged into Shopify
|
|
95
|
+
ℹ Creating a new Shopify app...
|
|
96
|
+
(Shopify CLI handles app creation interactively)
|
|
97
|
+
✔ Client ID: abc123...
|
|
102
98
|
? Paste your Client Secret: ********
|
|
103
99
|
? What API access does your app need?
|
|
104
100
|
❯ Read products read_products
|
package/lib/index.js
CHANGED
|
@@ -128,6 +128,47 @@ function listFirebaseProjects() {
|
|
|
128
128
|
return [];
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// ─── Run an interactive command (user sees prompts) ─────────────────────
|
|
132
|
+
function execInteractive(cmd, cwd) {
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
const child = spawn(cmd, {
|
|
135
|
+
cwd,
|
|
136
|
+
shell: true,
|
|
137
|
+
stdio: "inherit",
|
|
138
|
+
});
|
|
139
|
+
child.on("close", (code) => {
|
|
140
|
+
if (code === 0) resolve();
|
|
141
|
+
else reject(new Error(`Command exited with code ${code}`));
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ─── Check if logged into Shopify CLI ───────────────────────────────────
|
|
147
|
+
function isShopifyLoggedIn() {
|
|
148
|
+
try {
|
|
149
|
+
// `shopify app config link --help` doesn't need auth,
|
|
150
|
+
// but `shopify app info` does — use a quick check
|
|
151
|
+
const out = execSync("shopify auth login --help", {
|
|
152
|
+
encoding: "utf8",
|
|
153
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
154
|
+
});
|
|
155
|
+
return true;
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ─── Parse TOML file for client_id ──────────────────────────────────────
|
|
162
|
+
function parseTomlClientId(tomlPath) {
|
|
163
|
+
try {
|
|
164
|
+
const content = fs.readFileSync(tomlPath, "utf8");
|
|
165
|
+
const match = content.match(/client_id\s*=\s*"([^"]+)"/);
|
|
166
|
+
return match ? match[1] : null;
|
|
167
|
+
} catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
131
172
|
// ─── Create Firebase project ─────────────────────────────────────────────
|
|
132
173
|
async function createFirebaseProject(projectId, displayName) {
|
|
133
174
|
try {
|
|
@@ -205,48 +246,97 @@ async function getConfig(args) {
|
|
|
205
246
|
// ═══════════════════════════════════════════════════════════════════
|
|
206
247
|
section("Shopify Setup");
|
|
207
248
|
|
|
249
|
+
const hasShopifyCli = hasCommand("shopify");
|
|
250
|
+
|
|
251
|
+
// Build choices based on what's available
|
|
252
|
+
const shopifyChoices = [];
|
|
253
|
+
if (hasShopifyCli) {
|
|
254
|
+
shopifyChoices.push(
|
|
255
|
+
{ title: "Create a new app via Shopify CLI", value: "cli-create" },
|
|
256
|
+
{ title: "Link an existing app via Shopify CLI", value: "cli-link" },
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
shopifyChoices.push(
|
|
260
|
+
{ title: `Enter credentials manually ${c.dim}(Client ID + Secret)${c.reset}`, value: "manual" },
|
|
261
|
+
);
|
|
262
|
+
|
|
208
263
|
const { shopifySetup } = await prompts({
|
|
209
264
|
type: "select",
|
|
210
265
|
name: "shopifySetup",
|
|
211
266
|
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
|
-
],
|
|
267
|
+
choices: shopifyChoices,
|
|
216
268
|
}, { onCancel });
|
|
217
269
|
|
|
218
270
|
let apiKey, apiSecret;
|
|
219
271
|
|
|
220
|
-
if (shopifySetup === "create") {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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");
|
|
272
|
+
if (shopifySetup === "cli-create" || shopifySetup === "cli-link") {
|
|
273
|
+
// ── Use Shopify CLI to create/link the app ────────────────────
|
|
274
|
+
// We need a temp directory with a shopify.app.toml for the CLI to work
|
|
275
|
+
const tmpDir = path.resolve(process.cwd(), `__shopify_setup_${Date.now()}`);
|
|
276
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
232
277
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
278
|
+
try {
|
|
279
|
+
// Ensure logged in
|
|
280
|
+
console.log();
|
|
281
|
+
info("Logging into Shopify CLI...");
|
|
282
|
+
info("A browser window will open — sign in to your Partner account.");
|
|
283
|
+
console.log();
|
|
284
|
+
await execInteractive("shopify auth login", tmpDir);
|
|
285
|
+
ok("Logged into Shopify");
|
|
286
|
+
|
|
287
|
+
// Run config link — it handles both create and link interactively
|
|
288
|
+
console.log();
|
|
289
|
+
if (shopifySetup === "cli-create") {
|
|
290
|
+
info("Creating a new Shopify app...");
|
|
291
|
+
info(`Select ${c.bold}"Create a new app"${c.reset} when prompted.`);
|
|
292
|
+
} else {
|
|
293
|
+
info("Linking an existing Shopify app...");
|
|
294
|
+
info("Select your app from the list when prompted.");
|
|
295
|
+
}
|
|
296
|
+
console.log();
|
|
297
|
+
await execInteractive("shopify app config link", tmpDir);
|
|
298
|
+
|
|
299
|
+
// Parse the generated TOML to get client_id
|
|
300
|
+
const tomlFiles = fs.readdirSync(tmpDir).filter((f) => f.endsWith(".toml"));
|
|
301
|
+
let parsedKey = null;
|
|
302
|
+
for (const f of tomlFiles) {
|
|
303
|
+
parsedKey = parseTomlClientId(path.join(tmpDir, f));
|
|
304
|
+
if (parsedKey) break;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (parsedKey) {
|
|
308
|
+
apiKey = parsedKey;
|
|
309
|
+
ok(`Client ID: ${c.cyan}${apiKey}${c.reset}`);
|
|
310
|
+
} else {
|
|
311
|
+
warn("Could not read Client ID from TOML");
|
|
312
|
+
const res = await prompts({
|
|
313
|
+
type: "text",
|
|
314
|
+
name: "apiKey",
|
|
315
|
+
message: "Paste your Client ID (API Key)",
|
|
316
|
+
validate: (v) => (v.trim() ? true : "Required"),
|
|
317
|
+
}, { onCancel });
|
|
318
|
+
apiKey = res.apiKey;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// API Secret is never in the TOML — always need to ask
|
|
322
|
+
console.log();
|
|
323
|
+
info("The API Secret is not stored in config files for security.");
|
|
324
|
+
info(`Find it at: ${c.cyan}https://partners.shopify.com${c.reset} → Apps → your app → Client credentials`);
|
|
325
|
+
console.log();
|
|
326
|
+
const { secret } = await prompts({
|
|
241
327
|
type: "password",
|
|
242
|
-
name: "
|
|
328
|
+
name: "secret",
|
|
243
329
|
message: "Paste your Client Secret (API Secret)",
|
|
244
|
-
validate: (v) => (v.trim() ? true : "Required"),
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
330
|
+
validate: (v) => (v.trim() ? true : "Required — find it in Partner Dashboard → Apps → Client credentials"),
|
|
331
|
+
}, { onCancel });
|
|
332
|
+
apiSecret = secret;
|
|
333
|
+
|
|
334
|
+
} finally {
|
|
335
|
+
// Clean up temp directory
|
|
336
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
337
|
+
}
|
|
249
338
|
} else {
|
|
339
|
+
// ── Manual entry ──────────────────────────────────────────────
|
|
250
340
|
const creds = await prompts([
|
|
251
341
|
{
|
|
252
342
|
type: "text",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-shopify-firebase-app",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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",
|