chargebee-init 1.0.0-beta.1
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/bin/chargebee-init +12 -0
- package/dist/checks.d.ts +17 -0
- package/dist/checks.js +46 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +44 -0
- package/dist/frameworks.d.ts +17 -0
- package/dist/frameworks.js +42 -0
- package/dist/git.d.ts +1 -0
- package/dist/git.js +10 -0
- package/dist/help.d.ts +11 -0
- package/dist/help.js +58 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +66 -0
- package/dist/package.d.ts +5 -0
- package/dist/package.js +31 -0
- package/dist/prompts.d.ts +10 -0
- package/dist/prompts.js +58 -0
- package/dist/templates/express/controllers.ts +184 -0
- package/dist/templates/nextjs/checkout/callback/route.ts +15 -0
- package/dist/templates/nextjs/checkout/manage-payment-sources/route.ts +20 -0
- package/dist/templates/nextjs/checkout/one-time-charge/route.ts +33 -0
- package/dist/templates/nextjs/checkout/subscription/route.ts +32 -0
- package/dist/templates/nextjs/portal/route.ts +19 -0
- package/dist/templates/nextjs/webhook/route.ts +25 -0
- package/dist/templates.d.ts +7 -0
- package/dist/templates.js +52 -0
- package/package.json +38 -0
package/dist/checks.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PackageManifest } from "@pnpm/types";
|
|
2
|
+
import { type DetectedFramework } from "./frameworks.js";
|
|
3
|
+
export type Checks = "git" | "package.json" | "framework";
|
|
4
|
+
export type CheckError = {
|
|
5
|
+
check: Checks;
|
|
6
|
+
msg: string;
|
|
7
|
+
bail?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type CheckResponse<P extends string, T> = {
|
|
10
|
+
[k in P]?: T;
|
|
11
|
+
} & {
|
|
12
|
+
errors: CheckError[];
|
|
13
|
+
};
|
|
14
|
+
export type PreflightResponse = CheckResponse<"pkg", PackageManifest>;
|
|
15
|
+
export type FrameworkResponse = CheckResponse<"framework", DetectedFramework>;
|
|
16
|
+
export declare const preflightChecks: (path: string) => Promise<PreflightResponse>;
|
|
17
|
+
export declare const frameworkChecks: (pkg: PackageManifest) => Promise<FrameworkResponse>;
|
package/dist/checks.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import colors from "ansi-colors";
|
|
2
|
+
import { detectFramework, satisfiesMinVersion, supportedFrameworks, } from "./frameworks.js";
|
|
3
|
+
import { isCleanTree } from "./git.js";
|
|
4
|
+
import { getPackageJson } from "./package.js";
|
|
5
|
+
export const preflightChecks = async (path) => {
|
|
6
|
+
const errors = [];
|
|
7
|
+
// We raise a warning if we can't validate that the target directory isn't
|
|
8
|
+
// a git tree, but give them the option to continue
|
|
9
|
+
const cleanTree = await isCleanTree(path);
|
|
10
|
+
if (!cleanTree) {
|
|
11
|
+
errors.push({
|
|
12
|
+
check: "git",
|
|
13
|
+
msg: `Could not validate if the target directory is a clean git tree`,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
// This is a hard stop as we can't continue without a valid package.json
|
|
17
|
+
const pkg = getPackageJson(path);
|
|
18
|
+
if (pkg) {
|
|
19
|
+
return { pkg, errors };
|
|
20
|
+
}
|
|
21
|
+
errors.push({
|
|
22
|
+
check: "package.json",
|
|
23
|
+
msg: `Could not read the contents of ${path}/package.json to perform the required checks. Please ensure your app has a valid package.json.`,
|
|
24
|
+
bail: true,
|
|
25
|
+
});
|
|
26
|
+
return { errors };
|
|
27
|
+
};
|
|
28
|
+
export const frameworkChecks = async (pkg) => {
|
|
29
|
+
const errors = [];
|
|
30
|
+
const framework = detectFramework(pkg);
|
|
31
|
+
if (!framework) {
|
|
32
|
+
errors.push({
|
|
33
|
+
check: "framework",
|
|
34
|
+
msg: `No supported framework detected in package.json. We currently support: ${colors.red(Object.keys(supportedFrameworks).join(", "))}`,
|
|
35
|
+
bail: true,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else if (!satisfiesMinVersion(framework)) {
|
|
39
|
+
errors.push({
|
|
40
|
+
check: "framework",
|
|
41
|
+
msg: `${framework.name}@${framework.version} does not satisfy the minimum version we support (${framework.info.minVersion}). Can't proceed!`,
|
|
42
|
+
bail: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return { framework, errors };
|
|
46
|
+
};
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function run(): Promise<void>;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import colors from "ansi-colors";
|
|
2
|
+
import meow from "meow";
|
|
3
|
+
import * as help from "./help.js";
|
|
4
|
+
import { init } from "./init.js";
|
|
5
|
+
const cli = meow(`
|
|
6
|
+
Usage
|
|
7
|
+
$ chargebee-init <command> [subcommand]
|
|
8
|
+
|
|
9
|
+
Options
|
|
10
|
+
--help display this help text
|
|
11
|
+
|
|
12
|
+
Examples
|
|
13
|
+
$ chargebee-init
|
|
14
|
+
$ chargebee-init help
|
|
15
|
+
$ chargebee-init help nextjs|express
|
|
16
|
+
`, {
|
|
17
|
+
importMeta: import.meta,
|
|
18
|
+
autoHelp: true,
|
|
19
|
+
autoVersion: true,
|
|
20
|
+
});
|
|
21
|
+
export async function run() {
|
|
22
|
+
const [command = "", subcommand = ""] = cli.input;
|
|
23
|
+
switch (command) {
|
|
24
|
+
case "":
|
|
25
|
+
case "init":
|
|
26
|
+
await init();
|
|
27
|
+
break;
|
|
28
|
+
case "help":
|
|
29
|
+
{
|
|
30
|
+
if (!subcommand || !(subcommand in help.messages)) {
|
|
31
|
+
console.log(colors.cyanBright(help.cliHelpMsg(cli.pkg.version, cli.pkg.description)));
|
|
32
|
+
console.log(cli.help.replace(cli.pkg.description, "").trim());
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const framework = subcommand;
|
|
36
|
+
console.log(help.messages[framework].preinit);
|
|
37
|
+
console.log(colors.yellowBright(help.messages[framework].postinit));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
default:
|
|
42
|
+
cli.showHelp();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PackageManifest } from "@pnpm/types";
|
|
2
|
+
export type Framework = "nextjs" | "express";
|
|
3
|
+
export type Features = "checkout" | "webhook";
|
|
4
|
+
export interface FrameworkInfo {
|
|
5
|
+
packageName: string;
|
|
6
|
+
minVersion: string;
|
|
7
|
+
dependencies: string[];
|
|
8
|
+
appDirectories: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare const supportedFrameworks: Record<Framework, FrameworkInfo>;
|
|
11
|
+
export type DetectedFramework = {
|
|
12
|
+
name: Framework;
|
|
13
|
+
version: string;
|
|
14
|
+
info: FrameworkInfo;
|
|
15
|
+
};
|
|
16
|
+
export declare function detectFramework(pkgJson: PackageManifest): DetectedFramework | undefined;
|
|
17
|
+
export declare function satisfiesMinVersion({ version, info, }: DetectedFramework): boolean;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import semver from "semver";
|
|
3
|
+
export const supportedFrameworks = {
|
|
4
|
+
nextjs: {
|
|
5
|
+
packageName: "next",
|
|
6
|
+
minVersion: "15",
|
|
7
|
+
dependencies: ["@chargebee/nextjs:~0.1.0"],
|
|
8
|
+
appDirectories: ["app", path.join("src", "app")],
|
|
9
|
+
},
|
|
10
|
+
express: {
|
|
11
|
+
packageName: "express",
|
|
12
|
+
minVersion: ">=5",
|
|
13
|
+
dependencies: ["@chargebee/express:~0.1.0"],
|
|
14
|
+
appDirectories: ["app", "src"],
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
// We read the version from package.json, which will not
|
|
18
|
+
// give us the exact installed version thanks to semver. But as
|
|
19
|
+
// long as it satisfies our minVersion, that should be ok.
|
|
20
|
+
//
|
|
21
|
+
// TODO: What if there are multiple detected frameworks?
|
|
22
|
+
export function detectFramework(pkgJson) {
|
|
23
|
+
if (!pkgJson.dependencies) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
for (const [framework, info] of Object.entries(supportedFrameworks)) {
|
|
27
|
+
const version = pkgJson.dependencies[info.packageName];
|
|
28
|
+
if (version) {
|
|
29
|
+
return {
|
|
30
|
+
name: framework,
|
|
31
|
+
version,
|
|
32
|
+
info,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
export function satisfiesMinVersion({ version, info, }) {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
const ver = (_b = (_a = semver.coerce(version)) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : "";
|
|
41
|
+
return semver.satisfies(ver, info.minVersion);
|
|
42
|
+
}
|
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isCleanTree(dir: string): Promise<boolean>;
|
package/dist/git.js
ADDED
package/dist/help.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const cliHelpMsg: (version: string, description: string) => string;
|
|
2
|
+
export declare const messages: {
|
|
3
|
+
readonly nextjs: {
|
|
4
|
+
readonly preinit: "\n* --------------------------\n* Chargebee Next.js Adapter\n* --------------------------\n* Integrates with Next.js version 15 \n* Only App Router is supported at the moment\n* Routes will be created under the chargebee directory by default\n";
|
|
5
|
+
readonly postinit: "\nPlease complete the following steps before you test out the Chargebee integration:\n\n* Define the required process.env.* variables either by adding them to your .env file or replacing them at build time:\n\n\tCHARGEBEE_SITE=\"site-name\"\n\tCHARGEBEE_API_KEY=\"\"\n\tCHARGEBEE_WEBHOOK_AUTH=\"username:password\"\n\n* Run npm|pnpm|bun install to grab the required packages\n\n* Configure the webhook URL in the Chargebee dashboard with basic auth set to the username and password defined in CHARGEBEE_WEBHOOK_AUTH\n\n* Review the routes created under the chargebee/ directory and make necessary changes\n";
|
|
6
|
+
};
|
|
7
|
+
readonly express: {
|
|
8
|
+
readonly preinit: "\n* --------------------------\n* Chargebee Express Adapter\n* --------------------------\n* Integrates with Express version 5\n";
|
|
9
|
+
readonly postinit: "\nPlease complete the following steps before you test out the Chargebee integration:\n\n* Define the required process.env.* variables either by adding them to your .env file or replacing them at build time:\n\n\tCHARGEBEE_SITE=\"site-name\"\n\tCHARGEBEE_API_KEY=\"\"\n\tCHARGEBEE_WEBHOOK_AUTH=\"username:password\"\n\n* Run npm|pnpm|bun install to grab the required packages\n\n* Configure the webhook URL in the Chargebee dashboard with basic auth set to the username and password defined in CHARGEBEE_WEBHOOK_AUTH\n\n* Review chargebee/controllers.ts and make necessary changes\n\n* Wire up the routes in your express app:\n\n\timport chargebeeInit from \"./chargebee/controllers.ts\"\n\tchargebeeInit(app);\n";
|
|
10
|
+
};
|
|
11
|
+
};
|
package/dist/help.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const cliHelpMsg = (version, description) => `
|
|
2
|
+
chargebee-init v${version}
|
|
3
|
+
|
|
4
|
+
${description}. Supports these popular frameworks:
|
|
5
|
+
|
|
6
|
+
Next.js v15
|
|
7
|
+
Express v5
|
|
8
|
+
|
|
9
|
+
And these features using the Chargebee Node SDK:
|
|
10
|
+
|
|
11
|
+
Checkout (Hosted Pages): One time, subscription and manage payment method
|
|
12
|
+
Webhook: Incoming webhooks
|
|
13
|
+
`;
|
|
14
|
+
const commonPostInit = `Please complete the following steps before you test out the Chargebee integration:
|
|
15
|
+
|
|
16
|
+
* Define the required process.env.* variables either by adding them to your .env file or replacing them at build time:
|
|
17
|
+
|
|
18
|
+
CHARGEBEE_SITE="site-name"
|
|
19
|
+
CHARGEBEE_API_KEY=""
|
|
20
|
+
CHARGEBEE_WEBHOOK_AUTH="username:password"
|
|
21
|
+
|
|
22
|
+
* Run npm|pnpm|bun install to grab the required packages
|
|
23
|
+
|
|
24
|
+
* Configure the webhook URL in the Chargebee dashboard with basic auth set to the username and password defined in CHARGEBEE_WEBHOOK_AUTH
|
|
25
|
+
`;
|
|
26
|
+
export const messages = {
|
|
27
|
+
nextjs: {
|
|
28
|
+
preinit: `
|
|
29
|
+
* --------------------------
|
|
30
|
+
* Chargebee Next.js Adapter
|
|
31
|
+
* --------------------------
|
|
32
|
+
* Integrates with Next.js version 15
|
|
33
|
+
* Only App Router is supported at the moment
|
|
34
|
+
* Routes will be created under the chargebee directory by default
|
|
35
|
+
`,
|
|
36
|
+
postinit: `
|
|
37
|
+
${commonPostInit}
|
|
38
|
+
* Review the routes created under the chargebee/ directory and make necessary changes
|
|
39
|
+
`,
|
|
40
|
+
},
|
|
41
|
+
express: {
|
|
42
|
+
preinit: `
|
|
43
|
+
* --------------------------
|
|
44
|
+
* Chargebee Express Adapter
|
|
45
|
+
* --------------------------
|
|
46
|
+
* Integrates with Express version 5
|
|
47
|
+
`,
|
|
48
|
+
postinit: `
|
|
49
|
+
${commonPostInit}
|
|
50
|
+
* Review chargebee/controllers.ts and make necessary changes
|
|
51
|
+
|
|
52
|
+
* Wire up the routes in your express app:
|
|
53
|
+
|
|
54
|
+
import chargebeeInit from "./chargebee/controllers.ts"
|
|
55
|
+
chargebeeInit(app);
|
|
56
|
+
`,
|
|
57
|
+
},
|
|
58
|
+
};
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const init: () => Promise<void>;
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import colors from "ansi-colors";
|
|
2
|
+
import Enquirer from "enquirer";
|
|
3
|
+
import { frameworkChecks, preflightChecks } from "./checks.js";
|
|
4
|
+
import { supportedFrameworks } from "./frameworks.js";
|
|
5
|
+
import * as help from "./help.js";
|
|
6
|
+
import { updateDependencies, writePackageJson } from "./package.js";
|
|
7
|
+
import { confirmWritePrompt, gitPrompt, pathPrefixPrompt, targetDirPrompt, } from "./prompts.js";
|
|
8
|
+
import { copyTemplates } from "./templates.js";
|
|
9
|
+
const error = (...lines) => {
|
|
10
|
+
console.error(`\n${lines.map((line) => colors.red(`✖ ${line}`)).join("\n")}\n`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
};
|
|
13
|
+
const checkErrors = ({ errors }) => {
|
|
14
|
+
const bailErrors = errors.filter((err) => err.bail);
|
|
15
|
+
if (bailErrors.length > 0) {
|
|
16
|
+
error(...bailErrors.map((err) => err.msg));
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export const init = async () => {
|
|
20
|
+
const cwd = process.cwd();
|
|
21
|
+
const enquirer = new Enquirer();
|
|
22
|
+
const { targetDir } = (await enquirer.prompt(targetDirPrompt(cwd)));
|
|
23
|
+
// General checks
|
|
24
|
+
const preflightResponse = await preflightChecks(targetDir);
|
|
25
|
+
checkErrors(preflightResponse);
|
|
26
|
+
if (preflightResponse.errors.length > 0) {
|
|
27
|
+
const { gitConfirm } = (await enquirer.prompt(gitPrompt(preflightResponse)));
|
|
28
|
+
if (!gitConfirm) {
|
|
29
|
+
error("Did not make any changes");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Check target framework and version
|
|
33
|
+
// biome-ignore lint/style/noNonNullAssertion: pkg will always be available here
|
|
34
|
+
const pkg = preflightResponse.pkg;
|
|
35
|
+
const frameworkResponse = await frameworkChecks(pkg);
|
|
36
|
+
checkErrors(frameworkResponse);
|
|
37
|
+
if (!frameworkResponse) {
|
|
38
|
+
throw new Error(`Could not determine framework in package`);
|
|
39
|
+
}
|
|
40
|
+
const { pathPrefix } = (await enquirer.prompt(pathPrefixPrompt()));
|
|
41
|
+
// biome-ignore lint/style/noNonNullAssertion: framework will always be available here
|
|
42
|
+
const detectedFramework = frameworkResponse.framework;
|
|
43
|
+
const { confirmWrite } = (await enquirer.prompt(confirmWritePrompt(detectedFramework)));
|
|
44
|
+
if (!confirmWrite) {
|
|
45
|
+
console.log(colors.yellow("Not proceeding, did not make any changes"));
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
// Copy templates
|
|
49
|
+
try {
|
|
50
|
+
const frameworkName = detectedFramework.name;
|
|
51
|
+
const updatedFiles = copyTemplates({
|
|
52
|
+
targetDir,
|
|
53
|
+
framework: frameworkName,
|
|
54
|
+
frameworkInfo: supportedFrameworks[frameworkName],
|
|
55
|
+
pathPrefix,
|
|
56
|
+
});
|
|
57
|
+
const updatedPkg = updateDependencies(pkg, detectedFramework);
|
|
58
|
+
writePackageJson(targetDir, updatedPkg);
|
|
59
|
+
updatedFiles.push(`package.json`);
|
|
60
|
+
console.log(colors.green(`\nThe following files were created or updated: \n${updatedFiles.join("\n")}\n`));
|
|
61
|
+
console.log(colors.yellow(help.messages[frameworkName].postinit));
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
error("Could not copy files to the app directory", err);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PackageManifest } from "@pnpm/types";
|
|
2
|
+
import type { DetectedFramework } from "./frameworks.js";
|
|
3
|
+
export declare function getPackageJson(dir: string): PackageManifest | undefined;
|
|
4
|
+
export declare function updateDependencies(pkg: PackageManifest, framework: DetectedFramework): PackageManifest;
|
|
5
|
+
export declare function writePackageJson(dir: string, pkg: PackageManifest): void;
|
package/dist/package.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export function getPackageJson(dir) {
|
|
4
|
+
try {
|
|
5
|
+
const pkgJson = fs.readFileSync(path.join(dir, "package.json"), {
|
|
6
|
+
encoding: "utf8",
|
|
7
|
+
});
|
|
8
|
+
return typeof pkgJson === "string"
|
|
9
|
+
? JSON.parse(pkgJson)
|
|
10
|
+
: undefined;
|
|
11
|
+
}
|
|
12
|
+
catch (_err) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function updateDependencies(pkg, framework) {
|
|
17
|
+
var _a;
|
|
18
|
+
(_a = pkg.dependencies) !== null && _a !== void 0 ? _a : (pkg.dependencies = {});
|
|
19
|
+
framework.info.dependencies.forEach((dep) => {
|
|
20
|
+
const [name, version] = dep.split(":");
|
|
21
|
+
if (name && version && pkg.dependencies) {
|
|
22
|
+
pkg.dependencies[name] = version;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return pkg;
|
|
26
|
+
}
|
|
27
|
+
export function writePackageJson(dir, pkg) {
|
|
28
|
+
fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify(pkg, null, 2), {
|
|
29
|
+
encoding: "utf8",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type Enquirer from "enquirer";
|
|
2
|
+
import type { PreflightResponse } from "./checks.js";
|
|
3
|
+
import type { DetectedFramework } from "./frameworks.js";
|
|
4
|
+
type Prompt = Parameters<typeof Enquirer.prompt>[0];
|
|
5
|
+
export declare const targetDirPrompt: (cwd: string) => Prompt;
|
|
6
|
+
export declare const gitPrompt: (preflight: PreflightResponse) => Prompt;
|
|
7
|
+
export declare const confirmWritePrompt: (framework: DetectedFramework) => Prompt;
|
|
8
|
+
export declare const apiAuthPrompt: () => Prompt;
|
|
9
|
+
export declare const pathPrefixPrompt: () => Prompt;
|
|
10
|
+
export {};
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import colors from "ansi-colors";
|
|
3
|
+
import * as help from "./help.js";
|
|
4
|
+
export const targetDirPrompt = (cwd) => ({
|
|
5
|
+
type: "text",
|
|
6
|
+
name: "targetDir",
|
|
7
|
+
message: `Path to your existing app`,
|
|
8
|
+
initial: cwd,
|
|
9
|
+
validate(path) {
|
|
10
|
+
if (!(fs.existsSync(path) && fs.lstatSync(path).isDirectory())) {
|
|
11
|
+
return `${path} doesn't exist or is not a directory`;
|
|
12
|
+
}
|
|
13
|
+
return true;
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
export const gitPrompt = (preflight) => ({
|
|
17
|
+
type: "confirm",
|
|
18
|
+
name: "gitConfirm",
|
|
19
|
+
message: () => {
|
|
20
|
+
var _a;
|
|
21
|
+
return colors.yellow(`${(_a = preflight.errors.filter((err) => !err.bail)[0]) === null || _a === void 0 ? void 0 : _a.msg}. Do you want to continue?`);
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
export const confirmWritePrompt = (framework) => ({
|
|
25
|
+
type: "confirm",
|
|
26
|
+
name: "confirmWrite",
|
|
27
|
+
message: () => `Supported version of ${framework.name} found! Please read these details to continue: \n
|
|
28
|
+
${colors.cyanBright(help.messages[framework.name].preinit)}
|
|
29
|
+
|
|
30
|
+
The next step is to create the required files and update package.json with the dependencies.
|
|
31
|
+
${colors.green("Do you want to continue?")}`,
|
|
32
|
+
});
|
|
33
|
+
export const apiAuthPrompt = () => [
|
|
34
|
+
{
|
|
35
|
+
type: "input",
|
|
36
|
+
name: "siteName",
|
|
37
|
+
message: "Site name",
|
|
38
|
+
initial: "site-test",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: "password",
|
|
42
|
+
name: "apiKey",
|
|
43
|
+
message() {
|
|
44
|
+
const siteName = this.state.answers.siteName;
|
|
45
|
+
const url = `https://${siteName}.chargebee.com/apikeys_and_webhooks/api`;
|
|
46
|
+
return `API key [${colors.underline(colors.gray(url))}]`;
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
export const pathPrefixPrompt = () => ({
|
|
51
|
+
type: "input",
|
|
52
|
+
name: "pathPrefix",
|
|
53
|
+
message: "The base path prefix for all the routes created. You can edit the generated files to change this later",
|
|
54
|
+
initial: "",
|
|
55
|
+
result(value) {
|
|
56
|
+
return value.trim() ? `/${value.trim().replace(/^\/*|\/*$/g, "")}` : "";
|
|
57
|
+
},
|
|
58
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Chargebee,
|
|
3
|
+
ChargebeeClient,
|
|
4
|
+
type ChargeInput,
|
|
5
|
+
createOneTimeCheckout,
|
|
6
|
+
createPortalSession,
|
|
7
|
+
createSubscriptionCheckout,
|
|
8
|
+
type ManageInput,
|
|
9
|
+
managePaymentSources,
|
|
10
|
+
type PortalCreateInput,
|
|
11
|
+
type SubscriptionInput,
|
|
12
|
+
validateBasicAuth,
|
|
13
|
+
} from "@chargebee/express";
|
|
14
|
+
import type { Application, Request, Response } from "express";
|
|
15
|
+
|
|
16
|
+
const apiKey = process.env.CHARGEBEE_API_KEY!;
|
|
17
|
+
const site = process.env.CHARGEBEE_SITE!;
|
|
18
|
+
const webhookBasicAuth = process.env.CHARGEBEE_WEBHOOK_AUTH;
|
|
19
|
+
|
|
20
|
+
const chargebee = new ChargebeeClient({ apiKey, site });
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Checkout an item without creating a subscription
|
|
24
|
+
*/
|
|
25
|
+
const chargeController = createOneTimeCheckout({
|
|
26
|
+
apiKey,
|
|
27
|
+
site,
|
|
28
|
+
apiPayload: async (req: Request) => {
|
|
29
|
+
console.warn(
|
|
30
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
31
|
+
);
|
|
32
|
+
// https://api-explorer.chargebee.com/item_prices/list_item_prices
|
|
33
|
+
const { list } = await chargebee.itemPrice.list({
|
|
34
|
+
item_type: {
|
|
35
|
+
is: "charge",
|
|
36
|
+
},
|
|
37
|
+
status: {
|
|
38
|
+
is: "active",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
item_prices: list.map((entry) => ({
|
|
44
|
+
item_price_id: entry.item_price.id,
|
|
45
|
+
})),
|
|
46
|
+
redirect_url: `${req.baseUrl}{{pathPrefix}}/checkout/callback`,
|
|
47
|
+
} as ChargeInput;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create a subscription
|
|
53
|
+
*/
|
|
54
|
+
const subscriptionController = createSubscriptionCheckout({
|
|
55
|
+
apiKey,
|
|
56
|
+
site,
|
|
57
|
+
apiPayload: async (req: Request) => {
|
|
58
|
+
console.warn(
|
|
59
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
60
|
+
);
|
|
61
|
+
// https://api-explorer.chargebee.com/item_prices/list_item_prices
|
|
62
|
+
const { list } = await chargebee.itemPrice.list({
|
|
63
|
+
limit: 1,
|
|
64
|
+
item_type: {
|
|
65
|
+
is: "plan",
|
|
66
|
+
},
|
|
67
|
+
status: {
|
|
68
|
+
is: "active",
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
subscription_items: [{ item_price_id: list[0]?.item_price.id }],
|
|
74
|
+
redirect_url: `${req.baseUrl}{{pathPrefix}}/checkout/callback`,
|
|
75
|
+
} as SubscriptionInput;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Let the customer add/remove payment sources
|
|
81
|
+
*/
|
|
82
|
+
const manageController = managePaymentSources({
|
|
83
|
+
apiKey,
|
|
84
|
+
site,
|
|
85
|
+
apiPayload: async (req: Request) => {
|
|
86
|
+
console.warn(
|
|
87
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
88
|
+
);
|
|
89
|
+
// https://apidocs.chargebee.com/docs/api/hosted_pages?lang=node#manage_payment_sources
|
|
90
|
+
return {
|
|
91
|
+
customer: {
|
|
92
|
+
id: "chargebee-customer-id",
|
|
93
|
+
redirect_url: `${req.baseUrl}{{pathPrefix}}/checkout/callback`,
|
|
94
|
+
pass_thru_content: crypto.randomUUID(),
|
|
95
|
+
},
|
|
96
|
+
} as ManageInput;
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Open the Chargebee portal for the given cutomer ID
|
|
102
|
+
*/
|
|
103
|
+
const portalController = createPortalSession({
|
|
104
|
+
apiKey,
|
|
105
|
+
site,
|
|
106
|
+
apiPayload: async (req: Request) => {
|
|
107
|
+
console.warn(
|
|
108
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
109
|
+
);
|
|
110
|
+
// TODO: Return the authenticated customer here
|
|
111
|
+
return {
|
|
112
|
+
customer: {
|
|
113
|
+
id: "chargebee-customer-id",
|
|
114
|
+
},
|
|
115
|
+
redirect_url: `${req.baseUrl}/users/`,
|
|
116
|
+
} as PortalCreateInput;
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Checkout callback function
|
|
122
|
+
*/
|
|
123
|
+
async function callbackController(req: Request, _res: Response) {
|
|
124
|
+
console.warn(
|
|
125
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
126
|
+
);
|
|
127
|
+
const { searchParams } = new URL(req.originalUrl);
|
|
128
|
+
const id = searchParams.get("id");
|
|
129
|
+
const state = searchParams.get("state");
|
|
130
|
+
// TODO: validate state and do something with the hosted page id
|
|
131
|
+
if (state === "succeeded") {
|
|
132
|
+
const { hosted_page } = await chargebee.hostedPage.retrieve(id!);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Handle incoming webhook events
|
|
138
|
+
*/
|
|
139
|
+
async function webhookController(req: Request, _res: Response) {
|
|
140
|
+
console.warn(
|
|
141
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
142
|
+
);
|
|
143
|
+
// HTTP Basic Auth is currently optional when adding a new webhook
|
|
144
|
+
// url in the Chargebee dashboard. However, we expect it's set by default.
|
|
145
|
+
// Please set the env variable CHARGEBEE_WEBHOOK_BASIC_AUTH to "user:pass"
|
|
146
|
+
// which is validated here
|
|
147
|
+
try {
|
|
148
|
+
validateBasicAuth(webhookBasicAuth, req.get("authorization"));
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error(error);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const data = req.body as Chargebee.Event;
|
|
154
|
+
// TODO: handle the incoming webhook data
|
|
155
|
+
console.log(data);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Initialize the Express app with the Chargebee controllers
|
|
160
|
+
* @param app Express application
|
|
161
|
+
* @returns Express application
|
|
162
|
+
*/
|
|
163
|
+
export default function init(
|
|
164
|
+
app: Application,
|
|
165
|
+
{ routePrefix = "{{pathPrefix}}" } = {} as { routePrefix: string },
|
|
166
|
+
) {
|
|
167
|
+
// Checkout
|
|
168
|
+
app.get(`${routePrefix}/checkout/one-time-charge`, chargeController);
|
|
169
|
+
app.get(`${routePrefix}/checkout/subscription`, subscriptionController);
|
|
170
|
+
app.get(`${routePrefix}/checkout/manage-payment-sources`, manageController);
|
|
171
|
+
app.get(`${routePrefix}/checkout/callback`, callbackController);
|
|
172
|
+
|
|
173
|
+
// Portal
|
|
174
|
+
app.get(`${routePrefix}/portal`, portalController);
|
|
175
|
+
|
|
176
|
+
// Webhook
|
|
177
|
+
app.post(`${routePrefix}/webhook`, webhookController);
|
|
178
|
+
|
|
179
|
+
return app;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Usage:
|
|
183
|
+
// import chargebeeInit from "./chargebee/controllers.ts"
|
|
184
|
+
// chargebeeInit(app);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { client } from "@chargebee/nextjs";
|
|
2
|
+
import type { NextRequest } from "next/server.js";
|
|
3
|
+
|
|
4
|
+
export const GET = async (req: NextRequest) => {
|
|
5
|
+
console.warn(
|
|
6
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
7
|
+
);
|
|
8
|
+
const id = req.nextUrl.searchParams.get("id");
|
|
9
|
+
const state = req.nextUrl.searchParams.get("state");
|
|
10
|
+
// TODO: validate state and do something with the hosted page id
|
|
11
|
+
const chargebee = await client.getFromEnv();
|
|
12
|
+
if (state === "succeeded") {
|
|
13
|
+
const { hosted_page } = await chargebee.hostedPage.retrieve(id!);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ManageInput, managePaymentSources } from "@chargebee/nextjs";
|
|
2
|
+
import type { NextRequest } from "next/server.js";
|
|
3
|
+
|
|
4
|
+
export const GET = managePaymentSources({
|
|
5
|
+
apiKey: process.env.CHARGEBEE_API_KEY!,
|
|
6
|
+
site: process.env.CHARGEBEE_SITE!,
|
|
7
|
+
apiPayload: (req: NextRequest): ManageInput => {
|
|
8
|
+
console.warn(
|
|
9
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
10
|
+
);
|
|
11
|
+
// https://apidocs.chargebee.com/docs/api/hosted_pages?lang=node#manage_payment_sources
|
|
12
|
+
return {
|
|
13
|
+
customer: {
|
|
14
|
+
id: "chargebee-customer-id",
|
|
15
|
+
redirect_url: `${req.nextUrl.origin}{{pathPrefix}}/checkout/callback`,
|
|
16
|
+
pass_thru_content: crypto.randomUUID(),
|
|
17
|
+
},
|
|
18
|
+
} as ManageInput;
|
|
19
|
+
},
|
|
20
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ChargeInput,
|
|
3
|
+
client,
|
|
4
|
+
createOneTimeCheckout,
|
|
5
|
+
} from "@chargebee/nextjs";
|
|
6
|
+
import type { NextRequest } from "next/server.js";
|
|
7
|
+
|
|
8
|
+
export const GET = createOneTimeCheckout({
|
|
9
|
+
apiKey: process.env.CHARGEBEE_API_KEY!,
|
|
10
|
+
site: process.env.CHARGEBEE_SITE!,
|
|
11
|
+
apiPayload: async (req: NextRequest): Promise<ChargeInput> => {
|
|
12
|
+
console.warn(
|
|
13
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
14
|
+
);
|
|
15
|
+
const chargebee = await client.getFromEnv();
|
|
16
|
+
// https://api-explorer.chargebee.com/item_prices/list_item_prices
|
|
17
|
+
const { list } = await chargebee.itemPrice.list({
|
|
18
|
+
item_type: {
|
|
19
|
+
is: "charge",
|
|
20
|
+
},
|
|
21
|
+
status: {
|
|
22
|
+
is: "active",
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
item_prices: list.map((entry) => ({
|
|
28
|
+
item_price_id: entry.item_price.id,
|
|
29
|
+
})),
|
|
30
|
+
redirect_url: `${req.nextUrl.origin}{{pathPrefix}}/checkout/callback`,
|
|
31
|
+
} as ChargeInput;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
client,
|
|
3
|
+
createSubscriptionCheckout,
|
|
4
|
+
type SubscriptionInput,
|
|
5
|
+
} from "@chargebee/nextjs";
|
|
6
|
+
import type { NextRequest } from "next/server.js";
|
|
7
|
+
|
|
8
|
+
export const GET = createSubscriptionCheckout({
|
|
9
|
+
apiKey: process.env.CHARGEBEE_API_KEY!,
|
|
10
|
+
site: process.env.CHARGEBEE_SITE!,
|
|
11
|
+
apiPayload: async (req: NextRequest): Promise<SubscriptionInput> => {
|
|
12
|
+
console.warn(
|
|
13
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
14
|
+
);
|
|
15
|
+
const chargebee = await client.getFromEnv();
|
|
16
|
+
// https://api-explorer.chargebee.com/item_prices/list_item_prices
|
|
17
|
+
const { list } = await chargebee.itemPrice.list({
|
|
18
|
+
limit: 1,
|
|
19
|
+
item_type: {
|
|
20
|
+
is: "plan",
|
|
21
|
+
},
|
|
22
|
+
status: {
|
|
23
|
+
is: "active",
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
subscription_items: [{ item_price_id: list[0]?.item_price.id }],
|
|
29
|
+
redirect_url: `${req.nextUrl.origin}{{pathPrefix}}/checkout/callback`,
|
|
30
|
+
} as SubscriptionInput;
|
|
31
|
+
},
|
|
32
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createPortalSession, type PortalCreateInput } from "@chargebee/nextjs";
|
|
2
|
+
import type { NextRequest } from "next/server.js";
|
|
3
|
+
|
|
4
|
+
export const GET = createPortalSession({
|
|
5
|
+
apiKey: process.env.CHARGEBEE_API_KEY!,
|
|
6
|
+
site: process.env.CHARGEBEE_SITE!,
|
|
7
|
+
apiPayload: async (req: NextRequest): Promise<PortalCreateInput> => {
|
|
8
|
+
console.warn(
|
|
9
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
10
|
+
);
|
|
11
|
+
// TODO: Return the authenticated customer here
|
|
12
|
+
return {
|
|
13
|
+
customer: {
|
|
14
|
+
id: "",
|
|
15
|
+
},
|
|
16
|
+
redirect_url: `${req.nextUrl.origin}/users/`,
|
|
17
|
+
} as PortalCreateInput;
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Chargebee, validateBasicAuth } from "@chargebee/nextjs";
|
|
2
|
+
import { type NextRequest, NextResponse } from "next/server.js";
|
|
3
|
+
|
|
4
|
+
export async function POST(req: NextRequest) {
|
|
5
|
+
console.warn(
|
|
6
|
+
`⚠ This is the default implementation from chargebee-init and must be reviewed!`,
|
|
7
|
+
);
|
|
8
|
+
// HTTP Basic Auth is currently optional when adding a new webhook
|
|
9
|
+
// url in the Chargebee dashboard. However, we expect it's set by default.
|
|
10
|
+
// Please set the env variable CHARGEBEE_WEBHOOK_BASIC_AUTH to "user:pass"
|
|
11
|
+
// which is validated here
|
|
12
|
+
try {
|
|
13
|
+
validateBasicAuth(
|
|
14
|
+
process.env.CHARGEBEE_WEBHOOK_AUTH,
|
|
15
|
+
req.headers.get("authorization"),
|
|
16
|
+
);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error(error);
|
|
19
|
+
return NextResponse.error();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const data = (await req.json()) as Chargebee.Event;
|
|
23
|
+
// TODO: handle the incoming webhook data
|
|
24
|
+
console.log(data);
|
|
25
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Framework, FrameworkInfo } from "./frameworks.js";
|
|
2
|
+
export declare const copyTemplates: ({ targetDir, framework, frameworkInfo, pathPrefix, }: {
|
|
3
|
+
targetDir: string;
|
|
4
|
+
framework: Framework;
|
|
5
|
+
frameworkInfo: FrameworkInfo;
|
|
6
|
+
pathPrefix: string;
|
|
7
|
+
}) => string[];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const __dirname = (_a = globalThis.__dirname) !== null && _a !== void 0 ? _a : import.meta.dirname;
|
|
5
|
+
export const copyTemplates = ({ targetDir, framework, frameworkInfo, pathPrefix = "", }) => {
|
|
6
|
+
const srcDir = path.join(__dirname, "templates", framework);
|
|
7
|
+
// Determine app directory to write files to
|
|
8
|
+
let appDir = targetDir;
|
|
9
|
+
for (let dir of frameworkInfo.appDirectories) {
|
|
10
|
+
dir = path.join(appDir, dir);
|
|
11
|
+
if (fs.existsSync(dir)) {
|
|
12
|
+
appDir = dir;
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (appDir === targetDir || !fs.existsSync(appDir)) {
|
|
17
|
+
throw new Error(`Could not find expected directories to copy files to: ${frameworkInfo.appDirectories.join(", ")}`);
|
|
18
|
+
}
|
|
19
|
+
const copiedFiles = [];
|
|
20
|
+
const templatesDir = path.join(appDir, pathPrefix);
|
|
21
|
+
copyDirectory(srcDir, templatesDir, { pathPrefix }, copiedFiles);
|
|
22
|
+
return copiedFiles;
|
|
23
|
+
};
|
|
24
|
+
function replaceVariables(text, replacements) {
|
|
25
|
+
return text.replace(/{{\s*(\w+)\s*}}/g, (_, key) => { var _a; return (_a = replacements[key]) !== null && _a !== void 0 ? _a : ""; });
|
|
26
|
+
}
|
|
27
|
+
// Recursively copy the directory and replace templated strings
|
|
28
|
+
function copyDirectory(srcDir, destDir, replacements, copiedFiles) {
|
|
29
|
+
// Ensure destination directory exists
|
|
30
|
+
if (!fs.existsSync(destDir)) {
|
|
31
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
// Read all items in the current source directory
|
|
34
|
+
const items = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
35
|
+
for (const item of items) {
|
|
36
|
+
const srcPath = path.join(srcDir, item.name);
|
|
37
|
+
const destPath = path.join(destDir, item.name);
|
|
38
|
+
if (item.isDirectory()) {
|
|
39
|
+
// Recursively copy subdirectory
|
|
40
|
+
copyDirectory(srcPath, destPath, replacements, copiedFiles);
|
|
41
|
+
}
|
|
42
|
+
else if (item.isFile()) {
|
|
43
|
+
let content = fs.readFileSync(srcPath, "utf-8");
|
|
44
|
+
// Only perform replacements for .ts files
|
|
45
|
+
if (path.extname(item.name) === ".ts") {
|
|
46
|
+
content = replaceVariables(content, replacements);
|
|
47
|
+
}
|
|
48
|
+
fs.writeFileSync(destPath, content, "utf-8");
|
|
49
|
+
copiedFiles.push(destPath);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "chargebee-init",
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "Chargebee",
|
|
6
|
+
"email": "dx@chargebee.com"
|
|
7
|
+
},
|
|
8
|
+
"description": "A CLI to help integrate Chargebee services with your existing app",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"exports": "./dist/cli.js",
|
|
11
|
+
"bin": {
|
|
12
|
+
"chargebee-init": "bin/chargebee-init"
|
|
13
|
+
},
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"bin"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"ansi-colors": "^4.1.3",
|
|
23
|
+
"enquirer": "^2.4.1",
|
|
24
|
+
"meow": "^13.2.0",
|
|
25
|
+
"simple-git": "^3.27.0",
|
|
26
|
+
"semver": "^7.7.2"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@pnpm/types": "^1000.6.0",
|
|
30
|
+
"@types/semver": "^7.7.0"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"prebuild": "rm -rf dist && mkdir -p dist/templates",
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"postbuild": "for n in nextjs express; do cp -R ../$n/templates dist/templates/$n/; done;",
|
|
36
|
+
"watch": "pnpm build && tsc --watch"
|
|
37
|
+
}
|
|
38
|
+
}
|