@shipindays/shipindays 0.1.13 → 0.1.15
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/index.js
CHANGED
|
@@ -42,12 +42,26 @@ const EMAIL_PROVIDERS = {
|
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
// Add to your CLI constants
|
|
46
|
+
const PAYMENT_PROVIDERS = {
|
|
47
|
+
stripe: {
|
|
48
|
+
label: "Stripe",
|
|
49
|
+
hint: "Subscriptions + One-time payments via Stripe Checkout",
|
|
50
|
+
},
|
|
51
|
+
dodopayments: {
|
|
52
|
+
label: "Dodo Payments",
|
|
53
|
+
hint: "Merchant of Record — simplifies global tax/compliance",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
45
57
|
const ENV_VARS = {
|
|
46
58
|
base: {
|
|
47
59
|
"# App": [
|
|
48
60
|
"NEXT_PUBLIC_APP_URL=http://localhost:3000",
|
|
49
61
|
],
|
|
50
62
|
},
|
|
63
|
+
|
|
64
|
+
// auth env's
|
|
51
65
|
auth: {
|
|
52
66
|
supabase: {
|
|
53
67
|
"# Supabase (supabase.com → project → settings → API)": [
|
|
@@ -65,12 +79,10 @@ const ENV_VARS = {
|
|
|
65
79
|
"AUTH_GOOGLE_ID=",
|
|
66
80
|
"AUTH_GOOGLE_SECRET=",
|
|
67
81
|
],
|
|
68
|
-
"# OAuth — GitHub (github.com → Settings → Developer settings → OAuth Apps)": [
|
|
69
|
-
"AUTH_GITHUB_ID=",
|
|
70
|
-
"AUTH_GITHUB_SECRET=",
|
|
71
|
-
],
|
|
72
82
|
},
|
|
73
83
|
},
|
|
84
|
+
|
|
85
|
+
// emails env's
|
|
74
86
|
email: {
|
|
75
87
|
resend: {
|
|
76
88
|
"# Resend (resend.com → API Keys)": [
|
|
@@ -83,13 +95,42 @@ const ENV_VARS = {
|
|
|
83
95
|
"MAILGUN_DOMAIN=",
|
|
84
96
|
],
|
|
85
97
|
},
|
|
98
|
+
|
|
99
|
+
// payments env's
|
|
100
|
+
payments: {
|
|
101
|
+
stripe: {
|
|
102
|
+
"# Stripe (dashboard.stripe.com → Developers → API keys)": [
|
|
103
|
+
"STRIPE_SECRET_KEY=",
|
|
104
|
+
"STRIPE_WEBHOOK_SECRET=",
|
|
105
|
+
"NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=",
|
|
106
|
+
],
|
|
107
|
+
"# Stripe Pricing (Create products in Stripe dashboard)": [
|
|
108
|
+
"NEXT_PUBLIC_STRIPE_PRICE_ID_BASIC=",
|
|
109
|
+
"NEXT_PUBLIC_STRIPE_PRICE_ID_PRO=",
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
dodopayments: {
|
|
114
|
+
"# Dodo Payments (app.dodopayments.com → Developers → API Keys)": [
|
|
115
|
+
"DODO_PAYMENTS_API_KEY=",
|
|
116
|
+
"DODO_PAYMENTS_WEBHOOK_KEY=",
|
|
117
|
+
],
|
|
118
|
+
"# Dodo Pricing (Create products in Dodo dashboard)": [
|
|
119
|
+
"NEXT_PUBLIC_DODO_PRICE_ID_BASIC=",
|
|
120
|
+
"NEXT_PUBLIC_DODO_PRICE_ID_PRO=",
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
|
|
86
125
|
},
|
|
87
126
|
};
|
|
88
127
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
128
|
+
/**
|
|
129
|
+
* RECURSIVE DIRECTORY MERGE
|
|
130
|
+
* * This function walks through the source directory and copies files to the destination.
|
|
131
|
+
* If a directory exists in both places, it dives deeper to merge contents rather
|
|
132
|
+
* than replacing the entire folder.
|
|
133
|
+
*/
|
|
93
134
|
async function copyDir(src, dest, skipNames = []) {
|
|
94
135
|
await fs.ensureDir(dest);
|
|
95
136
|
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
@@ -101,71 +142,68 @@ async function copyDir(src, dest, skipNames = []) {
|
|
|
101
142
|
const destPath = path.join(dest, entry.name);
|
|
102
143
|
|
|
103
144
|
if (entry.isDirectory()) {
|
|
104
|
-
//
|
|
105
|
-
|
|
145
|
+
// If it's a directory, recurse into it to ensure we don't
|
|
146
|
+
// overwrite existing folders in the target, but merge into them.
|
|
106
147
|
await copyDir(srcPath, destPath, skipNames);
|
|
107
148
|
} else {
|
|
108
|
-
//
|
|
149
|
+
// If it's a file, copy/overwrite it into the target.
|
|
109
150
|
await fs.copy(srcPath, destPath, { overwrite: true });
|
|
110
151
|
}
|
|
111
152
|
}
|
|
112
153
|
}
|
|
113
154
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
155
|
+
/**
|
|
156
|
+
* BLOCK INJECTION LOGIC
|
|
157
|
+
* * Improved to handle deep nesting. It takes everything inside the provider folder
|
|
158
|
+
* (except package.json and node_modules) and merges it into the project root.
|
|
159
|
+
*/
|
|
119
160
|
async function injectBlock(feature, provider, targetPath) {
|
|
120
161
|
const blockRoot = path.join(BLOCKS_DIR, feature, provider);
|
|
121
|
-
const blockSrcDir = path.join(blockRoot, "src");
|
|
122
162
|
|
|
123
163
|
if (!await fs.pathExists(blockRoot)) {
|
|
124
|
-
throw new Error(`Block
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// 1. Copy everything inside block/src/ to target/src/
|
|
128
|
-
if (await fs.pathExists(blockSrcDir)) {
|
|
129
|
-
// We skip node_modules and .next if they accidentally exist in the block
|
|
130
|
-
await copyDir(blockSrcDir, path.join(targetPath, "src"), ["node_modules", ".next"]);
|
|
164
|
+
throw new Error(`Block folder missing: ${blockRoot}`);
|
|
131
165
|
}
|
|
132
166
|
|
|
133
|
-
// 2. Handle non-src files (like public/ or drizzle/ if the block has them)
|
|
134
|
-
// Check if there are other folders in the block root that aren't 'src' or 'package.json'
|
|
135
167
|
const blockEntries = await fs.readdir(blockRoot, { withFileTypes: true });
|
|
168
|
+
|
|
136
169
|
for (const entry of blockEntries) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
170
|
+
// 1. Skip package.json (handled by mergePackageJson)
|
|
171
|
+
// 2. Skip node_modules or .next if they exist in the template
|
|
172
|
+
if (
|
|
173
|
+
entry.name === "package.json" ||
|
|
174
|
+
entry.name === "node_modules" ||
|
|
175
|
+
entry.name === ".next"
|
|
176
|
+
) continue;
|
|
177
|
+
|
|
178
|
+
const srcPath = path.join(blockRoot, entry.name);
|
|
179
|
+
const destPath = path.join(targetPath, entry.name);
|
|
180
|
+
|
|
181
|
+
if (entry.isDirectory()) {
|
|
182
|
+
// This will now correctly merge 'src', 'public', 'hooks', etc.
|
|
183
|
+
// because copyDir is recursive.
|
|
184
|
+
await copyDir(srcPath, destPath, ["node_modules", ".next"]);
|
|
185
|
+
} else {
|
|
186
|
+
await fs.copy(srcPath, destPath, { overwrite: true });
|
|
142
187
|
}
|
|
143
188
|
}
|
|
144
189
|
}
|
|
145
190
|
|
|
146
|
-
|
|
147
|
-
|
|
191
|
+
/**
|
|
192
|
+
* PACKAGE.JSON MERGER
|
|
193
|
+
* * Deep merges dependencies and devDependencies so the final project
|
|
194
|
+
* has all required libraries from every selected block.
|
|
195
|
+
*/
|
|
148
196
|
async function mergePackageJson(targetPath, feature, provider) {
|
|
149
197
|
const blockPkgPath = path.join(BLOCKS_DIR, feature, provider, "package.json");
|
|
150
198
|
const targetPkgPath = path.join(targetPath, "package.json");
|
|
151
199
|
|
|
152
|
-
if (!await fs.pathExists(blockPkgPath)) return;
|
|
153
|
-
if (!await fs.pathExists(targetPkgPath)) return;
|
|
200
|
+
if (!await fs.pathExists(blockPkgPath) || !await fs.pathExists(targetPkgPath)) return;
|
|
154
201
|
|
|
155
202
|
const targetPkg = await fs.readJson(targetPkgPath);
|
|
156
203
|
const blockPkg = await fs.readJson(blockPkgPath);
|
|
157
204
|
|
|
158
|
-
|
|
159
|
-
targetPkg.
|
|
160
|
-
...(targetPkg.dependencies ?? {}),
|
|
161
|
-
...(blockPkg.dependencies ?? {}),
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
// Merge devDependencies
|
|
165
|
-
targetPkg.devDependencies = {
|
|
166
|
-
...(targetPkg.devDependencies ?? {}),
|
|
167
|
-
...(blockPkg.devDependencies ?? {}),
|
|
168
|
-
};
|
|
205
|
+
targetPkg.dependencies = { ...(targetPkg.dependencies ?? {}), ...(blockPkg.dependencies ?? {}) };
|
|
206
|
+
targetPkg.devDependencies = { ...(targetPkg.devDependencies ?? {}), ...(blockPkg.devDependencies ?? {}) };
|
|
169
207
|
|
|
170
208
|
await fs.writeJson(targetPkgPath, targetPkg, { spaces: 2 });
|
|
171
209
|
}
|
|
@@ -322,7 +360,14 @@ async function main() {
|
|
|
322
360
|
});
|
|
323
361
|
if (p.isCancel(emailProvider)) { p.cancel("Cancelled."); process.exit(0); }
|
|
324
362
|
|
|
325
|
-
const
|
|
363
|
+
const paymentProvider = await p.select({
|
|
364
|
+
message: "Payment provider",
|
|
365
|
+
options: Object.entries(PAYMENT_PROVIDERS).map(([value, { label, hint }]) => ({
|
|
366
|
+
value, label, hint,
|
|
367
|
+
})),
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const choices = { auth: authProvider, email: emailProvider, payment: paymentProvider };
|
|
326
371
|
|
|
327
372
|
// 4. Git + install preferences
|
|
328
373
|
const initGit = await p.confirm({
|
|
@@ -342,24 +387,21 @@ async function main() {
|
|
|
342
387
|
|
|
343
388
|
// 5. Copy base template
|
|
344
389
|
spin.start("Copying base template...");
|
|
345
|
-
if (!await fs.pathExists(BASE_DIR)) {
|
|
346
|
-
spin.stop(chalk.red(`Base template not found: ${BASE_DIR}`));
|
|
347
|
-
process.exit(1);
|
|
348
|
-
}
|
|
349
390
|
await copyDir(BASE_DIR, targetPath, ["node_modules", ".next", ".turbo"]);
|
|
350
391
|
spin.stop("Base template copied.");
|
|
351
392
|
|
|
352
|
-
// 6. Inject
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
393
|
+
// 6. Inject Blocks
|
|
394
|
+
// The injectBlock function now handles the internal recursion correctly.
|
|
395
|
+
const features = ["auth", "email", "payments"];
|
|
396
|
+
for (const feature of features) {
|
|
397
|
+
const provider = choices[feature];
|
|
398
|
+
if (provider) {
|
|
399
|
+
spin.start(`Injecting ${feature}: ${provider}...`);
|
|
400
|
+
await injectBlock(feature, provider, targetPath);
|
|
401
|
+
await mergePackageJson(targetPath, feature, provider);
|
|
402
|
+
spin.stop(`${feature} injected ✓`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
363
405
|
|
|
364
406
|
// 8. Write .env.example
|
|
365
407
|
spin.start("Writing .env.example...");
|
package/package.json
CHANGED