@agility/create-next-app 1.0.0-beta.2
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/.claude/settings.json +7 -0
- package/.claude/settings.local.json +24 -0
- package/FEATURE_ROADMAP.md +343 -0
- package/README.md +205 -0
- package/TESTING.md +131 -0
- package/bin/create-agility-app.js +48 -0
- package/dist/agility/api-keys/generateApiKeys.d.ts +9 -0
- package/dist/agility/api-keys/generateApiKeys.d.ts.map +1 -0
- package/dist/agility/api-keys/generateApiKeys.js +99 -0
- package/dist/agility/api-keys/generateApiKeys.js.map +1 -0
- package/dist/agility/api-keys/getApiKeys.d.ts +9 -0
- package/dist/agility/api-keys/getApiKeys.d.ts.map +1 -0
- package/dist/agility/api-keys/getApiKeys.js +14 -0
- package/dist/agility/api-keys/getApiKeys.js.map +1 -0
- package/dist/agility/index.d.ts +3 -0
- package/dist/agility/index.d.ts.map +1 -0
- package/dist/agility/index.js +8 -0
- package/dist/agility/index.js.map +1 -0
- package/dist/agility/instance/createNewInstance.d.ts +8 -0
- package/dist/agility/instance/createNewInstance.d.ts.map +1 -0
- package/dist/agility/instance/createNewInstance.js +65 -0
- package/dist/agility/instance/createNewInstance.js.map +1 -0
- package/dist/agility/instance/getAvailableInstances.d.ts +8 -0
- package/dist/agility/instance/getAvailableInstances.d.ts.map +1 -0
- package/dist/agility/instance/getAvailableInstances.js +43 -0
- package/dist/agility/instance/getAvailableInstances.js.map +1 -0
- package/dist/agility/instance/manageInstance.d.ts +9 -0
- package/dist/agility/instance/manageInstance.d.ts.map +1 -0
- package/dist/agility/instance/manageInstance.js +82 -0
- package/dist/agility/instance/manageInstance.js.map +1 -0
- package/dist/agility/utils/getMgmtAPIUrl.d.ts +20 -0
- package/dist/agility/utils/getMgmtAPIUrl.d.ts.map +1 -0
- package/dist/agility/utils/getMgmtAPIUrl.js +61 -0
- package/dist/agility/utils/getMgmtAPIUrl.js.map +1 -0
- package/dist/auth/api-key/authenticateWithApiKey.d.ts +6 -0
- package/dist/auth/api-key/authenticateWithApiKey.d.ts.map +1 -0
- package/dist/auth/api-key/authenticateWithApiKey.js +28 -0
- package/dist/auth/api-key/authenticateWithApiKey.js.map +1 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/oauth/authenticate.d.ts +6 -0
- package/dist/auth/oauth/authenticate.d.ts.map +1 -0
- package/dist/auth/oauth/authenticate.js +162 -0
- package/dist/auth/oauth/authenticate.js.map +1 -0
- package/dist/auth/oauth/constants.d.ts +5 -0
- package/dist/auth/oauth/constants.d.ts.map +1 -0
- package/dist/auth/oauth/constants.js +9 -0
- package/dist/auth/oauth/constants.js.map +1 -0
- package/dist/auth/oauth/exchangeCodeForToken.d.ts +7 -0
- package/dist/auth/oauth/exchangeCodeForToken.d.ts.map +1 -0
- package/dist/auth/oauth/exchangeCodeForToken.js +39 -0
- package/dist/auth/oauth/exchangeCodeForToken.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +290 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/promptForMissingOptions.d.ts +8 -0
- package/dist/cli/promptForMissingOptions.d.ts.map +1 -0
- package/dist/cli/promptForMissingOptions.js +92 -0
- package/dist/cli/promptForMissingOptions.js.map +1 -0
- package/dist/config/env/createEnvFile.d.ts +6 -0
- package/dist/config/env/createEnvFile.d.ts.map +1 -0
- package/dist/config/env/createEnvFile.js +31 -0
- package/dist/config/env/createEnvFile.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/mcp/createMcpConfig.d.ts +5 -0
- package/dist/config/mcp/createMcpConfig.d.ts.map +1 -0
- package/dist/config/mcp/createMcpConfig.js +32 -0
- package/dist/config/mcp/createMcpConfig.js.map +1 -0
- package/dist/config/packages/installAgilityPackages.d.ts +6 -0
- package/dist/config/packages/installAgilityPackages.d.ts.map +1 -0
- package/dist/config/packages/installAgilityPackages.js +61 -0
- package/dist/config/packages/installAgilityPackages.js.map +1 -0
- package/dist/config/setupProject.d.ts +8 -0
- package/dist/config/setupProject.d.ts.map +1 -0
- package/dist/config/setupProject.js +32 -0
- package/dist/config/setupProject.js.map +1 -0
- package/dist/create-next-app/createNextApp.d.ts +9 -0
- package/dist/create-next-app/createNextApp.d.ts.map +1 -0
- package/dist/create-next-app/createNextApp.js +83 -0
- package/dist/create-next-app/createNextApp.js.map +1 -0
- package/dist/create-next-app/index.d.ts +3 -0
- package/dist/create-next-app/index.d.ts.map +1 -0
- package/dist/create-next-app/index.js +8 -0
- package/dist/create-next-app/index.js.map +1 -0
- package/dist/scaffold/components/createPageComponents.d.ts +6 -0
- package/dist/scaffold/components/createPageComponents.d.ts.map +1 -0
- package/dist/scaffold/components/createPageComponents.js +62 -0
- package/dist/scaffold/components/createPageComponents.js.map +1 -0
- package/dist/scaffold/containers/createContainers.d.ts +6 -0
- package/dist/scaffold/containers/createContainers.d.ts.map +1 -0
- package/dist/scaffold/containers/createContainers.js +48 -0
- package/dist/scaffold/containers/createContainers.js.map +1 -0
- package/dist/scaffold/index.d.ts +2 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +6 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/instance/createBlankInstance.d.ts +8 -0
- package/dist/scaffold/instance/createBlankInstance.d.ts.map +1 -0
- package/dist/scaffold/instance/createBlankInstance.js +51 -0
- package/dist/scaffold/instance/createBlankInstance.js.map +1 -0
- package/dist/scaffold/models/createContentModels.d.ts +6 -0
- package/dist/scaffold/models/createContentModels.d.ts.map +1 -0
- package/dist/scaffold/models/createContentModels.js +70 -0
- package/dist/scaffold/models/createContentModels.js.map +1 -0
- package/dist/templates/copyDirectory.d.ts +5 -0
- package/dist/templates/copyDirectory.d.ts.map +1 -0
- package/dist/templates/copyDirectory.js +28 -0
- package/dist/templates/copyDirectory.js.map +1 -0
- package/dist/templates/copyTemplates.d.ts +8 -0
- package/dist/templates/copyTemplates.d.ts.map +1 -0
- package/dist/templates/copyTemplates.js +58 -0
- package/dist/templates/copyTemplates.js.map +1 -0
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +6 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/types/index.d.ts +50 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +71 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/validation.d.ts +45 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +180 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +45 -0
- package/src/agility/api-keys/generateApiKeys.ts +100 -0
- package/src/agility/api-keys/getApiKeys.ts +13 -0
- package/src/agility/index.ts +3 -0
- package/src/agility/instance/createNewInstance.ts +67 -0
- package/src/agility/instance/getAvailableInstances.ts +49 -0
- package/src/agility/instance/manageInstance.ts +90 -0
- package/src/agility/utils/getMgmtAPIUrl.ts +68 -0
- package/src/auth/api-key/authenticateWithApiKey.ts +24 -0
- package/src/auth/index.ts +3 -0
- package/src/auth/oauth/authenticate.ts +165 -0
- package/src/auth/oauth/constants.ts +6 -0
- package/src/auth/oauth/exchangeCodeForToken.ts +43 -0
- package/src/cli/index.ts +281 -0
- package/src/cli/promptForMissingOptions.ts +104 -0
- package/src/config/env/createEnvFile.ts +30 -0
- package/src/config/index.ts +2 -0
- package/src/config/mcp/createMcpConfig.ts +30 -0
- package/src/config/packages/installAgilityPackages.ts +63 -0
- package/src/config/setupProject.ts +31 -0
- package/src/create-next-app/createNextApp.ts +75 -0
- package/src/create-next-app/index.ts +3 -0
- package/src/scaffold/components/createPageComponents.ts +74 -0
- package/src/scaffold/containers/createContainers.ts +55 -0
- package/src/scaffold/index.ts +2 -0
- package/src/scaffold/instance/createBlankInstance.ts +55 -0
- package/src/scaffold/models/createContentModels.ts +83 -0
- package/src/templates/copyDirectory.ts +24 -0
- package/src/templates/copyTemplates.ts +57 -0
- package/src/templates/index.ts +2 -0
- package/src/types/index.ts +55 -0
- package/src/utils/git.ts +74 -0
- package/src/utils/validation.ts +184 -0
- package/templates/.claude/QUICK-START.md +230 -0
- package/templates/.claude/README.md +32 -0
- package/templates/.claude/settings.json +8 -0
- package/templates/BLANK-INSTANCE-SETUP.md +375 -0
- package/templates/DEVELOPMENT.md +160 -0
- package/templates/EXAMPLE-PROMPTS.md +643 -0
- package/templates/PROMPTS.md +410 -0
- package/templates/README.md +281 -0
- package/templates/agents.md +429 -0
- package/templates/app/[locale]/[...slug]/error.tsx +17 -0
- package/templates/app/[locale]/[...slug]/not-found.tsx +9 -0
- package/templates/app/[locale]/[...slug]/page.tsx +102 -0
- package/templates/app/[locale]/layout.tsx +22 -0
- package/templates/app/[locale]/page.tsx +12 -0
- package/templates/app/api/dynamic-redirect/route.ts +24 -0
- package/templates/app/api/preview/exit/route.ts +34 -0
- package/templates/app/api/preview/route.ts +63 -0
- package/templates/app/api/revalidate/route.ts +118 -0
- package/templates/components/agility-components/RichTextArea.tsx +66 -0
- package/templates/components/agility-components/index.ts +30 -0
- package/templates/components/agility-pages/MainTemplate.tsx +36 -0
- package/templates/components/agility-pages/index.ts +11 -0
- package/templates/docs/01-agility-cms-overview.md +139 -0
- package/templates/docs/02-page-routing.md +251 -0
- package/templates/docs/03-creating-components.md +462 -0
- package/templates/docs/04-data-fetching.md +484 -0
- package/templates/docs/05-containers-and-lists.md +596 -0
- package/templates/docs/06-localization.md +561 -0
- package/templates/docs/07-caching-strategies.md +410 -0
- package/templates/docs/08-common-components.md +756 -0
- package/templates/docs/09-whats-included.md +279 -0
- package/templates/docs/10-mcp-server-setup.md +153 -0
- package/templates/docs/11-linked-nested-content.md +611 -0
- package/templates/docs/README.md +164 -0
- package/templates/lib/cms/getAgilityContext.ts +28 -0
- package/templates/lib/cms/getAgilityPage.ts +51 -0
- package/templates/lib/cms/getAgilitySDK.ts +22 -0
- package/templates/lib/cms/getContentItem.ts +20 -0
- package/templates/lib/cms/getContentList.ts +19 -0
- package/templates/lib/cms/getRedirections.ts +85 -0
- package/templates/lib/cms/getSitemapFlat.ts +19 -0
- package/templates/lib/cms/getSitemapNested.ts +19 -0
- package/templates/lib/env.ts +99 -0
- package/templates/lib/i18n/config.ts +28 -0
- package/templates/proxy.ts +101 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Agility CMS + Next.js Documentation
|
|
2
|
+
|
|
3
|
+
Complete documentation for building websites with Agility CMS and Next.js. Optimized for both human developers and AI coding assistants.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
1. **[01-agility-cms-overview.md](./01-agility-cms-overview.md)** - Understand how Agility CMS works
|
|
8
|
+
2. **[03-creating-components.md](./03-creating-components.md)** - Create your first module
|
|
9
|
+
3. **[08-common-components.md](./08-common-components.md)** - See production-ready examples
|
|
10
|
+
|
|
11
|
+
## 📚 Documentation Index
|
|
12
|
+
|
|
13
|
+
### Core Concepts
|
|
14
|
+
- **[01-agility-cms-overview.md](./01-agility-cms-overview.md)** - How Agility CMS works with Next.js
|
|
15
|
+
- **[02-page-routing.md](./02-page-routing.md)** - Dynamic routing, URLs, and the proxy
|
|
16
|
+
|
|
17
|
+
### Development Guide
|
|
18
|
+
- **[03-creating-components.md](./03-creating-components.md)** - Build module components step-by-step
|
|
19
|
+
- **[04-data-fetching.md](./04-data-fetching.md)** - Fetch content from Agility CMS
|
|
20
|
+
- **[05-containers-and-lists.md](./05-containers-and-lists.md)** - Work with content lists (blogs, testimonials, etc.)
|
|
21
|
+
- **[08-common-components.md](./08-common-components.md)** - Production-ready component examples
|
|
22
|
+
|
|
23
|
+
### Advanced Features
|
|
24
|
+
- **[06-localization.md](./06-localization.md)** - Multi-language support
|
|
25
|
+
- **[07-caching-strategies.md](./07-caching-strategies.md)** - Performance and caching
|
|
26
|
+
- **[11-linked-nested-content.md](./11-linked-nested-content.md)** - Linked and nested content patterns
|
|
27
|
+
|
|
28
|
+
### Setup & Configuration
|
|
29
|
+
- **[10-mcp-server-setup.md](./10-mcp-server-setup.md)** - Connect AI assistants to Agility CMS via MCP
|
|
30
|
+
- **[09-whats-included.md](./09-whats-included.md)** - Feature checklist
|
|
31
|
+
|
|
32
|
+
## 🎯 Common Tasks
|
|
33
|
+
|
|
34
|
+
### Add a Blog
|
|
35
|
+
1. Read [05-containers-and-lists.md](./05-containers-and-lists.md)
|
|
36
|
+
2. Create PostListing component using examples
|
|
37
|
+
3. Configure "posts" content list in Agility CMS
|
|
38
|
+
4. Add content and it renders automatically
|
|
39
|
+
|
|
40
|
+
### Add a Hero Section
|
|
41
|
+
1. See examples in [08-common-components.md](./08-common-components.md)
|
|
42
|
+
2. Copy Hero component code
|
|
43
|
+
3. Register in `components/agility-components/index.ts`
|
|
44
|
+
4. Create Hero module in Agility CMS
|
|
45
|
+
|
|
46
|
+
### Enable Multi-Language
|
|
47
|
+
1. Read [06-localization.md](./06-localization.md)
|
|
48
|
+
2. Update `AGILITY_LOCALES` environment variable
|
|
49
|
+
3. Update `lib/i18n/config.ts` with new locales
|
|
50
|
+
4. Add locale-specific content in Agility CMS
|
|
51
|
+
|
|
52
|
+
### Optimize Performance
|
|
53
|
+
1. Read [07-caching-strategies.md](./07-caching-strategies.md)
|
|
54
|
+
2. Configure revalidation time in page routes
|
|
55
|
+
3. Set up webhooks for on-demand revalidation
|
|
56
|
+
4. Monitor and debug cache behavior
|
|
57
|
+
|
|
58
|
+
## 🏗️ Project Architecture
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
/
|
|
62
|
+
├── app/
|
|
63
|
+
│ └── [locale]/
|
|
64
|
+
│ ├── [...slug]/
|
|
65
|
+
│ │ └── page.tsx # All pages route here
|
|
66
|
+
│ └── layout.tsx
|
|
67
|
+
├── components/
|
|
68
|
+
│ ├── agility-components/ # CMS modules (Hero, Blog, etc.)
|
|
69
|
+
│ │ ├── index.ts # Component registry
|
|
70
|
+
│ │ ├── RichTextArea.tsx
|
|
71
|
+
│ │ └── ...
|
|
72
|
+
│ └── agility-pages/ # Page templates
|
|
73
|
+
│ ├── index.ts # Template registry
|
|
74
|
+
│ ├── MainTemplate.tsx
|
|
75
|
+
│ └── ...
|
|
76
|
+
├── lib/
|
|
77
|
+
│ ├── cms/ # CMS helper functions
|
|
78
|
+
│ │ ├── getAgilityPage.ts # Fetch page with modules
|
|
79
|
+
│ │ ├── getContentItem.ts # Fetch single item
|
|
80
|
+
│ │ ├── getContentList.ts # Fetch list of items
|
|
81
|
+
│ │ └── ...
|
|
82
|
+
│ └── i18n/
|
|
83
|
+
│ └── config.ts # Locale configuration
|
|
84
|
+
├── proxy.ts # Routing proxy (Next.js 16+)
|
|
85
|
+
└── docs/ # This documentation
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 🔑 Key Concepts
|
|
89
|
+
|
|
90
|
+
### Modules
|
|
91
|
+
Reusable components that content editors add to pages in Agility CMS.
|
|
92
|
+
- Examples: Hero, PostListing, Testimonials, ContactForm
|
|
93
|
+
- Stored in: `components/agility-components/`
|
|
94
|
+
- Registered in: `components/agility-components/index.ts`
|
|
95
|
+
|
|
96
|
+
### Page Templates
|
|
97
|
+
Define the layout structure of pages. Each template has named content zones where modules can be placed.
|
|
98
|
+
- Stored in: `components/agility-pages/`
|
|
99
|
+
- Registered in: `components/agility-pages/index.ts`
|
|
100
|
+
|
|
101
|
+
### Content Lists
|
|
102
|
+
Collections of structured content items (posts, team members, testimonials).
|
|
103
|
+
- Defined in Agility CMS
|
|
104
|
+
- Fetched with `getContentList()`
|
|
105
|
+
- Can be filtered, sorted, and paginated
|
|
106
|
+
|
|
107
|
+
### Locales
|
|
108
|
+
Multi-language support with clean URLs.
|
|
109
|
+
- Default locale has no prefix: `/about`
|
|
110
|
+
- Other locales use prefix: `/fr/about`, `/es/about`
|
|
111
|
+
- Configured in: `lib/i18n/config.ts`
|
|
112
|
+
|
|
113
|
+
### Caching
|
|
114
|
+
Automatic caching with Next.js Data Cache.
|
|
115
|
+
- Time-based revalidation (ISR)
|
|
116
|
+
- On-demand revalidation via webhooks
|
|
117
|
+
- Cache tags for targeted updates
|
|
118
|
+
|
|
119
|
+
## 🛠️ CMS Helper Functions
|
|
120
|
+
|
|
121
|
+
| Function | Purpose | Example |
|
|
122
|
+
|----------|---------|---------|
|
|
123
|
+
| `getAgilityPage()` | Fetch complete page with modules | Page routes |
|
|
124
|
+
| `getContentItem()` | Fetch single content item | Module component |
|
|
125
|
+
| `getContentList()` | Fetch collection of items | Blog listing |
|
|
126
|
+
| `getSitemapFlat()` | Fetch flat sitemap | Navigation |
|
|
127
|
+
| `getSitemapNested()` | Fetch hierarchical sitemap | Breadcrumbs |
|
|
128
|
+
|
|
129
|
+
## 🤖 For AI Coding Assistants
|
|
130
|
+
|
|
131
|
+
This documentation enables AI tools to:
|
|
132
|
+
- ✅ Generate production-ready components following established patterns
|
|
133
|
+
- ✅ Fetch content correctly with proper TypeScript types
|
|
134
|
+
- ✅ Handle locales, caching, and routing automatically
|
|
135
|
+
- ✅ Create maintainable, type-safe code
|
|
136
|
+
- ✅ Connect to Agility CMS via MCP server (optional)
|
|
137
|
+
|
|
138
|
+
**AI-Specific Guide**: See [../.claude/agents.md](../.claude/agents.md)
|
|
139
|
+
|
|
140
|
+
## 📖 Documentation Philosophy
|
|
141
|
+
|
|
142
|
+
1. **Example-Driven** - Lots of copy-paste ready code
|
|
143
|
+
2. **Pattern-Based** - Consistent patterns throughout
|
|
144
|
+
3. **Production-Ready** - Real-world examples that work
|
|
145
|
+
4. **Type-Safe** - TypeScript types in all examples
|
|
146
|
+
|
|
147
|
+
## ✅ Success Metrics
|
|
148
|
+
|
|
149
|
+
You know the docs are working when:
|
|
150
|
+
- You can build features without checking other files
|
|
151
|
+
- Generated code follows existing patterns
|
|
152
|
+
- TypeScript types are always included
|
|
153
|
+
- CMS integration "just works"
|
|
154
|
+
|
|
155
|
+
## 🔗 Related Documentation
|
|
156
|
+
|
|
157
|
+
- **[../.claude/agents.md](../.claude/agents.md)** - Guide for AI coding assistants
|
|
158
|
+
- **[../.claude/QUICK-START.md](../.claude/QUICK-START.md)** - 5-minute quick start
|
|
159
|
+
- **[../DEVELOPMENT.md](../DEVELOPMENT.md)** - Development workflow
|
|
160
|
+
- **[../README.md](../README.md)** - Project overview
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
**Need help?** Start with [01-agility-cms-overview.md](./01-agility-cms-overview.md) or jump to [08-common-components.md](./08-common-components.md) for examples. 🚀
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { draftMode } from 'next/headers';
|
|
2
|
+
import { agilityConfig } from "@agility/nextjs"
|
|
3
|
+
import { type Locale, defaultLocale, isValidLocale, locales } from "@/lib/i18n/config"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Gets the Agility context for the current request.
|
|
7
|
+
*/
|
|
8
|
+
export const getAgilityContext = async (locale?: string) => {
|
|
9
|
+
//determine if we're in preview mode based on "draft" mode from next.js
|
|
10
|
+
const { isEnabled } = await draftMode()
|
|
11
|
+
|
|
12
|
+
const isDevelopmentMode = process.env.NODE_ENV === "development"
|
|
13
|
+
|
|
14
|
+
//determine whether it's preview or dev mode
|
|
15
|
+
const isPreview = isEnabled || isDevelopmentMode
|
|
16
|
+
|
|
17
|
+
// Validate and use the provided locale, fallback to default
|
|
18
|
+
const validatedLocale: Locale = (locale && isValidLocale(locale, locales)) ? locale : defaultLocale
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
locales: agilityConfig.locales,
|
|
22
|
+
locale: validatedLocale,
|
|
23
|
+
sitemap: agilityConfig.channelName,
|
|
24
|
+
isPreview,
|
|
25
|
+
isDevelopmentMode
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import { getAgilityPageProps } from "@agility/nextjs/node";
|
|
3
|
+
import { getAgilityContext } from "./getAgilityContext";
|
|
4
|
+
|
|
5
|
+
export interface PageProps {
|
|
6
|
+
params: Promise<{ slug: string[], locale: string }>
|
|
7
|
+
searchParams?: Promise<{ [key: string]: string | string[] | undefined }>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get a page with caching information added.
|
|
12
|
+
*/
|
|
13
|
+
export const getAgilityPage = async ({ params }: PageProps) => {
|
|
14
|
+
const awaitedParams = await params
|
|
15
|
+
const { isPreview: preview, locale } = await getAgilityContext(awaitedParams.locale)
|
|
16
|
+
|
|
17
|
+
if (!awaitedParams.slug) awaitedParams.slug = [""]
|
|
18
|
+
|
|
19
|
+
//check the last element of the slug to see if it has search params encoded (from middleware)
|
|
20
|
+
let lastSlug = awaitedParams.slug[awaitedParams.slug.length - 1]
|
|
21
|
+
let searchParams: { [key: string]: string } = {}
|
|
22
|
+
if (lastSlug && lastSlug.startsWith("~~~") && lastSlug.endsWith("~~~")) {
|
|
23
|
+
//we have search params encoded here
|
|
24
|
+
lastSlug = lastSlug.replace(/~~~+/g, "")
|
|
25
|
+
const decoded = decodeURIComponent(lastSlug)
|
|
26
|
+
const parts = decoded.split("&").map(part => part.trim())
|
|
27
|
+
|
|
28
|
+
parts.forEach(part => {
|
|
29
|
+
const kvp = part.split("=")
|
|
30
|
+
if (kvp.length === 2) {
|
|
31
|
+
searchParams[kvp[0]] = kvp[1]
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
awaitedParams.slug = awaitedParams.slug.slice(0, awaitedParams.slug.length - 1)
|
|
36
|
+
if (awaitedParams.slug.length === 0) awaitedParams.slug = [""]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
//get the page
|
|
40
|
+
const page = await getAgilityPageProps({
|
|
41
|
+
params: awaitedParams, preview, locale, apiOptions: {
|
|
42
|
+
contentLinkDepth: 0
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
page.globalData = page.globalData || {};
|
|
47
|
+
page.globalData["searchParams"] = searchParams;
|
|
48
|
+
|
|
49
|
+
return page
|
|
50
|
+
}
|
|
51
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
|
|
3
|
+
import agility from '@agility/content-fetch'
|
|
4
|
+
import { draftMode } from 'next/headers';
|
|
5
|
+
|
|
6
|
+
const getAgilitySDK = async () => {
|
|
7
|
+
//get the preview data
|
|
8
|
+
const isDevelopmentMode = process.env.NODE_ENV === "development"
|
|
9
|
+
const { isEnabled: isDraftMode } = await draftMode()
|
|
10
|
+
const isPreview = isDevelopmentMode || isDraftMode
|
|
11
|
+
|
|
12
|
+
const apiKey = isPreview ? process.env.AGILITY_API_PREVIEW_KEY : process.env.AGILITY_API_FETCH_KEY
|
|
13
|
+
|
|
14
|
+
return agility.getApi({
|
|
15
|
+
guid: process.env.AGILITY_GUID,
|
|
16
|
+
apiKey,
|
|
17
|
+
isPreview
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default getAgilitySDK
|
|
22
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ContentItemRequestParams } from "@agility/content-fetch/dist/methods/getContentItem"
|
|
2
|
+
import getAgilitySDK from "@/lib/cms/getAgilitySDK"
|
|
3
|
+
import { type ContentItem } from "@agility/content-fetch"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get a content item with caching information added.
|
|
7
|
+
*/
|
|
8
|
+
export const getContentItem = async <T>(params: ContentItemRequestParams) => {
|
|
9
|
+
const agilitySDK = await getAgilitySDK()
|
|
10
|
+
|
|
11
|
+
agilitySDK.config.fetchConfig = {
|
|
12
|
+
next: {
|
|
13
|
+
tags: [`agility-content-${params.contentID}-${params.languageCode || params.locale}`],
|
|
14
|
+
revalidate: 60,
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return await agilitySDK.getContentItem(params) as ContentItem<T>
|
|
19
|
+
}
|
|
20
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import getAgilitySDK from "@/lib/cms/getAgilitySDK"
|
|
2
|
+
import type { ContentListRequestParams } from "@agility/content-fetch/dist/methods/getContentList"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get a content list with caching information added.
|
|
6
|
+
*/
|
|
7
|
+
export const getContentList = async <T>(params: ContentListRequestParams) => {
|
|
8
|
+
const agilitySDK = await getAgilitySDK()
|
|
9
|
+
|
|
10
|
+
agilitySDK.config.fetchConfig = {
|
|
11
|
+
next: {
|
|
12
|
+
tags: [`agility-content-${params.referenceName.toLowerCase()}-${params.languageCode || params.locale}`],
|
|
13
|
+
revalidate: 60,
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return await agilitySDK.getContentList(params)
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import agility from '@agility/content-fetch'
|
|
2
|
+
|
|
3
|
+
export interface Redirection {
|
|
4
|
+
id: number
|
|
5
|
+
originUrl: string
|
|
6
|
+
destinationUrl: string
|
|
7
|
+
statusCode: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface Redirections {
|
|
11
|
+
lastAccessDate: string
|
|
12
|
+
isUpToDate: boolean
|
|
13
|
+
items: Redirection[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface RedirectionsMap {
|
|
17
|
+
lastAccessDate: string
|
|
18
|
+
isUpToDate: boolean
|
|
19
|
+
items: { [key: string]: Redirection }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface Props {
|
|
23
|
+
forceUpdate?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get the list of redirections from Agility CMS.
|
|
28
|
+
*/
|
|
29
|
+
export const getRedirections = async ({ forceUpdate = false }: Props): Promise<RedirectionsMap> => {
|
|
30
|
+
const apiKey = process.env.AGILITY_API_FETCH_KEY
|
|
31
|
+
|
|
32
|
+
const agilitySDK = agility.getApi({
|
|
33
|
+
guid: process.env.AGILITY_GUID,
|
|
34
|
+
apiKey,
|
|
35
|
+
isPreview: false
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
agilitySDK.config.fetchConfig = {
|
|
39
|
+
next: {
|
|
40
|
+
revalidate: 0,
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
let lastAccessDate: Date | null | undefined = undefined
|
|
46
|
+
const redirectionsFromServer = await agilitySDK.getUrlRedirections({ lastAccessDate }) as Redirections
|
|
47
|
+
|
|
48
|
+
if (!redirectionsFromServer.isUpToDate || forceUpdate) {
|
|
49
|
+
const redirectionsMap: RedirectionsMap = {
|
|
50
|
+
lastAccessDate: redirectionsFromServer.lastAccessDate,
|
|
51
|
+
isUpToDate: redirectionsFromServer.isUpToDate,
|
|
52
|
+
items: {}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
redirectionsFromServer.items.forEach((redirection) => {
|
|
56
|
+
let key = redirection.originUrl.toLowerCase()
|
|
57
|
+
if (key.startsWith("~/")) key = key.substring(1)
|
|
58
|
+
if (key.includes("://")) {
|
|
59
|
+
const hostIndex = key.indexOf("/", key.indexOf("://") + 3)
|
|
60
|
+
key = key.substring(hostIndex)
|
|
61
|
+
}
|
|
62
|
+
if (redirection.destinationUrl.startsWith("~/")) {
|
|
63
|
+
redirection.destinationUrl = redirection.destinationUrl.substring(1)
|
|
64
|
+
}
|
|
65
|
+
redirectionsMap.items[key] = redirection
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return redirectionsMap
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
lastAccessDate: redirectionsFromServer.lastAccessDate,
|
|
73
|
+
isUpToDate: redirectionsFromServer.isUpToDate,
|
|
74
|
+
items: {}
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Failed to fetch redirections:', error);
|
|
78
|
+
return {
|
|
79
|
+
lastAccessDate: new Date().toISOString(),
|
|
80
|
+
isUpToDate: false,
|
|
81
|
+
items: {}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import getAgilitySDK from "@/lib/cms/getAgilitySDK"
|
|
2
|
+
import { type SitemapFlatRequestParams } from "@agility/content-fetch/dist/methods/getSitemapFlat"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get the flat sitemap for the given language code, with caching information added.
|
|
6
|
+
*/
|
|
7
|
+
export const getSitemapFlat = async (params: SitemapFlatRequestParams) => {
|
|
8
|
+
const agilitySDK = await getAgilitySDK()
|
|
9
|
+
|
|
10
|
+
agilitySDK.config.fetchConfig = {
|
|
11
|
+
next: {
|
|
12
|
+
tags: [`agility-sitemap-flat-${params.languageCode || params.locale}`],
|
|
13
|
+
revalidate: 60,
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return await agilitySDK.getSitemapFlat(params)
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import getAgilitySDK from "@/lib/cms/getAgilitySDK"
|
|
2
|
+
import { type SitemapNestedRequestParams } from "@agility/content-fetch/dist/methods/getSitemapNested"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get the nested sitemap for the given language code, with caching information added.
|
|
6
|
+
*/
|
|
7
|
+
export const getSitemapNested = async (params: SitemapNestedRequestParams) => {
|
|
8
|
+
const agilitySDK = await getAgilitySDK()
|
|
9
|
+
|
|
10
|
+
agilitySDK.config.fetchConfig = {
|
|
11
|
+
next: {
|
|
12
|
+
tags: [`agility-sitemap-nested-${params.languageCode || params.locale}`],
|
|
13
|
+
revalidate: 60,
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return await agilitySDK.getSitemapNested(params)
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strongly typed environment variables utility
|
|
3
|
+
* Provides runtime validation and type safety for environment variables
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
type RequiredEnvVars = {
|
|
7
|
+
// Agility CMS
|
|
8
|
+
AGILITY_GUID: string
|
|
9
|
+
AGILITY_API_FETCH_KEY: string
|
|
10
|
+
AGILITY_API_PREVIEW_KEY: string
|
|
11
|
+
AGILITY_SECURITY_KEY: string
|
|
12
|
+
AGILITY_LOCALES: string
|
|
13
|
+
AGILITY_SITEMAP: string
|
|
14
|
+
AGILITY_FETCH_CACHE_DURATION: string
|
|
15
|
+
AGILITY_PATH_REVALIDATE_DURATION: string
|
|
16
|
+
|
|
17
|
+
// Node.js
|
|
18
|
+
NODE_ENV: 'development' | 'production' | 'test'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type OptionalEnvVars = {
|
|
22
|
+
// Add any optional environment variables here
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type EnvVars = RequiredEnvVars & Partial<OptionalEnvVars>
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get a required environment variable with runtime validation
|
|
29
|
+
*/
|
|
30
|
+
function getRequiredEnvVar<K extends keyof RequiredEnvVars>(key: K): RequiredEnvVars[K] {
|
|
31
|
+
const value = process.env[key]
|
|
32
|
+
|
|
33
|
+
if (!value) {
|
|
34
|
+
throw new Error(`Missing required environment variable: ${key}`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Special validation for NODE_ENV
|
|
38
|
+
if (key === 'NODE_ENV' && !['development', 'production', 'test'].includes(value)) {
|
|
39
|
+
throw new Error(`Invalid NODE_ENV value: ${value}. Must be 'development', 'production', or 'test'`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return value as RequiredEnvVars[K]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get an optional environment variable
|
|
47
|
+
*/
|
|
48
|
+
function getOptionalEnvVar<K extends keyof OptionalEnvVars>(key: K, defaultValue?: OptionalEnvVars[K]): OptionalEnvVars[K] | undefined {
|
|
49
|
+
return process.env[key] as OptionalEnvVars[K] || defaultValue
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get all environment variables with validation
|
|
54
|
+
*/
|
|
55
|
+
function getAllEnvVars(): EnvVars {
|
|
56
|
+
return {
|
|
57
|
+
// Agility CMS
|
|
58
|
+
AGILITY_GUID: getRequiredEnvVar('AGILITY_GUID'),
|
|
59
|
+
AGILITY_API_FETCH_KEY: getRequiredEnvVar('AGILITY_API_FETCH_KEY'),
|
|
60
|
+
AGILITY_API_PREVIEW_KEY: getRequiredEnvVar('AGILITY_API_PREVIEW_KEY'),
|
|
61
|
+
AGILITY_SECURITY_KEY: getRequiredEnvVar('AGILITY_SECURITY_KEY'),
|
|
62
|
+
AGILITY_LOCALES: getRequiredEnvVar('AGILITY_LOCALES'),
|
|
63
|
+
AGILITY_SITEMAP: getRequiredEnvVar('AGILITY_SITEMAP'),
|
|
64
|
+
AGILITY_FETCH_CACHE_DURATION: getRequiredEnvVar('AGILITY_FETCH_CACHE_DURATION'),
|
|
65
|
+
AGILITY_PATH_REVALIDATE_DURATION: getRequiredEnvVar('AGILITY_PATH_REVALIDATE_DURATION'),
|
|
66
|
+
|
|
67
|
+
// Node.js
|
|
68
|
+
NODE_ENV: getRequiredEnvVar('NODE_ENV'),
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Typed environment variables object
|
|
74
|
+
* Use this instead of process.env for type safety
|
|
75
|
+
*/
|
|
76
|
+
export const env = {
|
|
77
|
+
get: getRequiredEnvVar,
|
|
78
|
+
getOptional: getOptionalEnvVar,
|
|
79
|
+
getAll: getAllEnvVars,
|
|
80
|
+
|
|
81
|
+
// Direct access to commonly used variables
|
|
82
|
+
get AGILITY_GUID() { return getRequiredEnvVar('AGILITY_GUID') },
|
|
83
|
+
get AGILITY_API_FETCH_KEY() { return getRequiredEnvVar('AGILITY_API_FETCH_KEY') },
|
|
84
|
+
get AGILITY_API_PREVIEW_KEY() { return getRequiredEnvVar('AGILITY_API_PREVIEW_KEY') },
|
|
85
|
+
get AGILITY_SECURITY_KEY() { return getRequiredEnvVar('AGILITY_SECURITY_KEY') },
|
|
86
|
+
get AGILITY_LOCALES() { return getRequiredEnvVar('AGILITY_LOCALES') },
|
|
87
|
+
get AGILITY_SITEMAP() { return getRequiredEnvVar('AGILITY_SITEMAP') },
|
|
88
|
+
get AGILITY_FETCH_CACHE_DURATION() { return getRequiredEnvVar('AGILITY_FETCH_CACHE_DURATION') },
|
|
89
|
+
get AGILITY_PATH_REVALIDATE_DURATION() { return getRequiredEnvVar('AGILITY_PATH_REVALIDATE_DURATION') },
|
|
90
|
+
get NODE_ENV() { return getRequiredEnvVar('NODE_ENV') },
|
|
91
|
+
|
|
92
|
+
// Computed values
|
|
93
|
+
get isDevelopment() { return getRequiredEnvVar('NODE_ENV') === 'development' },
|
|
94
|
+
get isProduction() { return getRequiredEnvVar('NODE_ENV') === 'production' },
|
|
95
|
+
get isTest() { return getRequiredEnvVar('NODE_ENV') === 'test' },
|
|
96
|
+
} as const
|
|
97
|
+
|
|
98
|
+
export type { RequiredEnvVars, OptionalEnvVars, EnvVars }
|
|
99
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Get locales from environment variable, fallback to default
|
|
2
|
+
const envLocales = process.env.AGILITY_LOCALES?.split(',') || ['en-us']
|
|
3
|
+
export const locales = envLocales as readonly string[]
|
|
4
|
+
export const defaultLocale = envLocales[0] || 'en-us'
|
|
5
|
+
export type Locale = typeof locales[number]
|
|
6
|
+
|
|
7
|
+
export function isValidLocale(locale: string, locales: readonly string[]): locale is Locale {
|
|
8
|
+
return locales.includes(locale as Locale)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getLocaleFromPathname(pathname: string, locales: readonly string[]): Locale | null {
|
|
12
|
+
const segments = pathname.split('/')
|
|
13
|
+
const potentialLocale = segments[1]
|
|
14
|
+
|
|
15
|
+
if (isValidLocale(potentialLocale, locales)) {
|
|
16
|
+
return potentialLocale
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function removeLocaleFromPathname(pathname: string, locale: Locale): string {
|
|
23
|
+
if (pathname.startsWith(`/${locale}`)) {
|
|
24
|
+
return pathname.slice(`/${locale}`.length) || '/'
|
|
25
|
+
}
|
|
26
|
+
return pathname
|
|
27
|
+
}
|
|
28
|
+
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server'
|
|
2
|
+
import type { NextRequest } from 'next/server'
|
|
3
|
+
import { defaultLocale, locales, isValidLocale, getLocaleFromPathname, removeLocaleFromPathname } from './lib/i18n/config'
|
|
4
|
+
|
|
5
|
+
export async function proxy(request: NextRequest) {
|
|
6
|
+
/*****************************
|
|
7
|
+
* *** AGILITY PROXY ***
|
|
8
|
+
* 1: Check if this is a preview request,
|
|
9
|
+
* 2: Check if we are exiting preview
|
|
10
|
+
* 3: Check if this is a direct to a dynamic page
|
|
11
|
+
* based on a content id
|
|
12
|
+
*******************************/
|
|
13
|
+
|
|
14
|
+
let pathname = request.nextUrl.pathname
|
|
15
|
+
const previewQ = request.nextUrl.searchParams.get("AgilityPreview")
|
|
16
|
+
let contentIDStr = request.nextUrl.searchParams.get("ContentID") as string || ""
|
|
17
|
+
|
|
18
|
+
const ext = request.nextUrl.pathname.includes(".") ? request.nextUrl.pathname.split('.').pop() : null
|
|
19
|
+
|
|
20
|
+
if (request.nextUrl.searchParams.has("agilitypreviewkey")) {
|
|
21
|
+
//*** this is a preview request ***
|
|
22
|
+
const agilityPreviewKey = request.nextUrl.searchParams.get("agilitypreviewkey") || ""
|
|
23
|
+
const locale = request.nextUrl.searchParams.get("lang")
|
|
24
|
+
const slug = request.nextUrl.pathname
|
|
25
|
+
let redirectUrl = `${request.nextUrl.protocol}//${request.nextUrl.host}/api/preview?locale=${locale}&ContentID=${contentIDStr}&slug=${encodeURIComponent(slug)}&agilitypreviewkey=${encodeURIComponent(agilityPreviewKey)}`
|
|
26
|
+
return NextResponse.redirect(redirectUrl)
|
|
27
|
+
} else if (previewQ === "0") {
|
|
28
|
+
//*** exit preview
|
|
29
|
+
const locale = request.nextUrl.searchParams.get("lang")
|
|
30
|
+
const slug = request.nextUrl.pathname
|
|
31
|
+
let redirectUrl = `${request.nextUrl.protocol}//${request.nextUrl.host}/api/preview/exit?locale=${locale}&ContentID=${contentIDStr}&slug=${encodeURIComponent(slug)}`
|
|
32
|
+
return NextResponse.redirect(redirectUrl)
|
|
33
|
+
} else if (contentIDStr) {
|
|
34
|
+
const contentID = parseInt(contentIDStr)
|
|
35
|
+
if (!isNaN(contentID) && contentID > 0) {
|
|
36
|
+
//*** this is a dynamic page request ***
|
|
37
|
+
let dynredirectUrl = `${request.nextUrl.protocol}//${request.nextUrl.host}/api/dynamic-redirect?ContentID=${contentID}`
|
|
38
|
+
return NextResponse.rewrite(dynredirectUrl)
|
|
39
|
+
}
|
|
40
|
+
} else if ((!ext || ext.length === 0)) {
|
|
41
|
+
/**************************************
|
|
42
|
+
* SPECIAL CASE FOR lang= QUERY PARAM *
|
|
43
|
+
**************************************/
|
|
44
|
+
const langParam = request.nextUrl.searchParams.get("lang")
|
|
45
|
+
const currentLocale = getLocaleFromPathname(pathname, locales) || defaultLocale
|
|
46
|
+
|
|
47
|
+
if (langParam && isValidLocale(langParam, locales) && langParam !== currentLocale) {
|
|
48
|
+
if (langParam === defaultLocale) {
|
|
49
|
+
const redirectUrl = new URL(request.nextUrl.toString())
|
|
50
|
+
redirectUrl.pathname = removeLocaleFromPathname(pathname, currentLocale)
|
|
51
|
+
redirectUrl.searchParams.delete("lang")
|
|
52
|
+
return NextResponse.redirect(redirectUrl)
|
|
53
|
+
} else {
|
|
54
|
+
const redirectUrl = new URL(request.nextUrl.toString())
|
|
55
|
+
const pathnameWithoutLocale = removeLocaleFromPathname(pathname, currentLocale)
|
|
56
|
+
redirectUrl.pathname = `/${langParam}${pathnameWithoutLocale}`
|
|
57
|
+
redirectUrl.searchParams.delete("lang")
|
|
58
|
+
return NextResponse.redirect(redirectUrl)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/************************
|
|
63
|
+
* HANDLE SEARCH PARAMS *
|
|
64
|
+
************************/
|
|
65
|
+
let searchParams = request.nextUrl.searchParams.toString()
|
|
66
|
+
let hasSearchParams = searchParams && searchParams.length > 0
|
|
67
|
+
if (!hasSearchParams) {
|
|
68
|
+
searchParams = ""
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (searchParams && searchParams.length > 0) {
|
|
72
|
+
const searchParamPortion = `~~~${encodeURIComponent(searchParams)}~~~`
|
|
73
|
+
pathname = pathname.endsWith("/") ? `${pathname}${searchParamPortion}` : `${pathname}/${searchParamPortion}`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/************************
|
|
77
|
+
* LOCALE BASED ROUTING *
|
|
78
|
+
************************/
|
|
79
|
+
const hasLocalePrefix = locales.some(locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`)
|
|
80
|
+
const isStaticFile = pathname.includes('.') || pathname.startsWith('/_next')
|
|
81
|
+
|
|
82
|
+
const baseUrl = request.nextUrl.origin
|
|
83
|
+
|
|
84
|
+
if (!hasLocalePrefix && !isStaticFile) {
|
|
85
|
+
const localeBasedUrl = new URL(`/${defaultLocale}${pathname}`, baseUrl)
|
|
86
|
+
return NextResponse.rewrite(localeBasedUrl)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (hasSearchParams) {
|
|
90
|
+
const searchParamUrl = new URL(pathname, baseUrl)
|
|
91
|
+
return NextResponse.rewrite(searchParamUrl)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const config = {
|
|
97
|
+
matcher: [
|
|
98
|
+
'/((?!api|assets|_next/static|_next/image|favicon.ico).*)',
|
|
99
|
+
],
|
|
100
|
+
}
|
|
101
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"moduleResolution": "node",
|
|
14
|
+
"declaration": true,
|
|
15
|
+
"declarationMap": true,
|
|
16
|
+
"sourceMap": true
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"],
|
|
19
|
+
"exclude": ["node_modules", "dist", "templates"]
|
|
20
|
+
}
|
|
21
|
+
|