@moneydevkit/create 0.3.2 → 0.4.0-beta.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 +33 -6
- package/dist/index.cjs +514 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +513 -26
- package/dist/index.js.map +1 -1
- package/dist/templates/nextjs/app/api/mdk/route.js +1 -0
- package/dist/templates/nextjs/app/api/mdk/route.ts +1 -0
- package/dist/templates/nextjs/app/checkout/[id]/page.js +8 -0
- package/dist/templates/nextjs/app/checkout/[id]/page.tsx +8 -0
- package/dist/templates/readme-sync.test.ts +56 -0
- package/package.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -28,8 +28,8 @@ var import_client = require("@orpc/client");
|
|
|
28
28
|
var import_fetch = require("@orpc/client/fetch");
|
|
29
29
|
var p = __toESM(require("@clack/prompts"), 1);
|
|
30
30
|
var import_minimist = __toESM(require("minimist"), 1);
|
|
31
|
-
var
|
|
32
|
-
var
|
|
31
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
32
|
+
var import_node_fs5 = __toESM(require("fs"), 1);
|
|
33
33
|
var import_node_os = __toESM(require("os"), 1);
|
|
34
34
|
var import_open = __toESM(require("open"), 1);
|
|
35
35
|
var import_clipboardy = __toESM(require("clipboardy"), 1);
|
|
@@ -72,6 +72,392 @@ function deriveProjectName(input, webhookUrl) {
|
|
|
72
72
|
return webhookUrl;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
// src/utils/nextjs-detector.ts
|
|
76
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
77
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
78
|
+
var import_semver = __toESM(require("semver"), 1);
|
|
79
|
+
var NEXT_CONFIG_BASENAMES = [
|
|
80
|
+
"next.config.js",
|
|
81
|
+
"next.config.cjs",
|
|
82
|
+
"next.config.mjs",
|
|
83
|
+
"next.config.ts",
|
|
84
|
+
"next.config.mts"
|
|
85
|
+
];
|
|
86
|
+
var APP_DIR_CANDIDATES = ["app", import_node_path2.default.join("src", "app")];
|
|
87
|
+
var PAGES_DIR_CANDIDATES = ["pages", import_node_path2.default.join("src", "pages")];
|
|
88
|
+
function fileExists(target) {
|
|
89
|
+
try {
|
|
90
|
+
return import_node_fs.default.existsSync(target);
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function readPackageJson(pkgPath) {
|
|
96
|
+
try {
|
|
97
|
+
const content = import_node_fs.default.readFileSync(pkgPath, "utf8");
|
|
98
|
+
return JSON.parse(content);
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function hasNextDependency(pkg) {
|
|
104
|
+
const deps = pkg.dependencies;
|
|
105
|
+
const devDeps = pkg.devDependencies;
|
|
106
|
+
return Boolean(deps?.next || devDeps?.next);
|
|
107
|
+
}
|
|
108
|
+
function extractNextVersion(pkg) {
|
|
109
|
+
const deps = pkg.dependencies;
|
|
110
|
+
const devDeps = pkg.devDependencies;
|
|
111
|
+
return deps?.next ?? devDeps?.next ?? void 0;
|
|
112
|
+
}
|
|
113
|
+
function findNearestPackageJson(startDir) {
|
|
114
|
+
let current = import_node_path2.default.resolve(startDir);
|
|
115
|
+
while (true) {
|
|
116
|
+
const candidate = import_node_path2.default.join(current, "package.json");
|
|
117
|
+
if (fileExists(candidate)) {
|
|
118
|
+
return candidate;
|
|
119
|
+
}
|
|
120
|
+
const parent = import_node_path2.default.dirname(current);
|
|
121
|
+
if (parent === current) {
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
current = parent;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function findNextConfig(rootDir) {
|
|
128
|
+
for (const basename of NEXT_CONFIG_BASENAMES) {
|
|
129
|
+
const candidate = import_node_path2.default.join(rootDir, basename);
|
|
130
|
+
if (fileExists(candidate)) {
|
|
131
|
+
return candidate;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
function detectNextJsProject(startDir) {
|
|
137
|
+
const pkgPath = findNearestPackageJson(startDir);
|
|
138
|
+
const rootDir = pkgPath ? import_node_path2.default.dirname(pkgPath) : import_node_path2.default.resolve(startDir);
|
|
139
|
+
const pkg = pkgPath ? readPackageJson(pkgPath) : null;
|
|
140
|
+
const hasNext = pkg ? hasNextDependency(pkg) : false;
|
|
141
|
+
const nextVersion = pkg ? extractNextVersion(pkg) : void 0;
|
|
142
|
+
let versionIsSupported = true;
|
|
143
|
+
if (nextVersion) {
|
|
144
|
+
const minVersion = import_semver.default.minVersion(nextVersion);
|
|
145
|
+
if (minVersion) {
|
|
146
|
+
versionIsSupported = import_semver.default.gte(minVersion, "15.0.0");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const nextConfigPath = findNextConfig(rootDir);
|
|
150
|
+
const appDir = APP_DIR_CANDIDATES.map((candidate) => import_node_path2.default.join(rootDir, candidate)).find(
|
|
151
|
+
(candidate) => fileExists(candidate)
|
|
152
|
+
);
|
|
153
|
+
const pagesDir = PAGES_DIR_CANDIDATES.map((candidate) => import_node_path2.default.join(rootDir, candidate)).find(
|
|
154
|
+
(candidate) => fileExists(candidate)
|
|
155
|
+
);
|
|
156
|
+
const usesTypeScript = fileExists(import_node_path2.default.join(rootDir, "tsconfig.json")) || fileExists(import_node_path2.default.join(rootDir, "next-env.d.ts"));
|
|
157
|
+
const found = Boolean(hasNext || nextConfigPath || appDir || pagesDir);
|
|
158
|
+
return {
|
|
159
|
+
found,
|
|
160
|
+
rootDir: found ? rootDir : void 0,
|
|
161
|
+
nextConfigPath,
|
|
162
|
+
appDir,
|
|
163
|
+
pagesDir,
|
|
164
|
+
usesTypeScript,
|
|
165
|
+
nextVersion,
|
|
166
|
+
versionIsSupported
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/scaffold/nextjs.ts
|
|
171
|
+
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
172
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
173
|
+
var import_node_child_process = require("child_process");
|
|
174
|
+
|
|
175
|
+
// src/utils/package-manager.ts
|
|
176
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
177
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
178
|
+
function detectPackageManager(rootDir) {
|
|
179
|
+
if (import_node_fs2.default.existsSync(import_node_path3.default.join(rootDir, "pnpm-lock.yaml"))) return "pnpm";
|
|
180
|
+
if (import_node_fs2.default.existsSync(import_node_path3.default.join(rootDir, "yarn.lock"))) return "yarn";
|
|
181
|
+
if (import_node_fs2.default.existsSync(import_node_path3.default.join(rootDir, "bun.lockb"))) return "bun";
|
|
182
|
+
if (import_node_fs2.default.existsSync(import_node_path3.default.join(rootDir, "package-lock.json"))) return "npm";
|
|
183
|
+
return "npm";
|
|
184
|
+
}
|
|
185
|
+
function hasDependency(rootDir, depName) {
|
|
186
|
+
try {
|
|
187
|
+
const pkg = JSON.parse(import_node_fs2.default.readFileSync(import_node_path3.default.join(rootDir, "package.json"), "utf8"));
|
|
188
|
+
return Boolean(pkg.dependencies?.[depName] || pkg.devDependencies?.[depName]);
|
|
189
|
+
} catch {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// src/utils/fs-utils.ts
|
|
195
|
+
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
196
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
197
|
+
function ensureDir(filePath) {
|
|
198
|
+
const dir = import_node_path4.default.dirname(filePath);
|
|
199
|
+
import_node_fs3.default.mkdirSync(dir, { recursive: true });
|
|
200
|
+
}
|
|
201
|
+
function readFileSafe(filePath) {
|
|
202
|
+
try {
|
|
203
|
+
return import_node_fs3.default.readFileSync(filePath, "utf8");
|
|
204
|
+
} catch {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function writeFileIfAbsent(filePath, content) {
|
|
209
|
+
if (import_node_fs3.default.existsSync(filePath)) {
|
|
210
|
+
const existing = readFileSafe(filePath);
|
|
211
|
+
if (existing?.trim() === content.trim()) {
|
|
212
|
+
return { status: "skipped-exists", path: filePath };
|
|
213
|
+
}
|
|
214
|
+
return { status: "skipped-different", path: filePath };
|
|
215
|
+
}
|
|
216
|
+
ensureDir(filePath);
|
|
217
|
+
import_node_fs3.default.writeFileSync(filePath, content, "utf8");
|
|
218
|
+
return { status: "created", path: filePath };
|
|
219
|
+
}
|
|
220
|
+
function writeFileWithBackup(filePath, content) {
|
|
221
|
+
if (!import_node_fs3.default.existsSync(filePath)) {
|
|
222
|
+
ensureDir(filePath);
|
|
223
|
+
import_node_fs3.default.writeFileSync(filePath, content, "utf8");
|
|
224
|
+
return { status: "created", path: filePath };
|
|
225
|
+
}
|
|
226
|
+
const existing = readFileSafe(filePath) ?? "";
|
|
227
|
+
if (existing.trim() === content.trim()) {
|
|
228
|
+
return { status: "skipped-exists", path: filePath };
|
|
229
|
+
}
|
|
230
|
+
const backupPath = `${filePath}.mdk-backup`;
|
|
231
|
+
import_node_fs3.default.writeFileSync(backupPath, existing, "utf8");
|
|
232
|
+
import_node_fs3.default.writeFileSync(filePath, content, "utf8");
|
|
233
|
+
return { status: "updated-with-backup", path: filePath, backupPath };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/scaffold/nextjs.ts
|
|
237
|
+
var import_meta = {};
|
|
238
|
+
var templateRoot = new URL("../templates/nextjs/", import_meta.url);
|
|
239
|
+
function readTemplate(relativePath) {
|
|
240
|
+
return import_node_fs4.default.readFileSync(new URL(relativePath, templateRoot), "utf8");
|
|
241
|
+
}
|
|
242
|
+
function findExistingConfig(rootDir, preferred) {
|
|
243
|
+
if (preferred && import_node_fs4.default.existsSync(preferred)) return preferred;
|
|
244
|
+
const candidates = [
|
|
245
|
+
"next.config.js",
|
|
246
|
+
"next.config.cjs",
|
|
247
|
+
"next.config.mjs",
|
|
248
|
+
"next.config.ts",
|
|
249
|
+
"next.config.mts"
|
|
250
|
+
];
|
|
251
|
+
for (const candidate of candidates) {
|
|
252
|
+
const fullPath = import_node_path5.default.join(rootDir, candidate);
|
|
253
|
+
if (import_node_fs4.default.existsSync(fullPath)) {
|
|
254
|
+
return fullPath;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return void 0;
|
|
258
|
+
}
|
|
259
|
+
async function installNextjsPackage(rootDir, packageManager) {
|
|
260
|
+
if (hasDependency(rootDir, "@moneydevkit/nextjs")) {
|
|
261
|
+
return { installed: false, skipped: true };
|
|
262
|
+
}
|
|
263
|
+
const commandForPm = {
|
|
264
|
+
pnpm: ["pnpm", ["add", "@moneydevkit/nextjs"]],
|
|
265
|
+
yarn: ["yarn", ["add", "@moneydevkit/nextjs"]],
|
|
266
|
+
npm: ["npm", ["install", "@moneydevkit/nextjs"]],
|
|
267
|
+
bun: ["bun", ["add", "@moneydevkit/nextjs"]]
|
|
268
|
+
};
|
|
269
|
+
const [cmd, args] = commandForPm[packageManager];
|
|
270
|
+
await new Promise((resolve, reject) => {
|
|
271
|
+
const child = (0, import_node_child_process.spawn)(cmd, args, { stdio: "inherit", cwd: rootDir });
|
|
272
|
+
child.on("exit", (code) => {
|
|
273
|
+
if (code === 0) {
|
|
274
|
+
resolve();
|
|
275
|
+
} else {
|
|
276
|
+
reject(new Error(`${cmd} ${args.join(" ")} failed with code ${code}`));
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
child.on("error", reject);
|
|
280
|
+
});
|
|
281
|
+
return { installed: true, skipped: false };
|
|
282
|
+
}
|
|
283
|
+
function createAppRouteContent(isTypeScript) {
|
|
284
|
+
return readTemplate(`app/api/mdk/route.${isTypeScript ? "ts" : "js"}`);
|
|
285
|
+
}
|
|
286
|
+
function createAppCheckoutPageContent(isTypeScript) {
|
|
287
|
+
return readTemplate(
|
|
288
|
+
`app/checkout/[id]/page.${isTypeScript ? "tsx" : "js"}`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
function isTypeScriptConfig(configPath) {
|
|
292
|
+
return configPath.endsWith(".ts") || configPath.endsWith(".mts");
|
|
293
|
+
}
|
|
294
|
+
function patchNextConfigTypes(source) {
|
|
295
|
+
let patched = source.replace(
|
|
296
|
+
/import\s+type\s+\{\s*NextConfig\s*\}\s+from\s+["']next["'];?\s*\n?/g,
|
|
297
|
+
""
|
|
298
|
+
);
|
|
299
|
+
patched = patched.replace(/:\s*NextConfig\b/g, ": NextConfigOverrides");
|
|
300
|
+
return patched;
|
|
301
|
+
}
|
|
302
|
+
function updateConfigFile(configPath) {
|
|
303
|
+
const isTs = isTypeScriptConfig(configPath);
|
|
304
|
+
const pluginImport = isTs ? 'import withMdkCheckout, { type NextConfigOverrides } from "@moneydevkit/nextjs/next-plugin";' : 'import withMdkCheckout from "@moneydevkit/nextjs/next-plugin";';
|
|
305
|
+
if (!import_node_fs4.default.existsSync(configPath)) {
|
|
306
|
+
const content = [
|
|
307
|
+
pluginImport,
|
|
308
|
+
"",
|
|
309
|
+
"// Wrap your existing Next.js config with withMdkCheckout to enable Money Dev Kit.",
|
|
310
|
+
"// Example: export default withMdkCheckout(yourConfig)",
|
|
311
|
+
isTs ? "const nextConfig: NextConfigOverrides = {};" : "const nextConfig = {};",
|
|
312
|
+
"",
|
|
313
|
+
"export default withMdkCheckout(nextConfig);",
|
|
314
|
+
""
|
|
315
|
+
].join("\n");
|
|
316
|
+
const writeResult = writeFileWithBackup(configPath, content);
|
|
317
|
+
return {
|
|
318
|
+
status: "created",
|
|
319
|
+
path: configPath,
|
|
320
|
+
backupPath: writeResult.status === "updated-with-backup" ? writeResult.backupPath : void 0
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
const original = readFileSafe(configPath) ?? "";
|
|
324
|
+
if (original.includes("@moneydevkit/nextjs/next-plugin") || original.includes("withMdkCheckout")) {
|
|
325
|
+
return { status: "skipped", path: configPath, reason: "already configured" };
|
|
326
|
+
}
|
|
327
|
+
if (original.includes("module.exports")) {
|
|
328
|
+
const re = /module\.exports\s*=\s*(\{[\s\S]*?\});?/;
|
|
329
|
+
const match = original.match(re);
|
|
330
|
+
if (match) {
|
|
331
|
+
const prefix = 'const withMdkCheckout = require("@moneydevkit/nextjs/next-plugin").default ?? require("@moneydevkit/nextjs/next-plugin");\n';
|
|
332
|
+
const replaced = original.replace(
|
|
333
|
+
re,
|
|
334
|
+
`module.exports = withMdkCheckout(${match[1]});`
|
|
335
|
+
);
|
|
336
|
+
const result = writeFileWithBackup(configPath, `${prefix}${replaced}`);
|
|
337
|
+
return {
|
|
338
|
+
status: "updated",
|
|
339
|
+
path: configPath,
|
|
340
|
+
backupPath: result.status === "updated-with-backup" ? result.backupPath : void 0
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (original.includes("export default")) {
|
|
345
|
+
const reDefaultObject = /export\s+default\s+(\{[\s\S]*?\});?/;
|
|
346
|
+
const objectMatch = original.match(reDefaultObject);
|
|
347
|
+
if (objectMatch) {
|
|
348
|
+
const content = [
|
|
349
|
+
pluginImport,
|
|
350
|
+
"",
|
|
351
|
+
isTs ? "const nextConfig: NextConfigOverrides = " + objectMatch[1] + ";" : "const nextConfig = " + objectMatch[1] + ";",
|
|
352
|
+
"",
|
|
353
|
+
"export default withMdkCheckout(nextConfig);",
|
|
354
|
+
""
|
|
355
|
+
].join("\n");
|
|
356
|
+
const writeResult = writeFileWithBackup(configPath, content);
|
|
357
|
+
return {
|
|
358
|
+
status: "updated",
|
|
359
|
+
path: configPath,
|
|
360
|
+
backupPath: writeResult.status === "updated-with-backup" ? writeResult.backupPath : void 0
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
const reNamed = /export\s+default\s+([a-zA-Z0-9_]+)\s*;?/;
|
|
364
|
+
const namedMatch = original.match(reNamed);
|
|
365
|
+
if (namedMatch) {
|
|
366
|
+
const name = namedMatch[1];
|
|
367
|
+
const patched = isTs && original.includes("NextConfig") ? patchNextConfigTypes(original) : original;
|
|
368
|
+
const lines = [
|
|
369
|
+
pluginImport,
|
|
370
|
+
patched.replace(reNamed, `export default withMdkCheckout(${name});`)
|
|
371
|
+
];
|
|
372
|
+
const writeResult = writeFileWithBackup(configPath, lines.join("\n"));
|
|
373
|
+
return {
|
|
374
|
+
status: "updated",
|
|
375
|
+
path: configPath,
|
|
376
|
+
backupPath: writeResult.status === "updated-with-backup" ? writeResult.backupPath : void 0
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
status: "skipped",
|
|
382
|
+
path: configPath,
|
|
383
|
+
reason: "unrecognized format; wrap your export with withMdkCheckout, e.g. export default withMdkCheckout(yourConfig)"
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function scaffoldAppRouter(appDir, isTypeScript) {
|
|
387
|
+
const added = [];
|
|
388
|
+
const skipped = [];
|
|
389
|
+
const routePath = import_node_path5.default.join(
|
|
390
|
+
appDir,
|
|
391
|
+
"api",
|
|
392
|
+
"mdk",
|
|
393
|
+
`route.${isTypeScript ? "ts" : "js"}`
|
|
394
|
+
);
|
|
395
|
+
const routeResult = writeFileIfAbsent(
|
|
396
|
+
routePath,
|
|
397
|
+
createAppRouteContent(isTypeScript)
|
|
398
|
+
);
|
|
399
|
+
if (routeResult.status === "created") {
|
|
400
|
+
added.push(routeResult.path);
|
|
401
|
+
} else {
|
|
402
|
+
skipped.push(routeResult.path);
|
|
403
|
+
}
|
|
404
|
+
const pagePath = import_node_path5.default.join(
|
|
405
|
+
appDir,
|
|
406
|
+
"checkout",
|
|
407
|
+
"[id]",
|
|
408
|
+
`page.${isTypeScript ? "tsx" : "js"}`
|
|
409
|
+
);
|
|
410
|
+
const pageResult = writeFileIfAbsent(
|
|
411
|
+
pagePath,
|
|
412
|
+
createAppCheckoutPageContent(isTypeScript)
|
|
413
|
+
);
|
|
414
|
+
if (pageResult.status === "created") {
|
|
415
|
+
added.push(pageResult.path);
|
|
416
|
+
} else {
|
|
417
|
+
skipped.push(pageResult.path);
|
|
418
|
+
}
|
|
419
|
+
return { added, skipped };
|
|
420
|
+
}
|
|
421
|
+
async function scaffoldNextJs(options) {
|
|
422
|
+
const { detection, jsonMode, skipInstall } = options;
|
|
423
|
+
if (!detection.rootDir) {
|
|
424
|
+
throw new Error("Next.js project root not found for scaffolding.");
|
|
425
|
+
}
|
|
426
|
+
const warnings = [];
|
|
427
|
+
const rootDir = detection.rootDir;
|
|
428
|
+
const packageManager = detectPackageManager(rootDir);
|
|
429
|
+
const installResult = skipInstall ? { installed: false, skipped: true } : await installNextjsPackage(rootDir, packageManager);
|
|
430
|
+
const configPath = findExistingConfig(rootDir, detection.nextConfigPath) ?? import_node_path5.default.join(rootDir, "next.config.js");
|
|
431
|
+
const configResult = updateConfigFile(configPath);
|
|
432
|
+
if (configResult.status === "skipped") {
|
|
433
|
+
warnings.push(
|
|
434
|
+
`Could not automatically update ${import_node_path5.default.basename(configPath)} (${configResult.reason}). Please wrap your Next.js config with withMdkCheckout manually.`
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
const appDir = detection.appDir ?? import_node_path5.default.join(rootDir, "app");
|
|
438
|
+
const fileResults = scaffoldAppRouter(appDir, detection.usesTypeScript);
|
|
439
|
+
if (!detection.appDir) {
|
|
440
|
+
warnings.push(
|
|
441
|
+
"No app/ directory detected; created App Router scaffolding in app/."
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
if (!jsonMode) {
|
|
445
|
+
if (!installResult.installed && installResult.skipped) {
|
|
446
|
+
console.log("@moneydevkit/nextjs already present; skipping install.");
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
rootDir,
|
|
451
|
+
packageManager,
|
|
452
|
+
installedPackage: installResult.installed,
|
|
453
|
+
installSkipped: installResult.skipped,
|
|
454
|
+
addedFiles: fileResults.added,
|
|
455
|
+
skippedFiles: fileResults.skipped,
|
|
456
|
+
config: configResult,
|
|
457
|
+
warnings
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
75
461
|
// src/index.ts
|
|
76
462
|
var DEFAULT_BASE_URL = "https://moneydevkit.com";
|
|
77
463
|
var DEFAULT_ENV_FILE = ".env.local";
|
|
@@ -109,7 +495,7 @@ var CookieJar = class {
|
|
|
109
495
|
};
|
|
110
496
|
function parseFlags(argv) {
|
|
111
497
|
const result = (0, import_minimist.default)(argv, {
|
|
112
|
-
boolean: ["json", "no-clipboard", "no-open", "force-new-webhook", "
|
|
498
|
+
boolean: ["json", "no-clipboard", "no-open", "force-new-webhook", "scaffold-nextjs"],
|
|
113
499
|
string: [
|
|
114
500
|
"base-url",
|
|
115
501
|
"env-target",
|
|
@@ -125,14 +511,14 @@ function parseFlags(argv) {
|
|
|
125
511
|
"no-open": false,
|
|
126
512
|
json: false,
|
|
127
513
|
"force-new-webhook": false,
|
|
128
|
-
|
|
514
|
+
"scaffold-nextjs": false
|
|
129
515
|
}
|
|
130
516
|
});
|
|
131
517
|
return {
|
|
132
518
|
json: Boolean(result.json),
|
|
133
519
|
noClipboard: Boolean(result["no-clipboard"]),
|
|
134
520
|
noOpen: Boolean(result["no-open"]),
|
|
135
|
-
|
|
521
|
+
scaffoldNextjs: Boolean(result["scaffold-nextjs"]),
|
|
136
522
|
baseUrl: result["base-url"],
|
|
137
523
|
envFile: result["env-target"],
|
|
138
524
|
projectName: typeof result["project-name"] === "string" ? result["project-name"] : void 0,
|
|
@@ -142,20 +528,20 @@ function parseFlags(argv) {
|
|
|
142
528
|
};
|
|
143
529
|
}
|
|
144
530
|
function normalizeDirectory(dir) {
|
|
145
|
-
if (
|
|
146
|
-
return
|
|
531
|
+
if (import_node_path6.default.isAbsolute(dir)) return dir;
|
|
532
|
+
return import_node_path6.default.resolve(process.cwd(), dir);
|
|
147
533
|
}
|
|
148
534
|
function ensureDirectoryExists(dir) {
|
|
149
|
-
if (!
|
|
150
|
-
|
|
535
|
+
if (!import_node_fs5.default.existsSync(dir)) {
|
|
536
|
+
import_node_fs5.default.mkdirSync(dir, { recursive: true });
|
|
151
537
|
}
|
|
152
538
|
}
|
|
153
539
|
function readEnvFile(filePath) {
|
|
154
540
|
const env = /* @__PURE__ */ new Map();
|
|
155
|
-
if (!
|
|
541
|
+
if (!import_node_fs5.default.existsSync(filePath)) {
|
|
156
542
|
return env;
|
|
157
543
|
}
|
|
158
|
-
const contents =
|
|
544
|
+
const contents = import_node_fs5.default.readFileSync(filePath, "utf8");
|
|
159
545
|
for (const line of contents.split(/\r?\n/)) {
|
|
160
546
|
if (!line || line.startsWith("#")) continue;
|
|
161
547
|
const [key, ...rest] = line.split("=");
|
|
@@ -176,22 +562,23 @@ function writeEnvFile(filePath, existing, updates) {
|
|
|
176
562
|
existing.set(key, value);
|
|
177
563
|
}
|
|
178
564
|
const content = Array.from(existing.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${key}=${value}`).join(import_node_os.default.EOL) + import_node_os.default.EOL;
|
|
179
|
-
|
|
565
|
+
import_node_fs5.default.writeFileSync(filePath, content, "utf8");
|
|
180
566
|
}
|
|
181
567
|
function ensureEnvFileExists(filePath) {
|
|
182
|
-
const dir =
|
|
568
|
+
const dir = import_node_path6.default.dirname(filePath);
|
|
183
569
|
ensureDirectoryExists(dir);
|
|
184
|
-
if (!
|
|
185
|
-
|
|
570
|
+
if (!import_node_fs5.default.existsSync(filePath)) {
|
|
571
|
+
import_node_fs5.default.writeFileSync(filePath, "", "utf8");
|
|
186
572
|
}
|
|
187
573
|
}
|
|
188
574
|
function resolveLocalEnvPath(options) {
|
|
189
|
-
const
|
|
190
|
-
|
|
575
|
+
const organization = options.organizationId?.trim();
|
|
576
|
+
const apiKey = options.apiKeyId?.trim();
|
|
577
|
+
if (!organization || !apiKey) {
|
|
191
578
|
return void 0;
|
|
192
579
|
}
|
|
193
580
|
const homeDir = import_node_os.default.homedir();
|
|
194
|
-
return
|
|
581
|
+
return import_node_path6.default.join(homeDir, ".mdk", organization, apiKey, ".env");
|
|
195
582
|
}
|
|
196
583
|
function isValidHttpUrl(value) {
|
|
197
584
|
if (!value) return false;
|
|
@@ -255,7 +642,7 @@ async function runDeviceFlow(options) {
|
|
|
255
642
|
p.note(
|
|
256
643
|
[
|
|
257
644
|
`Device code: ${device.userCode}`,
|
|
258
|
-
`
|
|
645
|
+
`Domain: ${webhookUrl}`,
|
|
259
646
|
"Open the authorization page, click Authorize, then return to this terminal."
|
|
260
647
|
].join("\n"),
|
|
261
648
|
"Authorize this device"
|
|
@@ -384,7 +771,7 @@ async function main() {
|
|
|
384
771
|
}
|
|
385
772
|
envFile = envPrompt.trim() || DEFAULT_ENV_FILE;
|
|
386
773
|
}
|
|
387
|
-
const envPath =
|
|
774
|
+
const envPath = import_node_path6.default.join(projectDir, envFile);
|
|
388
775
|
const existingEnvValues = readEnvFile(envPath);
|
|
389
776
|
const mnemonicAlreadySet = existingEnvValues.get("MDK_MNEMONIC")?.trim();
|
|
390
777
|
if (mnemonicAlreadySet) {
|
|
@@ -411,7 +798,7 @@ async function main() {
|
|
|
411
798
|
}
|
|
412
799
|
while (!webhookUrl) {
|
|
413
800
|
const webhookInput = await p.text({
|
|
414
|
-
message: "
|
|
801
|
+
message: "Domain for your application",
|
|
415
802
|
initialValue: "https://",
|
|
416
803
|
placeholder: "https://yourapp.com",
|
|
417
804
|
validate: (value) => isValidHttpUrl(value?.trim()) ? void 0 : "Enter a valid http(s) URL (e.g. https://yourapp.com)"
|
|
@@ -435,6 +822,39 @@ async function main() {
|
|
|
435
822
|
projectName = namePrompt.trim() || void 0;
|
|
436
823
|
}
|
|
437
824
|
projectName = deriveProjectName(projectName, webhookUrl);
|
|
825
|
+
const nextJsDetection = detectNextJsProject(projectDir);
|
|
826
|
+
let shouldScaffoldNextJs = false;
|
|
827
|
+
if (flags.scaffoldNextjs) {
|
|
828
|
+
if (nextJsDetection.found) {
|
|
829
|
+
if (nextJsDetection.versionIsSupported) {
|
|
830
|
+
shouldScaffoldNextJs = true;
|
|
831
|
+
} else {
|
|
832
|
+
console.warn(
|
|
833
|
+
`Next.js version ${nextJsDetection.nextVersion ?? "unknown"} detected, but @moneydevkit/nextjs requires Next.js 15+. Skipping installation and scaffolding.`
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
} else {
|
|
837
|
+
console.warn(
|
|
838
|
+
"No NextJS app found, skipping @moneydevkit/nextjs installation. Please install manually."
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
} else if (!jsonMode && nextJsDetection.found) {
|
|
842
|
+
if (!nextJsDetection.versionIsSupported) {
|
|
843
|
+
console.warn(
|
|
844
|
+
`Next.js version ${nextJsDetection.nextVersion ?? "unknown"} detected, but @moneydevkit/nextjs requires Next.js 15+. Skipping scaffolding prompt.`
|
|
845
|
+
);
|
|
846
|
+
} else {
|
|
847
|
+
const scaffoldPrompt = await p.confirm({
|
|
848
|
+
message: `Next.js application detected at ${nextJsDetection.rootDir ?? projectDir} (version ${nextJsDetection.nextVersion ?? "unknown"}). Install and scaffold @moneydevkit/nextjs?`,
|
|
849
|
+
initialValue: true
|
|
850
|
+
});
|
|
851
|
+
if (p.isCancel(scaffoldPrompt)) {
|
|
852
|
+
p.cancel("Aborted.");
|
|
853
|
+
process.exit(1);
|
|
854
|
+
}
|
|
855
|
+
shouldScaffoldNextJs = Boolean(scaffoldPrompt);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
438
858
|
try {
|
|
439
859
|
const result = await runDeviceFlow({
|
|
440
860
|
flags,
|
|
@@ -445,7 +865,6 @@ async function main() {
|
|
|
445
865
|
});
|
|
446
866
|
const updates = {
|
|
447
867
|
MDK_ACCESS_TOKEN: result.credentials.apiKey,
|
|
448
|
-
MDK_WEBHOOK_SECRET: result.credentials.webhookSecret,
|
|
449
868
|
MDK_MNEMONIC: result.mnemonic
|
|
450
869
|
};
|
|
451
870
|
ensureEnvFileExists(envPath);
|
|
@@ -453,7 +872,8 @@ async function main() {
|
|
|
453
872
|
const preview = renderEnvPreview(existingEnv, updates);
|
|
454
873
|
writeEnvFile(envPath, existingEnv, updates);
|
|
455
874
|
const localEnvPath = resolveLocalEnvPath({
|
|
456
|
-
organizationId: result.credentials.organizationId
|
|
875
|
+
organizationId: result.credentials.organizationId,
|
|
876
|
+
apiKeyId: result.credentials.apiKeyId
|
|
457
877
|
});
|
|
458
878
|
if (localEnvPath) {
|
|
459
879
|
ensureEnvFileExists(localEnvPath);
|
|
@@ -465,11 +885,77 @@ async function main() {
|
|
|
465
885
|
}
|
|
466
886
|
if (!flags.noClipboard) {
|
|
467
887
|
await import_clipboardy.default.write(
|
|
468
|
-
[`MDK_ACCESS_TOKEN=${updates.MDK_ACCESS_TOKEN}`, `
|
|
888
|
+
[`MDK_ACCESS_TOKEN=${updates.MDK_ACCESS_TOKEN}`, `MDK_MNEMONIC=${updates.MDK_MNEMONIC}`].join(
|
|
469
889
|
"\n"
|
|
470
890
|
)
|
|
471
891
|
);
|
|
472
892
|
}
|
|
893
|
+
let scaffoldSummary = null;
|
|
894
|
+
if (shouldScaffoldNextJs && nextJsDetection.found) {
|
|
895
|
+
try {
|
|
896
|
+
scaffoldSummary = await scaffoldNextJs({
|
|
897
|
+
detection: nextJsDetection,
|
|
898
|
+
jsonMode
|
|
899
|
+
});
|
|
900
|
+
if (!jsonMode && scaffoldSummary) {
|
|
901
|
+
const lines = [
|
|
902
|
+
scaffoldSummary.installedPackage ? `Installed @moneydevkit/nextjs with ${scaffoldSummary.packageManager}.` : scaffoldSummary.installSkipped ? "@moneydevkit/nextjs already installed; skipped package install." : "Skipped @moneydevkit/nextjs installation."
|
|
903
|
+
];
|
|
904
|
+
if (scaffoldSummary.config) {
|
|
905
|
+
const cfg = scaffoldSummary.config;
|
|
906
|
+
if (cfg.status === "created") {
|
|
907
|
+
lines.push(`Created ${cfg.path} with withMdkCheckout().`);
|
|
908
|
+
if (cfg.backupPath) {
|
|
909
|
+
lines.push(`Created backup at ${cfg.backupPath}.`);
|
|
910
|
+
}
|
|
911
|
+
} else if (cfg.status === "updated") {
|
|
912
|
+
lines.push(`Updated ${cfg.path} with withMdkCheckout().`);
|
|
913
|
+
if (cfg.backupPath) {
|
|
914
|
+
lines.push(`Created backup at ${cfg.backupPath}.`);
|
|
915
|
+
}
|
|
916
|
+
} else {
|
|
917
|
+
lines.push(
|
|
918
|
+
`Could not update ${cfg.path} automatically (${cfg.reason ?? "unknown reason"}).`
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
if (scaffoldSummary.addedFiles.length > 0) {
|
|
923
|
+
lines.push(
|
|
924
|
+
`Added: ${scaffoldSummary.addedFiles.map((p2) => import_node_path6.default.relative(projectDir, p2)).join(", ")}`
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
if (scaffoldSummary.skippedFiles.length > 0) {
|
|
928
|
+
lines.push(
|
|
929
|
+
`Skipped existing files: ${scaffoldSummary.skippedFiles.map((p2) => import_node_path6.default.relative(projectDir, p2)).join(", ")}`
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
p.note(lines.join("\n"), "Next.js scaffolding");
|
|
933
|
+
}
|
|
934
|
+
if (scaffoldSummary?.warnings.length) {
|
|
935
|
+
for (const warning of scaffoldSummary.warnings) {
|
|
936
|
+
console.warn(warning);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
} catch (error) {
|
|
940
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
941
|
+
if (jsonMode) {
|
|
942
|
+
console.error(
|
|
943
|
+
JSON.stringify(
|
|
944
|
+
{
|
|
945
|
+
status: "error",
|
|
946
|
+
error: {
|
|
947
|
+
message: `Next.js scaffolding failed: ${message}`
|
|
948
|
+
}
|
|
949
|
+
},
|
|
950
|
+
null,
|
|
951
|
+
2
|
|
952
|
+
)
|
|
953
|
+
);
|
|
954
|
+
} else {
|
|
955
|
+
console.warn(`Next.js scaffolding failed: ${message}`);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
473
959
|
const summary = {
|
|
474
960
|
projectDir,
|
|
475
961
|
envFile: envPath,
|
|
@@ -477,7 +963,8 @@ async function main() {
|
|
|
477
963
|
webhookId: result.credentials.webhookId,
|
|
478
964
|
organizationId: result.credentials.organizationId,
|
|
479
965
|
webhookUrl: result.credentials.webhookUrl,
|
|
480
|
-
mnemonic: updates.MDK_MNEMONIC
|
|
966
|
+
mnemonic: updates.MDK_MNEMONIC,
|
|
967
|
+
scaffoldedNextjs: Boolean(scaffoldSummary)
|
|
481
968
|
};
|
|
482
969
|
if (jsonMode) {
|
|
483
970
|
console.log(
|
|
@@ -492,7 +979,8 @@ async function main() {
|
|
|
492
979
|
webhookSecret: result.credentials.webhookSecret,
|
|
493
980
|
webhookUrl: result.credentials.webhookUrl,
|
|
494
981
|
organizationId: result.credentials.organizationId,
|
|
495
|
-
mnemonic: updates.MDK_MNEMONIC
|
|
982
|
+
mnemonic: updates.MDK_MNEMONIC,
|
|
983
|
+
scaffoldedNextjs: Boolean(scaffoldSummary)
|
|
496
984
|
}
|
|
497
985
|
},
|
|
498
986
|
null,
|