Yuki Takei 3 лет назад
Родитель
Сommit
7ea5ba69df

+ 58 - 6
packages/app/src/client/services/activate-plugin.ts

@@ -1,8 +1,11 @@
 import { readFileSync } from 'fs';
 import { readFileSync } from 'fs';
 import path from 'path';
 import path from 'path';
 
 
-import { GrowiPlugin } from '~/interfaces/plugin';
+import { GrowiCustomThemeSummary, ViteManifest } from '@growi/core';
+
+import { GrowiPlugin, GrowiPluginResourceType } from '~/interfaces/plugin';
 import { initializeGrowiFacade } from '~/utils/growi-facade';
 import { initializeGrowiFacade } from '~/utils/growi-facade';
+import loggerFactory from '~/utils/logger';
 import { resolveFromRoot } from '~/utils/project-dir-utils';
 import { resolveFromRoot } from '~/utils/project-dir-utils';
 
 
 
 
@@ -16,19 +19,68 @@ declare global {
   };
   };
 }
 }
 
 
+const logger = loggerFactory('growi:cli:ActivatePluginService');
+
+export type GrowiPluginThemeSummariesEntries = [growiPlugin: GrowiPlugin, summaries: GrowiCustomThemeSummary[]][];
+export type GrowiPluginManifestEntries = [growiPlugin: GrowiPlugin, manifest: ViteManifest][];
 
 
-export type GrowiPluginManifestEntries = [growiPlugin: GrowiPlugin, manifest: any][];
 
 
+async function retrievePluginManifest(growiPlugin: GrowiPlugin): Promise<ViteManifest> {
+  const manifestPath = resolveFromRoot(path.join('tmp/plugins', growiPlugin.installedPath, 'dist/manifest.json'));
+  const manifestStr: string = await readFileSync(manifestPath, 'utf-8');
+  return JSON.parse(manifestStr);
+}
 
 
 export class ActivatePluginService {
 export class ActivatePluginService {
 
 
-  static async retrievePluginManifests(growiPlugins: GrowiPlugin[]): Promise<GrowiPluginManifestEntries> {
+  static async retrieveThemeHref(growiPlugins: GrowiPlugin[], theme: string): Promise<string | undefined> {
+    const entries = await this.retrievePluginThemeSummariesEntries(growiPlugins);
+
+    let matchedPlugin: GrowiPlugin | undefined;
+    let matchedSummary: GrowiCustomThemeSummary | undefined;
+    for (const [growiPlugin, summaries] of entries) {
+      matchedSummary = summaries.find(s => s.name === theme);
+      if (matchedSummary == null) {
+        continue;
+      }
+
+      // found
+      matchedPlugin = growiPlugin;
+      break;
+    }
+
+    if (matchedPlugin != null && matchedSummary != null) {
+      const manifest = await retrievePluginManifest(matchedPlugin);
+      return '/static/plugins' // configured by express.static
+        + `/${matchedPlugin.installedPath}/dist/${manifest[matchedSummary.manifestKey].file}`;
+    }
+  }
+
+  static async retrievePluginThemeSummariesEntries(growiPlugins: GrowiPlugin[]): Promise<GrowiPluginThemeSummariesEntries> {
+    const entries: GrowiPluginThemeSummariesEntries = [];
+
+    growiPlugins
+      .filter(p => p.meta.types.includes(GrowiPluginResourceType.Theme))
+      .forEach(async(growiPlugin) => {
+        const summaryPath = resolveFromRoot(path.join('tmp/plugins', growiPlugin.installedPath, 'summary.json'));
+        const summaryStr: string = await readFileSync(summaryPath, 'utf-8');
+        entries.push([growiPlugin, JSON.parse(summaryStr)]);
+      });
+
+    return entries;
+  }
+
+  static async retrievePluginManifestEntries(growiPlugins: GrowiPlugin[]): Promise<GrowiPluginManifestEntries> {
     const entries: GrowiPluginManifestEntries = [];
     const entries: GrowiPluginManifestEntries = [];
 
 
     growiPlugins.forEach(async(growiPlugin) => {
     growiPlugins.forEach(async(growiPlugin) => {
-      const manifestPath = resolveFromRoot(path.join('tmp/plugins', growiPlugin.installedPath, 'dist/manifest.json'));
-      const customManifestStr: string = await readFileSync(manifestPath, 'utf-8');
-      entries.push([growiPlugin, JSON.parse(customManifestStr)]);
+      try {
+        const manifest = await retrievePluginManifest(growiPlugin);
+        entries.push([growiPlugin, manifest]);
+      }
+      catch (e) {
+        logger.warn(e);
+      }
     });
     });
 
 
     return entries;
     return entries;

+ 32 - 20
packages/app/src/pages/_document.page.tsx

@@ -1,7 +1,7 @@
 /* eslint-disable @next/next/google-font-display */
 /* eslint-disable @next/next/google-font-display */
 import React from 'react';
 import React from 'react';
 
 
-import type { PresetThemesManifest } from '@growi/preset-themes';
+import type { GrowiCustomThemeSummary, ViteManifest, ViteManifestValue } from '@growi/core';
 import { getManifestKeyFromTheme } from '@growi/preset-themes';
 import { getManifestKeyFromTheme } from '@growi/preset-themes';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
 import Document, {
 import Document, {
@@ -16,25 +16,34 @@ import loggerFactory from '~/utils/logger';
 
 
 const logger = loggerFactory('growi:page:_document');
 const logger = loggerFactory('growi:page:_document');
 
 
-type HeadersForPresetThemesProps = {
+type HeadersForThemesProps = {
   theme: string,
   theme: string,
-  manifest: PresetThemesManifest,
+  presetThemesManifest: ViteManifest,
+  pluginThemeHref: string | undefined,
 }
 }
-const HeadersForPresetThemes = (props: HeadersForPresetThemesProps): JSX.Element => {
-  const { theme, manifest } = props;
-
-  let themeKey = getManifestKeyFromTheme(theme);
-  if (!(themeKey in manifest)) {
-    logger.warn(`The key for '${theme} does not exist in preset-themes manifest`);
-    themeKey = getManifestKeyFromTheme('default');
-  }
-  const href = `/static/preset-themes/${manifest[themeKey].file}`; // configured by express.static
+const HeadersForThemes = (props: HeadersForThemesProps): JSX.Element => {
+  const {
+    theme, presetThemesManifest, pluginThemeHref,
+  } = props;
 
 
   const elements: JSX.Element[] = [];
   const elements: JSX.Element[] = [];
 
 
-  elements.push(
-    <link rel="stylesheet" key={`link_preset-themes-${theme}`} href={href} />,
-  );
+  if (pluginThemeHref != null) {
+    elements.push(
+      <link rel="stylesheet" key={`link_custom-themes-${theme}`} href={pluginThemeHref} />,
+    );
+  }
+  else {
+    let themeKey = getManifestKeyFromTheme(theme);
+    if (!(themeKey in presetThemesManifest)) {
+      logger.warn(`Use default theme because the key for '${theme} does not exist in preset-themes manifest`);
+      themeKey = getManifestKeyFromTheme('default');
+    }
+    const href = `/static/preset-themes/${presetThemesManifest[themeKey].file}`; // configured by express.static
+    elements.push(
+      <link rel="stylesheet" key={`link_preset-themes-${theme}`} href={href} />,
+    );
+  }
 
 
   return <>{elements}</>;
   return <>{elements}</>;
 };
 };
@@ -77,7 +86,8 @@ const HeadersForGrowiPlugin = (props: HeadersForGrowiPluginProps): JSX.Element =
 interface GrowiDocumentProps {
 interface GrowiDocumentProps {
   theme: string,
   theme: string,
   customCss: string;
   customCss: string;
-  presetThemesManifest: PresetThemesManifest,
+  presetThemesManifest: ViteManifest,
+  pluginThemeHref: string | undefined,
   pluginManifestEntries: GrowiPluginManifestEntries;
   pluginManifestEntries: GrowiPluginManifestEntries;
 }
 }
 declare type GrowiDocumentInitialProps = DocumentInitialProps & GrowiDocumentProps;
 declare type GrowiDocumentInitialProps = DocumentInitialProps & GrowiDocumentProps;
@@ -98,16 +108,17 @@ class GrowiDocument extends Document<GrowiDocumentInitialProps> {
     // retrieve plugin manifests
     // retrieve plugin manifests
     const GrowiPlugin = mongoose.model<GrowiPlugin>('GrowiPlugin');
     const GrowiPlugin = mongoose.model<GrowiPlugin>('GrowiPlugin');
     const growiPlugins = await GrowiPlugin.find({ isEnabled: true });
     const growiPlugins = await GrowiPlugin.find({ isEnabled: true });
-    const pluginManifestEntries: GrowiPluginManifestEntries = await ActivatePluginService.retrievePluginManifests(growiPlugins);
+    const pluginManifestEntries = await ActivatePluginService.retrievePluginManifestEntries(growiPlugins);
+    const pluginThemeHref = await ActivatePluginService.retrieveThemeHref(growiPlugins, theme);
 
 
     return {
     return {
-      ...initialProps, theme, customCss, presetThemesManifest, pluginManifestEntries,
+      ...initialProps, theme, customCss, presetThemesManifest, pluginThemeHref, pluginManifestEntries,
     };
     };
   }
   }
 
 
   override render(): JSX.Element {
   override render(): JSX.Element {
     const {
     const {
-      customCss, theme, presetThemesManifest, pluginManifestEntries,
+      customCss, theme, presetThemesManifest, pluginThemeHref, pluginManifestEntries,
     } = this.props;
     } = this.props;
 
 
     return (
     return (
@@ -126,7 +137,8 @@ class GrowiDocument extends Document<GrowiDocumentInitialProps> {
           <link rel='preload' href="/static/fonts/Lato-Regular-latin-ext.woff2" as="font" type="font/woff2" />
           <link rel='preload' href="/static/fonts/Lato-Regular-latin-ext.woff2" as="font" type="font/woff2" />
           <link rel='preload' href="/static/fonts/Lato-Bold-latin.woff2" as="font" type="font/woff2" />
           <link rel='preload' href="/static/fonts/Lato-Bold-latin.woff2" as="font" type="font/woff2" />
           <link rel='preload' href="/static/fonts/Lato-Bold-latin-ext.woff2" as="font" type="font/woff2" />
           <link rel='preload' href="/static/fonts/Lato-Bold-latin-ext.woff2" as="font" type="font/woff2" />
-          <HeadersForPresetThemes theme={theme} manifest={presetThemesManifest} />
+          <HeadersForThemes theme={theme}
+            presetThemesManifest={presetThemesManifest} pluginThemeHref={pluginThemeHref} />
           <HeadersForGrowiPlugin pluginManifestEntries={pluginManifestEntries} />
           <HeadersForGrowiPlugin pluginManifestEntries={pluginManifestEntries} />
         </Head>
         </Head>
         <body>
         <body>

+ 1 - 0
packages/core/src/index.ts

@@ -25,6 +25,7 @@ export * from './interfaces/subscription';
 export * from './interfaces/tag';
 export * from './interfaces/tag';
 export * from './interfaces/template';
 export * from './interfaces/template';
 export * from './interfaces/user';
 export * from './interfaces/user';
+export * from './interfaces/vite';
 export * from './models/devided-page-path';
 export * from './models/devided-page-path';
 export * from './models/vo/error-apiv3';
 export * from './models/vo/error-apiv3';
 export * from './service/localstorage-manager';
 export * from './service/localstorage-manager';

+ 9 - 0
packages/core/src/interfaces/vite.ts

@@ -0,0 +1,9 @@
+export type ViteManifestValue = {
+  file: string,
+  src: string,
+  isEntry?: boolean,
+}
+
+export type ViteManifest = {
+  [key: string]: ViteManifestValue,
+}

+ 0 - 1
packages/preset-themes/src/index.ts

@@ -1,3 +1,2 @@
-export * from './interfaces/manifest';
 export * from './consts/preset-themes';
 export * from './consts/preset-themes';
 export * from './utils';
 export * from './utils';

+ 0 - 7
packages/preset-themes/src/interfaces/manifest.ts

@@ -1,7 +0,0 @@
-export type PresetThemesManifest = {
-  [key: string]: {
-    file: string,
-    src: string,
-    isEntry?: boolean,
-  }
-}