@merkle-open/magnolia-headless-frontend-nextjs 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/package.json +5 -4
  2. package/src/Constants.ts +4 -0
  3. package/src/Index.ts +0 -1
  4. package/src/api/ErrorPageApi.ts +2 -3
  5. package/src/api/RobotsTxtApi.ts +5 -6
  6. package/src/api/SitemapApi.ts +5 -6
  7. package/src/api/_Dependencies.ts +3 -5
  8. package/src/config/ComponentMappingsProvider.ts +3 -0
  9. package/src/config/ConfigProvider.ts +3 -0
  10. package/src/config/Logger.ts +3 -0
  11. package/src/config/StylesheetProvider.ts +5 -1
  12. package/src/config/_Index.ts +1 -1
  13. package/src/config/_README.md +16 -16
  14. package/src/dynamic-page/AbstractDynamicErrorPage.tsx +3 -3
  15. package/src/dynamic-page/AbstractDynamicPage.tsx +8 -21
  16. package/src/dynamic-page/DynamicErrorPage.tsx +13 -13
  17. package/src/dynamic-page/DynamicErrorPageGlobal.tsx +27 -20
  18. package/src/dynamic-page/DynamicPage.tsx +13 -14
  19. package/src/dynamic-page/DynamicPageLayout.tsx +2 -4
  20. package/src/dynamic-page/PageProps.ts +1 -0
  21. package/src/dynamic-page/_Dependencies.ts +5 -7
  22. package/src/helper/BrowserLanguageProvider.ts +1 -3
  23. package/src/helper/Logger.ts +2 -3
  24. package/src/helper/MagnoliaContextProvider.ts +3 -5
  25. package/src/helper/MagnoliaPageRestClient.ts +4 -6
  26. package/src/helper/MetadataProvider.ts +4 -2
  27. package/src/helper/RestClient.ts +2 -4
  28. package/src/helper/ThemeValidator.ts +25 -0
  29. package/src/helper/_Dependencies.ts +8 -8
  30. package/src/helper/_Index.ts +7 -0
  31. package/src/middleware/Middleware.ts +7 -3
  32. package/src/middleware/_Dependencies.ts +5 -7
  33. package/src/middleware/_README.md +2 -2
  34. package/src/middleware/impl/DynamicResponseHeaderMiddleware.ts +5 -5
  35. package/src/middleware/impl/I18nRedirectMiddleware.ts +4 -4
  36. package/src/middleware/impl/VanityMiddleware.ts +7 -7
  37. package/src/templates/ComponentMappingsProvider.ts +2 -3
  38. package/src/templates/_Dependencies.ts +2 -3
  39. package/src/templates/_Index.ts +2 -0
  40. package/src/templates/components/_redirect/RedirectComponent.tsx +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@merkle-open/magnolia-headless-frontend-nextjs",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Magnolia headless frontend nextJs",
5
5
  "license": "MIT",
6
6
  "author": "Merkle Inc.",
@@ -14,7 +14,8 @@
14
14
  "files": [
15
15
  "README.md",
16
16
  "index.ts",
17
- "src"
17
+ "src",
18
+ "src/**/_README.md"
18
19
  ],
19
20
  "repository": {
20
21
  "type": "git",
@@ -49,11 +50,11 @@
49
50
  "eslint-plugin-prettier": "^5.5.5",
50
51
  "eslint-plugin-react": "^7.37.5",
51
52
  "husky": "^9.1.7",
53
+ "jiti": "^2.6.1",
52
54
  "lint-staged": "^16.2.7",
53
55
  "prettier": "^3.8.1",
54
56
  "tsx": "^4.21.0",
55
- "typescript-eslint": "^8.56.1",
56
- "jiti": "^2.6.1"
57
+ "typescript-eslint": "^8.56.1"
57
58
  },
58
59
  "lint-staged": {
59
60
  "*.{js,jsx,ts,tsx,md,mdx,html,css}": "npm run format"
package/src/Constants.ts CHANGED
@@ -1 +1,5 @@
1
1
  export const TOKEN_PREFIX = 'com.merkle.oss.magnolia.headless.';
2
+
3
+ export function token(suffix: string): symbol {
4
+ return Symbol(TOKEN_PREFIX + suffix);
5
+ }
package/src/Index.ts CHANGED
@@ -4,5 +4,4 @@ export * from './config/_Index.ts';
4
4
  export * from './helper/_Index.ts';
5
5
  export * from './middleware/_Index.ts';
6
6
  export * from './templates/_Index.ts';
7
- export * from './Constants.ts';
8
7
  export * from './Dependencies.ts';
@@ -1,12 +1,11 @@
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
1
2
  import { NextRequest } from 'next/server';
2
3
  import { inject, injectable } from 'tsyringe';
3
4
  import { ErrorType, MagnoliaPageRestClient } from '../helper/MagnoliaPageRestClient.ts';
4
5
 
5
- import { TOKEN_PREFIX } from '../Constants.ts';
6
-
7
6
  @injectable()
8
7
  export class ErrorPageApi {
9
- constructor(@inject(TOKEN_PREFIX + 'MagnoliaPageRestClient') private magnoliaPageRestClient: MagnoliaPageRestClient) {}
8
+ constructor(@inject(MagnoliaPageRestClient) private magnoliaPageRestClient: MagnoliaPageRestClient) {}
10
9
 
11
10
  public async get(req: NextRequest, language: string): Promise<Response> {
12
11
  const queryParams = req.nextUrl.searchParams;
@@ -1,17 +1,16 @@
1
- import { RestClient } from '../helper/RestClient.ts';
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
2
2
  import { NextRequest } from 'next/server';
3
+ import { RestClient } from '../helper/RestClient.ts';
3
4
  import { inject, injectable } from 'tsyringe';
4
- import type { HeadlessConfigProviderI, MagnoliaApiEndpointsProvider } from '../config/ConfigProvider.ts';
5
-
6
- import { TOKEN_PREFIX } from '../Constants.ts';
5
+ import { type HeadlessConfigProviderI, MagnoliaApiEndpointsProvider, HEADLESS_CONFIG_PROVIDER_TOKEN } from '../config/ConfigProvider.ts';
7
6
 
8
7
  @injectable()
9
8
  export class RobotsTxtApi {
10
9
  private readonly apisProvider: MagnoliaApiEndpointsProvider;
11
10
 
12
11
  constructor(
13
- @inject(TOKEN_PREFIX + 'HeadlessConfigProviderI') configProvider: HeadlessConfigProviderI,
14
- @inject(TOKEN_PREFIX + 'RestClient') private restClient: RestClient,
12
+ @inject(HEADLESS_CONFIG_PROVIDER_TOKEN) configProvider: HeadlessConfigProviderI,
13
+ @inject(RestClient) private restClient: RestClient,
15
14
  ) {
16
15
  this.apisProvider = configProvider.get().magnoliaApisProvider;
17
16
  }
@@ -1,17 +1,16 @@
1
- import { RestClient } from '../helper/RestClient.ts';
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
2
2
  import { NextRequest } from 'next/server';
3
+ import { RestClient } from '../helper/RestClient.ts';
3
4
  import { inject, injectable } from 'tsyringe';
4
- import type { HeadlessConfigProviderI, MagnoliaApiEndpointsProvider } from '../config/ConfigProvider.ts';
5
-
6
- import { TOKEN_PREFIX } from '../Constants.ts';
5
+ import { type HeadlessConfigProviderI, MagnoliaApiEndpointsProvider, HEADLESS_CONFIG_PROVIDER_TOKEN } from '../config/ConfigProvider.ts';
7
6
 
8
7
  @injectable()
9
8
  export class SitemapApi {
10
9
  private readonly apisProvider: MagnoliaApiEndpointsProvider;
11
10
 
12
11
  constructor(
13
- @inject(TOKEN_PREFIX + 'HeadlessConfigProviderI') configProvider: HeadlessConfigProviderI,
14
- @inject(TOKEN_PREFIX + 'RestClient') private restClient: RestClient,
12
+ @inject(HEADLESS_CONFIG_PROVIDER_TOKEN) configProvider: HeadlessConfigProviderI,
13
+ @inject(RestClient) private restClient: RestClient,
15
14
  ) {
16
15
  this.apisProvider = configProvider.get().magnoliaApisProvider;
17
16
  }
@@ -3,10 +3,8 @@ import { ErrorPageApi } from './ErrorPageApi.ts';
3
3
  import { RobotsTxtApi } from './RobotsTxtApi.ts';
4
4
  import { SitemapApi } from './SitemapApi.ts';
5
5
 
6
- import { TOKEN_PREFIX } from '../Constants.ts';
7
-
8
6
  export default function register(container: DependencyContainer) {
9
- container.register(TOKEN_PREFIX + 'ErrorPageApi', { useClass: ErrorPageApi });
10
- container.register(TOKEN_PREFIX + 'RobotsTxtApi', { useClass: RobotsTxtApi });
11
- container.register(TOKEN_PREFIX + 'SitemapApi', { useClass: SitemapApi });
7
+ container.register(ErrorPageApi, { useClass: ErrorPageApi });
8
+ container.register(RobotsTxtApi, { useClass: RobotsTxtApi });
9
+ container.register(SitemapApi, { useClass: SitemapApi });
12
10
  }
@@ -1,5 +1,8 @@
1
1
  import { ComponentMappings } from '@magnolia/react-editor';
2
+ import { token } from '../Constants.ts';
2
3
 
3
4
  export interface ComponentMappingsProviderI {
4
5
  getComponentMappings(): ComponentMappings;
5
6
  }
7
+
8
+ export const COMPONENTS_MAPPINGS_PROVIDER_TOKEN = token('ComponentMappingsProviderI');
@@ -1,3 +1,5 @@
1
+ import { token } from '../Constants.ts';
2
+
1
3
  export interface MagnoliaApiEndpointsProvider {
2
4
  annotationTemplates(): string;
3
5
  pageContent(language: string): string;
@@ -33,3 +35,4 @@ export type Config = {
33
35
  export interface HeadlessConfigProviderI {
34
36
  get(): Config;
35
37
  }
38
+ export const HEADLESS_CONFIG_PROVIDER_TOKEN = token('HeadlessConfigProviderI');
@@ -1,3 +1,5 @@
1
+ import { token } from '../Constants.ts';
2
+
1
3
  export enum Level {
2
4
  DEBUG = 'DEBUG',
3
5
  INFO = 'INFO',
@@ -8,3 +10,4 @@ export enum Level {
8
10
  export interface LoggerI {
9
11
  log(level: Level, message: string): void;
10
12
  }
13
+ export const LOGGER_TOKEN = token('LoggerI');
@@ -1,3 +1,7 @@
1
+ import { ExtendedMagnoliaContext } from '../helper/MagnoliaContextProvider.ts';
2
+ import { token } from '../Constants.ts';
3
+
1
4
  export interface StylesheetProviderI {
2
- get(theme: string): string;
5
+ get(magnoliaContext: ExtendedMagnoliaContext, theme: string): string[];
3
6
  }
7
+ export const STYLESHEET_PROVIDER_TOKEN = token('StylesheetProviderI');
@@ -1,4 +1,4 @@
1
1
  export * from './Logger.ts';
2
2
  export * from './ConfigProvider.ts';
3
3
  export * from './ComponentMappingsProvider.ts';
4
- export { type StylesheetProviderI } from './StylesheetProvider.ts';
4
+ export * from './StylesheetProvider.ts';
@@ -80,14 +80,15 @@ export class SampleComponentMappingsProvider implements ComponentMappingsProvide
80
80
  `/app/config/SampleStylesheetProvider.ts`
81
81
 
82
82
  ```typescript
83
- import { injectable } from 'tsyringe';
84
- import { StylesheetProviderI } from '@merkle-open/magnolia-headless-frontend-nextjs';
83
+ import {injectable} from 'tsyringe';
84
+ import {StylesheetProviderI} from '@merkle-open/magnolia-headless-frontend-nextjs';
85
+ import {ExtendedMagnoliaContext} from "./MagnoliaContextProvider";
85
86
 
86
87
  @injectable()
87
88
  export class SampleStylesheetProvider implements StylesheetProviderI {
88
- public get(theme: string): string {
89
- return `/css/theme/${theme}.min.css`;
90
- }
89
+ public get(magnoliaContext: ExtendedMagnoliaContext, theme: string): string[] {
90
+ return [`/css/theme/${theme}.min.css`];
91
+ }
91
92
  }
92
93
  ```
93
94
 
@@ -97,19 +98,18 @@ export class SampleStylesheetProvider implements StylesheetProviderI {
97
98
 
98
99
  ```typescript
99
100
  import { DependencyContainer } from 'tsyringe';
100
- import { TOKEN_PREFIX } from '@merkle-open/magnolia-headless-frontend-nextjs';
101
+ import {
102
+ HEADLESS_CONFIG_PROVIDER_TOKEN,
103
+ COMPONENTS_MAPPINGS_PROVIDER_TOKEN,
104
+ LOGGER_TOKEN,
105
+ STYLESHEET_PROVIDER_I_TOKEN
106
+ } from '@merkle-open/magnolia-headless-frontend-nextjs';
101
107
  import { Logger } from '@merkle-open/magnolia-headless-frontend-nextjs';
102
108
 
103
109
  export default function register(container: DependencyContainer) {
104
- container.register(TOKEN_PREFIX + 'HeadlessConfigProviderI', {
105
- useClass: SampleHeadlessConfigProvider,
106
- });
107
- container.register(TOKEN_PREFIX + 'ComponentMappingsProviderI', {
108
- useClass: SampleComponentMappingsProvider,
109
- });
110
- container.register(TOKEN_PREFIX + 'StylesheetProviderI', {
111
- useClass: SampleStylesheetProvider,
112
- });
113
- container.register(TOKEN_PREFIX + 'LoggerI', { useClass: SampleLogger });
110
+ container.register(HEADLESS_CONFIG_PROVIDER_TOKEN, {useClass: SampleHeadlessConfigProvider});
111
+ container.register(COMPONENTS_MAPPINGS_PROVIDER_TOKEN, {useClass: SampleComponentMappingsProvider});
112
+ container.register(STYLESHEET_PROVIDER_I_TOKEN, {useClass: SampleStylesheetProvider});
113
+ container.register(LOGGER_TOKEN, { useClass: SampleLogger });
114
114
  }
115
115
  ```
@@ -4,12 +4,12 @@ import { ReactNode } from 'react';
4
4
  import { ErrorType } from '../helper/MagnoliaPageRestClient.ts';
5
5
  import { StaticErrorPage } from '../templates/pages/_error-static/ErrorStatic.tsx';
6
6
  import { AbstractDynamicPage } from './AbstractDynamicPage.tsx';
7
- import { Logger } from '../helper/Logger.ts';
8
7
  import { RestClient } from '../helper/RestClient.ts';
9
8
  import { MagnoliaContextProvider } from '../helper/MagnoliaContextProvider.ts';
10
9
  import type { FrontendApiEndpointsProvider, HeadlessConfigProviderI } from '../config/ConfigProvider.ts';
11
10
  import { type StylesheetProviderI } from '../config/StylesheetProvider.ts';
12
11
  import { type ComponentMappingsProviderI } from '../config/ComponentMappingsProvider.ts';
12
+ import { ThemeValidator } from '../helper/ThemeValidator.ts';
13
13
 
14
14
  export abstract class AbstractDynamicErrorPage extends AbstractDynamicPage {
15
15
  private readonly frontendApisProvider: FrontendApiEndpointsProvider;
@@ -18,12 +18,12 @@ export abstract class AbstractDynamicErrorPage extends AbstractDynamicPage {
18
18
  componentMappingsProvider: ComponentMappingsProviderI,
19
19
  configProvider: HeadlessConfigProviderI,
20
20
  StylesheetProviderI: StylesheetProviderI,
21
- logger: Logger,
21
+ themeValidator: ThemeValidator,
22
22
  private readonly restClient: RestClient,
23
23
  private readonly staticErrorPage: StaticErrorPage,
24
24
  protected readonly magnoliaContextProvider: MagnoliaContextProvider,
25
25
  ) {
26
- super(componentMappingsProvider, configProvider, StylesheetProviderI, logger);
26
+ super(componentMappingsProvider, StylesheetProviderI, themeValidator);
27
27
  this.frontendApisProvider = configProvider.get().frontendApisProvider;
28
28
  }
29
29
 
@@ -4,22 +4,16 @@ import { MgnlTemplateAnnotations } from '@magnolia/frontend-helpers-base';
4
4
  import React, { ReactNode } from 'react';
5
5
  import { MagnoliaConfig, RefService } from '@magnolia/react-editor';
6
6
  import { EditablePage } from '../templates/pages/__magnolia-editable-page/EditablePage.tsx';
7
- import { Logger } from '../helper/Logger.ts';
8
- import type { HeadlessConfigProviderI, ThemesProvider } from '../config/ConfigProvider.ts';
9
7
  import { type StylesheetProviderI } from '../config/StylesheetProvider.ts';
10
8
  import { type ComponentMappingsProviderI } from '../config/ComponentMappingsProvider.ts';
9
+ import { ThemeValidator } from '../helper/ThemeValidator.ts';
11
10
 
12
11
  export abstract class AbstractDynamicPage {
13
- private themesProvider: ThemesProvider;
14
-
15
12
  protected constructor(
16
13
  private readonly componentMappingsProvider: ComponentMappingsProviderI,
17
- readonly configProvider: HeadlessConfigProviderI,
18
14
  private readonly StylesheetProviderI: StylesheetProviderI,
19
- private readonly logger: Logger,
20
- ) {
21
- this.themesProvider = configProvider.get().themesProvider;
22
- }
15
+ private readonly themeValidator: ThemeValidator,
16
+ ) {}
23
17
 
24
18
  protected renderBase(magnoliaContext: ExtendedMagnoliaContext, content: Content, templateAnnotations: MgnlTemplateAnnotations): ReactNode {
25
19
  const config: MagnoliaConfig = {
@@ -32,22 +26,15 @@ export abstract class AbstractDynamicPage {
32
26
  return (
33
27
  <div>
34
28
  {/* is moved to <head> see https://react.dev/blog/2024/12/05/react-19#support-for-stylesheets */}
35
- <link rel="stylesheet" href={this.getStylesheet(content)} precedence="high" />
29
+ {this.getStylesheet(magnoliaContext, content).map((stylesheet) => (
30
+ <link rel="stylesheet" href={stylesheet} precedence="high" />
31
+ ))}
36
32
  <EditablePage content={content} config={config} templateAnnotations={templateAnnotations} magnoliaContext={magnoliaContext} />
37
33
  </div>
38
34
  );
39
35
  }
40
36
 
41
- private getStylesheet(content: Content) {
42
- return this.StylesheetProviderI.get(this.getValidatedTheme(content.theme));
43
- }
44
- private getValidatedTheme(theme?: string): string {
45
- if (!theme || !this.themesProvider.getAll().includes(theme)) {
46
- if (theme) {
47
- this.logger.error(`Got invalid theme '${theme}' from magnolia! falling back to ${this.themesProvider.getFallback()}`);
48
- }
49
- return this.themesProvider.getFallback();
50
- }
51
- return theme;
37
+ private getStylesheet(magnoliaContext: ExtendedMagnoliaContext, content: Content): string[] {
38
+ return this.StylesheetProviderI.get(magnoliaContext, this.themeValidator.getValidatedTheme(content.theme));
52
39
  }
53
40
  }
@@ -3,32 +3,32 @@
3
3
  import { ReactNode, useEffect, useState } from 'react';
4
4
  import { ErrorType } from '../helper/MagnoliaPageRestClient.ts';
5
5
  import { inject, injectable } from 'tsyringe';
6
- import { Logger } from '../helper/Logger.ts';
7
6
  import { RestClient } from '../helper/RestClient.ts';
8
7
  import { MagnoliaContextProvider } from '../helper/MagnoliaContextProvider.ts';
9
- import type { HeadlessConfigProviderI } from '../config/ConfigProvider.ts';
8
+ import { type HeadlessConfigProviderI, HEADLESS_CONFIG_PROVIDER_TOKEN } from '../config/ConfigProvider.ts';
10
9
  import { AbstractDynamicErrorPage } from './AbstractDynamicErrorPage.tsx';
11
10
 
12
- import { TOKEN_PREFIX } from '../Constants.ts';
13
- import { type StylesheetProviderI } from '../config/StylesheetProvider.ts';
11
+ import { type StylesheetProviderI, STYLESHEET_PROVIDER_TOKEN } from '../config/StylesheetProvider.ts';
14
12
  import { type ComponentMappingsProviderI } from '../config/ComponentMappingsProvider.ts';
15
13
  import { StaticErrorPage } from '../templates/pages/_error-static/ErrorStatic.tsx';
14
+ import { CombinedComponentMappingsProvider } from '../templates/ComponentMappingsProvider.ts';
15
+ import { ThemeValidator } from '../helper/ThemeValidator.ts';
16
16
 
17
17
  @injectable()
18
18
  export class DynamicErrorPage extends AbstractDynamicErrorPage {
19
19
  constructor(
20
- @inject(TOKEN_PREFIX + 'CombinedComponentMappingsProvider') componentMappingsProvider: ComponentMappingsProviderI,
21
- @inject(TOKEN_PREFIX + 'HeadlessConfigProviderI') configProvider: HeadlessConfigProviderI,
22
- @inject(TOKEN_PREFIX + 'StylesheetProviderI') StylesheetProviderI: StylesheetProviderI,
23
- @inject(TOKEN_PREFIX + 'Logger') logger: Logger,
24
- @inject(TOKEN_PREFIX + 'RestClient') restClient: RestClient,
25
- @inject(TOKEN_PREFIX + 'StaticErrorPage') staticErrorPage: StaticErrorPage,
26
- @inject(TOKEN_PREFIX + 'MagnoliaContextProvider') magnoliaContextProvider: MagnoliaContextProvider,
20
+ @inject(CombinedComponentMappingsProvider) componentMappingsProvider: ComponentMappingsProviderI,
21
+ @inject(HEADLESS_CONFIG_PROVIDER_TOKEN) configProvider: HeadlessConfigProviderI,
22
+ @inject(STYLESHEET_PROVIDER_TOKEN) StylesheetProviderI: StylesheetProviderI,
23
+ @inject(ThemeValidator) themeValidator: ThemeValidator,
24
+ @inject(RestClient) restClient: RestClient,
25
+ @inject(StaticErrorPage) staticErrorPage: StaticErrorPage,
26
+ @inject(MagnoliaContextProvider) magnoliaContextProvider: MagnoliaContextProvider,
27
27
  ) {
28
- super(componentMappingsProvider, configProvider, StylesheetProviderI, logger, restClient, staticErrorPage, magnoliaContextProvider);
28
+ super(componentMappingsProvider, configProvider, StylesheetProviderI, themeValidator, restClient, staticErrorPage, magnoliaContextProvider);
29
29
  }
30
30
 
31
- public async render(errorType: ErrorType): Promise<ReactNode> {
31
+ public render(errorType: ErrorType): ReactNode {
32
32
  const [errorPage, setErrorPage] = useState(null);
33
33
 
34
34
  useEffect(() => {
@@ -1,31 +1,33 @@
1
+ 'use client';
2
+
1
3
  import { ReactNode, useEffect, useState } from 'react';
2
4
  import { ErrorType } from '../helper/MagnoliaPageRestClient.ts';
3
5
  import { inject, injectable } from 'tsyringe';
4
6
  import { DynamicPageLayout } from './DynamicPageLayout.tsx';
5
7
  import { AbstractDynamicErrorPage } from './AbstractDynamicErrorPage.tsx';
6
- import type { HeadlessConfigProviderI } from '../config/ConfigProvider.ts';
8
+ import { type HeadlessConfigProviderI, HEADLESS_CONFIG_PROVIDER_TOKEN } from '../config/ConfigProvider.ts';
7
9
  import { RestClient } from '../helper/RestClient.ts';
8
10
  import { MagnoliaContextProvider } from '../helper/MagnoliaContextProvider.ts';
9
- import { Logger } from '../helper/Logger.ts';
10
11
 
11
- import { TOKEN_PREFIX } from '../Constants.ts';
12
- import { type StylesheetProviderI } from '../config/StylesheetProvider.ts';
12
+ import { type StylesheetProviderI, STYLESHEET_PROVIDER_TOKEN } from '../config/StylesheetProvider.ts';
13
13
  import { type ComponentMappingsProviderI } from '../config/ComponentMappingsProvider.ts';
14
14
  import { StaticErrorPage } from '../templates/pages/_error-static/ErrorStatic.tsx';
15
+ import { CombinedComponentMappingsProvider } from '../templates/ComponentMappingsProvider.ts';
16
+ import { ThemeValidator } from '../helper/ThemeValidator.ts';
15
17
 
16
18
  @injectable()
17
19
  export class DynamicErrorPageGlobal extends AbstractDynamicErrorPage {
18
20
  constructor(
19
- @inject(TOKEN_PREFIX + 'CombinedComponentMappingsProvider') componentMappingsProvider: ComponentMappingsProviderI,
20
- @inject(TOKEN_PREFIX + 'HeadlessConfigProviderI') configProvider: HeadlessConfigProviderI,
21
- @inject(TOKEN_PREFIX + 'StylesheetProviderI') StylesheetProviderI: StylesheetProviderI,
22
- @inject(TOKEN_PREFIX + 'Logger') logger: Logger,
23
- @inject(TOKEN_PREFIX + 'RestClient') restClient: RestClient,
24
- @inject(TOKEN_PREFIX + 'StaticErrorPage') staticErrorPage: StaticErrorPage,
25
- @inject(TOKEN_PREFIX + 'MagnoliaContextProvider') magnoliaContextProvider: MagnoliaContextProvider,
26
- @inject(TOKEN_PREFIX + 'DynamicPageLayout') private readonly dynamicPageLayout: DynamicPageLayout,
21
+ @inject(CombinedComponentMappingsProvider) componentMappingsProvider: ComponentMappingsProviderI,
22
+ @inject(HEADLESS_CONFIG_PROVIDER_TOKEN) configProvider: HeadlessConfigProviderI,
23
+ @inject(STYLESHEET_PROVIDER_TOKEN) StylesheetProviderI: StylesheetProviderI,
24
+ @inject(ThemeValidator) themeValidator: ThemeValidator,
25
+ @inject(RestClient) restClient: RestClient,
26
+ @inject(StaticErrorPage) staticErrorPage: StaticErrorPage,
27
+ @inject(MagnoliaContextProvider) magnoliaContextProvider: MagnoliaContextProvider,
28
+ @inject(DynamicPageLayout) private readonly dynamicPageLayout: DynamicPageLayout,
27
29
  ) {
28
- super(componentMappingsProvider, configProvider, StylesheetProviderI, logger, restClient, staticErrorPage, magnoliaContextProvider);
30
+ super(componentMappingsProvider, configProvider, StylesheetProviderI, themeValidator, restClient, staticErrorPage, magnoliaContextProvider);
29
31
  }
30
32
 
31
33
  public render(errorType: ErrorType): ReactNode {
@@ -39,17 +41,22 @@ export class DynamicErrorPageGlobal extends AbstractDynamicErrorPage {
39
41
  }, []);
40
42
 
41
43
  if (!errorPage) {
42
- return (
43
- <html>
44
- <body>
45
- <div>Loading...</div>
46
- </body>
47
- </html>
48
- );
44
+ return this.renderLoader(errorType);
49
45
  }
50
46
  return errorPage;
51
47
  }
52
48
 
49
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
50
+ protected renderLoader(errorType: ErrorType): ReactNode {
51
+ return (
52
+ <html>
53
+ <body>
54
+ <div>Loading...</div>
55
+ </body>
56
+ </html>
57
+ );
58
+ }
59
+
53
60
  private async renderGlobalDynamicError(language: string, errorType: ErrorType): Promise<ReactNode> {
54
61
  const currentUrl = new URL(window.location.href);
55
62
  currentUrl.pathname = language;
@@ -1,33 +1,32 @@
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
2
+ import { notFound } from 'next/navigation';
1
3
  import { ReactNode } from 'react';
2
4
  import { MagnoliaContextProvider } from '../helper/MagnoliaContextProvider.ts';
3
5
  import { MagnoliaPageRestClient } from '../helper/MagnoliaPageRestClient.ts';
4
- import { notFound } from 'next/navigation';
5
6
  import { MgnlTemplateAnnotations } from '@magnolia/frontend-helpers-base';
6
7
  import { Metadata } from 'next';
7
8
  import { MetadataProvider } from '../helper/MetadataProvider.ts';
8
9
  import { inject, injectable } from 'tsyringe';
9
10
  import { AbstractDynamicPage } from './AbstractDynamicPage.tsx';
10
- import { Logger } from '../helper/Logger.ts';
11
- import type { HeadlessConfigProviderI } from '../config/ConfigProvider.ts';
12
11
  import PageProps, { UrlProvider } from './PageProps.ts';
13
12
 
14
- import { TOKEN_PREFIX } from '../Constants.ts';
15
- import { type StylesheetProviderI } from '../config/StylesheetProvider.ts';
13
+ import { type StylesheetProviderI, STYLESHEET_PROVIDER_TOKEN } from '../config/StylesheetProvider.ts';
16
14
  import { type ComponentMappingsProviderI } from '../config/ComponentMappingsProvider.ts';
15
+ import { CombinedComponentMappingsProvider } from '../templates/ComponentMappingsProvider.ts';
16
+ import { ThemeValidator } from '../helper/ThemeValidator.ts';
17
17
 
18
18
  @injectable()
19
19
  export class DynamicPage extends AbstractDynamicPage {
20
20
  constructor(
21
- @inject(TOKEN_PREFIX + 'CombinedComponentMappingsProvider') componentMappingsProvider: ComponentMappingsProviderI,
22
- @inject(TOKEN_PREFIX + 'HeadlessConfigProviderI') configProvider: HeadlessConfigProviderI,
23
- @inject(TOKEN_PREFIX + 'StylesheetProviderI') StylesheetProviderI: StylesheetProviderI,
24
- @inject(TOKEN_PREFIX + 'Logger') logger: Logger,
25
- @inject(TOKEN_PREFIX + 'MagnoliaContextProvider') private readonly magnoliaContextProvider: MagnoliaContextProvider,
26
- @inject(TOKEN_PREFIX + 'MagnoliaPageRestClient') private readonly magnoliaPageRestClient: MagnoliaPageRestClient,
27
- @inject(TOKEN_PREFIX + 'MetadataProvider') private readonly metadataProvider: MetadataProvider,
28
- @inject(TOKEN_PREFIX + 'UrlProvider') private readonly urlProvider: UrlProvider,
21
+ @inject(CombinedComponentMappingsProvider) componentMappingsProvider: ComponentMappingsProviderI,
22
+ @inject(STYLESHEET_PROVIDER_TOKEN) StylesheetProviderI: StylesheetProviderI,
23
+ @inject(ThemeValidator) themeValidator: ThemeValidator,
24
+ @inject(MagnoliaContextProvider) private readonly magnoliaContextProvider: MagnoliaContextProvider,
25
+ @inject(MagnoliaPageRestClient) private readonly magnoliaPageRestClient: MagnoliaPageRestClient,
26
+ @inject(MetadataProvider) private readonly metadataProvider: MetadataProvider,
27
+ @inject(UrlProvider) private readonly urlProvider: UrlProvider,
29
28
  ) {
30
- super(componentMappingsProvider, configProvider, StylesheetProviderI, logger);
29
+ super(componentMappingsProvider, StylesheetProviderI, themeValidator);
31
30
  }
32
31
 
33
32
  public async render(pageProps: PageProps): Promise<ReactNode> {
@@ -3,8 +3,6 @@ import { inject, injectable } from 'tsyringe';
3
3
  import { MagnoliaContextProvider } from '../helper/MagnoliaContextProvider.ts';
4
4
  import { UrlProvider, Params } from './PageProps.ts';
5
5
 
6
- import { TOKEN_PREFIX } from '../Constants.ts';
7
-
8
6
  export interface Props {
9
7
  children: ReactNode;
10
8
  params: Promise<Params>;
@@ -13,8 +11,8 @@ export interface Props {
13
11
  @injectable()
14
12
  export class DynamicPageLayout {
15
13
  constructor(
16
- @inject(TOKEN_PREFIX + 'MagnoliaContextProvider') private readonly magnoliaContextProvider: MagnoliaContextProvider,
17
- @inject(TOKEN_PREFIX + 'UrlProvider') private readonly urlProvider: UrlProvider,
14
+ @inject(MagnoliaContextProvider) private readonly magnoliaContextProvider: MagnoliaContextProvider,
15
+ @inject(UrlProvider) private readonly urlProvider: UrlProvider,
18
16
  ) {}
19
17
 
20
18
  public async renderUrl(props: Props): Promise<ReactNode> {
@@ -1,3 +1,4 @@
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
1
2
  import { headers } from 'next/headers';
2
3
  import { injectable } from 'tsyringe';
3
4
 
@@ -5,12 +5,10 @@ import { DynamicErrorPageGlobal } from './DynamicErrorPageGlobal.tsx';
5
5
  import { DynamicPageLayout } from './DynamicPageLayout.tsx';
6
6
  import { UrlProvider } from './PageProps.ts';
7
7
 
8
- import { TOKEN_PREFIX } from '../Constants.ts';
9
-
10
8
  export default function register(container: DependencyContainer) {
11
- container.register(TOKEN_PREFIX + 'DynamicErrorPage', { useClass: DynamicErrorPage });
12
- container.register(TOKEN_PREFIX + 'DynamicErrorPageGlobal', { useClass: DynamicErrorPageGlobal });
13
- container.register(TOKEN_PREFIX + 'DynamicPage', { useClass: DynamicPage });
14
- container.register(TOKEN_PREFIX + 'DynamicPageLayout', { useClass: DynamicPageLayout });
15
- container.register(TOKEN_PREFIX + 'UrlProvider', { useClass: UrlProvider });
9
+ container.register(DynamicErrorPage, { useClass: DynamicErrorPage });
10
+ container.register(DynamicErrorPageGlobal, { useClass: DynamicErrorPageGlobal });
11
+ container.register(DynamicPage, { useClass: DynamicPage });
12
+ container.register(DynamicPageLayout, { useClass: DynamicPageLayout });
13
+ container.register(UrlProvider, { useClass: UrlProvider });
16
14
  }
@@ -3,11 +3,9 @@ import { NextRequest } from 'next/server.d.ts';
3
3
  import { injectable, inject } from 'tsyringe';
4
4
  import { Logger } from './Logger.ts';
5
5
 
6
- import { TOKEN_PREFIX } from '../Constants.ts';
7
-
8
6
  @injectable()
9
7
  export class BrowserLanguageProvider {
10
- constructor(@inject(TOKEN_PREFIX + 'Logger') private readonly logger: Logger) {}
8
+ constructor(@inject(Logger) private readonly logger: Logger) {}
11
9
 
12
10
  public getBrowserLanguage(req: NextRequest): string {
13
11
  try {
@@ -1,11 +1,10 @@
1
1
  import { inject, injectable } from 'tsyringe';
2
2
 
3
- import { TOKEN_PREFIX } from '../Constants.ts';
4
- import { Level, type LoggerI } from '../config/Logger.ts';
3
+ import { Level, type LoggerI, LOGGER_TOKEN } from '../config/Logger.ts';
5
4
 
6
5
  @injectable()
7
6
  export class Logger {
8
- constructor(@inject(TOKEN_PREFIX + 'LoggerI') private readonly logger: LoggerI) {}
7
+ constructor(@inject(LOGGER_TOKEN) private readonly logger: LoggerI) {}
9
8
 
10
9
  public debug(message: string): void {
11
10
  this.logger.log(Level.DEBUG, message);
@@ -1,9 +1,7 @@
1
1
  import { EditorContextService, IMagnoliaContext } from '@magnolia/frontend-helpers-base';
2
2
  import { inject, injectable } from 'tsyringe';
3
3
  import { Logger } from './Logger.ts';
4
- import type { HeadlessConfigProviderI } from '../config/ConfigProvider.ts';
5
-
6
- import { TOKEN_PREFIX } from '../Constants.ts';
4
+ import { type HeadlessConfigProviderI, HEADLESS_CONFIG_PROVIDER_TOKEN } from '../config/ConfigProvider.ts';
7
5
 
8
6
  export interface ExtendedMagnoliaContext extends IMagnoliaContext {
9
7
  domain: string;
@@ -16,8 +14,8 @@ export class MagnoliaContextProvider {
16
14
  private readonly multiTree: boolean;
17
15
 
18
16
  constructor(
19
- @inject(TOKEN_PREFIX + 'HeadlessConfigProviderI') readonly configProvider: HeadlessConfigProviderI,
20
- @inject(TOKEN_PREFIX + 'Logger') private readonly logger: Logger,
17
+ @inject(HEADLESS_CONFIG_PROVIDER_TOKEN) readonly configProvider: HeadlessConfigProviderI,
18
+ @inject(Logger) private readonly logger: Logger,
21
19
  ) {
22
20
  this.multiTree = configProvider.get().multiTree;
23
21
  }
@@ -3,9 +3,7 @@ import { RestClient } from './RestClient.ts';
3
3
  import { MagnoliaContextProvider } from './MagnoliaContextProvider.ts';
4
4
  import MetaProps from './MetaProps.ts';
5
5
  import { inject, injectable } from 'tsyringe';
6
- import type { HeadlessConfigProviderI, MagnoliaApiEndpointsProvider } from '../config/ConfigProvider.ts';
7
-
8
- import { TOKEN_PREFIX } from '../Constants.ts';
6
+ import { type HeadlessConfigProviderI, MagnoliaApiEndpointsProvider, HEADLESS_CONFIG_PROVIDER_TOKEN } from '../config/ConfigProvider.ts';
9
7
 
10
8
  export interface Content extends MgnlContent {
11
9
  cspHeader?: string;
@@ -23,9 +21,9 @@ export class MagnoliaPageRestClient {
23
21
  private readonly apisProvider: MagnoliaApiEndpointsProvider;
24
22
 
25
23
  constructor(
26
- @inject(TOKEN_PREFIX + 'HeadlessConfigProviderI') readonly configProvider: HeadlessConfigProviderI,
27
- @inject(TOKEN_PREFIX + 'MagnoliaContextProvider') private readonly magnoliaContextProvider: MagnoliaContextProvider,
28
- @inject(TOKEN_PREFIX + 'RestClient') private readonly restClient: RestClient,
24
+ @inject(HEADLESS_CONFIG_PROVIDER_TOKEN) readonly configProvider: HeadlessConfigProviderI,
25
+ @inject(MagnoliaContextProvider) private readonly magnoliaContextProvider: MagnoliaContextProvider,
26
+ @inject(RestClient) private readonly restClient: RestClient,
29
27
  ) {
30
28
  this.apisProvider = configProvider.get().magnoliaApisProvider;
31
29
  }
@@ -1,7 +1,9 @@
1
- import MetaProps, { OpenGraph as HeadlessOpenGraph } from './MetaProps.ts';
2
- import { Metadata } from 'next';
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
3
2
  import type { OpenGraph } from 'next/dist/lib/metadata/types/opengraph-types';
3
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
4
4
  import type { Twitter } from 'next/dist/lib/metadata/types/twitter-types';
5
+ import MetaProps, { OpenGraph as HeadlessOpenGraph } from './MetaProps.ts';
6
+ import { Metadata } from 'next';
5
7
  import { injectable } from 'tsyringe';
6
8
 
7
9
  @injectable()
@@ -1,14 +1,12 @@
1
1
  import { createBasicAuthHeader } from './BasicAuth.ts';
2
2
  import { inject, injectable } from 'tsyringe';
3
- import type { Credentials, HeadlessConfigProviderI } from '../config/ConfigProvider.ts';
4
-
5
- import { TOKEN_PREFIX } from '../Constants.ts';
3
+ import { Credentials, type HeadlessConfigProviderI, HEADLESS_CONFIG_PROVIDER_TOKEN } from '../config/ConfigProvider.ts';
6
4
 
7
5
  @injectable()
8
6
  export class RestClient {
9
7
  private readonly magnoliaCredentials: Credentials;
10
8
 
11
- constructor(@inject(TOKEN_PREFIX + 'HeadlessConfigProviderI') readonly configProvider: HeadlessConfigProviderI) {
9
+ constructor(@inject(HEADLESS_CONFIG_PROVIDER_TOKEN) readonly configProvider: HeadlessConfigProviderI) {
12
10
  this.magnoliaCredentials = configProvider.get().magnoliaCredentials;
13
11
  }
14
12
 
@@ -0,0 +1,25 @@
1
+ import { inject, injectable } from 'tsyringe';
2
+ import { type HeadlessConfigProviderI, HEADLESS_CONFIG_PROVIDER_TOKEN, ThemesProvider } from '../config/ConfigProvider.ts';
3
+ import { Logger } from './Logger.ts';
4
+
5
+ @injectable()
6
+ export class ThemeValidator {
7
+ private readonly themesProvider: ThemesProvider;
8
+
9
+ constructor(
10
+ @inject(HEADLESS_CONFIG_PROVIDER_TOKEN) configProvider: HeadlessConfigProviderI,
11
+ @inject(Logger) private readonly logger: Logger,
12
+ ) {
13
+ this.themesProvider = configProvider.get().themesProvider;
14
+ }
15
+
16
+ public getValidatedTheme(theme?: string): string {
17
+ if (!theme || !this.themesProvider.getAll().includes(theme)) {
18
+ if (theme) {
19
+ this.logger.error(`Got invalid theme '${theme}' from magnolia! falling back to ${this.themesProvider.getFallback()}`);
20
+ }
21
+ return this.themesProvider.getFallback();
22
+ }
23
+ return theme;
24
+ }
25
+ }
@@ -5,14 +5,14 @@ import { Logger } from './Logger.ts';
5
5
  import { BrowserLanguageProvider } from './BrowserLanguageProvider.ts';
6
6
  import { MagnoliaContextProvider } from './MagnoliaContextProvider.ts';
7
7
  import { MagnoliaPageRestClient } from './MagnoliaPageRestClient.ts';
8
-
9
- import { TOKEN_PREFIX } from '../Constants.ts';
8
+ import { ThemeValidator } from './ThemeValidator.ts';
10
9
 
11
10
  export default function register(container: DependencyContainer) {
12
- container.register(TOKEN_PREFIX + 'MetadataProvider', { useClass: MetadataProvider });
13
- container.register(TOKEN_PREFIX + 'Logger', { useClass: Logger });
14
- container.register(TOKEN_PREFIX + 'BrowserLanguageProvider', { useClass: BrowserLanguageProvider });
15
- container.register(TOKEN_PREFIX + 'MagnoliaContextProvider', { useClass: MagnoliaContextProvider });
16
- container.register(TOKEN_PREFIX + 'MagnoliaPageRestClient', { useClass: MagnoliaPageRestClient });
17
- container.register(TOKEN_PREFIX + 'RestClient', { useClass: RestClient });
11
+ container.register(MetadataProvider, { useClass: MetadataProvider });
12
+ container.register(Logger, { useClass: Logger });
13
+ container.register(BrowserLanguageProvider, { useClass: BrowserLanguageProvider });
14
+ container.register(MagnoliaContextProvider, { useClass: MagnoliaContextProvider });
15
+ container.register(MagnoliaPageRestClient, { useClass: MagnoliaPageRestClient });
16
+ container.register(RestClient, { useClass: RestClient });
17
+ container.register(ThemeValidator, { useClass: ThemeValidator });
18
18
  }
@@ -1,2 +1,9 @@
1
1
  export { ErrorType } from './MagnoliaPageRestClient.ts';
2
+ export { type ExtendedMagnoliaContext } from './MagnoliaContextProvider.ts';
3
+ export * from './BasicAuth.ts';
4
+ export * from './BrowserLanguageProvider.ts';
2
5
  export * from './Logger.ts';
6
+ export * from './RestClient.ts';
7
+ export * from './MetadataProvider.ts';
8
+ export * from './MetaProps.ts';
9
+ export * from './ThemeValidator.ts';
@@ -1,10 +1,13 @@
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
1
2
  import { NextFetchEvent, NextProxy, NextRequest, NextResponse } from 'next/server';
3
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
2
4
  import { NextMiddlewareResult } from 'next/dist/server/web/types';
3
5
  import { Logger } from '../helper/Logger.ts';
4
6
  import { inject, injectable, injectAllWithTransform } from 'tsyringe';
7
+ // @ts-expect-error: tsyringe missing exports prevents ESM resolution with 'nodenext'.
5
8
  import { Transform } from 'tsyringe/dist/typings/types';
6
9
 
7
- import { TOKEN_PREFIX } from '../Constants.ts';
10
+ import { token } from '../Constants.ts';
8
11
 
9
12
  export interface Middleware {
10
13
  apply(request: NextRequest, response: NextResponse, event: NextFetchEvent): NextMiddlewareResult | Promise<NextMiddlewareResult>;
@@ -12,6 +15,7 @@ export interface Middleware {
12
15
  getOrder(): number;
13
16
  getName(): string;
14
17
  }
18
+ export const MIDDLEWARE_TOKEN = token('Middleware');
15
19
 
16
20
  export abstract class AbstractMiddleware implements Middleware {
17
21
  public abstract apply(request: NextRequest, response: NextResponse, event: NextFetchEvent): NextMiddlewareResult | Promise<NextMiddlewareResult>;
@@ -36,8 +40,8 @@ class MiddlewareTransform implements Transform<Middleware[], Middleware[]> {
36
40
  @injectable()
37
41
  export class MiddlewareComposer {
38
42
  constructor(
39
- @injectAllWithTransform(TOKEN_PREFIX + 'Middleware', MiddlewareTransform) private middlewares: Middleware[],
40
- @inject(TOKEN_PREFIX + 'Logger') private logger: Logger,
43
+ @injectAllWithTransform(MIDDLEWARE_TOKEN, MiddlewareTransform) private middlewares: Middleware[],
44
+ @inject(Logger) private logger: Logger,
41
45
  ) {}
42
46
 
43
47
  public composeMiddleware(): NextProxy {
@@ -2,13 +2,11 @@ import { DependencyContainer } from 'tsyringe';
2
2
  import { DynamicResponseHeaderMiddleware } from './impl/DynamicResponseHeaderMiddleware.ts';
3
3
  import { I18nRedirectMiddleware } from './impl/I18nRedirectMiddleware.ts';
4
4
  import { VanityMiddleware } from './impl/VanityMiddleware.ts';
5
- import { MiddlewareComposer } from './Middleware.ts';
6
-
7
- import { TOKEN_PREFIX } from '../Constants.ts';
5
+ import { MiddlewareComposer, MIDDLEWARE_TOKEN } from './Middleware.ts';
8
6
 
9
7
  export default function register(container: DependencyContainer) {
10
- container.register(TOKEN_PREFIX + 'Middleware', { useClass: DynamicResponseHeaderMiddleware });
11
- container.register(TOKEN_PREFIX + 'Middleware', { useClass: I18nRedirectMiddleware });
12
- container.register(TOKEN_PREFIX + 'Middleware', { useClass: VanityMiddleware });
13
- container.register(TOKEN_PREFIX + 'MiddlewareComposer', { useClass: MiddlewareComposer });
8
+ container.register(MIDDLEWARE_TOKEN, { useClass: DynamicResponseHeaderMiddleware });
9
+ container.register(MIDDLEWARE_TOKEN, { useClass: I18nRedirectMiddleware });
10
+ container.register(MIDDLEWARE_TOKEN, { useClass: VanityMiddleware });
11
+ container.register(MiddlewareComposer, { useClass: MiddlewareComposer });
14
12
  }
@@ -63,10 +63,10 @@ export class SomeMiddleware extends AbstractMiddleware {
63
63
  Add binding:
64
64
 
65
65
  ```typescript
66
- import { TOKEN_PREFIX } from '@merkle-open/magnolia-headless-frontend-nextjs';
66
+ import { MIDDLEWARE_TOKEN } from '@merkle-open/magnolia-headless-frontend-nextjs';
67
67
  import { DependencyContainer } from 'tsyringe';
68
68
 
69
69
  function bindSomeMiddleware(container: DependencyContainer): void {
70
- container.register(TOKEN_PREFIX + 'Middleware', {useClass: SomeMiddleware});
70
+ container.register(MIDDLEWARE_TOKEN, {useClass: SomeMiddleware});
71
71
  }
72
72
  ```
@@ -1,19 +1,19 @@
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
1
2
  import { NextRequest, NextResponse } from 'next/server';
3
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
2
4
  import { NextMiddlewareResult } from 'next/dist/server/web/types';
3
5
  import { RestClient } from '../../helper/RestClient.ts';
4
6
  import { AbstractMiddleware } from '../Middleware.ts';
5
7
  import { inject, injectable } from 'tsyringe';
6
- import type { HeadlessConfigProviderI, MagnoliaApiEndpointsProvider } from '../../config/ConfigProvider.ts';
7
-
8
- import { TOKEN_PREFIX } from '../../Constants.ts';
8
+ import { type HeadlessConfigProviderI, MagnoliaApiEndpointsProvider, HEADLESS_CONFIG_PROVIDER_TOKEN } from '../../config/ConfigProvider.ts';
9
9
 
10
10
  @injectable()
11
11
  export class DynamicResponseHeaderMiddleware extends AbstractMiddleware {
12
12
  private apisProvider: MagnoliaApiEndpointsProvider;
13
13
 
14
14
  constructor(
15
- @inject(TOKEN_PREFIX + 'HeadlessConfigProviderI') configProvider: HeadlessConfigProviderI,
16
- @inject(TOKEN_PREFIX + 'RestClient') private restClient: RestClient,
15
+ @inject(HEADLESS_CONFIG_PROVIDER_TOKEN) configProvider: HeadlessConfigProviderI,
16
+ @inject(RestClient) private restClient: RestClient,
17
17
  ) {
18
18
  super();
19
19
  this.apisProvider = configProvider.get().magnoliaApisProvider;
@@ -1,14 +1,14 @@
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
1
2
  import { NextRequest, NextResponse } from 'next/server';
3
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
2
4
  import { NextMiddlewareResult } from 'next/dist/server/web/types';
3
5
  import { AbstractMiddleware } from '../Middleware.ts';
4
- import type { BrowserLanguageProvider } from '../../helper/BrowserLanguageProvider.ts';
6
+ import { BrowserLanguageProvider } from '../../helper/BrowserLanguageProvider.ts';
5
7
  import { inject, injectable } from 'tsyringe';
6
8
 
7
- import { TOKEN_PREFIX } from '../../Constants.ts';
8
-
9
9
  @injectable()
10
10
  export class I18nRedirectMiddleware extends AbstractMiddleware {
11
- constructor(@inject(TOKEN_PREFIX + 'BrowserLanguageProvider') private browserLanguageProvider: BrowserLanguageProvider) {
11
+ constructor(@inject(BrowserLanguageProvider) private browserLanguageProvider: BrowserLanguageProvider) {
12
12
  super();
13
13
  }
14
14
 
@@ -1,12 +1,12 @@
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
1
2
  import { NextRequest, NextResponse } from 'next/server';
3
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
2
4
  import { NextMiddlewareResult } from 'next/dist/server/web/types';
3
5
  import { RestClient } from '../../helper/RestClient.ts';
4
6
  import { AbstractMiddleware } from '../Middleware.ts';
5
- import type { BrowserLanguageProvider } from '../../helper/BrowserLanguageProvider.ts';
7
+ import { BrowserLanguageProvider } from '../../helper/BrowserLanguageProvider.ts';
6
8
  import { inject, injectable } from 'tsyringe';
7
- import type { HeadlessConfigProviderI, MagnoliaApiEndpointsProvider } from '../../config/ConfigProvider.ts';
8
-
9
- import { TOKEN_PREFIX } from '../../Constants.ts';
9
+ import { type HeadlessConfigProviderI, MagnoliaApiEndpointsProvider, HEADLESS_CONFIG_PROVIDER_TOKEN } from '../../config/ConfigProvider.ts';
10
10
 
11
11
  export enum RedirectType {
12
12
  TEMPORARY = 'redirect',
@@ -26,9 +26,9 @@ export class VanityMiddleware extends AbstractMiddleware {
26
26
  private apisProvider: MagnoliaApiEndpointsProvider;
27
27
 
28
28
  constructor(
29
- @inject(TOKEN_PREFIX + 'HeadlessConfigProviderI') configProvider: HeadlessConfigProviderI,
30
- @inject(TOKEN_PREFIX + 'RestClient') private restClient: RestClient,
31
- @inject(TOKEN_PREFIX + 'BrowserLanguageProvider') private browserLanguageProvider: BrowserLanguageProvider,
29
+ @inject(HEADLESS_CONFIG_PROVIDER_TOKEN) configProvider: HeadlessConfigProviderI,
30
+ @inject(RestClient) private restClient: RestClient,
31
+ @inject(BrowserLanguageProvider) private browserLanguageProvider: BrowserLanguageProvider,
32
32
  ) {
33
33
  super();
34
34
  this.apisProvider = configProvider.get().magnoliaApisProvider;
@@ -1,12 +1,11 @@
1
1
  import { ComponentMappings } from '@magnolia/react-editor';
2
2
  import { inject, injectable } from 'tsyringe';
3
- import { TOKEN_PREFIX } from '../Constants.ts';
4
3
  import getComponentMappings from './magnolia.config.ts';
5
- import { type ComponentMappingsProviderI } from '../config/ComponentMappingsProvider.ts';
4
+ import { type ComponentMappingsProviderI, COMPONENTS_MAPPINGS_PROVIDER_TOKEN } from '../config/ComponentMappingsProvider.ts';
6
5
 
7
6
  @injectable()
8
7
  export class CombinedComponentMappingsProvider implements ComponentMappingsProviderI {
9
- constructor(@inject(TOKEN_PREFIX + 'ComponentMappingsProviderI') private readonly componentMappingsProvider: ComponentMappingsProviderI) {}
8
+ constructor(@inject(COMPONENTS_MAPPINGS_PROVIDER_TOKEN) private readonly componentMappingsProvider: ComponentMappingsProviderI) {}
10
9
 
11
10
  public getComponentMappings(): ComponentMappings {
12
11
  return {
@@ -1,10 +1,9 @@
1
1
  import { DependencyContainer } from 'tsyringe';
2
2
 
3
- import { TOKEN_PREFIX } from '../Constants.ts';
4
3
  import { CombinedComponentMappingsProvider } from './ComponentMappingsProvider.ts';
5
4
  import { StaticErrorPage } from './pages/_error-static/ErrorStatic.tsx';
6
5
 
7
6
  export default function register(container: DependencyContainer) {
8
- container.register(TOKEN_PREFIX + 'CombinedComponentMappingsProvider', { useClass: CombinedComponentMappingsProvider });
9
- container.register(TOKEN_PREFIX + 'StaticErrorPage', { useClass: StaticErrorPage });
7
+ container.register(CombinedComponentMappingsProvider, { useClass: CombinedComponentMappingsProvider });
8
+ container.register(StaticErrorPage, { useClass: StaticErrorPage });
10
9
  }
@@ -3,3 +3,5 @@ export type { default as PageBaseProps } from './pages/BaseProps.ts';
3
3
  export type { default as AreaBaseProps } from './areas/BaseProps.ts';
4
4
  export * from './areas/__magnolia-editable-area/EditableArea.tsx';
5
5
  export type { default as ComponentBaseProps } from './components/BaseProps.ts';
6
+ export * from './pages/_error-static/ErrorStatic.tsx';
7
+ export type { default as StaticErrorPageProps } from './pages/_error-static/ErrorStaticProps.ts';
@@ -1,5 +1,6 @@
1
- import { ReactNode } from 'react';
1
+ // @ts-expect-error: Next.js missing exports prevents ESM resolution with 'nodenext'.
2
2
  import { redirect, permanentRedirect } from 'next/navigation';
3
+ import { ReactNode } from 'react';
3
4
  import RedirectComponentProps, { RedirectType } from './RedirectComponentProps.ts';
4
5
 
5
6
  export default function Redirect({ destination, statusCode }: RedirectComponentProps): ReactNode {