_document.page.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /* eslint-disable @next/next/google-font-display */
  2. import React from 'react';
  3. import type { PresetThemesManifest } from '@growi/preset-themes';
  4. import { getManifestKeyFromTheme } from '@growi/preset-themes';
  5. import mongoose from 'mongoose';
  6. import Document, {
  7. DocumentContext, DocumentInitialProps,
  8. Html, Head, Main, NextScript,
  9. } from 'next/document';
  10. import { ActivatePluginService, GrowiPluginManifestEntries } from '~/client/services/activate-plugin';
  11. import type { CrowiRequest } from '~/interfaces/crowi-request';
  12. import { GrowiPlugin, GrowiPluginResourceType } from '~/interfaces/plugin';
  13. import type { GrowiThemes } from '~/interfaces/theme';
  14. import loggerFactory from '~/utils/logger';
  15. const logger = loggerFactory('growi:page:_document');
  16. type HeadersForPresetThemesProps = {
  17. theme: GrowiThemes,
  18. manifest: PresetThemesManifest,
  19. }
  20. const HeadersForPresetThemes = (props: HeadersForPresetThemesProps): JSX.Element => {
  21. const { theme, manifest } = props;
  22. let themeKey = getManifestKeyFromTheme(theme);
  23. if (!(themeKey in manifest)) {
  24. logger.warn(`The key for '${theme} does not exist in preset-themes manifest`);
  25. themeKey = getManifestKeyFromTheme('default');
  26. }
  27. const href = `/static/preset-themes/${manifest[themeKey].file}`; // configured by express.static
  28. const elements: JSX.Element[] = [];
  29. elements.push(
  30. <link rel="stylesheet" key={`link_preset-themes-${theme}`} href={href} />,
  31. );
  32. return <>{elements}</>;
  33. };
  34. type HeadersForGrowiPluginProps = {
  35. pluginManifestEntries: GrowiPluginManifestEntries;
  36. }
  37. const HeadersForGrowiPlugin = (props: HeadersForGrowiPluginProps): JSX.Element => {
  38. const { pluginManifestEntries } = props;
  39. return (
  40. <>
  41. { pluginManifestEntries.map(([growiPlugin, manifest]) => {
  42. const { types } = growiPlugin.meta;
  43. const elements: JSX.Element[] = [];
  44. // add script
  45. if (types.includes(GrowiPluginResourceType.Script) || types.includes(GrowiPluginResourceType.Template)) {
  46. elements.push(<>
  47. {/* eslint-disable-next-line @next/next/no-sync-scripts */ }
  48. <script type="module" key={`script_${growiPlugin.installedPath}`}
  49. src={`/plugins/${growiPlugin.installedPath}/dist/${manifest['client-entry.tsx'].file}`} />
  50. </>);
  51. }
  52. // add link
  53. if (types.includes(GrowiPluginResourceType.Script) || types.includes(GrowiPluginResourceType.Style)) {
  54. elements.push(<>
  55. <link rel="stylesheet" key={`link_${growiPlugin.installedPath}`}
  56. href={`/plugins/${growiPlugin.installedPath}/dist/${manifest['client-entry.tsx'].css}`} />
  57. </>);
  58. }
  59. return elements;
  60. }) }
  61. </>
  62. );
  63. };
  64. interface GrowiDocumentProps {
  65. theme: GrowiThemes,
  66. customCss: string;
  67. presetThemesManifest: PresetThemesManifest,
  68. pluginManifestEntries: GrowiPluginManifestEntries;
  69. }
  70. declare type GrowiDocumentInitialProps = DocumentInitialProps & GrowiDocumentProps;
  71. class GrowiDocument extends Document<GrowiDocumentInitialProps> {
  72. static override async getInitialProps(ctx: DocumentContext): Promise<GrowiDocumentInitialProps> {
  73. const initialProps: DocumentInitialProps = await Document.getInitialProps(ctx);
  74. const { crowi } = ctx.req as CrowiRequest<any>;
  75. const { configManager, customizeService } = crowi;
  76. const theme = configManager.getConfig('crowi', 'customize:theme');
  77. const customCss: string = customizeService.getCustomCss();
  78. // import preset-themes manifest
  79. const presetThemesManifest = await import('@growi/preset-themes/dist/themes/manifest.json').then(imported => imported.default);
  80. // retrieve plugin manifests
  81. const GrowiPlugin = mongoose.model<GrowiPlugin>('GrowiPlugin');
  82. const growiPlugins = await GrowiPlugin.find({ isEnabled: true });
  83. const pluginManifestEntries: GrowiPluginManifestEntries = await ActivatePluginService.retrievePluginManifests(growiPlugins);
  84. return {
  85. ...initialProps, theme, customCss, presetThemesManifest, pluginManifestEntries,
  86. };
  87. }
  88. override render(): JSX.Element {
  89. const {
  90. customCss, theme, presetThemesManifest, pluginManifestEntries,
  91. } = this.props;
  92. return (
  93. <Html>
  94. <Head>
  95. <style>
  96. {customCss}
  97. </style>
  98. {/*
  99. {renderScriptTagsByGroup('basis')}
  100. {renderStyleTagsByGroup('basis')}
  101. */}
  102. <link rel='preload' href="/static/fonts/PressStart2P-latin.woff2" as="font" type="font/woff2" />
  103. <link rel='preload' href="/static/fonts/PressStart2P-latin-ext.woff2" as="font" type="font/woff2" />
  104. <link rel='preload' href="/static/fonts/Lato-Regular-latin.woff2" as="font" type="font/woff2" />
  105. <link rel='preload' href="/static/fonts/Lato-Regular-latin-ext.woff2" as="font" type="font/woff2" />
  106. <link rel='preload' href="/static/fonts/Lato-Bold-latin.woff2" as="font" type="font/woff2" />
  107. <link rel='preload' href="/static/fonts/Lato-Bold-latin-ext.woff2" as="font" type="font/woff2" />
  108. <HeadersForPresetThemes theme={theme} manifest={presetThemesManifest} />
  109. <HeadersForGrowiPlugin pluginManifestEntries={pluginManifestEntries} />
  110. </Head>
  111. <body>
  112. <Main />
  113. <NextScript />
  114. </body>
  115. </Html>
  116. );
  117. }
  118. }
  119. export default GrowiDocument;