@sitecore-content-sdk/nextjs 2.1.1 → 2.2.0-canary.20260525085832

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.
@@ -133,15 +133,9 @@ exports.getPreviewCookies = getPreviewCookies;
133
133
  * @returns {string[]} list of required parameters for validation
134
134
  */
135
135
  const getRequiredEditingParamsList = (mode) => {
136
- const editingRequiredParams = ['sc_site', 'sc_itemid', 'sc_lang', 'route', 'mode'];
137
- const componentRequiredParams = [
138
- 'sc_site',
139
- 'sc_itemid',
140
- 'sc_renderingId',
141
- 'sc_uid',
142
- 'sc_lang',
143
- 'mode',
144
- ];
136
+ const baseRequiredParams = ['sc_site', 'sc_itemid', 'sc_lang'];
137
+ const editingRequiredParams = [...baseRequiredParams, 'route', 'mode'];
138
+ const componentRequiredParams = [...baseRequiredParams, 'sc_uid', 'mode'];
145
139
  return (0, editing_1.isDesignLibraryMode)(mode) ? componentRequiredParams : editingRequiredParams;
146
140
  };
147
141
  exports.getRequiredEditingParamsList = getRequiredEditingParamsList;
@@ -212,7 +206,9 @@ const getEditingRequestHtml = async (requestUrl, propagatedQsParams, propagatedH
212
206
  // We need to handle not found error provided by Vercel
213
207
  // for `fallback: false` pages
214
208
  // Or preview content is not found or access is denied
215
- if (err.response.status === 404 || err.response.status === 403) {
209
+ if (err.response.status === 404 ||
210
+ err.response.status === 403 ||
211
+ err.response.status === 500) {
216
212
  return err.response;
217
213
  }
218
214
  throw err;
@@ -7,7 +7,6 @@ exports.RedirectsProxy = void 0;
7
7
  const site_1 = require("@sitecore-content-sdk/content/site");
8
8
  const tools_1 = require("@sitecore-content-sdk/core/tools");
9
9
  const server_1 = require("next/server");
10
- const regex_parser_1 = __importDefault(require("regex-parser"));
11
10
  const proxy_1 = require("./proxy");
12
11
  const debug_1 = __importDefault(require("../debug"));
13
12
  const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
@@ -126,9 +125,9 @@ class RedirectsProxy extends proxy_1.ProxyBase {
126
125
  existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
127
126
  const reqUrl = this.normalizeUrl(req.nextUrl.clone());
128
127
  // Apply regex replacements to the target URL if the pattern is a regex
129
- const matched = reqUrl.pathname
130
- .replace(/\/*$/gi, '')
131
- .match((0, regex_parser_1.default)(existsRedirect.pattern));
128
+ const sourcePath = existsRedirect.matchedPath || reqUrl.pathname;
129
+ const pathForCaptureMatch = sourcePath.replace(/\/*$/gi, '') || '/';
130
+ const matched = pathForCaptureMatch.match(this.getRedirectPatternRegex(existsRedirect.pattern));
132
131
  if (matched) {
133
132
  existsRedirect.target = existsRedirect.target.replace(/\$(\d+)/g, (_, index) => {
134
133
  return matched[parseInt(index, 10)] || '';
@@ -199,6 +198,7 @@ class RedirectsProxy extends proxy_1.ProxyBase {
199
198
  * Method returns RedirectInfo when matches
200
199
  * @param {NextRequest} req request
201
200
  * @param {string} siteName site name
201
+ * @param {string} requestLocale locale used for locale redirect matching
202
202
  * @returns Promise<RedirectInfo | undefined>
203
203
  * @private
204
204
  */
@@ -253,28 +253,28 @@ class RedirectsProxy extends proxy_1.ProxyBase {
253
253
  (0, tools_1.areURLSearchParamsEqual)(new URLSearchParams(patternQS), new URLSearchParams(incomingQS))));
254
254
  }
255
255
  // process regex rules
256
- // Modify the redirect pattern to ignore the language prefix in the path
257
- // And escapes non-special "?" characters in a string or regex.
258
- redirect.pattern = (0, tools_1.escapeNonSpecialQuestionMarks)('^' + redirect.pattern.replace(new RegExp(`^[^]?/${urlLocale}/`, 'gi'), '') // ensure function thinks input is regex
259
- );
260
- // Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
261
- redirect.pattern = `/^\/${redirect.pattern
262
- .replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
263
- .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
264
- .replace(/^\^|\$$/g, '') // Further cleans up anchors
265
- .replace(/\$\/g$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
266
- // Redirect pattern matches the full incoming URL with query string present
267
- matchedQueryString = [
268
- (0, regex_parser_1.default)(redirect.pattern).test(`/${localePath}${incomingQS}`),
269
- (0, regex_parser_1.default)(redirect.pattern).test(`${normalizedPath}${incomingQS}`),
270
- ].some(Boolean)
271
- ? incomingQS
256
+ const regex = this.getRedirectPatternRegex(redirect.pattern);
257
+ const testRegex = (value) => {
258
+ regex.lastIndex = 0;
259
+ return regex.test(value);
260
+ };
261
+ const localeStrippedIncoming = this.getLocaleStrippedPath(incomingURL, urlLocale);
262
+ const localeStrippedNormalized = this.getLocaleStrippedPath(normalizedPath, urlLocale);
263
+ const pathCandidates = [
264
+ incomingURL,
265
+ normalizedPath,
266
+ localeStrippedIncoming,
267
+ localeStrippedNormalized,
268
+ ].filter((candidate, index, array) => array.indexOf(candidate) === index);
269
+ const matchedPath = pathCandidates.find((candidate) => testRegex(candidate));
270
+ const matchedPathWithQuery = incomingQS
271
+ ? pathCandidates.find((candidate) => testRegex(`${candidate}${incomingQS}`))
272
272
  : undefined;
273
+ matchedQueryString = matchedPathWithQuery ? incomingQS : undefined;
273
274
  // Save the matched query string (if found) into the redirect object
274
275
  redirect.matchedQueryString = matchedQueryString || '';
275
- return (!!((0, regex_parser_1.default)(redirect.pattern).test(`/${urlLocale}${incomingURL}`) ||
276
- (0, regex_parser_1.default)(redirect.pattern).test(incomingURL) ||
277
- matchedQueryString) &&
276
+ redirect.matchedPath = matchedPath || matchedPathWithQuery || '';
277
+ return (!!(matchedPath || matchedQueryString) &&
278
278
  (redirect.locale ? redirect.locale.toLowerCase() === urlLocale.toLowerCase() : true));
279
279
  })
280
280
  : undefined;
@@ -420,5 +420,37 @@ class RedirectsProxy extends proxy_1.ProxyBase {
420
420
  }
421
421
  return redirect;
422
422
  }
423
+ /**
424
+ * Converts a redirect pattern string into a RegExp.
425
+ * Supports both JS literal form (`/pattern/i`) and plain regex source (`^/path$`).
426
+ * @param {string} pattern redirect pattern from redirect map
427
+ * @returns {RegExp} normalized regex instance
428
+ * @private
429
+ */
430
+ getRedirectPatternRegex(pattern) {
431
+ const normalizedPattern = (0, tools_1.escapeNonSpecialQuestionMarks)(pattern);
432
+ const literalMatch = normalizedPattern.match(/^\/(.+)\/([a-z]*)$/i);
433
+ if (literalMatch) {
434
+ const [, source, flags] = literalMatch;
435
+ const safeFlags = flags || 'i';
436
+ return new RegExp(source, safeFlags);
437
+ }
438
+ return new RegExp(normalizedPattern, 'i');
439
+ }
440
+ /**
441
+ * Strips locale prefix from path when present.
442
+ * @param {string} path incoming request path
443
+ * @param {string} urlLocale locale from Next.js URL
444
+ * @returns {string} locale-stripped path
445
+ * @private
446
+ */
447
+ getLocaleStrippedPath(path, urlLocale) {
448
+ if (!urlLocale) {
449
+ return path;
450
+ }
451
+ const localePrefixRegex = new RegExp(`^/${urlLocale}(?=/|$)`, 'i');
452
+ const strippedPath = path.replace(localePrefixRegex, '') || '/';
453
+ return strippedPath.startsWith('/') ? strippedPath : `/${strippedPath}`;
454
+ }
423
455
  }
424
456
  exports.RedirectsProxy = RedirectsProxy;
@@ -33,11 +33,39 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.generateMap = exports.defaultClientMapTemplate = void 0;
36
+ exports.generateMap = exports.defaultClientMapTemplate = exports.defaultServerMapTemplate = void 0;
37
37
  const tools_1 = require("@sitecore-content-sdk/content/tools");
38
38
  const path = __importStar(require("path"));
39
39
  const fs = __importStar(require("fs"));
40
40
  const utils_1 = require("./templating/utils");
41
+ const DEFAULT_HEADER_COMMENT = "Below are built-in components that are available in the app, it's recommended to keep them as is";
42
+ const APP_ROUTER_BUILTIN_IMPORTS = `
43
+ import { BYOCServerWrapper, NextjsContentSdkComponent, FEaaSServerWrapper } from '@sitecore-content-sdk/nextjs';
44
+ import { Form } from '@sitecore-content-sdk/nextjs';
45
+ `;
46
+ const APP_ROUTER_BUILTIN_ENTRIES = [
47
+ `['BYOCWrapper', BYOCServerWrapper]`,
48
+ `['FEaaSWrapper', FEaaSServerWrapper]`,
49
+ `['Form', { ...Form, componentType: 'client' }]`,
50
+ ];
51
+ const PAGES_ROUTER_BUILTIN_IMPORTS = `
52
+ import { BYOCWrapper, NextjsContentSdkComponent, FEaaSWrapper } from '@sitecore-content-sdk/nextjs';
53
+ import { Form } from '@sitecore-content-sdk/nextjs';
54
+ `;
55
+ const PAGES_ROUTER_BUILTIN_ENTRIES = [
56
+ `['BYOCWrapper', BYOCWrapper]`,
57
+ `['FEaaSWrapper', FEaaSWrapper]`,
58
+ `['Form', Form]`,
59
+ ];
60
+ const CLIENT_MAP_BUILTIN_IMPORTS = `
61
+ import { BYOCClientWrapper, NextjsContentSdkComponent, FEaaSClientWrapper } from '@sitecore-content-sdk/nextjs';
62
+ import { Form } from '@sitecore-content-sdk/nextjs';
63
+ `;
64
+ const CLIENT_MAP_BUILTIN_ENTRIES = [
65
+ `['BYOCWrapper', BYOCClientWrapper]`,
66
+ `['FEaaSWrapper', FEaaSClientWrapper]`,
67
+ `['Form', Form]`,
68
+ ];
41
69
  // Common builder for Next.js component map content
42
70
  const prepareComponentsForMap = (components, opts) => {
43
71
  const groups = new Map();
@@ -113,21 +141,16 @@ const prepareComponentsForMap = (components, opts) => {
113
141
  }
114
142
  return entries;
115
143
  };
116
- const buildNextjsMapContent = (entries, componentImports, options = {}) => {
117
- const isAppRouter = (0, utils_1.detectRouterType)() === 'app';
118
- const { headerComment = "Below are built-in components that are available in the app, it's recommended to keep them as is", isClientMap = false, } = options;
144
+ /**
145
+ * Distinguishes the simple 2-arity ComponentMapTemplate from the 3-arity EnhancedComponentMapTemplate.
146
+ * @param {ComponentMapTemplate | EnhancedComponentMapTemplate} fn The template function to check.
147
+ * @internal
148
+ */
149
+ const isComponentMapTemplate = (fn) => fn.length === 2;
150
+ const buildNextjsMapContent = (entries, componentImports, options) => {
151
+ const { headerComment = DEFAULT_HEADER_COMMENT, isClientMap = false, builtInImports, builtInMapEntries, } = options;
119
152
  const wildcardImports = [];
120
153
  const namedImports = [];
121
- const builtInImports = options.builtInImports ||
122
- `
123
- import { BYOCWrapper, NextjsContentSdkComponent, FEaaSWrapper } from '@sitecore-content-sdk/nextjs';
124
- import { Form } from '@sitecore-content-sdk/nextjs';
125
- `;
126
- const builtInMapEntries = options.builtInMapEntries || [
127
- `['BYOCWrapper', BYOCWrapper]`,
128
- `['FEaaSWrapper', FEaaSWrapper]`,
129
- `['Form', ${isAppRouter ? '{ ...Form, componentType: \'client\' }' : 'Form'}]`,
130
- ];
131
154
  // Add per-entry imports
132
155
  entries.forEach((e) => wildcardImports.push(...e.imports));
133
156
  // Handle package imports
@@ -145,8 +168,8 @@ import { Form } from '@sitecore-content-sdk/nextjs';
145
168
  ...namedImports,
146
169
  ].filter(Boolean);
147
170
  const importsSection = importLines.length ? `\n${importLines.join('\n')}` : '';
148
- // Build entry lines (package named imports are appended below)
149
- const componentMapEntries = builtInMapEntries;
171
+ // Clone to avoid mutating the caller's array
172
+ const componentMapEntries = structuredClone(builtInMapEntries);
150
173
  for (const e of entries) {
151
174
  const value = !isClientMap && e.annotateClient
152
175
  ? `{ ${e.valueExpr}, componentType: 'client' }`
@@ -179,26 +202,31 @@ ${componentMapEntries
179
202
  export default componentMap;
180
203
  `;
181
204
  };
182
- // default client template
205
+ // Default App Router (server) component map template
206
+ const defaultServerMapTemplate = (components, componentImports, ctx) => {
207
+ var _a, _b;
208
+ const entries = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.entries) !== null && _a !== void 0 ? _a : prepareComponentsForMap(components, {
209
+ includeVariants: (_b = ctx === null || ctx === void 0 ? void 0 : ctx.includeVariants) !== null && _b !== void 0 ? _b : true,
210
+ });
211
+ return buildNextjsMapContent(entries, componentImports, {
212
+ headerComment: DEFAULT_HEADER_COMMENT,
213
+ isClientMap: false,
214
+ builtInImports: APP_ROUTER_BUILTIN_IMPORTS,
215
+ builtInMapEntries: APP_ROUTER_BUILTIN_ENTRIES,
216
+ });
217
+ };
218
+ exports.defaultServerMapTemplate = defaultServerMapTemplate;
219
+ // Default client-safe component map template for App Router
183
220
  const defaultClientMapTemplate = (components, componentImports, ctx) => {
184
221
  var _a, _b;
185
- const builtInImports = `
186
- import { BYOCClientWrapper, NextjsContentSdkComponent, FEaaSClientWrapper } from '@sitecore-content-sdk/nextjs';
187
- import { Form } from '@sitecore-content-sdk/nextjs';
188
- `;
189
- const builtInMapEntries = [
190
- `['BYOCWrapper', BYOCClientWrapper]`,
191
- `['FEaaSWrapper', FEaaSClientWrapper]`,
192
- `['Form', Form]`,
193
- ];
194
222
  const entries = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.entries) !== null && _a !== void 0 ? _a : prepareComponentsForMap(components, {
195
223
  includeVariants: (_b = ctx === null || ctx === void 0 ? void 0 : ctx.includeVariants) !== null && _b !== void 0 ? _b : true,
196
224
  });
197
225
  return buildNextjsMapContent(entries, componentImports, {
198
226
  headerComment: 'Client-safe component map for App Router',
199
227
  isClientMap: true,
200
- builtInImports,
201
- builtInMapEntries,
228
+ builtInImports: CLIENT_MAP_BUILTIN_IMPORTS,
229
+ builtInMapEntries: CLIENT_MAP_BUILTIN_ENTRIES,
202
230
  });
203
231
  };
204
232
  exports.defaultClientMapTemplate = defaultClientMapTemplate;
@@ -219,116 +247,93 @@ const collectComponents = (opts) => {
219
247
  /**
220
248
  * Generate and write componentMap.ts files based on provided params.
221
249
  *
222
- * When clientComponentMap is true, generates:
250
+ * Pages Router:
251
+ * - component-map.ts : Single component map with Pages Router wrappers
252
+ *
253
+ * App Router (clientComponentMap=true or undefined):
223
254
  * - component-map.ts : Full component map with all components (server, client, universal)
224
- * - component-map.client.ts : Client-safe map with only client + universal components
255
+ * - component-map.client.ts : Client-safe map with client + universal components
225
256
  *
226
- * When clientComponentMap is false, generates:
227
- * - component-map.ts : Single component map (traditional behavior)
257
+ * App Router (clientComponentMap=false):
258
+ * - component-map.ts : Full component map with all components (server, client, universal)
259
+ * - component-map.client.ts : Client-safe map with built-in components only (no user components)
228
260
  *
229
- * When includeVariants is true (in either mode):
261
+ * When includeVariants is true:
230
262
  * - Includes component **variants** in the generated map(s) alongside base components
231
263
  * - Preserves the same client/server filtering rules (variants obey clientComponentMap filtering)
232
264
  * - Variant entries are emitted using the same naming/keys convention as their base components
233
265
  *
234
266
  * Template Customization:
235
267
  * - mapTemplate: Custom template for main component map (works for both single and dual map modes)
236
- * - clientMapTemplate: Custom template for client component map (only used when clientComponentMap is true)
268
+ * - clientMapTemplate: Custom template for client component map (App Router only)
237
269
  * @param {GenerateMapArgs} params - The parameters for the generateMap function.
238
270
  * @public
239
271
  */
240
272
  const generateMap = ({ paths, destination = '.sitecore', exclude, componentImports, mapTemplate, clientMapTemplate, clientComponentMap, includeVariants = true, }) => {
241
- const isAppRouter = (0, utils_1.detectRouterType)() === 'app';
242
- const shouldGenerateClientMap = clientComponentMap !== null && clientComponentMap !== void 0 ? clientComponentMap : isAppRouter;
243
- if (shouldGenerateClientMap) {
244
- // App Router case, main map
245
- const getComponents = collectComponents({ paths, exclude, includeVariants, filter: 'all' });
246
- let mainContent;
247
- if (mapTemplate) {
248
- mainContent = mapTemplate(getComponents.raw, componentImports, {
249
- entries: getComponents.entries,
273
+ const routerType = (0, utils_1.detectRouterType)();
274
+ const allComponents = collectComponents({ paths, exclude, includeVariants, filter: 'all' });
275
+ if (routerType === utils_1.ROUTER_TYPE.PAGES) {
276
+ const content = mapTemplate
277
+ ? mapTemplate(allComponents.raw, componentImports, {
278
+ entries: allComponents.entries,
250
279
  includeVariants,
251
280
  isClientMap: false,
252
- });
253
- }
254
- else {
255
- // default app router server map
256
- const builtInImports = `
257
- import { BYOCServerWrapper, NextjsContentSdkComponent, FEaaSServerWrapper } from '@sitecore-content-sdk/nextjs';
258
- import { Form } from '@sitecore-content-sdk/nextjs';
259
- `;
260
- const builtInMapEntries = [
261
- `['BYOCWrapper', BYOCServerWrapper]`,
262
- `['FEaaSWrapper', FEaaSServerWrapper]`,
263
- `['Form', { ...Form, componentType: 'client' }]`,
264
- ];
265
- mainContent = buildNextjsMapContent(getComponents.entries, componentImports, {
266
- headerComment: "Below are built-in components that are available in the app, it's recommended to keep them as is",
281
+ })
282
+ : buildNextjsMapContent(allComponents.entries, componentImports, {
267
283
  isClientMap: false,
268
- builtInImports,
269
- builtInMapEntries,
284
+ builtInImports: PAGES_ROUTER_BUILTIN_IMPORTS,
285
+ builtInMapEntries: PAGES_ROUTER_BUILTIN_ENTRIES,
270
286
  });
287
+ try {
288
+ fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.ts'), content, 'utf8');
271
289
  }
272
- fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.ts'), mainContent, 'utf8');
273
- // App Router, client map
274
- const clientComponents = collectComponents({
275
- paths,
276
- exclude,
277
- includeVariants,
278
- filter: 'client',
279
- });
280
- const clientTemplate = clientMapTemplate || exports.defaultClientMapTemplate;
281
- let clientContent;
282
- if (clientTemplate.length >= 2) {
283
- clientContent = clientTemplate(clientComponents.raw, componentImports);
284
- }
285
- else {
286
- clientContent = clientTemplate(clientComponents.raw, componentImports, {
287
- entries: clientComponents.entries,
288
- includeVariants,
289
- isClientMap: true,
290
- });
290
+ catch (error) {
291
+ console.error(`Component Map generation failed. Error writing to file ${destination}:`, error);
292
+ throw error;
291
293
  }
292
- fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.client.ts'), clientContent, 'utf8');
294
+ return;
293
295
  }
294
- else {
295
- // Either in pages/app router or clientComponentMap = false
296
- const components = collectComponents({
297
- paths,
298
- exclude,
296
+ const mainContent = mapTemplate
297
+ ? mapTemplate(allComponents.raw, componentImports, {
298
+ entries: allComponents.entries,
299
+ includeVariants,
300
+ isClientMap: false,
301
+ })
302
+ : (0, exports.defaultServerMapTemplate)(allComponents.raw, componentImports, {
303
+ entries: allComponents.entries,
299
304
  includeVariants,
300
- filter: 'all',
301
- }).entries;
302
- const content = buildNextjsMapContent(components, componentImports, {
303
- headerComment: "Below are built-in components that are available in the app, it's recommended to keep them as is",
304
305
  isClientMap: false,
305
306
  });
306
- fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.ts'), content, 'utf8');
307
- // For App Router compatibility, always generate client map file even when clientComponentMap is false
308
- // When clientComponentMap is false, only include built-in components (no custom client components)
309
- if (shouldGenerateClientMap || isAppRouter) {
310
- const clientMapTemplateToUse = clientMapTemplate || exports.defaultClientMapTemplate;
311
- const components = collectComponents({ paths: [], includeVariants, filter: 'all' });
312
- let clientMapContent;
313
- if (clientMapTemplateToUse.length >= 2) {
314
- clientMapContent = clientMapTemplateToUse([], componentImports);
315
- }
316
- else {
317
- clientMapContent = clientMapTemplateToUse([], componentImports, {
318
- entries: components.entries,
319
- includeVariants,
320
- isClientMap: true,
321
- });
322
- }
323
- const clientMapFile = path.join(process.cwd(), destination, 'component-map.client.ts');
324
- try {
325
- fs.writeFileSync(clientMapFile, clientMapContent, { encoding: 'utf8' });
326
- }
327
- catch (error) {
328
- console.error(`Client Component Map generation failed. Error writing to file ${destination}:`, error);
329
- throw error;
330
- }
331
- }
307
+ try {
308
+ fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.ts'), mainContent, 'utf8');
309
+ }
310
+ catch (error) {
311
+ console.error(`Main Component Map generation failed. Error writing to file ${destination}:`, error);
312
+ throw error;
313
+ }
314
+ // clientComponentMap=true -> include user client+universal components
315
+ // clientComponentMap=undefined -> include user client+universal components
316
+ // clientComponentMap=false -> built-ins only
317
+ const shouldGenerateClientMap = clientComponentMap !== null && clientComponentMap !== void 0 ? clientComponentMap : true;
318
+ const clientComponents = shouldGenerateClientMap
319
+ ? collectComponents({ paths, exclude, includeVariants, filter: 'client' })
320
+ : { raw: [], entries: [] };
321
+ const clientTemplate = clientMapTemplate || exports.defaultClientMapTemplate;
322
+ let clientContent;
323
+ if (isComponentMapTemplate(clientTemplate))
324
+ clientContent = clientTemplate(clientComponents.raw, componentImports);
325
+ else
326
+ clientContent = clientTemplate(clientComponents.raw, componentImports, {
327
+ entries: clientComponents.entries,
328
+ includeVariants,
329
+ isClientMap: true,
330
+ });
331
+ try {
332
+ fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.client.ts'), clientContent, 'utf8');
333
+ }
334
+ catch (error) {
335
+ console.error(`Client Component Map generation failed. Error writing to file ${destination}:`, error);
336
+ throw error;
332
337
  }
333
338
  };
334
339
  exports.generateMap = generateMap;
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ROUTER_TYPE = void 0;
6
7
  exports.detectRouterType = detectRouterType;
7
8
  exports.detectComponentType = detectComponentType;
8
9
  exports.getComponentListWithTypes = getComponentListWithTypes;
@@ -12,6 +13,17 @@ exports.nextjsDefaultMapTemplate = nextjsDefaultMapTemplate;
12
13
  const node_tools_1 = require("@sitecore-content-sdk/content/node-tools");
13
14
  const typescript_1 = __importDefault(require("typescript"));
14
15
  const fs_1 = __importDefault(require("fs"));
16
+ /**
17
+ * Constants for Next.js router types. Used for consistent detection and comparison throughout the codebase.
18
+ * Values are based on Next.js conventions:
19
+ * - 'app' for App Router (src/app or app directory)
20
+ * - 'pages' for Pages Router (src/pages or pages directory)
21
+ * @internal
22
+ */
23
+ exports.ROUTER_TYPE = {
24
+ APP: 'app',
25
+ PAGES: 'pages',
26
+ };
15
27
  /**
16
28
  * Detects the Next.js router type (App Router or Pages Router) based on directory structure.
17
29
  * @param {string} projectRoot - The project root directory. Defaults to current working directory.
@@ -19,15 +31,15 @@ const fs_1 = __importDefault(require("fs"));
19
31
  * @internal
20
32
  */
21
33
  function detectRouterType(projectRoot = process.cwd()) {
22
- const appDirExists = fs_1.default.existsSync(`${projectRoot}/src/app`) || fs_1.default.existsSync(`${projectRoot}/app`);
23
- const pagesDirExists = fs_1.default.existsSync(`${projectRoot}/src/pages`) || fs_1.default.existsSync(`${projectRoot}/pages`);
24
- if (appDirExists) {
25
- return 'app';
26
- }
27
- if (pagesDirExists) {
28
- return 'pages';
29
- }
30
- return 'pages';
34
+ const appDirExists = fs_1.default.existsSync(`${projectRoot}/src/${exports.ROUTER_TYPE.APP}`) ||
35
+ fs_1.default.existsSync(`${projectRoot}/${exports.ROUTER_TYPE.APP}`);
36
+ const pagesDirExists = fs_1.default.existsSync(`${projectRoot}/src/${exports.ROUTER_TYPE.PAGES}`) ||
37
+ fs_1.default.existsSync(`${projectRoot}/${exports.ROUTER_TYPE.PAGES}`);
38
+ if (appDirExists)
39
+ return exports.ROUTER_TYPE.APP;
40
+ if (pagesDirExists)
41
+ return exports.ROUTER_TYPE.PAGES;
42
+ return exports.ROUTER_TYPE.PAGES;
31
43
  }
32
44
  /**
33
45
  * Detects the component type based on directives, imports, and router context.
@@ -155,7 +167,7 @@ function detectComponentType(filePath, routerType) {
155
167
  // Router-aware defaults:
156
168
  // - App Router: defaults to server (RSC by default)
157
169
  // - Pages Router: defaults to universal (isomorphic by default)
158
- if (detectedRouterType === 'app') {
170
+ if (detectedRouterType === exports.ROUTER_TYPE.APP) {
159
171
  return 'server';
160
172
  }
161
173
  else {
@@ -125,15 +125,9 @@ export const getPreviewCookies = (site) => {
125
125
  * @returns {string[]} list of required parameters for validation
126
126
  */
127
127
  export const getRequiredEditingParamsList = (mode) => {
128
- const editingRequiredParams = ['sc_site', 'sc_itemid', 'sc_lang', 'route', 'mode'];
129
- const componentRequiredParams = [
130
- 'sc_site',
131
- 'sc_itemid',
132
- 'sc_renderingId',
133
- 'sc_uid',
134
- 'sc_lang',
135
- 'mode',
136
- ];
128
+ const baseRequiredParams = ['sc_site', 'sc_itemid', 'sc_lang'];
129
+ const editingRequiredParams = [...baseRequiredParams, 'route', 'mode'];
130
+ const componentRequiredParams = [...baseRequiredParams, 'sc_uid', 'mode'];
137
131
  return isDesignLibraryMode(mode) ? componentRequiredParams : editingRequiredParams;
138
132
  };
139
133
  /**
@@ -201,7 +195,9 @@ export const getEditingRequestHtml = async (requestUrl, propagatedQsParams, prop
201
195
  // We need to handle not found error provided by Vercel
202
196
  // for `fallback: false` pages
203
197
  // Or preview content is not found or access is denied
204
- if (err.response.status === 404 || err.response.status === 403) {
198
+ if (err.response.status === 404 ||
199
+ err.response.status === 403 ||
200
+ err.response.status === 500) {
205
201
  return err.response;
206
202
  }
207
203
  throw err;
@@ -1,7 +1,6 @@
1
1
  import { RedirectsService, REDIRECT_TYPE_301, REDIRECT_TYPE_302, REDIRECT_TYPE_SERVER_TRANSFER, } from '@sitecore-content-sdk/content/site';
2
2
  import { areURLSearchParamsEqual, escapeNonSpecialQuestionMarks, isRegexOrUrl, mergeURLSearchParams, } from '@sitecore-content-sdk/core/tools';
3
3
  import { NextResponse } from 'next/server';
4
- import regexParser from 'regex-parser';
5
4
  import { ProxyBase, REWRITE_HEADER_NAME } from './proxy';
6
5
  import debug from '../debug';
7
6
  const REGEXP_CONTEXT_SITE_LANG = new RegExp(/\$siteLang/, 'i');
@@ -120,9 +119,9 @@ export class RedirectsProxy extends ProxyBase {
120
119
  existsRedirect.target = existsRedirect.target.replace(REGEXP_CONTEXT_SITE_LANG, site.language);
121
120
  const reqUrl = this.normalizeUrl(req.nextUrl.clone());
122
121
  // Apply regex replacements to the target URL if the pattern is a regex
123
- const matched = reqUrl.pathname
124
- .replace(/\/*$/gi, '')
125
- .match(regexParser(existsRedirect.pattern));
122
+ const sourcePath = existsRedirect.matchedPath || reqUrl.pathname;
123
+ const pathForCaptureMatch = sourcePath.replace(/\/*$/gi, '') || '/';
124
+ const matched = pathForCaptureMatch.match(this.getRedirectPatternRegex(existsRedirect.pattern));
126
125
  if (matched) {
127
126
  existsRedirect.target = existsRedirect.target.replace(/\$(\d+)/g, (_, index) => {
128
127
  return matched[parseInt(index, 10)] || '';
@@ -193,6 +192,7 @@ export class RedirectsProxy extends ProxyBase {
193
192
  * Method returns RedirectInfo when matches
194
193
  * @param {NextRequest} req request
195
194
  * @param {string} siteName site name
195
+ * @param {string} requestLocale locale used for locale redirect matching
196
196
  * @returns Promise<RedirectInfo | undefined>
197
197
  * @private
198
198
  */
@@ -247,28 +247,28 @@ export class RedirectsProxy extends ProxyBase {
247
247
  areURLSearchParamsEqual(new URLSearchParams(patternQS), new URLSearchParams(incomingQS))));
248
248
  }
249
249
  // process regex rules
250
- // Modify the redirect pattern to ignore the language prefix in the path
251
- // And escapes non-special "?" characters in a string or regex.
252
- redirect.pattern = escapeNonSpecialQuestionMarks('^' + redirect.pattern.replace(new RegExp(`^[^]?/${urlLocale}/`, 'gi'), '') // ensure function thinks input is regex
253
- );
254
- // Prepare the redirect pattern as a regular expression, making it more flexible for matching URLs
255
- redirect.pattern = `/^\/${redirect.pattern
256
- .replace(/^\/|\/$/g, '') // Removes leading and trailing slashes
257
- .replace(/^\^\/|\/\$$/g, '') // Removes unnecessary start (^) and end ($) anchors
258
- .replace(/^\^|\$$/g, '') // Further cleans up anchors
259
- .replace(/\$\/g$/g, '')}[\/]?$/i`; // Ensures the pattern allows an optional trailing slash
260
- // Redirect pattern matches the full incoming URL with query string present
261
- matchedQueryString = [
262
- regexParser(redirect.pattern).test(`/${localePath}${incomingQS}`),
263
- regexParser(redirect.pattern).test(`${normalizedPath}${incomingQS}`),
264
- ].some(Boolean)
265
- ? incomingQS
250
+ const regex = this.getRedirectPatternRegex(redirect.pattern);
251
+ const testRegex = (value) => {
252
+ regex.lastIndex = 0;
253
+ return regex.test(value);
254
+ };
255
+ const localeStrippedIncoming = this.getLocaleStrippedPath(incomingURL, urlLocale);
256
+ const localeStrippedNormalized = this.getLocaleStrippedPath(normalizedPath, urlLocale);
257
+ const pathCandidates = [
258
+ incomingURL,
259
+ normalizedPath,
260
+ localeStrippedIncoming,
261
+ localeStrippedNormalized,
262
+ ].filter((candidate, index, array) => array.indexOf(candidate) === index);
263
+ const matchedPath = pathCandidates.find((candidate) => testRegex(candidate));
264
+ const matchedPathWithQuery = incomingQS
265
+ ? pathCandidates.find((candidate) => testRegex(`${candidate}${incomingQS}`))
266
266
  : undefined;
267
+ matchedQueryString = matchedPathWithQuery ? incomingQS : undefined;
267
268
  // Save the matched query string (if found) into the redirect object
268
269
  redirect.matchedQueryString = matchedQueryString || '';
269
- return (!!(regexParser(redirect.pattern).test(`/${urlLocale}${incomingURL}`) ||
270
- regexParser(redirect.pattern).test(incomingURL) ||
271
- matchedQueryString) &&
270
+ redirect.matchedPath = matchedPath || matchedPathWithQuery || '';
271
+ return (!!(matchedPath || matchedQueryString) &&
272
272
  (redirect.locale ? redirect.locale.toLowerCase() === urlLocale.toLowerCase() : true));
273
273
  })
274
274
  : undefined;
@@ -414,4 +414,36 @@ export class RedirectsProxy extends ProxyBase {
414
414
  }
415
415
  return redirect;
416
416
  }
417
+ /**
418
+ * Converts a redirect pattern string into a RegExp.
419
+ * Supports both JS literal form (`/pattern/i`) and plain regex source (`^/path$`).
420
+ * @param {string} pattern redirect pattern from redirect map
421
+ * @returns {RegExp} normalized regex instance
422
+ * @private
423
+ */
424
+ getRedirectPatternRegex(pattern) {
425
+ const normalizedPattern = escapeNonSpecialQuestionMarks(pattern);
426
+ const literalMatch = normalizedPattern.match(/^\/(.+)\/([a-z]*)$/i);
427
+ if (literalMatch) {
428
+ const [, source, flags] = literalMatch;
429
+ const safeFlags = flags || 'i';
430
+ return new RegExp(source, safeFlags);
431
+ }
432
+ return new RegExp(normalizedPattern, 'i');
433
+ }
434
+ /**
435
+ * Strips locale prefix from path when present.
436
+ * @param {string} path incoming request path
437
+ * @param {string} urlLocale locale from Next.js URL
438
+ * @returns {string} locale-stripped path
439
+ * @private
440
+ */
441
+ getLocaleStrippedPath(path, urlLocale) {
442
+ if (!urlLocale) {
443
+ return path;
444
+ }
445
+ const localePrefixRegex = new RegExp(`^/${urlLocale}(?=/|$)`, 'i');
446
+ const strippedPath = path.replace(localePrefixRegex, '') || '/';
447
+ return strippedPath.startsWith('/') ? strippedPath : `/${strippedPath}`;
448
+ }
417
449
  }
@@ -1,7 +1,35 @@
1
1
  import { filterComponentsByType, } from '@sitecore-content-sdk/content/tools';
2
2
  import * as path from 'path';
3
3
  import * as fs from 'fs';
4
- import { detectRouterType, getComponentListWithTypes } from './templating/utils';
4
+ import { detectRouterType, getComponentListWithTypes, ROUTER_TYPE } from './templating/utils';
5
+ const DEFAULT_HEADER_COMMENT = "Below are built-in components that are available in the app, it's recommended to keep them as is";
6
+ const APP_ROUTER_BUILTIN_IMPORTS = `
7
+ import { BYOCServerWrapper, NextjsContentSdkComponent, FEaaSServerWrapper } from '@sitecore-content-sdk/nextjs';
8
+ import { Form } from '@sitecore-content-sdk/nextjs';
9
+ `;
10
+ const APP_ROUTER_BUILTIN_ENTRIES = [
11
+ `['BYOCWrapper', BYOCServerWrapper]`,
12
+ `['FEaaSWrapper', FEaaSServerWrapper]`,
13
+ `['Form', { ...Form, componentType: 'client' }]`,
14
+ ];
15
+ const PAGES_ROUTER_BUILTIN_IMPORTS = `
16
+ import { BYOCWrapper, NextjsContentSdkComponent, FEaaSWrapper } from '@sitecore-content-sdk/nextjs';
17
+ import { Form } from '@sitecore-content-sdk/nextjs';
18
+ `;
19
+ const PAGES_ROUTER_BUILTIN_ENTRIES = [
20
+ `['BYOCWrapper', BYOCWrapper]`,
21
+ `['FEaaSWrapper', FEaaSWrapper]`,
22
+ `['Form', Form]`,
23
+ ];
24
+ const CLIENT_MAP_BUILTIN_IMPORTS = `
25
+ import { BYOCClientWrapper, NextjsContentSdkComponent, FEaaSClientWrapper } from '@sitecore-content-sdk/nextjs';
26
+ import { Form } from '@sitecore-content-sdk/nextjs';
27
+ `;
28
+ const CLIENT_MAP_BUILTIN_ENTRIES = [
29
+ `['BYOCWrapper', BYOCClientWrapper]`,
30
+ `['FEaaSWrapper', FEaaSClientWrapper]`,
31
+ `['Form', Form]`,
32
+ ];
5
33
  // Common builder for Next.js component map content
6
34
  const prepareComponentsForMap = (components, opts) => {
7
35
  const groups = new Map();
@@ -77,21 +105,16 @@ const prepareComponentsForMap = (components, opts) => {
77
105
  }
78
106
  return entries;
79
107
  };
80
- const buildNextjsMapContent = (entries, componentImports, options = {}) => {
81
- const isAppRouter = detectRouterType() === 'app';
82
- const { headerComment = "Below are built-in components that are available in the app, it's recommended to keep them as is", isClientMap = false, } = options;
108
+ /**
109
+ * Distinguishes the simple 2-arity ComponentMapTemplate from the 3-arity EnhancedComponentMapTemplate.
110
+ * @param {ComponentMapTemplate | EnhancedComponentMapTemplate} fn The template function to check.
111
+ * @internal
112
+ */
113
+ const isComponentMapTemplate = (fn) => fn.length === 2;
114
+ const buildNextjsMapContent = (entries, componentImports, options) => {
115
+ const { headerComment = DEFAULT_HEADER_COMMENT, isClientMap = false, builtInImports, builtInMapEntries, } = options;
83
116
  const wildcardImports = [];
84
117
  const namedImports = [];
85
- const builtInImports = options.builtInImports ||
86
- `
87
- import { BYOCWrapper, NextjsContentSdkComponent, FEaaSWrapper } from '@sitecore-content-sdk/nextjs';
88
- import { Form } from '@sitecore-content-sdk/nextjs';
89
- `;
90
- const builtInMapEntries = options.builtInMapEntries || [
91
- `['BYOCWrapper', BYOCWrapper]`,
92
- `['FEaaSWrapper', FEaaSWrapper]`,
93
- `['Form', ${isAppRouter ? '{ ...Form, componentType: \'client\' }' : 'Form'}]`,
94
- ];
95
118
  // Add per-entry imports
96
119
  entries.forEach((e) => wildcardImports.push(...e.imports));
97
120
  // Handle package imports
@@ -109,8 +132,8 @@ import { Form } from '@sitecore-content-sdk/nextjs';
109
132
  ...namedImports,
110
133
  ].filter(Boolean);
111
134
  const importsSection = importLines.length ? `\n${importLines.join('\n')}` : '';
112
- // Build entry lines (package named imports are appended below)
113
- const componentMapEntries = builtInMapEntries;
135
+ // Clone to avoid mutating the caller's array
136
+ const componentMapEntries = structuredClone(builtInMapEntries);
114
137
  for (const e of entries) {
115
138
  const value = !isClientMap && e.annotateClient
116
139
  ? `{ ${e.valueExpr}, componentType: 'client' }`
@@ -143,26 +166,30 @@ ${componentMapEntries
143
166
  export default componentMap;
144
167
  `;
145
168
  };
146
- // default client template
169
+ // Default App Router (server) component map template
170
+ export const defaultServerMapTemplate = (components, componentImports, ctx) => {
171
+ var _a, _b;
172
+ const entries = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.entries) !== null && _a !== void 0 ? _a : prepareComponentsForMap(components, {
173
+ includeVariants: (_b = ctx === null || ctx === void 0 ? void 0 : ctx.includeVariants) !== null && _b !== void 0 ? _b : true,
174
+ });
175
+ return buildNextjsMapContent(entries, componentImports, {
176
+ headerComment: DEFAULT_HEADER_COMMENT,
177
+ isClientMap: false,
178
+ builtInImports: APP_ROUTER_BUILTIN_IMPORTS,
179
+ builtInMapEntries: APP_ROUTER_BUILTIN_ENTRIES,
180
+ });
181
+ };
182
+ // Default client-safe component map template for App Router
147
183
  export const defaultClientMapTemplate = (components, componentImports, ctx) => {
148
184
  var _a, _b;
149
- const builtInImports = `
150
- import { BYOCClientWrapper, NextjsContentSdkComponent, FEaaSClientWrapper } from '@sitecore-content-sdk/nextjs';
151
- import { Form } from '@sitecore-content-sdk/nextjs';
152
- `;
153
- const builtInMapEntries = [
154
- `['BYOCWrapper', BYOCClientWrapper]`,
155
- `['FEaaSWrapper', FEaaSClientWrapper]`,
156
- `['Form', Form]`,
157
- ];
158
185
  const entries = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.entries) !== null && _a !== void 0 ? _a : prepareComponentsForMap(components, {
159
186
  includeVariants: (_b = ctx === null || ctx === void 0 ? void 0 : ctx.includeVariants) !== null && _b !== void 0 ? _b : true,
160
187
  });
161
188
  return buildNextjsMapContent(entries, componentImports, {
162
189
  headerComment: 'Client-safe component map for App Router',
163
190
  isClientMap: true,
164
- builtInImports,
165
- builtInMapEntries,
191
+ builtInImports: CLIENT_MAP_BUILTIN_IMPORTS,
192
+ builtInMapEntries: CLIENT_MAP_BUILTIN_ENTRIES,
166
193
  });
167
194
  };
168
195
  // Collect components from specified paths, apply exclude and type filter, and prepare map entries.
@@ -182,115 +209,92 @@ const collectComponents = (opts) => {
182
209
  /**
183
210
  * Generate and write componentMap.ts files based on provided params.
184
211
  *
185
- * When clientComponentMap is true, generates:
212
+ * Pages Router:
213
+ * - component-map.ts : Single component map with Pages Router wrappers
214
+ *
215
+ * App Router (clientComponentMap=true or undefined):
186
216
  * - component-map.ts : Full component map with all components (server, client, universal)
187
- * - component-map.client.ts : Client-safe map with only client + universal components
217
+ * - component-map.client.ts : Client-safe map with client + universal components
188
218
  *
189
- * When clientComponentMap is false, generates:
190
- * - component-map.ts : Single component map (traditional behavior)
219
+ * App Router (clientComponentMap=false):
220
+ * - component-map.ts : Full component map with all components (server, client, universal)
221
+ * - component-map.client.ts : Client-safe map with built-in components only (no user components)
191
222
  *
192
- * When includeVariants is true (in either mode):
223
+ * When includeVariants is true:
193
224
  * - Includes component **variants** in the generated map(s) alongside base components
194
225
  * - Preserves the same client/server filtering rules (variants obey clientComponentMap filtering)
195
226
  * - Variant entries are emitted using the same naming/keys convention as their base components
196
227
  *
197
228
  * Template Customization:
198
229
  * - mapTemplate: Custom template for main component map (works for both single and dual map modes)
199
- * - clientMapTemplate: Custom template for client component map (only used when clientComponentMap is true)
230
+ * - clientMapTemplate: Custom template for client component map (App Router only)
200
231
  * @param {GenerateMapArgs} params - The parameters for the generateMap function.
201
232
  * @public
202
233
  */
203
234
  export const generateMap = ({ paths, destination = '.sitecore', exclude, componentImports, mapTemplate, clientMapTemplate, clientComponentMap, includeVariants = true, }) => {
204
- const isAppRouter = detectRouterType() === 'app';
205
- const shouldGenerateClientMap = clientComponentMap !== null && clientComponentMap !== void 0 ? clientComponentMap : isAppRouter;
206
- if (shouldGenerateClientMap) {
207
- // App Router case, main map
208
- const getComponents = collectComponents({ paths, exclude, includeVariants, filter: 'all' });
209
- let mainContent;
210
- if (mapTemplate) {
211
- mainContent = mapTemplate(getComponents.raw, componentImports, {
212
- entries: getComponents.entries,
235
+ const routerType = detectRouterType();
236
+ const allComponents = collectComponents({ paths, exclude, includeVariants, filter: 'all' });
237
+ if (routerType === ROUTER_TYPE.PAGES) {
238
+ const content = mapTemplate
239
+ ? mapTemplate(allComponents.raw, componentImports, {
240
+ entries: allComponents.entries,
213
241
  includeVariants,
214
242
  isClientMap: false,
215
- });
216
- }
217
- else {
218
- // default app router server map
219
- const builtInImports = `
220
- import { BYOCServerWrapper, NextjsContentSdkComponent, FEaaSServerWrapper } from '@sitecore-content-sdk/nextjs';
221
- import { Form } from '@sitecore-content-sdk/nextjs';
222
- `;
223
- const builtInMapEntries = [
224
- `['BYOCWrapper', BYOCServerWrapper]`,
225
- `['FEaaSWrapper', FEaaSServerWrapper]`,
226
- `['Form', { ...Form, componentType: 'client' }]`,
227
- ];
228
- mainContent = buildNextjsMapContent(getComponents.entries, componentImports, {
229
- headerComment: "Below are built-in components that are available in the app, it's recommended to keep them as is",
243
+ })
244
+ : buildNextjsMapContent(allComponents.entries, componentImports, {
230
245
  isClientMap: false,
231
- builtInImports,
232
- builtInMapEntries,
246
+ builtInImports: PAGES_ROUTER_BUILTIN_IMPORTS,
247
+ builtInMapEntries: PAGES_ROUTER_BUILTIN_ENTRIES,
233
248
  });
249
+ try {
250
+ fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.ts'), content, 'utf8');
234
251
  }
235
- fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.ts'), mainContent, 'utf8');
236
- // App Router, client map
237
- const clientComponents = collectComponents({
238
- paths,
239
- exclude,
240
- includeVariants,
241
- filter: 'client',
242
- });
243
- const clientTemplate = clientMapTemplate || defaultClientMapTemplate;
244
- let clientContent;
245
- if (clientTemplate.length >= 2) {
246
- clientContent = clientTemplate(clientComponents.raw, componentImports);
247
- }
248
- else {
249
- clientContent = clientTemplate(clientComponents.raw, componentImports, {
250
- entries: clientComponents.entries,
251
- includeVariants,
252
- isClientMap: true,
253
- });
252
+ catch (error) {
253
+ console.error(`Component Map generation failed. Error writing to file ${destination}:`, error);
254
+ throw error;
254
255
  }
255
- fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.client.ts'), clientContent, 'utf8');
256
+ return;
256
257
  }
257
- else {
258
- // Either in pages/app router or clientComponentMap = false
259
- const components = collectComponents({
260
- paths,
261
- exclude,
258
+ const mainContent = mapTemplate
259
+ ? mapTemplate(allComponents.raw, componentImports, {
260
+ entries: allComponents.entries,
261
+ includeVariants,
262
+ isClientMap: false,
263
+ })
264
+ : defaultServerMapTemplate(allComponents.raw, componentImports, {
265
+ entries: allComponents.entries,
262
266
  includeVariants,
263
- filter: 'all',
264
- }).entries;
265
- const content = buildNextjsMapContent(components, componentImports, {
266
- headerComment: "Below are built-in components that are available in the app, it's recommended to keep them as is",
267
267
  isClientMap: false,
268
268
  });
269
- fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.ts'), content, 'utf8');
270
- // For App Router compatibility, always generate client map file even when clientComponentMap is false
271
- // When clientComponentMap is false, only include built-in components (no custom client components)
272
- if (shouldGenerateClientMap || isAppRouter) {
273
- const clientMapTemplateToUse = clientMapTemplate || defaultClientMapTemplate;
274
- const components = collectComponents({ paths: [], includeVariants, filter: 'all' });
275
- let clientMapContent;
276
- if (clientMapTemplateToUse.length >= 2) {
277
- clientMapContent = clientMapTemplateToUse([], componentImports);
278
- }
279
- else {
280
- clientMapContent = clientMapTemplateToUse([], componentImports, {
281
- entries: components.entries,
282
- includeVariants,
283
- isClientMap: true,
284
- });
285
- }
286
- const clientMapFile = path.join(process.cwd(), destination, 'component-map.client.ts');
287
- try {
288
- fs.writeFileSync(clientMapFile, clientMapContent, { encoding: 'utf8' });
289
- }
290
- catch (error) {
291
- console.error(`Client Component Map generation failed. Error writing to file ${destination}:`, error);
292
- throw error;
293
- }
294
- }
269
+ try {
270
+ fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.ts'), mainContent, 'utf8');
271
+ }
272
+ catch (error) {
273
+ console.error(`Main Component Map generation failed. Error writing to file ${destination}:`, error);
274
+ throw error;
275
+ }
276
+ // clientComponentMap=true -> include user client+universal components
277
+ // clientComponentMap=undefined -> include user client+universal components
278
+ // clientComponentMap=false -> built-ins only
279
+ const shouldGenerateClientMap = clientComponentMap !== null && clientComponentMap !== void 0 ? clientComponentMap : true;
280
+ const clientComponents = shouldGenerateClientMap
281
+ ? collectComponents({ paths, exclude, includeVariants, filter: 'client' })
282
+ : { raw: [], entries: [] };
283
+ const clientTemplate = clientMapTemplate || defaultClientMapTemplate;
284
+ let clientContent;
285
+ if (isComponentMapTemplate(clientTemplate))
286
+ clientContent = clientTemplate(clientComponents.raw, componentImports);
287
+ else
288
+ clientContent = clientTemplate(clientComponents.raw, componentImports, {
289
+ entries: clientComponents.entries,
290
+ includeVariants,
291
+ isClientMap: true,
292
+ });
293
+ try {
294
+ fs.writeFileSync(path.join(process.cwd(), destination, 'component-map.client.ts'), clientContent, 'utf8');
295
+ }
296
+ catch (error) {
297
+ console.error(`Client Component Map generation failed. Error writing to file ${destination}:`, error);
298
+ throw error;
295
299
  }
296
300
  };
@@ -1,6 +1,17 @@
1
1
  import { getComponentList, defaultImportMapTemplate, } from '@sitecore-content-sdk/content/node-tools';
2
2
  import ts from 'typescript';
3
3
  import fs from 'fs';
4
+ /**
5
+ * Constants for Next.js router types. Used for consistent detection and comparison throughout the codebase.
6
+ * Values are based on Next.js conventions:
7
+ * - 'app' for App Router (src/app or app directory)
8
+ * - 'pages' for Pages Router (src/pages or pages directory)
9
+ * @internal
10
+ */
11
+ export const ROUTER_TYPE = {
12
+ APP: 'app',
13
+ PAGES: 'pages',
14
+ };
4
15
  /**
5
16
  * Detects the Next.js router type (App Router or Pages Router) based on directory structure.
6
17
  * @param {string} projectRoot - The project root directory. Defaults to current working directory.
@@ -8,15 +19,15 @@ import fs from 'fs';
8
19
  * @internal
9
20
  */
10
21
  export function detectRouterType(projectRoot = process.cwd()) {
11
- const appDirExists = fs.existsSync(`${projectRoot}/src/app`) || fs.existsSync(`${projectRoot}/app`);
12
- const pagesDirExists = fs.existsSync(`${projectRoot}/src/pages`) || fs.existsSync(`${projectRoot}/pages`);
13
- if (appDirExists) {
14
- return 'app';
15
- }
16
- if (pagesDirExists) {
17
- return 'pages';
18
- }
19
- return 'pages';
22
+ const appDirExists = fs.existsSync(`${projectRoot}/src/${ROUTER_TYPE.APP}`) ||
23
+ fs.existsSync(`${projectRoot}/${ROUTER_TYPE.APP}`);
24
+ const pagesDirExists = fs.existsSync(`${projectRoot}/src/${ROUTER_TYPE.PAGES}`) ||
25
+ fs.existsSync(`${projectRoot}/${ROUTER_TYPE.PAGES}`);
26
+ if (appDirExists)
27
+ return ROUTER_TYPE.APP;
28
+ if (pagesDirExists)
29
+ return ROUTER_TYPE.PAGES;
30
+ return ROUTER_TYPE.PAGES;
20
31
  }
21
32
  /**
22
33
  * Detects the component type based on directives, imports, and router context.
@@ -144,7 +155,7 @@ export function detectComponentType(filePath, routerType) {
144
155
  // Router-aware defaults:
145
156
  // - App Router: defaults to server (RSC by default)
146
157
  // - Pages Router: defaults to universal (isomorphic by default)
147
- if (detectedRouterType === 'app') {
158
+ if (detectedRouterType === ROUTER_TYPE.APP) {
148
159
  return 'server';
149
160
  }
150
161
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sitecore-content-sdk/nextjs",
3
- "version": "2.1.1",
3
+ "version": "2.2.0-canary.20260525085832",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "sideEffects": false,
@@ -32,8 +32,8 @@
32
32
  "url": "https://github.com/sitecore/content-sdk/issues"
33
33
  },
34
34
  "devDependencies": {
35
- "@sitecore-content-sdk/analytics-core": "^2.1.0",
36
- "@sitecore-content-sdk/personalize": "^2.1.0",
35
+ "@sitecore-content-sdk/analytics-core": "2.1.1-canary.20260525085832",
36
+ "@sitecore-content-sdk/personalize": "2.1.1-canary.20260525085832",
37
37
  "@stylistic/eslint-plugin": "^5.2.2",
38
38
  "@testing-library/dom": "^10.4.0",
39
39
  "@testing-library/react": "^16.3.0",
@@ -76,9 +76,9 @@
76
76
  "typescript": "~5.8.3"
77
77
  },
78
78
  "peerDependencies": {
79
- "@sitecore-content-sdk/analytics-core": "^2.1.0",
80
- "@sitecore-content-sdk/events": "^2.1.0",
81
- "@sitecore-content-sdk/personalize": "^2.1.0",
79
+ "@sitecore-content-sdk/analytics-core": "2.1.1-canary.20260525085832",
80
+ "@sitecore-content-sdk/events": "2.1.1-canary.20260525085832",
81
+ "@sitecore-content-sdk/personalize": "2.1.1-canary.20260525085832",
82
82
  "next": "^16.2.0",
83
83
  "react": "^19.2.1",
84
84
  "react-dom": "^19.2.1",
@@ -91,10 +91,10 @@
91
91
  },
92
92
  "dependencies": {
93
93
  "@babel/parser": "^7.27.2",
94
- "@sitecore-content-sdk/content": "^2.1.1",
95
- "@sitecore-content-sdk/core": "^2.1.0",
96
- "@sitecore-content-sdk/events": "^2.1.0",
97
- "@sitecore-content-sdk/react": "^2.1.0",
94
+ "@sitecore-content-sdk/content": "2.2.0-canary.20260525085832",
95
+ "@sitecore-content-sdk/core": "2.1.1-canary.20260525085832",
96
+ "@sitecore-content-sdk/events": "2.1.1-canary.20260525085832",
97
+ "@sitecore-content-sdk/react": "2.2.0-canary.20260525085832",
98
98
  "recast": "^0.23.11",
99
99
  "regex-parser": "^2.3.1",
100
100
  "sync-disk-cache": "^2.1.0"
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/editing/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAE9B,wBAAwB,EAIzB,MAAM,uCAAuC,CAAC;AAG/C,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AAE1E;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GAAI,KAAK,cAAc,GAAG,WAAW,yCAgB5E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO;IACtC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC,KAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAyBtC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAChC,aAAa;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,EACvC,gBAAgB,kBAAkB,KACjC,2BA4BF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;CAI3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,oBAc1E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,aAI7C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,GAAI,MAAM,wBAAwB,CAAC,MAAM,CAAC,aAYlF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,GACvC,OAAO,OAAO,CAAC;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE,CAAC,KACnD;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAazB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,mBAAmB,GAAG,OAAO,KACrC;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAazB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAChC,YAAY,GAAG,EACf,oBAAoB;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,EACzD,mBAAmB;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EAC5C,SAAS,MAAM,EAAE,EACjB,aAAa,iBAAiB,KAC7B,OAAO,CAAC,MAAM,CAkDhB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GACrC,MAAM,OAAO,KACZ,IAAI,IAAI,8BAOV,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,cAAc,GAAG,WAAW,WAmBjE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,cAIxB,CAAC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/editing/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAE9B,wBAAwB,EAIzB,MAAM,uCAAuC,CAAC;AAG/C,OAAO,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AAE1E;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GAAI,KAAK,cAAc,GAAG,WAAW,yCAgB5E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO;IACtC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC,KAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAyBtC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAChC,aAAa;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,EACvC,gBAAgB,kBAAkB,KACjC,2BA4BF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;CAI3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,oBAc1E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,aAI7C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,GAAI,MAAM,wBAAwB,CAAC,MAAM,CAAC,aAKlF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,GACvC,OAAO,OAAO,CAAC;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE,CAAC,KACnD;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAazB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,mBAAmB,GAAG,OAAO,KACrC;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAazB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAChC,YAAY,GAAG,EACf,oBAAoB;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAAE,EACzD,mBAAmB;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EAC5C,SAAS,MAAM,EAAE,EACjB,aAAa,iBAAiB,KAC7B,OAAO,CAAC,MAAM,CAsDhB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GACrC,MAAM,OAAO,KACZ,IAAI,IAAI,8BAOV,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,GAAI,KAAK,cAAc,GAAG,WAAW,WAmBjE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,cAIxB,CAAC"}
@@ -5,6 +5,7 @@ import { ProxyBase, ProxyBaseConfig } from './proxy';
5
5
  import { SitecoreConfig } from '../config';
6
6
  type RedirectResult = RedirectInfo & {
7
7
  matchedQueryString?: string;
8
+ matchedPath?: string;
8
9
  };
9
10
  /**
10
11
  * The interface for the RedirectsProxy configuration.
@@ -32,6 +33,7 @@ export declare class RedirectsProxy extends ProxyBase {
32
33
  * Method returns RedirectInfo when matches
33
34
  * @param {NextRequest} req request
34
35
  * @param {string} siteName site name
36
+ * @param {string} requestLocale locale used for locale redirect matching
35
37
  * @returns Promise<RedirectInfo | undefined>
36
38
  * @private
37
39
  */
@@ -83,6 +85,22 @@ export declare class RedirectsProxy extends ProxyBase {
83
85
  * @returns {NextResponse<unknown>} The redirect response.
84
86
  */
85
87
  protected createRedirectResponse(url: NextURL | string, res: Response | undefined, status: number, statusText: string): NextResponse;
88
+ /**
89
+ * Converts a redirect pattern string into a RegExp.
90
+ * Supports both JS literal form (`/pattern/i`) and plain regex source (`^/path$`).
91
+ * @param {string} pattern redirect pattern from redirect map
92
+ * @returns {RegExp} normalized regex instance
93
+ * @private
94
+ */
95
+ private getRedirectPatternRegex;
96
+ /**
97
+ * Strips locale prefix from path when present.
98
+ * @param {string} path incoming request path
99
+ * @param {string} urlLocale locale from Next.js URL
100
+ * @returns {string} locale-stripped path
101
+ * @private
102
+ */
103
+ private getLocaleStrippedPath;
86
104
  }
87
105
  export {};
88
106
  //# sourceMappingURL=redirects-proxy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"redirects-proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/redirects-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EAItB,YAAY,EAEb,MAAM,oCAAoC,CAAC;AAO5C,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAuB,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAM3C,KAAK,cAAc,GAAG,YAAY,GAAG;IAAE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,eAAe,CAAC,GACxF,cAAc,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAC7B,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GACpD,eAAe,GACf,cAAc,CAAC,WAAW,CAAC,GAAG;IAC5B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AACJ;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAO/B,SAAS,CAAC,MAAM,EAAE,oBAAoB;IANlD,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpD,OAAO,CAAC,OAAO,CAAW;IAE1B;;OAEG;gBACmB,MAAM,EAAE,oBAAoB;IAmDlD,MAAM,GAAU,KAAK,WAAW,EAAE,KAAK,YAAY,KAAG,OAAO,CAAC,YAAY,CAAC,CA4KzE;IAEF,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,GAAG,SAAS;IAU5E;;;;;;OAMG;cACa,iBAAiB,CAC/B,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IA0BtC;;;;;;;;OAQG;IACH,SAAS,CAAC,4BAA4B,CACpC,SAAS,EAAE,cAAc,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,cAAc,GAAG,SAAS;IAsE7B;;;;;;;OAOG;IACH,SAAS,CAAC,yBAAyB,CACjC,SAAS,EAAE,cAAc,EAAE,EAC3B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,cAAc,GAAG,SAAS;IAW7B;;;;;;OAMG;IACH,SAAS,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO;IA6C7C;;;;;;;;;OASG;IACH,SAAS,CAAC,gBAAgB,CACxB,MAAM,EAAE,OAAO,GAAG,MAAM,EACxB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,YAAY,EACjB,UAAU,UAAQ,GACjB,YAAY;IAkDf;;;;;;;OAOG;IACH,SAAS,CAAC,sBAAsB,CAC9B,GAAG,EAAE,OAAO,GAAG,MAAM,EACrB,GAAG,EAAE,QAAQ,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,YAAY;CAehB"}
1
+ {"version":3,"file":"redirects-proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/redirects-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EAItB,YAAY,EAEb,MAAM,oCAAoC,CAAC;AAO5C,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAuB,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAM3C,KAAK,cAAc,GAAG,YAAY,GAAG;IAAE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,sBAAsB,EAAE,OAAO,GAAG,eAAe,CAAC,GACxF,cAAc,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,GAC7B,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GACpD,eAAe,GACf,cAAc,CAAC,WAAW,CAAC,GAAG;IAC5B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AACJ;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAO/B,SAAS,CAAC,MAAM,EAAE,oBAAoB;IANlD,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpD,OAAO,CAAC,OAAO,CAAW;IAE1B;;OAEG;gBACmB,MAAM,EAAE,oBAAoB;IAmDlD,MAAM,GAAU,KAAK,WAAW,EAAE,KAAK,YAAY,KAAG,OAAO,CAAC,YAAY,CAAC,CA4KzE;IAEF,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,GAAG,SAAS;IAU5E;;;;;;;OAOG;cACa,iBAAiB,CAC/B,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IA0BtC;;;;;;;;OAQG;IACH,SAAS,CAAC,4BAA4B,CACpC,SAAS,EAAE,cAAc,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,cAAc,GAAG,SAAS;IAiE7B;;;;;;;OAOG;IACH,SAAS,CAAC,yBAAyB,CACjC,SAAS,EAAE,cAAc,EAAE,EAC3B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,cAAc,GAAG,SAAS;IAW7B;;;;;;OAMG;IACH,SAAS,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO;IA6C7C;;;;;;;;;OASG;IACH,SAAS,CAAC,gBAAgB,CACxB,MAAM,EAAE,OAAO,GAAG,MAAM,EACxB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,YAAY,EACjB,UAAU,UAAQ,GACjB,YAAY;IAkDf;;;;;;;OAOG;IACH,SAAS,CAAC,sBAAsB,CAC9B,GAAG,EAAE,OAAO,GAAG,MAAM,EACrB,GAAG,EAAE,QAAQ,GAAG,SAAS,EACzB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,YAAY;IAgBf;;;;;;OAMG;IACH,OAAO,CAAC,uBAAuB;IAW/B;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;CAQ9B"}
@@ -1,24 +1,29 @@
1
1
  import { GenerateMapFunction, EnhancedComponentMapTemplate } from '@sitecore-content-sdk/content/tools';
2
+ export declare const defaultServerMapTemplate: EnhancedComponentMapTemplate;
2
3
  export declare const defaultClientMapTemplate: EnhancedComponentMapTemplate;
3
4
  export type CollectFilter = 'all' | 'client' | 'server' | 'universal';
4
5
  /**
5
6
  * Generate and write componentMap.ts files based on provided params.
6
7
  *
7
- * When clientComponentMap is true, generates:
8
+ * Pages Router:
9
+ * - component-map.ts : Single component map with Pages Router wrappers
10
+ *
11
+ * App Router (clientComponentMap=true or undefined):
8
12
  * - component-map.ts : Full component map with all components (server, client, universal)
9
- * - component-map.client.ts : Client-safe map with only client + universal components
13
+ * - component-map.client.ts : Client-safe map with client + universal components
10
14
  *
11
- * When clientComponentMap is false, generates:
12
- * - component-map.ts : Single component map (traditional behavior)
15
+ * App Router (clientComponentMap=false):
16
+ * - component-map.ts : Full component map with all components (server, client, universal)
17
+ * - component-map.client.ts : Client-safe map with built-in components only (no user components)
13
18
  *
14
- * When includeVariants is true (in either mode):
19
+ * When includeVariants is true:
15
20
  * - Includes component **variants** in the generated map(s) alongside base components
16
21
  * - Preserves the same client/server filtering rules (variants obey clientComponentMap filtering)
17
22
  * - Variant entries are emitted using the same naming/keys convention as their base components
18
23
  *
19
24
  * Template Customization:
20
25
  * - mapTemplate: Custom template for main component map (works for both single and dual map modes)
21
- * - clientMapTemplate: Custom template for client component map (only used when clientComponentMap is true)
26
+ * - clientMapTemplate: Custom template for client component map (App Router only)
22
27
  * @param {GenerateMapArgs} params - The parameters for the generateMap function.
23
28
  * @public
24
29
  */
@@ -1 +1 @@
1
- {"version":3,"file":"generate-map.d.ts","sourceRoot":"","sources":["../../src/tools/generate-map.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,mBAAmB,EAInB,4BAA4B,EAG7B,MAAM,qCAAqC,CAAC;AAuM7C,eAAO,MAAM,wBAAwB,EAAE,4BA4BtC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AA6BtE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,WAAW,EAAE,mBAkIzB,CAAC"}
1
+ {"version":3,"file":"generate-map.d.ts","sourceRoot":"","sources":["../../src/tools/generate-map.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,mBAAmB,EAInB,4BAA4B,EAG7B,MAAM,qCAAqC,CAAC;AAyO7C,eAAO,MAAM,wBAAwB,EAAE,4BAiBtC,CAAC;AAGF,eAAO,MAAM,wBAAwB,EAAE,4BAiBtC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AA6BtE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,WAAW,EAAE,mBAiGzB,CAAC"}
@@ -1,5 +1,16 @@
1
1
  import { ComponentFileWithType, ComponentType, RouterType } from '@sitecore-content-sdk/content/tools';
2
2
  import { ModuleExports } from '@sitecore-content-sdk/content/node-tools';
3
+ /**
4
+ * Constants for Next.js router types. Used for consistent detection and comparison throughout the codebase.
5
+ * Values are based on Next.js conventions:
6
+ * - 'app' for App Router (src/app or app directory)
7
+ * - 'pages' for Pages Router (src/pages or pages directory)
8
+ * @internal
9
+ */
10
+ export declare const ROUTER_TYPE: {
11
+ readonly APP: "app";
12
+ readonly PAGES: "pages";
13
+ };
3
14
  /**
4
15
  * Detects the Next.js router type (App Router or Pages Router) based on directory structure.
5
16
  * @param {string} projectRoot - The project root directory. Defaults to current working directory.
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/tools/templating/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,UAAU,EACX,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAGL,aAAa,EACd,MAAM,0CAA0C,CAAC;AAIlD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,GAAE,MAAsB,GAAG,UAAU,CAehF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,aAAa,CA0J5F;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE,MAAM,EAAE,EAClB,eAAe,CAAC,EAAE,OAAO,EACzB,UAAU,CAAC,EAAE,UAAU,GACtB,qBAAqB,EAAE,CAQzB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,UAGnF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,UAEpF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,UAEpF"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/tools/templating/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,UAAU,EACX,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAGL,aAAa,EACd,MAAM,0CAA0C,CAAC;AAIlD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW;;;CAGd,CAAC;AAEX;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,GAAE,MAAsB,GAAG,UAAU,CAahF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,aAAa,CA0J5F;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,CAAC,EAAE,MAAM,EAAE,EAClB,eAAe,CAAC,EAAE,OAAO,EACzB,UAAU,CAAC,EAAE,UAAU,GACtB,qBAAqB,EAAE,CAQzB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,UAGnF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,UAEpF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,UAEpF"}