frappe-nextjs 0.1.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 (98) hide show
  1. package/README.md +90 -0
  2. package/dist/bin/cli.d.ts +3 -0
  3. package/dist/bin/cli.d.ts.map +1 -0
  4. package/dist/bin/cli.js +156 -0
  5. package/dist/bin/cli.js.map +1 -0
  6. package/dist/build/boilerplates.d.ts +12 -0
  7. package/dist/build/boilerplates.d.ts.map +1 -0
  8. package/dist/build/boilerplates.js +261 -0
  9. package/dist/build/boilerplates.js.map +1 -0
  10. package/dist/build/build.d.ts +27 -0
  11. package/dist/build/build.d.ts.map +1 -0
  12. package/dist/build/build.js +172 -0
  13. package/dist/build/build.js.map +1 -0
  14. package/dist/build/context-generator.d.ts +13 -0
  15. package/dist/build/context-generator.d.ts.map +1 -0
  16. package/dist/build/context-generator.js +76 -0
  17. package/dist/build/context-generator.js.map +1 -0
  18. package/dist/build/hooks-patcher.d.ts +11 -0
  19. package/dist/build/hooks-patcher.d.ts.map +1 -0
  20. package/dist/build/hooks-patcher.js +78 -0
  21. package/dist/build/hooks-patcher.js.map +1 -0
  22. package/dist/build/index.d.ts +8 -0
  23. package/dist/build/index.d.ts.map +1 -0
  24. package/dist/build/index.js +33 -0
  25. package/dist/build/index.js.map +1 -0
  26. package/dist/build/init.d.ts +27 -0
  27. package/dist/build/init.d.ts.map +1 -0
  28. package/dist/build/init.js +175 -0
  29. package/dist/build/init.js.map +1 -0
  30. package/dist/build/nginx-generator.d.ts +22 -0
  31. package/dist/build/nginx-generator.d.ts.map +1 -0
  32. package/dist/build/nginx-generator.js +114 -0
  33. package/dist/build/nginx-generator.js.map +1 -0
  34. package/dist/build/supervisor-generator.d.ts +31 -0
  35. package/dist/build/supervisor-generator.d.ts.map +1 -0
  36. package/dist/build/supervisor-generator.js +105 -0
  37. package/dist/build/supervisor-generator.js.map +1 -0
  38. package/dist/client/FrappeClient.d.ts +143 -0
  39. package/dist/client/FrappeClient.d.ts.map +1 -0
  40. package/dist/client/FrappeClient.js +446 -0
  41. package/dist/client/FrappeClient.js.map +1 -0
  42. package/dist/client/index.d.ts +3 -0
  43. package/dist/client/index.d.ts.map +1 -0
  44. package/dist/client/index.js +21 -0
  45. package/dist/client/index.js.map +1 -0
  46. package/dist/client/types.d.ts +140 -0
  47. package/dist/client/types.d.ts.map +1 -0
  48. package/dist/client/types.js +17 -0
  49. package/dist/client/types.js.map +1 -0
  50. package/dist/hooks/FrappeProvider.d.ts +41 -0
  51. package/dist/hooks/FrappeProvider.d.ts.map +1 -0
  52. package/dist/hooks/FrappeProvider.js +48 -0
  53. package/dist/hooks/FrappeProvider.js.map +1 -0
  54. package/dist/hooks/index.d.ts +8 -0
  55. package/dist/hooks/index.d.ts.map +1 -0
  56. package/dist/hooks/index.js +17 -0
  57. package/dist/hooks/index.js.map +1 -0
  58. package/dist/hooks/useFrappeAuth.d.ts +37 -0
  59. package/dist/hooks/useFrappeAuth.d.ts.map +1 -0
  60. package/dist/hooks/useFrappeAuth.js +111 -0
  61. package/dist/hooks/useFrappeAuth.js.map +1 -0
  62. package/dist/hooks/useFrappeCall.d.ts +26 -0
  63. package/dist/hooks/useFrappeCall.d.ts.map +1 -0
  64. package/dist/hooks/useFrappeCall.js +47 -0
  65. package/dist/hooks/useFrappeCall.js.map +1 -0
  66. package/dist/hooks/useFrappeDoc.d.ts +37 -0
  67. package/dist/hooks/useFrappeDoc.d.ts.map +1 -0
  68. package/dist/hooks/useFrappeDoc.js +79 -0
  69. package/dist/hooks/useFrappeDoc.js.map +1 -0
  70. package/dist/hooks/useFrappeDocList.d.ts +37 -0
  71. package/dist/hooks/useFrappeDocList.d.ts.map +1 -0
  72. package/dist/hooks/useFrappeDocList.js +65 -0
  73. package/dist/hooks/useFrappeDocList.js.map +1 -0
  74. package/dist/hooks/useFrappeFileUpload.d.ts +40 -0
  75. package/dist/hooks/useFrappeFileUpload.d.ts.map +1 -0
  76. package/dist/hooks/useFrappeFileUpload.js +68 -0
  77. package/dist/hooks/useFrappeFileUpload.js.map +1 -0
  78. package/dist/index.d.ts +10 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +24 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/ssr/index.d.ts +4 -0
  83. package/dist/ssr/index.d.ts.map +1 -0
  84. package/dist/ssr/index.js +13 -0
  85. package/dist/ssr/index.js.map +1 -0
  86. package/dist/ssr/middleware.d.ts +18 -0
  87. package/dist/ssr/middleware.d.ts.map +1 -0
  88. package/dist/ssr/middleware.js +132 -0
  89. package/dist/ssr/middleware.js.map +1 -0
  90. package/dist/ssr/proxy-config.d.ts +33 -0
  91. package/dist/ssr/proxy-config.d.ts.map +1 -0
  92. package/dist/ssr/proxy-config.js +133 -0
  93. package/dist/ssr/proxy-config.js.map +1 -0
  94. package/dist/ssr/server-client.d.ts +73 -0
  95. package/dist/ssr/server-client.d.ts.map +1 -0
  96. package/dist/ssr/server-client.js +94 -0
  97. package/dist/ssr/server-client.js.map +1 -0
  98. package/package.json +79 -0
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # frappe-nextjs
2
+
3
+ A TypeScript package to manage **Next.js** frontends for **Frappe** apps — with full SSR support, React hooks, and CLI tools.
4
+
5
+ Think of it as [Doppio](https://github.com/NagariaHussain/doppio) but purpose-built for Next.js.
6
+
7
+ ## Features
8
+
9
+ - 🚀 **CLI** — Scaffold, build, and deploy Next.js apps inside Frappe custom apps
10
+ - 🔌 **API Client** — Type-safe Frappe REST API client with CRUD, method calls, file uploads
11
+ - ⚛️ **React Hooks** — SWR-based hooks for data fetching, auth, and file uploads
12
+ - 🖥️ **SSR** — Full server-side rendering via supervisor-managed Node.js server
13
+ - 📦 **Static Export** — Simple static build that works on Frappe Cloud
14
+ - 📖 **TypeScript** — Fully typed, first-class TypeScript support
15
+
16
+ ## Quick Start
17
+
18
+ ```bash
19
+ # Install
20
+ npm install -g frappe-nextjs
21
+
22
+ # Scaffold (inside your bench directory)
23
+ frappe-nextjs init --app myapp --name dashboard --mode ssr
24
+
25
+ # Start development
26
+ cd apps/myapp/dashboard && npm run dev
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ ### React Hooks
32
+
33
+ ```tsx
34
+ import { useFrappeDocList, useFrappeAuth } from 'frappe-nextjs';
35
+
36
+ function TodoList() {
37
+ const { isLoggedIn, user } = useFrappeAuth();
38
+ const { data, isLoading } = useFrappeDocList({
39
+ doctype: 'ToDo',
40
+ fields: ['name', 'description', 'status'],
41
+ filters: [['status', '=', 'Open']],
42
+ limit: 20,
43
+ });
44
+
45
+ return (/* ... */);
46
+ }
47
+ ```
48
+
49
+ ### Server Components (SSR)
50
+
51
+ ```tsx
52
+ // app/todos/page.tsx — Server Component
53
+ import { frappe } from "@/lib/frappe";
54
+
55
+ export default async function TodosPage() {
56
+ const todos = await frappe.getDocList({
57
+ doctype: "ToDo",
58
+ fields: ["name", "description"],
59
+ });
60
+
61
+ return (
62
+ <ul>
63
+ {todos.map((t) => (
64
+ <li key={t.name}>{t.description}</li>
65
+ ))}
66
+ </ul>
67
+ );
68
+ }
69
+ ```
70
+
71
+ ## CLI Commands
72
+
73
+ | Command | Description |
74
+ | ------------------------- | -------------------------------------------- |
75
+ | `frappe-nextjs init` | Scaffold Next.js project inside a Frappe app |
76
+ | `frappe-nextjs build` | Build and deploy (static or SSR) |
77
+ | `frappe-nextjs setup-ssr` | Generate supervisor + nginx configs |
78
+
79
+ ## Documentation
80
+
81
+ - [Getting Started](docs/01-getting-started.md)
82
+ - [Architecture](docs/02-architecture.md)
83
+ - [Connecting to Frappe](docs/03-connecting-to-frappe.md)
84
+ - [Data Fetching & SSR](docs/04-data-fetching-and-ssr.md)
85
+ - [Build & Deployment](docs/05-build-and-deployment.md)
86
+ - [API Reference](docs/06-api-reference.md)
87
+
88
+ ## License
89
+
90
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/bin/cli.ts"],"names":[],"mappings":""}
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const commander_1 = require("commander");
38
+ const path = __importStar(require("path"));
39
+ const fs = __importStar(require("fs"));
40
+ const init_1 = require("../build/init");
41
+ const build_1 = require("../build/build");
42
+ const supervisor_generator_1 = require("../build/supervisor-generator");
43
+ const nginx_generator_1 = require("../build/nginx-generator");
44
+ const program = new commander_1.Command();
45
+ // Read package version
46
+ // Compiled output is at dist/bin/cli.js, so go up two levels to reach root
47
+ const pkgPath = path.join(__dirname, '..', '..', 'package.json');
48
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
49
+ program
50
+ .name('frappe-nextjs')
51
+ .description('CLI to manage Next.js frontends for Frappe apps')
52
+ .version(pkg.version);
53
+ // ─── init command ────────────────────────────────────────────────
54
+ program
55
+ .command('init')
56
+ .description('Scaffold a new Next.js project inside a Frappe app')
57
+ .requiredOption('--app <app-name>', 'Frappe app name (directory name in apps/)')
58
+ .requiredOption('--name <spa-name>', 'Name for the SPA and route prefix (e.g. dashboard)')
59
+ .option('--mode <mode>', 'Deployment mode: ssr | static', 'ssr')
60
+ .option('--frappe-port <port>', 'Frappe webserver port', '8000')
61
+ .action(async (opts) => {
62
+ const benchPath = findBenchPath();
63
+ const appPath = path.join(benchPath, 'apps', opts.app);
64
+ if (!fs.existsSync(appPath)) {
65
+ console.error(`❌ App '${opts.app}' not found at ${appPath}`);
66
+ process.exit(1);
67
+ }
68
+ await (0, init_1.initNextJsProject)({
69
+ appPath,
70
+ appName: opts.app,
71
+ spaName: opts.name,
72
+ mode: opts.mode,
73
+ frappePort: parseInt(opts.frappePort),
74
+ });
75
+ });
76
+ // ─── build command ───────────────────────────────────────────────
77
+ program
78
+ .command('build')
79
+ .description('Build and deploy the Next.js project for Frappe')
80
+ .requiredOption('--app <app-name>', 'Frappe app name')
81
+ .requiredOption('--name <spa-name>', 'SPA name')
82
+ .option('--mode <mode>', 'Build mode: ssr | static', 'ssr')
83
+ .action(async (opts) => {
84
+ const benchPath = findBenchPath();
85
+ const appPath = path.join(benchPath, 'apps', opts.app);
86
+ await (0, build_1.buildNextJsProject)({
87
+ appPath,
88
+ appName: opts.app,
89
+ spaName: opts.name,
90
+ mode: opts.mode,
91
+ });
92
+ });
93
+ // ─── setup-ssr command ───────────────────────────────────────────
94
+ program
95
+ .command('setup-ssr')
96
+ .description('Generate supervisor and nginx configurations for SSR')
97
+ .requiredOption('--app <app-name>', 'Frappe app name')
98
+ .requiredOption('--name <spa-name>', 'SPA name')
99
+ .option('--port <port>', 'Next.js server port', '3100')
100
+ .option('--user <user>', 'System user to run the process as')
101
+ .action((opts) => {
102
+ const benchPath = findBenchPath();
103
+ const appPath = path.join(benchPath, 'apps', opts.app);
104
+ const spaPath = path.join(appPath, opts.name);
105
+ if (!fs.existsSync(spaPath)) {
106
+ console.error(`❌ SPA directory not found: ${spaPath}`);
107
+ process.exit(1);
108
+ }
109
+ const port = parseInt(opts.port);
110
+ // Generate supervisor config
111
+ const supervisorConfig = (0, supervisor_generator_1.generateSupervisorConfig)({
112
+ spaPath,
113
+ spaName: opts.name,
114
+ port,
115
+ benchPath,
116
+ user: opts.user,
117
+ });
118
+ // Save supervisor config
119
+ const supervisorFile = path.join(benchPath, 'config', `nextjs-${opts.name}.conf`);
120
+ fs.mkdirSync(path.join(benchPath, 'config'), { recursive: true });
121
+ fs.writeFileSync(supervisorFile, supervisorConfig, 'utf-8');
122
+ console.log(`📄 Supervisor config saved to: ${supervisorFile}`);
123
+ console.log(supervisorConfig);
124
+ console.log((0, supervisor_generator_1.getSupervisorInstructions)(opts.name, benchPath));
125
+ // Generate nginx config
126
+ const nginxConfig = (0, nginx_generator_1.generateNginxConfig)({
127
+ spaName: opts.name,
128
+ port,
129
+ spaPath,
130
+ });
131
+ const nginxFile = path.join(benchPath, 'config', `nextjs-${opts.name}-nginx.conf`);
132
+ fs.writeFileSync(nginxFile, nginxConfig, 'utf-8');
133
+ console.log(`📄 Nginx config saved to: ${nginxFile}`);
134
+ console.log(nginxConfig);
135
+ console.log((0, nginx_generator_1.getNginxInstructions)(opts.name));
136
+ });
137
+ // ─── Helper: find bench root ─────────────────────────────────────
138
+ function findBenchPath() {
139
+ // Look upward for a directory containing 'sites' and 'apps' dirs
140
+ let dir = process.cwd();
141
+ for (let i = 0; i < 10; i++) {
142
+ if (fs.existsSync(path.join(dir, 'sites')) &&
143
+ fs.existsSync(path.join(dir, 'apps'))) {
144
+ return dir;
145
+ }
146
+ const parent = path.dirname(dir);
147
+ if (parent === dir)
148
+ break; // reached root
149
+ dir = parent;
150
+ }
151
+ // Fallback: assume we're somewhere in the bench
152
+ console.warn('⚠️ Could not detect bench root. Using current directory.');
153
+ return process.cwd();
154
+ }
155
+ program.parse();
156
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/bin/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Template strings for scaffolding a Next.js project inside a Frappe app.
3
+ */
4
+ export declare function getContextPyBoilerplate(spaName: string): string;
5
+ export declare function getLayoutBoilerplate(spaName: string): string;
6
+ export declare function getProvidersBoilerplate(): string;
7
+ export declare function getHomePageBoilerplate(spaName: string): string;
8
+ export declare function getGlobalsCssBoilerplate(): string;
9
+ export declare function getServerClientBoilerplate(): string;
10
+ export declare function getEnvBoilerplate(frappePort?: number): string;
11
+ export declare function getHtmlEntryBoilerplate(spaName: string): string;
12
+ //# sourceMappingURL=boilerplates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boilerplates.d.ts","sourceRoot":"","sources":["../../src/build/boilerplates.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgC/D;AAID,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA0B5D;AAID,wBAAgB,uBAAuB,IAAI,MAAM,CAmBhD;AAID,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA6C9D;AAID,wBAAgB,wBAAwB,IAAI,MAAM,CAyDjD;AAID,wBAAgB,0BAA0B,IAAI,MAAM,CAoBnD;AAID,wBAAgB,iBAAiB,CAAC,UAAU,GAAE,MAAa,GAAG,MAAM,CAYnE;AAID,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB/D"}
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+ /**
3
+ * Template strings for scaffolding a Next.js project inside a Frappe app.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getContextPyBoilerplate = getContextPyBoilerplate;
7
+ exports.getLayoutBoilerplate = getLayoutBoilerplate;
8
+ exports.getProvidersBoilerplate = getProvidersBoilerplate;
9
+ exports.getHomePageBoilerplate = getHomePageBoilerplate;
10
+ exports.getGlobalsCssBoilerplate = getGlobalsCssBoilerplate;
11
+ exports.getServerClientBoilerplate = getServerClientBoilerplate;
12
+ exports.getEnvBoilerplate = getEnvBoilerplate;
13
+ exports.getHtmlEntryBoilerplate = getHtmlEntryBoilerplate;
14
+ // ─── Context Python File (served by Frappe as www page) ─────────
15
+ function getContextPyBoilerplate(spaName) {
16
+ return `import frappe
17
+ from frappe.utils import get_system_timezone
18
+
19
+ no_cache = 1
20
+
21
+
22
+ def get_context():
23
+ csrf_token = frappe.sessions.get_csrf_token()
24
+ context = frappe._dict()
25
+ context.boot = get_boot()
26
+ context.boot.csrf_token = csrf_token
27
+ return context
28
+
29
+
30
+ @frappe.whitelist(methods=["POST"], allow_guest=True)
31
+ def get_context_for_dev():
32
+ if not frappe.conf.developer_mode:
33
+ frappe.throw("This method is only meant for developer mode")
34
+ return get_boot()
35
+
36
+
37
+ def get_boot():
38
+ return frappe._dict(
39
+ {
40
+ "frappe_version": frappe.version,
41
+ "site_name": frappe.local.site,
42
+ "read_only_mode": frappe.flags.read_only,
43
+ "system_timezone": get_system_timezone(),
44
+ }
45
+ )
46
+ `;
47
+ }
48
+ // ─── Next.js Layout (Root) ──────────────────────────────────────
49
+ function getLayoutBoilerplate(spaName) {
50
+ return `import type { Metadata } from 'next';
51
+ import { Providers } from './providers';
52
+ import './globals.css';
53
+
54
+ export const metadata: Metadata = {
55
+ title: '${spaName}',
56
+ description: 'Powered by Next.js + Frappe',
57
+ };
58
+
59
+ export default function RootLayout({
60
+ children,
61
+ }: {
62
+ children: React.ReactNode;
63
+ }) {
64
+ return (
65
+ <html lang="en">
66
+ <body>
67
+ <Providers>
68
+ {children}
69
+ </Providers>
70
+ </body>
71
+ </html>
72
+ );
73
+ }
74
+ `;
75
+ }
76
+ // ─── Providers Component ────────────────────────────────────────
77
+ function getProvidersBoilerplate() {
78
+ return `'use client';
79
+
80
+ import { FrappeProvider } from 'frappe-nextjs';
81
+
82
+ export function Providers({ children }: { children: React.ReactNode }) {
83
+ return (
84
+ <FrappeProvider
85
+ config={{
86
+ // In dev mode, Next.js rewrites proxy /api/* to Frappe.
87
+ // In production (static export), set NEXT_PUBLIC_FRAPPE_URL.
88
+ baseUrl: process.env.NEXT_PUBLIC_FRAPPE_URL || '',
89
+ }}
90
+ >
91
+ {children}
92
+ </FrappeProvider>
93
+ );
94
+ }
95
+ `;
96
+ }
97
+ // ─── Home Page ──────────────────────────────────────────────────
98
+ function getHomePageBoilerplate(spaName) {
99
+ return `'use client';
100
+
101
+ import { useFrappeCall, useFrappeAuth } from 'frappe-nextjs';
102
+
103
+ export default function Home() {
104
+ const { isLoggedIn, user, login, logout, isLoading: authLoading } = useFrappeAuth();
105
+ const { data: pingData, isLoading: pingLoading } = useFrappeCall({
106
+ method: 'frappe.ping',
107
+ });
108
+
109
+ return (
110
+ <main style={{ maxWidth: '640px', margin: '4rem auto', padding: '0 1rem' }}>
111
+ <h1 style={{ fontSize: '2rem', marginBottom: '1rem' }}>
112
+ 🚀 ${spaName}
113
+ </h1>
114
+
115
+ <section style={{ marginBottom: '2rem' }}>
116
+ <h2>Frappe Connection</h2>
117
+ {pingLoading ? (
118
+ <p>Pinging Frappe...</p>
119
+ ) : (
120
+ <p>
121
+ ✅ Connected! Response: <code>{JSON.stringify(pingData)}</code>
122
+ </p>
123
+ )}
124
+ </section>
125
+
126
+ <section>
127
+ <h2>Authentication</h2>
128
+ {authLoading ? (
129
+ <p>Checking auth...</p>
130
+ ) : isLoggedIn ? (
131
+ <div>
132
+ <p>Logged in as: <strong>{user}</strong></p>
133
+ <button onClick={logout}>Logout</button>
134
+ </div>
135
+ ) : (
136
+ <p>Not logged in. Visit <a href="/login">Frappe login</a> first.</p>
137
+ )}
138
+ </section>
139
+ </main>
140
+ );
141
+ }
142
+ `;
143
+ }
144
+ // ─── Globals CSS ────────────────────────────────────────────────
145
+ function getGlobalsCssBoilerplate() {
146
+ return `* {
147
+ box-sizing: border-box;
148
+ margin: 0;
149
+ padding: 0;
150
+ }
151
+
152
+ html,
153
+ body {
154
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
155
+ Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
156
+ color: #1a1a2e;
157
+ background-color: #f8f9fa;
158
+ line-height: 1.6;
159
+ }
160
+
161
+ a {
162
+ color: #0070f3;
163
+ text-decoration: none;
164
+ }
165
+
166
+ a:hover {
167
+ text-decoration: underline;
168
+ }
169
+
170
+ button {
171
+ cursor: pointer;
172
+ padding: 0.5rem 1rem;
173
+ border: 1px solid #ddd;
174
+ border-radius: 4px;
175
+ background: white;
176
+ font-size: 0.875rem;
177
+ }
178
+
179
+ button:hover {
180
+ background: #f0f0f0;
181
+ }
182
+
183
+ code {
184
+ background: #eee;
185
+ padding: 0.15rem 0.4rem;
186
+ border-radius: 3px;
187
+ font-size: 0.875rem;
188
+ }
189
+
190
+ h1, h2, h3 {
191
+ margin-bottom: 0.5rem;
192
+ }
193
+
194
+ section {
195
+ padding: 1.5rem;
196
+ background: white;
197
+ border-radius: 8px;
198
+ border: 1px solid #e5e7eb;
199
+ margin-bottom: 1rem;
200
+ }
201
+ `;
202
+ }
203
+ // ─── Lib/frappe.ts (Server Client) ──────────────────────────────
204
+ function getServerClientBoilerplate() {
205
+ return `import { createServerClient } from 'frappe-nextjs/ssr';
206
+
207
+ /**
208
+ * Server-side Frappe client for use in Server Components,
209
+ * Route Handlers, and Server Actions.
210
+ *
211
+ * Requires these environment variables:
212
+ * FRAPPE_URL - e.g. http://localhost:8000
213
+ * FRAPPE_API_KEY - API Key from Frappe User settings
214
+ * FRAPPE_API_SECRET - API Secret from Frappe User settings
215
+ */
216
+ export const frappe = createServerClient({
217
+ baseUrl: process.env.FRAPPE_URL || 'http://localhost:8000',
218
+ token: {
219
+ apiKey: process.env.FRAPPE_API_KEY || '',
220
+ apiSecret: process.env.FRAPPE_API_SECRET || '',
221
+ },
222
+ });
223
+ `;
224
+ }
225
+ // ─── .env.local ─────────────────────────────────────────────────
226
+ function getEnvBoilerplate(frappePort = 8000) {
227
+ return `# Frappe backend URL (used by Next.js rewrites in dev and by server client)
228
+ FRAPPE_URL=http://localhost:${frappePort}
229
+
230
+ # API credentials for server-side data fetching (SSR)
231
+ # Generate these in Frappe: User > API Access > Generate Keys
232
+ FRAPPE_API_KEY=
233
+ FRAPPE_API_SECRET=
234
+
235
+ # Public Frappe URL for client-side (only needed if different from proxy)
236
+ # NEXT_PUBLIC_FRAPPE_URL=
237
+ `;
238
+ }
239
+ // ─── HTML entry point for static export ─────────────────────────
240
+ function getHtmlEntryBoilerplate(spaName) {
241
+ return `{%- extends "templates/web.html" -%}
242
+
243
+ {%- block page_content -%}
244
+ <div id="__next"></div>
245
+
246
+ <script>
247
+ window.csrf_token = '{{ csrf_token }}';
248
+ window.frappe_boot = {{ boot | tojson }};
249
+ </script>
250
+
251
+ {%- for file in frappe.utils.get_assets_json().get('${spaName}', {}).get('js', []) -%}
252
+ <script type="module" src="{{ file }}"></script>
253
+ {%- endfor -%}
254
+
255
+ {%- for file in frappe.utils.get_assets_json().get('${spaName}', {}).get('css', []) -%}
256
+ <link rel="stylesheet" href="{{ file }}">
257
+ {%- endfor -%}
258
+ {%- endblock -%}
259
+ `;
260
+ }
261
+ //# sourceMappingURL=boilerplates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boilerplates.js","sourceRoot":"","sources":["../../src/build/boilerplates.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAIH,0DAgCC;AAID,oDA0BC;AAID,0DAmBC;AAID,wDA6CC;AAID,4DAyDC;AAID,gEAoBC;AAID,8CAYC;AAID,0DAoBC;AArQD,mEAAmE;AAEnE,SAAgB,uBAAuB,CAAC,OAAe;IACrD,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BR,CAAC;AACF,CAAC;AAED,mEAAmE;AAEnE,SAAgB,oBAAoB,CAAC,OAAe;IAClD,OAAO;;;;;YAKG,OAAO;;;;;;;;;;;;;;;;;;;CAmBlB,CAAC;AACF,CAAC;AAED,mEAAmE;AAEnE,SAAgB,uBAAuB;IACrC,OAAO;;;;;;;;;;;;;;;;;CAiBR,CAAC;AACF,CAAC;AAED,mEAAmE;AAEnE,SAAgB,sBAAsB,CAAC,OAAe;IACpD,OAAO;;;;;;;;;;;;;aAaI,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BnB,CAAC;AACF,CAAC;AAED,mEAAmE;AAEnE,SAAgB,wBAAwB;IACtC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDR,CAAC;AACF,CAAC;AAED,mEAAmE;AAEnE,SAAgB,0BAA0B;IACxC,OAAO;;;;;;;;;;;;;;;;;;CAkBR,CAAC;AACF,CAAC;AAED,mEAAmE;AAEnE,SAAgB,iBAAiB,CAAC,aAAqB,IAAI;IACzD,OAAO;8BACqB,UAAU;;;;;;;;;CASvC,CAAC;AACF,CAAC;AAED,mEAAmE;AAEnE,SAAgB,uBAAuB,CAAC,OAAe;IACrD,OAAO;;;;;;;;;;sDAU6C,OAAO;;;;sDAIP,OAAO;;;;CAI5D,CAAC;AACF,CAAC"}
@@ -0,0 +1,27 @@
1
+ interface BuildOptions {
2
+ /** Absolute path to the Frappe app root */
3
+ appPath: string;
4
+ /** Python package name of the app */
5
+ appName: string;
6
+ /** SPA name */
7
+ spaName: string;
8
+ /** Build mode */
9
+ mode: 'ssr' | 'static';
10
+ }
11
+ /**
12
+ * Build the Next.js project and deploy it for Frappe.
13
+ *
14
+ * Static mode:
15
+ * 1. Runs `next build` (output: 'export')
16
+ * 2. Copies static files to <app>/public/<spa>/
17
+ * 3. Copies index.html to <app>/www/<spa>.html
18
+ * 4. Injects CSRF token
19
+ *
20
+ * SSR mode:
21
+ * 1. Runs `next build` (output: 'standalone')
22
+ * 2. Copies static assets for nginx to serve
23
+ * 3. Prints setup instructions for supervisor/nginx
24
+ */
25
+ export declare function buildNextJsProject(options: BuildOptions): Promise<void>;
26
+ export {};
27
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/build/build.ts"],"names":[],"mappings":"AAIA,UAAU,YAAY;IACpB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAC;CACxB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B7E"}