@djangocfg/nextjs 1.0.5 → 1.0.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/nextjs",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Next.js server utilities: sitemap, health, OG images, contact forms, navigation, config",
5
5
  "keywords": [
6
6
  "nextjs",
@@ -95,17 +95,18 @@
95
95
  "check-links": "tsx src/scripts/check-links.ts"
96
96
  },
97
97
  "peerDependencies": {
98
- "@djangocfg/api": "^1.4.35",
98
+ "@djangocfg/api": "^1.4.36",
99
99
  "next": "^15.4.4"
100
100
  },
101
101
  "devDependencies": {
102
- "@djangocfg/imgai": "^1.0.21",
103
- "@djangocfg/layouts": "^2.0.5",
104
- "@djangocfg/typescript-config": "^1.4.35",
102
+ "@djangocfg/imgai": "^1.0.22",
103
+ "@djangocfg/layouts": "^2.0.6",
104
+ "@djangocfg/typescript-config": "^1.4.36",
105
105
  "@types/node": "^24.7.2",
106
106
  "@types/react": "19.2.2",
107
107
  "@types/react-dom": "19.2.1",
108
108
  "@types/webpack": "^5.28.5",
109
+ "@vercel/og": "^0.8.5",
109
110
  "eslint": "^9.37.0",
110
111
  "linkinator": "^7.5.0",
111
112
  "lucide-react": "^0.469.0",
@@ -113,10 +114,12 @@
113
114
  "prompts": "^2.4.2",
114
115
  "tsup": "^8.0.1",
115
116
  "tsx": "^4.19.2",
116
- "typescript": "^5.9.3",
117
- "@vercel/og": "^0.8.5"
117
+ "typescript": "^5.9.3"
118
118
  },
119
119
  "publishConfig": {
120
120
  "access": "public"
121
+ },
122
+ "dependencies": {
123
+ "chalk": "^5.3.0"
121
124
  }
122
125
  }
@@ -23,6 +23,7 @@
23
23
 
24
24
  import type { NextConfig } from 'next';
25
25
  import type { Configuration as WebpackConfig } from 'webpack';
26
+ import chalk from 'chalk';
26
27
  import { deepMerge } from './deepMerge';
27
28
 
28
29
  // ─────────────────────────────────────────────────────────────────────────
@@ -32,6 +33,25 @@ import { deepMerge } from './deepMerge';
32
33
  const isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === 'true';
33
34
  const isDev = process.env.NODE_ENV === 'development';
34
35
 
36
+ // Global flag to track if browser was already opened (persists across hot reloads)
37
+ let browserOpened = false;
38
+ let bannerPrinted = false;
39
+
40
+ // ASCII Art Banner for Django CFG
41
+ const DJANGO_CFG_BANNER = `
42
+ 888 d8b .d888
43
+ 888 Y8P d88P"
44
+ 888 888
45
+ .d88888 8888 8888b. 88888b. .d88b. .d88b. .d8888b 888888 .d88b.
46
+ d88" 888 "888 "88b 888 "88b d88P"88b d88""88b d88P" 888 d88P"88b
47
+ 888 888 888 .d888888 888 888 888 888 888 888 888 888 888 888
48
+ Y88b 888 888 888 888 888 888 Y88b 888 Y88..88P Y88b. 888 Y88b 888
49
+ "Y88888 888 "Y888888 888 888 "Y88888 "Y88P" "Y8888P 888 "Y88888
50
+ 888 888 888
51
+ d88P Y8b d88P Y8b d88P
52
+ 888P" "Y88P" "Y88P"
53
+ `;
54
+
35
55
  // ─────────────────────────────────────────────────────────────────────────
36
56
  // Configuration Options
37
57
  // ─────────────────────────────────────────────────────────────────────────
@@ -43,6 +63,8 @@ export interface BaseNextConfigOptions {
43
63
  transpilePackages?: string[];
44
64
  /** Additional optimize package imports (merged with defaults) */
45
65
  optimizePackageImports?: string[];
66
+ /** Automatically open browser in dev mode (default: false) */
67
+ openBrowser?: boolean;
46
68
  /** Custom webpack configuration function (called after base webpack logic) */
47
69
  webpack?: (
48
70
  config: WebpackConfig,
@@ -153,6 +175,50 @@ export function createBaseNextConfig(
153
175
  webpack: (config: WebpackConfig, webpackOptions: { isServer: boolean; dev: boolean;[key: string]: any }) => {
154
176
  const { isServer, dev } = webpackOptions;
155
177
 
178
+ // Print banner and auto-open browser in dev mode (client-side only)
179
+ if (dev && !isServer) {
180
+ // Create a simple plugin to print banner and open browser after first compilation
181
+ const DevStartupPlugin = class {
182
+ apply(compiler: any) {
183
+ compiler.hooks.done.tap('DevStartupPlugin', () => {
184
+ // Print banner only once
185
+ if (!bannerPrinted) {
186
+ bannerPrinted = true;
187
+ console.log('\n' + chalk.yellowBright.bold(DJANGO_CFG_BANNER) + '\n');
188
+ }
189
+
190
+ // Auto-open browser if enabled
191
+ if (options.openBrowser && !browserOpened) {
192
+ browserOpened = true;
193
+ // Delay to ensure server is ready
194
+ setTimeout(() => {
195
+ const { exec } = require('child_process');
196
+ const port = process.env.PORT || '3000';
197
+ const url = `http://localhost:${port}`;
198
+
199
+ const command = process.platform === 'darwin'
200
+ ? 'open'
201
+ : process.platform === 'win32'
202
+ ? 'start'
203
+ : 'xdg-open';
204
+
205
+ exec(`${command} ${url}`, (error: Error | null) => {
206
+ if (error) {
207
+ console.warn(`Failed to open browser: ${error.message}`);
208
+ }
209
+ });
210
+ }, 2000); // Wait 2 seconds for server to be ready
211
+ }
212
+ });
213
+ }
214
+ };
215
+
216
+ if (!config.plugins) {
217
+ config.plugins = [];
218
+ }
219
+ config.plugins.push(new DevStartupPlugin());
220
+ }
221
+
156
222
  // Dev mode optimizations
157
223
  if (dev) {
158
224
  config.optimization = {
@@ -227,6 +293,7 @@ export function createBaseNextConfig(
227
293
  // These are internal to BaseNextConfigOptions and should not be in the final config
228
294
  delete (finalConfig as any).optimizePackageImports;
229
295
  delete (finalConfig as any).isDefaultCfgAdmin;
296
+ delete (finalConfig as any).openBrowser;
230
297
  // Note: We don't delete transpilePackages, experimental, env, webpack
231
298
  // as they are valid NextConfig keys and may have been overridden by user
232
299
 
@@ -4,6 +4,8 @@
4
4
  * Server-side function to submit contact form data to backend API.
5
5
  * Can be used in Next.js API routes to avoid CORS issues.
6
6
  *
7
+ * Uses Fetchers with server-side API instance for type safety and Zod validation.
8
+ *
7
9
  * @example
8
10
  * ```ts
9
11
  * import { submitContactForm } from '@djangocfg/nextjs/contact';
@@ -17,8 +19,9 @@
17
19
  * ```
18
20
  */
19
21
 
20
- import { API, Fetchers } from '@djangocfg/api';
21
- import type { Schemas } from '@djangocfg/api';
22
+ // Use server-only exports to avoid loading React hooks (useSWRConfig)
23
+ import { API, MemoryStorageAdapter, Fetchers } from '@djangocfg/api/server';
24
+ import type { Schemas } from '@djangocfg/api/server';
22
25
 
23
26
  export interface SubmitContactFormOptions {
24
27
  /** Lead submission data */
@@ -35,31 +38,11 @@ export interface SubmitContactFormResult {
35
38
  lead_id?: number;
36
39
  }
37
40
 
38
- /**
39
- * Simple server-side memory storage adapter.
40
- * Implements StorageAdapter interface without importing from client package.
41
- */
42
- class ServerMemoryStorageAdapter {
43
- private storage: Map<string, string> = new Map();
44
-
45
- getItem(key: string): string | null {
46
- return this.storage.get(key) || null;
47
- }
48
-
49
- setItem(key: string, value: string): void {
50
- this.storage.set(key, value);
51
- }
52
-
53
- removeItem(key: string): void {
54
- this.storage.delete(key);
55
- }
56
- }
57
-
58
41
  /**
59
42
  * Submit contact form data to backend API
60
43
  *
61
- * Server-side function that uses the typed fetcher for type safety
62
- * and runtime validation via Zod schemas.
44
+ * Server-side function that uses Fetchers with server-side API instance.
45
+ * This provides type safety, Zod validation, and proper error handling.
63
46
  */
64
47
  export async function submitContactForm({
65
48
  data,
@@ -75,10 +58,10 @@ export async function submitContactForm({
75
58
  throw new Error('API URL is required');
76
59
  }
77
60
 
78
- // Create server-side API instance with server-only memory storage adapter
79
- // This avoids importing MemoryStorageAdapter from client package
61
+ // Create server-side API instance with MemoryStorageAdapter
62
+ // This works on server-side and doesn't require browser APIs
80
63
  const serverApi = new API(apiUrl, {
81
- storage: new ServerMemoryStorageAdapter(),
64
+ storage: new MemoryStorageAdapter(),
82
65
  });
83
66
 
84
67
  // Prepare submission data with site_url
@@ -87,14 +70,24 @@ export async function submitContactForm({
87
70
  site_url: data.site_url || siteUrl,
88
71
  };
89
72
 
90
- // Use typed fetcher with server API instance
91
- // This provides type safety and runtime validation via Zod
92
- const result = await Fetchers.createLeadsSubmitCreate(submissionData, serverApi);
73
+ try {
74
+ // Use typed fetcher with server API instance
75
+ // This provides type safety and runtime validation via Zod
76
+ // Using Fetchers namespace to avoid loading hooks
77
+ const result = await Fetchers.createLeadsSubmitCreate(submissionData, serverApi);
93
78
 
94
- return {
95
- success: result.success,
96
- message: result.message,
97
- lead_id: result.lead_id,
98
- };
79
+ // Return formatted result
80
+ return {
81
+ success: result.success ?? true,
82
+ message: result.message || 'Contact form submitted successfully',
83
+ lead_id: result.lead_id,
84
+ };
85
+ } catch (error) {
86
+ // Handle API errors (including validation errors from Zod)
87
+ if (error instanceof Error) {
88
+ throw new Error(`Failed to submit contact form: ${error.message}`);
89
+ }
90
+ throw new Error('An unexpected error occurred while submitting the contact form');
91
+ }
99
92
  }
100
93