Przeglądaj źródła

reorganize modules

Yuki Takei 7 miesięcy temu
rodzic
commit
fb399c525e

+ 3 - 4
apps/app/src/pages/[[...path]]/index.page.tsx

@@ -35,12 +35,11 @@ import { useEditingMarkdown } from '~/states/ui/editor';
 import { useSWRMUTxCurrentPageYjsData } from '~/stores/yjs';
 
 import type { NextPageWithLayout } from '../_app.page';
-import { registerPageToShowRevisionWithMeta } from '../general-page/superjson/page-to-show-revision-with-meta';
 import type {
   Props, InitialProps, SameRouteEachProps,
-} from '../general-page/types';
-import { useInitialCSRFetch } from '../general-page/use-initial-skip-ssr-fetch';
-import { useSameRouteNavigation } from '../general-page/use-same-route-navigation';
+} from '../general-page';
+import { useInitialCSRFetch, useSameRouteNavigation } from '../general-page';
+import { registerPageToShowRevisionWithMeta } from '../general-page/superjson';
 import { NextjsRoutingType, detectNextjsRoutingType } from '../utils/nextjs-routing-utils';
 import { useCustomTitleForPage } from '../utils/page-title-customization';
 

+ 1 - 1
apps/app/src/pages/[[...path]]/page-data-props.ts

@@ -8,7 +8,7 @@ import type { CrowiRequest } from '~/interfaces/crowi-request';
 import type { PageModel } from '~/server/models/page';
 import type { IPageRedirect, PageRedirectModel } from '~/server/models/page-redirect';
 
-import type { InitialProps, SameRouteEachProps } from '../general-page/types';
+import type { InitialProps, SameRouteEachProps } from '../general-page';
 
 // Utility to resolve path, redirect, and identical path page check
 type PathResolutionResult = {

+ 7 - 5
apps/app/src/pages/[[...path]]/server-side-props.ts

@@ -3,16 +3,18 @@ import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
 import { addActivity } from '~/pages/utils/activity';
 import { getServerSideI18nProps } from '~/pages/utils/i18n';
 
+import type { InitialProps, SameRouteEachProps } from '../general-page';
+import {
+  getServerSideConfigurationProps, getServerSideRendererConfigProps, getServerSideSidebarConfigProps,
+  getActivityAction, isValidInitialAndSameRouteProps, isValidSameRouteProps,
+} from '../general-page';
 import { getServerSideCommonInitialProps, getServerSideCommonEachProps } from '../utils/commons';
+import { mergeGetServerSidePropsResults } from '../utils/get-server-side-props';
 import { getServerSideUserUISettingsProps } from '../utils/user-ui-settings';
 
-import { mergeGetServerSidePropsResults } from '../general-page/common-helpers';
-import { getServerSideConfigurationProps, getServerSideRendererConfigProps, getServerSideSidebarConfigProps } from '../general-page/configuration-props';
 import { NEXT_JS_ROUTING_PAGE } from './consts';
-import { getActivityAction } from '../general-page/get-activity-action';
 import { getPageDataForInitial, getPageDataForSameRoute } from './page-data-props';
-import type { InitialProps, SameRouteEachProps } from '../general-page/types';
-import { isValidInitialAndSameRouteProps, isValidSameRouteProps } from '../general-page/types';
+
 
 const nextjsRoutingProps = {
   props: {

+ 1 - 1
apps/app/src/pages/[[...path]]/use-shallow-routing.ts

@@ -3,7 +3,7 @@ import { useEffect, useRef } from 'react';
 import { isClient } from '@growi/core/dist/utils';
 import { useRouter } from 'next/router';
 
-import type { Props } from '../general-page/types';
+import type { Props } from '../general-page';
 
 /**
  * Custom hook for syncing pathname by Shallow Routing

+ 0 - 74
apps/app/src/pages/general-page/common-helpers.ts

@@ -1,74 +0,0 @@
-import { AllLang } from '@growi/core';
-import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
-import type { SSRConfig } from 'next-i18next';
-
-import type { CrowiRequest } from '~/interfaces/crowi-request';
-import { getLangAtServerSide } from '~/pages/utils/locale';
-
-// Shared helper function to create i18n config with proper configuration
-export async function createNextI18NextConfig(
-    context: GetServerSidePropsContext,
-    namespacesRequired?: string[],
-    preloadAllLang = false,
-): Promise<SSRConfig> {
-  const { serverSideTranslations } = await import('next-i18next/serverSideTranslations');
-
-  // Import configuration to fix the error
-  const nextI18NextConfig = await import('^/config/next-i18next.config');
-
-  // Determine language from request context
-  const req: CrowiRequest = context.req as CrowiRequest;
-  const lang = getLangAtServerSide(req);
-
-  // Prepare namespaces with commons as default
-  const namespaces = ['commons'];
-  if (namespacesRequired != null) {
-    namespaces.push(...namespacesRequired);
-  }
-  else {
-    // TODO: deprecate 'translation.json' in the future
-    namespaces.push('translation');
-  }
-
-  // Call serverSideTranslations with proper configuration
-  return serverSideTranslations(
-    lang,
-    namespaces,
-    nextI18NextConfig,
-    preloadAllLang ? AllLang : false,
-  );
-}
-
-// Type-safe GetServerSidePropsResult merger for two results
-export function mergeGetServerSidePropsResults<T1, T2>(
-    result1: GetServerSidePropsResult<T1>,
-    result2: GetServerSidePropsResult<T2>,
-): GetServerSidePropsResult<T1 & T2> {
-  // Check for redirect responses (return the first one found)
-  if ('redirect' in result1) return result1;
-  if ('redirect' in result2) return result2;
-
-  // Check for notFound responses (return the first one found)
-  if ('notFound' in result1) return result1;
-  if ('notFound' in result2) return result2;
-
-  // Both results must have props for successful merge
-  if (!('props' in result1) || !('props' in result2)) {
-    throw new Error('Invalid GetServerSidePropsResult - missing props');
-  }
-
-  return {
-    props: {
-      ...result1.props,
-      ...result2.props,
-    } as T1 & T2,
-  };
-}
-
-// Type-safe property extraction helper
-export function extractTypedProps<T>(result: unknown, errorMessage: string): T {
-  if (typeof result !== 'object' || result === null || !('props' in result)) {
-    throw new Error(errorMessage);
-  }
-  return (result as { props: T }).props;
-}

+ 6 - 0
apps/app/src/pages/general-page/index.ts

@@ -0,0 +1,6 @@
+export { getServerSideSidebarConfigProps, getServerSideRendererConfigProps, getServerSideConfigurationProps } from './configuration-props';
+export { getActivityAction } from './get-activity-action';
+export type * from './types';
+export { isValidInitialAndSameRouteProps, isValidSameRouteProps } from './type-guards';
+export { useInitialCSRFetch } from './use-initial-skip-ssr-fetch';
+export { useSameRouteNavigation } from './use-same-route-navigation';

+ 1 - 0
apps/app/src/pages/general-page/superjson/index.ts

@@ -0,0 +1 @@
+export { registerPageToShowRevisionWithMeta } from './page-to-show-revision-with-meta';

+ 89 - 0
apps/app/src/pages/general-page/type-guards.ts

@@ -0,0 +1,89 @@
+import loggerFactory from '~/utils/logger';
+
+import type { InitialProps, SameRouteEachProps } from '.';
+
+const logger = loggerFactory('growi:pages:general-page:type-guards');
+
+/**
+ * Type guard for SameRouteEachProps validation
+ * Lightweight validation for same-route navigation
+ */
+export function isValidSameRouteProps(props: unknown): props is SameRouteEachProps {
+  if (typeof props !== 'object' || props === null) {
+    logger.warn('isValidSameRouteProps: props is not an object or is null');
+    return false;
+  }
+
+  const p = props as Record<string, unknown>;
+
+  // Essential properties validation
+  if (typeof p.nextjsRoutingPage !== 'string' && p.nextjsRoutingPage !== null) {
+    logger.warn('isValidSameRouteProps: nextjsRoutingPage is not a string or null', { nextjsRoutingPage: p.nextjsRoutingPage });
+    return false;
+  }
+  if (typeof p.currentPathname !== 'string') {
+    logger.warn('isValidSameRouteProps: currentPathname is not a string', { currentPathname: p.currentPathname });
+    return false;
+  }
+  if (typeof p.csrfToken !== 'string') {
+    logger.warn('isValidSameRouteProps: csrfToken is not a string', { csrfToken: p.csrfToken });
+    return false;
+  }
+  if (typeof p.isMaintenanceMode !== 'boolean') {
+    logger.warn('isValidSameRouteProps: isMaintenanceMode is not a boolean', { isMaintenanceMode: p.isMaintenanceMode });
+    return false;
+  }
+  if (typeof p.isIdenticalPathPage !== 'boolean') {
+    logger.warn('isValidSameRouteProps: isIdenticalPathPage is not a boolean', { isIdenticalPathPage: p.isIdenticalPathPage });
+    return false;
+  }
+
+  return true;
+}
+
+/**
+ * Type guard for InitialProps & SameRouteEachProps validation
+ * First validates SameRouteEachProps, then checks InitialProps-specific properties
+ */
+export function isValidInitialAndSameRouteProps(props: unknown): props is InitialProps & SameRouteEachProps {
+  // First, validate SameRouteEachProps
+  if (!isValidSameRouteProps(props)) {
+    logger.warn('isValidInitialAndSameRouteProps: SameRouteEachProps validation failed');
+    return false;
+  }
+
+  const p = props as Record<string, unknown>;
+
+  // Then validate InitialProps-specific properties
+  // CommonInitialProps
+  if (p.isNextjsRoutingTypeInitial !== true) {
+    logger.warn('isValidInitialAndSameRouteProps: isNextjsRoutingTypeInitial is not true', { isNextjsRoutingTypeInitial: p.isNextjsRoutingTypeInitial });
+    return false;
+  }
+  if (typeof p.growiVersion !== 'string') {
+    logger.warn('isValidInitialAndSameRouteProps: growiVersion is not a string', { growiVersion: p.growiVersion });
+    return false;
+  }
+
+  // SSRProps
+  if (typeof p.skipSSR !== 'boolean') {
+    logger.warn('isValidInitialAndSameRouteProps: skipSSR is not a boolean', { skipSSR: p.skipSSR });
+    return false;
+  }
+
+  // InitialProps specific page state
+  if (typeof p.isNotFound !== 'boolean') {
+    logger.warn('isValidInitialAndSameRouteProps: isNotFound is not a boolean', { isNotFound: p.isNotFound });
+    return false;
+  }
+  if (typeof p.isForbidden !== 'boolean') {
+    logger.warn('isValidInitialAndSameRouteProps: isForbidden is not a boolean', { isForbidden: p.isForbidden });
+    return false;
+  }
+  if (typeof p.isNotCreatable !== 'boolean') {
+    logger.warn('isValidInitialAndSameRouteProps: isNotCreatable is not a boolean', { isNotCreatable: p.isNotCreatable });
+    return false;
+  }
+
+  return true;
+}

+ 0 - 87
apps/app/src/pages/general-page/types.ts

@@ -8,9 +8,6 @@ import type { CommonEachProps, CommonInitialProps } from '~/pages/utils/commons'
 import type { UserUISettingsProps } from '~/pages/utils/user-ui-settings';
 import type { PageDocument } from '~/server/models/page';
 import type { ServerConfigurationHyderateArgs } from '~/states/server-configurations/hydrate';
-import loggerFactory from '~/utils/logger';
-
-const logger = loggerFactory('growi:pages:[[...path]]:types');
 
 
 export type IPageToShowRevisionWithMeta = IDataWithMeta<IPagePopulatedToShowRevision & PageDocument, IPageInfo>;
@@ -47,87 +44,3 @@ export type SameRouteEachProps = CommonEachProps & {
 }
 
 export type Props = SameRouteEachProps | (InitialProps & SameRouteEachProps);
-
-/**
- * Type guard for SameRouteEachProps validation
- * Lightweight validation for same-route navigation
- */
-export function isValidSameRouteProps(props: unknown): props is SameRouteEachProps {
-  if (typeof props !== 'object' || props === null) {
-    logger.warn('isValidSameRouteProps: props is not an object or is null');
-    return false;
-  }
-
-  const p = props as Record<string, unknown>;
-
-  // Essential properties validation
-  if (typeof p.nextjsRoutingPage !== 'string' && p.nextjsRoutingPage !== null) {
-    logger.warn('isValidSameRouteProps: nextjsRoutingPage is not a string or null', { nextjsRoutingPage: p.nextjsRoutingPage });
-    return false;
-  }
-  if (typeof p.currentPathname !== 'string') {
-    logger.warn('isValidSameRouteProps: currentPathname is not a string', { currentPathname: p.currentPathname });
-    return false;
-  }
-  if (typeof p.csrfToken !== 'string') {
-    logger.warn('isValidSameRouteProps: csrfToken is not a string', { csrfToken: p.csrfToken });
-    return false;
-  }
-  if (typeof p.isMaintenanceMode !== 'boolean') {
-    logger.warn('isValidSameRouteProps: isMaintenanceMode is not a boolean', { isMaintenanceMode: p.isMaintenanceMode });
-    return false;
-  }
-  if (typeof p.isIdenticalPathPage !== 'boolean') {
-    logger.warn('isValidSameRouteProps: isIdenticalPathPage is not a boolean', { isIdenticalPathPage: p.isIdenticalPathPage });
-    return false;
-  }
-
-  return true;
-}
-
-/**
- * Type guard for InitialProps & SameRouteEachProps validation
- * First validates SameRouteEachProps, then checks InitialProps-specific properties
- */
-export function isValidInitialAndSameRouteProps(props: unknown): props is InitialProps & SameRouteEachProps {
-  // First, validate SameRouteEachProps
-  if (!isValidSameRouteProps(props)) {
-    logger.warn('isValidInitialAndSameRouteProps: SameRouteEachProps validation failed');
-    return false;
-  }
-
-  const p = props as Record<string, unknown>;
-
-  // Then validate InitialProps-specific properties
-  // CommonInitialProps
-  if (p.isNextjsRoutingTypeInitial !== true) {
-    logger.warn('isValidInitialAndSameRouteProps: isNextjsRoutingTypeInitial is not true', { isNextjsRoutingTypeInitial: p.isNextjsRoutingTypeInitial });
-    return false;
-  }
-  if (typeof p.growiVersion !== 'string') {
-    logger.warn('isValidInitialAndSameRouteProps: growiVersion is not a string', { growiVersion: p.growiVersion });
-    return false;
-  }
-
-  // SSRProps
-  if (typeof p.skipSSR !== 'boolean') {
-    logger.warn('isValidInitialAndSameRouteProps: skipSSR is not a boolean', { skipSSR: p.skipSSR });
-    return false;
-  }
-
-  // InitialProps specific page state
-  if (typeof p.isNotFound !== 'boolean') {
-    logger.warn('isValidInitialAndSameRouteProps: isNotFound is not a boolean', { isNotFound: p.isNotFound });
-    return false;
-  }
-  if (typeof p.isForbidden !== 'boolean') {
-    logger.warn('isValidInitialAndSameRouteProps: isForbidden is not a boolean', { isForbidden: p.isForbidden });
-    return false;
-  }
-  if (typeof p.isNotCreatable !== 'boolean') {
-    logger.warn('isValidInitialAndSameRouteProps: isNotCreatable is not a boolean', { isNotCreatable: p.isNotCreatable });
-    return false;
-  }
-
-  return true;
-}

+ 27 - 0
apps/app/src/pages/utils/get-server-side-props.ts

@@ -0,0 +1,27 @@
+import type { GetServerSidePropsResult } from 'next';
+
+// Type-safe GetServerSidePropsResult merger for two results
+export function mergeGetServerSidePropsResults<T1, T2>(
+    result1: GetServerSidePropsResult<T1>,
+    result2: GetServerSidePropsResult<T2>,
+): GetServerSidePropsResult<T1 & T2> {
+  // Check for redirect responses (return the first one found)
+  if ('redirect' in result1) return result1;
+  if ('redirect' in result2) return result2;
+
+  // Check for notFound responses (return the first one found)
+  if ('notFound' in result1) return result1;
+  if ('notFound' in result2) return result2;
+
+  // Both results must have props for successful merge
+  if (!('props' in result1) || !('props' in result2)) {
+    throw new Error('Invalid GetServerSidePropsResult - missing props');
+  }
+
+  return {
+    props: {
+      ...result1.props,
+      ...result2.props,
+    } as T1 & T2,
+  };
+}

+ 39 - 1
apps/app/src/pages/utils/i18n.ts

@@ -1,7 +1,45 @@
+import { AllLang } from '@growi/core';
 import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
 import type { SSRConfig } from 'next-i18next';
 
-import { createNextI18NextConfig } from '../general-page/common-helpers';
+import type { CrowiRequest } from '~/interfaces/crowi-request';
+import { getLangAtServerSide } from '~/pages/utils/locale';
+
+
+// Shared helper function to create i18n config with proper configuration
+async function createNextI18NextConfig(
+    context: GetServerSidePropsContext,
+    namespacesRequired?: string[],
+    preloadAllLang = false,
+): Promise<SSRConfig> {
+  const { serverSideTranslations } = await import('next-i18next/serverSideTranslations');
+
+  // Import configuration to fix the error
+  const nextI18NextConfig = await import('^/config/next-i18next.config');
+
+  // Determine language from request context
+  const req: CrowiRequest = context.req as CrowiRequest;
+  const lang = getLangAtServerSide(req);
+
+  // Prepare namespaces with commons as default
+  const namespaces = ['commons'];
+  if (namespacesRequired != null) {
+    namespaces.push(...namespacesRequired);
+  }
+  else {
+    // TODO: deprecate 'translation.json' in the future
+    namespaces.push('translation');
+  }
+
+  // Call serverSideTranslations with proper configuration
+  return serverSideTranslations(
+    lang,
+    namespaces,
+    nextI18NextConfig,
+    preloadAllLang ? AllLang : false,
+  );
+}
+
 
 export const getServerSideI18nProps = async(
     context: GetServerSidePropsContext,