From 212faf0ab49bb6ea5b3c7cbd96ea99a3fe3bfb09 Mon Sep 17 00:00:00 2001 From: caohuilin Date: Thu, 12 Dec 2024 10:18:18 +0800 Subject: [PATCH 01/11] feat: migrate runtime plugin --- packages/cli/core/src/context.ts | 2 +- .../runtime/plugin-runtime/src/cli/code.ts | 30 +- .../runtime/plugin-runtime/src/cli/index.ts | 258 +++++++++--------- packages/solutions/app-tools/src/types/new.ts | 1 + .../app-tools/src/utils/initAppContext.ts | 2 +- 5 files changed, 142 insertions(+), 151 deletions(-) diff --git a/packages/cli/core/src/context.ts b/packages/cli/core/src/context.ts index be5f796be2e7..276a53dd82d9 100644 --- a/packages/cli/core/src/context.ts +++ b/packages/cli/core/src/context.ts @@ -52,7 +52,7 @@ export const initAppContext = ({ appDirectory: string; plugins: CliPlugin[]; configFile: string | false; - runtimeConfigFile: string | false; + runtimeConfigFile: string; options?: { metaName?: string; srcDir?: string; diff --git a/packages/runtime/plugin-runtime/src/cli/code.ts b/packages/runtime/plugin-runtime/src/cli/code.ts index edf2b4ddc012..68d6b53ae3d7 100644 --- a/packages/runtime/plugin-runtime/src/cli/code.ts +++ b/packages/runtime/plugin-runtime/src/cli/code.ts @@ -1,14 +1,12 @@ import path from 'path'; import type { AppNormalizedConfig, - AppTools, - IAppContext, - NormalizedConfig, - RuntimePlugin, + AppToolsContext, + AppToolsFeatureHooks, + AppToolsNormalizedConfig, } from '@modern-js/app-tools'; -import type { MaybeAsync } from '@modern-js/plugin'; import type { Entrypoint } from '@modern-js/types'; -import { fs, MAIN_ENTRY_NAME } from '@modern-js/utils'; +import { fs } from '@modern-js/utils'; import { ENTRY_BOOTSTRAP_FILE_NAME, ENTRY_POINT_FILE_NAME, @@ -24,7 +22,7 @@ import * as serverTemplate from './template.server'; function getSSRMode( entry: string, - config: AppNormalizedConfig, + config: AppToolsNormalizedConfig, ): 'string' | 'stream' | false { const { ssr, ssrByEntries } = config.server; @@ -49,12 +47,9 @@ function getSSRMode( export const generateCode = async ( entrypoints: Entrypoint[], - appContext: IAppContext, - config: NormalizedConfig, - onCollectRuntimePlugins: (params: { - entrypoint: Entrypoint; - plugins: RuntimePlugin[]; - }) => MaybeAsync<{ entrypoint: Entrypoint; plugins: RuntimePlugin[] }>, + appContext: AppToolsContext<'shared'>, + config: AppToolsNormalizedConfig, + hooks: AppToolsFeatureHooks<'shared'>, ) => { const { mountId } = config.html; const { enableAsyncEntry } = config.source; @@ -75,10 +70,11 @@ export const generateCode = async ( customBootstrap, customServerEntry, } = entrypoint; - const { plugins: runtimePlugins } = await onCollectRuntimePlugins({ - entrypoint, - plugins: [], - }); + const { plugins: runtimePlugins } = + await hooks._internalRuntimePlugins.call({ + entrypoint, + plugins: [], + }); if (isAutoMount) { // index.jsx const indexCode = template.index({ diff --git a/packages/runtime/plugin-runtime/src/cli/index.ts b/packages/runtime/plugin-runtime/src/cli/index.ts index 59814b8bf5ec..825472df6aa4 100644 --- a/packages/runtime/plugin-runtime/src/cli/index.ts +++ b/packages/runtime/plugin-runtime/src/cli/index.ts @@ -1,5 +1,5 @@ import path from 'path'; -import type { AppTools, CliPlugin } from '@modern-js/app-tools'; +import type { AppTools, CliPluginFuture } from '@modern-js/app-tools'; import { isReact18 as checkIsReact18, cleanRequireCache, @@ -17,8 +17,8 @@ import { ssrPlugin } from './ssr'; export { isRuntimeEntry } from './entry'; export { statePlugin, ssrPlugin, routerPlugin, documentPlugin }; export const runtimePlugin = (params?: { - plugins?: CliPlugin[]; -}): CliPlugin => ({ + plugins?: CliPluginFuture>[]; +}): CliPluginFuture> => ({ name: '@modern-js/runtime', post: [ '@modern-js/plugin-ssr', @@ -29,150 +29,144 @@ export const runtimePlugin = (params?: { ], // the order of runtime plugins is affected by runtime hooks, mainly `init` and `hoc` hooks usePlugins: params?.plugins || [ - ssrPlugin(), + ssrPlugin() as any, routerPlugin(), statePlugin(), documentPlugin(), ], setup: api => { - return { - checkEntryPoint({ path, entry }) { - return { path, entry: entry || isRuntimeEntry(path) }; - }, - modifyEntrypoints({ entrypoints }) { - const { internalDirectory } = api.useAppContext(); - const { - source: { enableAsyncEntry }, - } = api.useResolvedConfigContext(); - const newEntryPoints = entrypoints.map(entrypoint => { - if (entrypoint.isAutoMount) { - entrypoint.internalEntry = path.resolve( - internalDirectory, - `./${entrypoint.entryName}/${ - enableAsyncEntry - ? ENTRY_BOOTSTRAP_FILE_NAME - : ENTRY_POINT_FILE_NAME - }`, - ); - } - return entrypoint; - }); - return { entrypoints: newEntryPoints }; - }, - async generateEntryCode({ entrypoints }) { - const appContext = api.useAppContext(); - const resolvedConfig = api.useResolvedConfigContext(); - const runners = api.useHookRunners(); - await generateCode( - entrypoints, - appContext, - resolvedConfig, - runners._internalRuntimePlugins, - ); - }, - /* Note that the execution time of the config hook is before prepare. + api.checkEntryPoint(({ path, entry }) => { + return { path, entry: entry || isRuntimeEntry(path) }; + }); + + api.modifyEntrypoints(({ entrypoints }) => { + const { internalDirectory } = api.getAppContext(); + const { + source: { enableAsyncEntry }, + } = api.getNormalizedConfig(); + const newEntryPoints = entrypoints.map(entrypoint => { + if (entrypoint.isAutoMount) { + entrypoint.internalEntry = path.resolve( + internalDirectory, + `./${entrypoint.entryName}/${ + enableAsyncEntry + ? ENTRY_BOOTSTRAP_FILE_NAME + : ENTRY_POINT_FILE_NAME + }`, + ); + } + return entrypoint; + }); + return { entrypoints: newEntryPoints }; + }); + api.generateEntryCode(async ({ entrypoints }) => { + const appContext = api.getAppContext(); + const resolvedConfig = api.getNormalizedConfig(); + const hooks = api.getHooks(); + await generateCode(entrypoints, appContext, resolvedConfig, hooks); + }); + + /* Note that the execution time of the config hook is before prepare. /* This means that the entry information cannot be obtained in the config hook. /* Therefore, aliases cannot be set directly in the config. */ - prepare() { - const { builder, entrypoints, internalDirectory, metaName } = - api.useAppContext(); - builder?.addPlugins([ - builderPluginAlias({ entrypoints, internalDirectory, metaName }), - ]); - }, - config() { - const { appDirectory, metaName, internalDirectory } = - api.useAppContext(); + api.onPrepare(() => { + const { builder, entrypoints, internalDirectory, metaName } = + api.getAppContext(); + builder?.addPlugins([ + builderPluginAlias({ entrypoints, internalDirectory, metaName }), + ]); + }); - const isReact18 = checkIsReact18(appDirectory); + api.config(() => { + const { appDirectory, metaName, internalDirectory } = api.getAppContext(); - process.env.IS_REACT18 = isReact18.toString(); + const isReact18 = checkIsReact18(appDirectory); - const pluginsExportsUtils = createRuntimeExportsUtils( - internalDirectory, - 'plugins', - ); + process.env.IS_REACT18 = isReact18.toString(); - return { - runtime: {}, - runtimeByEntries: {}, - source: { - alias: { - /** - * twin.macro inserts styled-components into the code during the compilation process - * But it will not be installed under the user project. - * So need to add alias - */ - 'styled-components': require.resolve('styled-components'), - /** - * Compatible with the reference path of the old version of the plugin. - */ - [`@${metaName}/runtime/plugins`]: pluginsExportsUtils.getPath(), - '@meta/runtime/browser$': require.resolve( - '@modern-js/runtime/browser', - ), - '@meta/runtime/react$': require.resolve( - '@modern-js/runtime/react', - ), - '@meta/runtime/context$': require.resolve( - '@modern-js/runtime/context', - ), - '@meta/runtime$': require.resolve('@modern-js/runtime'), - }, - globalVars: { - 'process.env.IS_REACT18': process.env.IS_REACT18, - }, - }, - tools: { - styledComponents: { - // https://github.com/styled-components/babel-plugin-styled-components/issues/287 - topLevelImportPaths: ['@modern-js/runtime/styled'], - }, - bundlerChain: chain => { - chain.module - .rule('modern-entry') - .test(/\.jsx?$/) - .include.add( - path.resolve(appDirectory, 'node_modules', `.${metaName}`), - ) - .end() - .sideEffects(true); - }, + const pluginsExportsUtils = createRuntimeExportsUtils( + internalDirectory, + 'plugins', + ); + + return { + runtime: {}, + runtimeByEntries: {}, + source: { + alias: { + /** + * twin.macro inserts styled-components into the code during the compilation process + * But it will not be installed under the user project. + * So need to add alias + */ + 'styled-components': require.resolve('styled-components'), /** - * Add IgnorePlugin to fix react-dom/client import error when use react17 + * Compatible with the reference path of the old version of the plugin. */ - webpackChain: (chain, { webpack }) => { - if (!isReact18) { - chain.plugin('ignore-plugin').use(webpack.IgnorePlugin, [ - { - resourceRegExp: /^react-dom\/client$/, - contextRegExp: /./, - }, - ]); - } - }, - rspack: (_config, { appendPlugins, rspack }) => { - if (!isReact18) { - appendPlugins([ - new rspack.IgnorePlugin({ - resourceRegExp: /^react-dom\/client$/, - contextRegExp: /./, - }), - ]); - } - }, + [`@${metaName}/runtime/plugins`]: pluginsExportsUtils.getPath(), + '@meta/runtime/browser$': require.resolve( + '@modern-js/runtime/browser', + ), + '@meta/runtime/react$': require.resolve('@modern-js/runtime/react'), + '@meta/runtime/context$': require.resolve( + '@modern-js/runtime/context', + ), + '@meta/runtime$': require.resolve('@modern-js/runtime'), }, - }; - }, - async beforeRestart() { - cleanRequireCache([ - require.resolve('../state/cli'), - require.resolve('../router/cli'), - require.resolve('./ssr'), - ]); - }, - }; + globalVars: { + 'process.env.IS_REACT18': process.env.IS_REACT18, + }, + }, + tools: { + styledComponents: { + // https://github.com/styled-components/babel-plugin-styled-components/issues/287 + topLevelImportPaths: ['@modern-js/runtime/styled'], + }, + bundlerChain: chain => { + chain.module + .rule('modern-entry') + .test(/\.jsx?$/) + .include.add( + path.resolve(appDirectory, 'node_modules', `.${metaName}`), + ) + .end() + .sideEffects(true); + }, + /** + * Add IgnorePlugin to fix react-dom/client import error when use react17 + */ + webpackChain: (chain, { webpack }) => { + if (!isReact18) { + chain.plugin('ignore-plugin').use(webpack.IgnorePlugin, [ + { + resourceRegExp: /^react-dom\/client$/, + contextRegExp: /./, + }, + ]); + } + }, + rspack: (_config, { appendPlugins, rspack }) => { + if (!isReact18) { + appendPlugins([ + new rspack.IgnorePlugin({ + resourceRegExp: /^react-dom\/client$/, + contextRegExp: /./, + }), + ]); + } + }, + }, + }; + }); + + api.onBeforeRestart(() => { + cleanRequireCache([ + require.resolve('../state/cli'), + require.resolve('../router/cli'), + require.resolve('./ssr'), + ]); + }); }, }); diff --git a/packages/solutions/app-tools/src/types/new.ts b/packages/solutions/app-tools/src/types/new.ts index 27f7ddea21ee..42b2123710d8 100644 --- a/packages/solutions/app-tools/src/types/new.ts +++ b/packages/solutions/app-tools/src/types/new.ts @@ -154,6 +154,7 @@ export type AppToolsExtendContext = { apiDirectory: string; lambdaDirectory: string; serverConfigFile: string; + runtimeConfigFile: string; serverPlugins: ServerPlugin[]; moduleType: 'module' | 'commonjs'; /** Information for entry points */ diff --git a/packages/solutions/app-tools/src/utils/initAppContext.ts b/packages/solutions/app-tools/src/utils/initAppContext.ts index bd3e1ec8cdbc..f7251ce4411c 100644 --- a/packages/solutions/app-tools/src/utils/initAppContext.ts +++ b/packages/solutions/app-tools/src/utils/initAppContext.ts @@ -9,7 +9,7 @@ export const initAppContext = ({ tempDir, }: { appDirectory: string; - runtimeConfigFile: string | false; + runtimeConfigFile: string; options?: { metaName?: string; srcDir?: string; From 4531b6d64a45df61656811e16cf577f2c53b012d Mon Sep 17 00:00:00 2001 From: caohuilin Date: Thu, 12 Dec 2024 11:13:56 +0800 Subject: [PATCH 02/11] fix: ssr plugin test case --- packages/runtime/plugin-runtime/package.json | 1 + .../runtime/plugin-runtime/src/cli/index.ts | 4 +- .../plugin-runtime/src/cli/ssr/index.ts | 136 +++++++++--------- 3 files changed, 70 insertions(+), 71 deletions(-) diff --git a/packages/runtime/plugin-runtime/package.json b/packages/runtime/plugin-runtime/package.json index c43c67ff5f68..60d56f7e1bf1 100644 --- a/packages/runtime/plugin-runtime/package.json +++ b/packages/runtime/plugin-runtime/package.json @@ -196,6 +196,7 @@ "@modern-js-reduck/react": "^1.1.10", "@modern-js-reduck/store": "^1.1.10", "@modern-js/plugin": "workspace:*", + "@modern-js/plugin-v2": "workspace:*", "@modern-js/plugin-data-loader": "workspace:*", "@modern-js/runtime-utils": "workspace:*", "@modern-js/types": "workspace:*", diff --git a/packages/runtime/plugin-runtime/src/cli/index.ts b/packages/runtime/plugin-runtime/src/cli/index.ts index 825472df6aa4..488ffac1601d 100644 --- a/packages/runtime/plugin-runtime/src/cli/index.ts +++ b/packages/runtime/plugin-runtime/src/cli/index.ts @@ -29,8 +29,8 @@ export const runtimePlugin = (params?: { ], // the order of runtime plugins is affected by runtime hooks, mainly `init` and `hoc` hooks usePlugins: params?.plugins || [ - ssrPlugin() as any, - routerPlugin(), + ssrPlugin(), + routerPlugin() as any, statePlugin(), documentPlugin(), ], diff --git a/packages/runtime/plugin-runtime/src/cli/ssr/index.ts b/packages/runtime/plugin-runtime/src/cli/ssr/index.ts index 8cf405cf2ec1..9273f02a03cc 100644 --- a/packages/runtime/plugin-runtime/src/cli/ssr/index.ts +++ b/packages/runtime/plugin-runtime/src/cli/ssr/index.ts @@ -1,15 +1,15 @@ import path from 'path'; import type { - AppNormalizedConfig, AppTools, - CliPlugin, - PluginAPI, + AppToolsNormalizedConfig, + CliPluginFuture, ServerUserConfig, } from '@modern-js/app-tools'; +import type { CLIPluginAPI } from '@modern-js/plugin-v2'; import { LOADABLE_STATS_FILE, isUseSSRBundle } from '@modern-js/utils'; import type { RsbuildPlugin } from '@rsbuild/core'; -const hasStringSSREntry = (userConfig: AppNormalizedConfig): boolean => { +const hasStringSSREntry = (userConfig: AppToolsNormalizedConfig): boolean => { const isStreaming = (ssr: ServerUserConfig['ssr']) => ssr && typeof ssr === 'object' && ssr.mode === 'stream'; @@ -34,7 +34,7 @@ const hasStringSSREntry = (userConfig: AppNormalizedConfig): boolean => { return false; }; -const checkUseStringSSR = (config: AppNormalizedConfig): boolean => { +const checkUseStringSSR = (config: AppToolsNormalizedConfig): boolean => { const { output } = config; // ssg is not support streaming ssr. @@ -42,14 +42,16 @@ const checkUseStringSSR = (config: AppNormalizedConfig): boolean => { return Boolean(output?.ssg) || hasStringSSREntry(config); }; -const ssrBuilderPlugin = (modernAPI: PluginAPI): RsbuildPlugin => ({ +const ssrBuilderPlugin = ( + modernAPI: CLIPluginAPI>, +): RsbuildPlugin => ({ name: '@modern-js/builder-plugin-ssr', setup(api) { api.modifyEnvironmentConfig((config, { name, mergeEnvironmentConfig }) => { const isServerEnvironment = config.output.target === 'node' || name === 'workerSSR'; - const userConfig = modernAPI.useResolvedConfigContext(); + const userConfig = modernAPI.getNormalizedConfig(); const useLoadablePlugin = isUseSSRBundle(userConfig) && @@ -81,78 +83,74 @@ const ssrBuilderPlugin = (modernAPI: PluginAPI): RsbuildPlugin => ({ }, }); -export const ssrPlugin = (): CliPlugin => ({ +export const ssrPlugin = (): CliPluginFuture> => ({ name: '@modern-js/plugin-ssr', required: ['@modern-js/runtime'], setup: api => { - const appContext = api.useAppContext(); - return { - // for bundle - config() { - const { bundlerType = 'webpack' } = api.useAppContext(); - const babelHandler = (() => { - // In webpack build, we should let `useLoader` support CSR & SSR both. - if (bundlerType === 'webpack') { - return (config: any) => { - const userConfig = api.useResolvedConfigContext(); - // Add id for useLoader method, - // The useLoader can be used even if the SSR is not enabled + const appContext = api.getAppContext(); + + api.config(() => { + const { bundlerType = 'webpack' } = api.getAppContext(); + const babelHandler = (() => { + // In webpack build, we should let `useLoader` support CSR & SSR both. + if (bundlerType === 'webpack') { + return (config: any) => { + const userConfig = api.getNormalizedConfig(); + // Add id for useLoader method, + // The useLoader can be used even if the SSR is not enabled + config.plugins?.push( + path.join(__dirname, './babel-plugin-ssr-loader-id'), + ); + + if (isUseSSRBundle(userConfig) && checkUseStringSSR(userConfig)) { + config.plugins?.push(require.resolve('@loadable/babel-plugin')); + } + }; + } else if (bundlerType === 'rspack') { + // In Rspack build, we need transform the babel-loader again. + // It would increase performance overhead, + // so we only use useLoader in CSR on Rspack build temporarily. + return (config: any) => { + const userConfig = api.useResolvedConfigContext(); + if (isUseSSRBundle(userConfig) && checkUseStringSSR(userConfig)) { config.plugins?.push( path.join(__dirname, './babel-plugin-ssr-loader-id'), ); - - if (isUseSSRBundle(userConfig) && checkUseStringSSR(userConfig)) { - config.plugins?.push(require.resolve('@loadable/babel-plugin')); - } - }; - } else if (bundlerType === 'rspack') { - // In Rspack build, we need transform the babel-loader again. - // It would increase performance overhead, - // so we only use useLoader in CSR on Rspack build temporarily. - return (config: any) => { - const userConfig = api.useResolvedConfigContext(); - if (isUseSSRBundle(userConfig) && checkUseStringSSR(userConfig)) { - config.plugins?.push( - path.join(__dirname, './babel-plugin-ssr-loader-id'), - ); - config.plugins?.push(require.resolve('@loadable/babel-plugin')); - } - }; - } - })(); - - return { - builderPlugins: [ssrBuilderPlugin(api)], - source: { - alias: { - // ensure that all packages use the same storage in @modern-js/runtime-utils/node - '@modern-js/runtime-utils/node$': require.resolve( - '@modern-js/runtime-utils/node', - ), - }, + config.plugins?.push(require.resolve('@loadable/babel-plugin')); + } + }; + } + })(); + + return { + builderPlugins: [ssrBuilderPlugin(api)], + source: { + alias: { + // ensure that all packages use the same storage in @modern-js/runtime-utils/node + '@modern-js/runtime-utils/node$': require.resolve( + '@modern-js/runtime-utils/node', + ), }, - tools: { - babel: babelHandler, - bundlerChain: (chain, { isServer }) => { - if (isServer && appContext.moduleType === 'module') { - chain.output - .libraryTarget('module') - .set('chunkFormat', 'module'); - chain.output.library({ - type: 'module', - }); - chain.experiments({ - ...chain.get('experiments'), - outputModule: true, - }); - } - }, + }, + tools: { + babel: babelHandler, + bundlerChain: (chain, { isServer }) => { + if (isServer && appContext.moduleType === 'module') { + chain.output.libraryTarget('module').set('chunkFormat', 'module'); + chain.output.library({ + type: 'module', + }); + chain.experiments({ + ...chain.get('experiments'), + outputModule: true, + }); + } }, - }; - }, - }; + }, + }; + }); }, }); From c76c7b9b511955940f697964b23f58ff67657be9 Mon Sep 17 00:00:00 2001 From: caohuilin Date: Thu, 12 Dec 2024 11:20:01 +0800 Subject: [PATCH 03/11] feat: migrate router plugin --- .../runtime/plugin-runtime/src/cli/index.ts | 10 +- .../src/router/cli/code/index.ts | 24 ++- .../src/router/cli/code/templates.ts | 7 +- .../plugin-runtime/src/router/cli/handler.ts | 36 ++-- .../plugin-runtime/src/router/cli/index.ts | 183 +++++++++--------- 5 files changed, 138 insertions(+), 122 deletions(-) diff --git a/packages/runtime/plugin-runtime/src/cli/index.ts b/packages/runtime/plugin-runtime/src/cli/index.ts index 488ffac1601d..5e99b3cba969 100644 --- a/packages/runtime/plugin-runtime/src/cli/index.ts +++ b/packages/runtime/plugin-runtime/src/cli/index.ts @@ -30,8 +30,8 @@ export const runtimePlugin = (params?: { // the order of runtime plugins is affected by runtime hooks, mainly `init` and `hoc` hooks usePlugins: params?.plugins || [ ssrPlugin(), - routerPlugin() as any, - statePlugin(), + routerPlugin(), + statePlugin() as any, documentPlugin(), ], setup: api => { @@ -67,9 +67,9 @@ export const runtimePlugin = (params?: { }); /* Note that the execution time of the config hook is before prepare. - /* This means that the entry information cannot be obtained in the config hook. - /* Therefore, aliases cannot be set directly in the config. - */ + /* This means that the entry information cannot be obtained in the config hook. + /* Therefore, aliases cannot be set directly in the config. + */ api.onPrepare(() => { const { builder, entrypoints, internalDirectory, metaName } = api.getAppContext(); diff --git a/packages/runtime/plugin-runtime/src/router/cli/code/index.ts b/packages/runtime/plugin-runtime/src/router/cli/code/index.ts index 3bc815b96f35..df0eb9552b7b 100644 --- a/packages/runtime/plugin-runtime/src/router/cli/code/index.ts +++ b/packages/runtime/plugin-runtime/src/router/cli/code/index.ts @@ -1,6 +1,10 @@ import path from 'path'; -import type { AppNormalizedConfig, AppTools } from '@modern-js/app-tools'; -import type { IAppContext, PluginAPI } from '@modern-js/core'; +import type { + AppNormalizedConfig, + AppTools, + AppToolsContext, +} from '@modern-js/app-tools'; +import type { CLIPluginAPI } from '@modern-js/plugin-v2'; import type { Entrypoint, NestedRouteForCli, @@ -31,10 +35,10 @@ import * as templates from './templates'; import { getServerCombinedModueFile, getServerLoadersFile } from './utils'; export const generateCode = async ( - appContext: IAppContext, + appContext: AppToolsContext<'shared'>, config: AppNormalizedConfig<'shared'>, entrypoints: Entrypoint[], - api: PluginAPI>, + api: CLIPluginAPI>, ) => { const { internalDirectory, @@ -44,7 +48,7 @@ export const generateCode = async ( packageName, } = appContext; - const hookRunners = api.useHookRunners(); + const hooks = api.getHooks(); const isV5 = isRouterV5(config); const getRoutes = isV5 ? getClientRoutesLegacy : getClientRoutes; @@ -63,7 +67,7 @@ export const generateCode = async ( pageRoutesEntry, nestedRoutesEntry, } = entrypoint; - const { metaName } = api.useAppContext(); + const { metaName } = api.getAppContext(); if (isAutoMount) { // generate routes file for file system routes entrypoint. if (pageRoutesEntry || nestedRoutesEntry) { @@ -101,7 +105,7 @@ export const generateCode = async ( } } - const config = api.useResolvedConfigContext(); + const config = api.getNormalizedConfig(); const ssrByRouteIds = config.server.ssrByRouteIds || []; const clonedRoutes = cloneDeep(initialRoutes); @@ -113,7 +117,7 @@ export const generateCode = async ( ) : initialRoutes; - const { routes } = await hookRunners.modifyFileSystemRoutes({ + const { routes } = await hooks.modifyFileSystemRoutes.call({ entrypoint, routes: markedRoutes, }); @@ -143,7 +147,7 @@ export const generateCode = async ( } } - const { code } = await hookRunners.beforeGenerateRoutes({ + const { code } = await hooks.onBeforeGenerateRoutes.call({ entrypoint, code: await templates.fileSystemRoutes({ metaName, @@ -197,7 +201,7 @@ export const generateCode = async ( const serverLoaderCombined = templates.ssrLoaderCombinedModule( entrypoints, entrypoint, - config, + config as AppNormalizedConfig<'shared'>, appContext, ); if (serverLoaderCombined) { diff --git a/packages/runtime/plugin-runtime/src/router/cli/code/templates.ts b/packages/runtime/plugin-runtime/src/router/cli/code/templates.ts index 3fc6ff955f80..ddcbb06257e5 100644 --- a/packages/runtime/plugin-runtime/src/router/cli/code/templates.ts +++ b/packages/runtime/plugin-runtime/src/router/cli/code/templates.ts @@ -1,5 +1,8 @@ import path from 'path'; -import type { AppNormalizedConfig, IAppContext } from '@modern-js/app-tools'; +import type { + AppNormalizedConfig, + AppToolsContext, +} from '@modern-js/app-tools'; import type { Entrypoint, NestedRouteForCli, @@ -452,7 +455,7 @@ export function ssrLoaderCombinedModule( entrypoints: Entrypoint[], entrypoint: Entrypoint, config: AppNormalizedConfig<'shared'>, - appContext: IAppContext, + appContext: AppToolsContext<'shared'>, ) { const { entryName, isMainEntry } = entrypoint; const { packageName, internalDirectory } = appContext; diff --git a/packages/runtime/plugin-runtime/src/router/cli/handler.ts b/packages/runtime/plugin-runtime/src/router/cli/handler.ts index 2c4d2b2fe58f..87c268df6b93 100644 --- a/packages/runtime/plugin-runtime/src/router/cli/handler.ts +++ b/packages/runtime/plugin-runtime/src/router/cli/handler.ts @@ -1,6 +1,6 @@ import path from 'path'; -import type { AppTools } from '@modern-js/app-tools'; -import type { PluginAPI } from '@modern-js/core'; +import type { AppNormalizedConfig, AppTools } from '@modern-js/app-tools'; +import type { CLIPluginAPI } from '@modern-js/plugin-v2'; import type { Entrypoint } from '@modern-js/types'; import { cloneDeep } from '@modern-js/utils/lodash'; import * as templates from './code/templates'; @@ -10,23 +10,28 @@ import { modifyEntrypoints } from './entry'; let originEntrypoints: any[] = []; export async function handleModifyEntrypoints( - api: PluginAPI>, + api: CLIPluginAPI>, entrypoints: Entrypoint[], ) { - const config = api.useResolvedConfigContext(); + const config = api.getNormalizedConfig(); return modifyEntrypoints(entrypoints, config); } export async function handleGeneratorEntryCode( - api: PluginAPI>, + api: CLIPluginAPI>, entrypoints: Entrypoint[], ) { - const appContext = api.useAppContext(); - const { internalDirectory } = api.useAppContext(); - const resolvedConfig = api.useResolvedConfigContext(); + const appContext = api.getAppContext(); + const { internalDirectory } = appContext; + const resolvedConfig = api.getNormalizedConfig(); const { generatorRegisterCode, generateCode } = await import('./code'); originEntrypoints = cloneDeep(entrypoints); - await generateCode(appContext, resolvedConfig, entrypoints, api); + await generateCode( + appContext, + resolvedConfig as AppNormalizedConfig<'shared'>, + entrypoints, + api, + ); await Promise.all( entrypoints.map(async entrypoint => { if (entrypoint.nestedRoutesEntry || entrypoint.pageRoutesEntry) { @@ -48,10 +53,10 @@ export async function handleGeneratorEntryCode( } export async function handleFileChange( - api: PluginAPI>, + api: CLIPluginAPI>, e: any, ) { - const appContext = api.useAppContext(); + const appContext = api.getAppContext(); const { appDirectory, entrypoints } = appContext; const { filename, eventType } = e; const nestedRouteEntries = entrypoints @@ -70,9 +75,14 @@ export async function handleFileChange( isPageFile(absoluteFilePath) && isPageComponentFile(absoluteFilePath); if (isRouteComponent && (eventType === 'add' || eventType === 'unlink')) { - const resolvedConfig = api.useResolvedConfigContext(); + const resolvedConfig = api.getNormalizedConfig(); const { generateCode } = await import('./code'); const entrypoints = cloneDeep(originEntrypoints); - await generateCode(appContext, resolvedConfig, entrypoints, api); + await generateCode( + appContext, + resolvedConfig as AppNormalizedConfig<'shared'>, + entrypoints, + api, + ); } } diff --git a/packages/runtime/plugin-runtime/src/router/cli/index.ts b/packages/runtime/plugin-runtime/src/router/cli/index.ts index ddb569bec088..95d1ed5120f7 100644 --- a/packages/runtime/plugin-runtime/src/router/cli/index.ts +++ b/packages/runtime/plugin-runtime/src/router/cli/index.ts @@ -1,5 +1,5 @@ import path from 'node:path'; -import type { AppTools, CliPlugin } from '@modern-js/app-tools'; +import type { AppTools, CliPluginFuture } from '@modern-js/app-tools'; import type { NestedRouteForCli, PageRoute, @@ -23,110 +23,109 @@ import { export { isRouteEntry } from './entry'; export { handleFileChange, handleModifyEntrypoints } from './handler'; -export const routerPlugin = (): CliPlugin> => ({ +export const routerPlugin = (): CliPluginFuture> => ({ name: '@modern-js/plugin-router', required: ['@modern-js/runtime'], setup: api => { const nestedRoutes: Record = {}; const nestedRoutesForServer: Record = {}; - return { - _internalRuntimePlugins({ entrypoint, plugins }) { - const { packageName, serverRoutes, metaName } = api.useAppContext(); - const serverBase = serverRoutes - .filter( - (route: ServerRoute) => route.entryName === entrypoint.entryName, - ) - .map(route => route.urlPath) - .sort((a, b) => (a.length - b.length > 0 ? -1 : 1)); - const userConfig = api.useResolvedConfigContext(); - const routerConfig = getEntryOptions( - entrypoint.entryName, - entrypoint.isMainEntry, - userConfig.runtime, - userConfig.runtimeByEntries, - packageName, - )?.router; - if (routerConfig && !isV5(userConfig)) { - plugins.push({ - name: 'router', - path: `@${metaName}/runtime/router`, - config: - typeof routerConfig === 'boolean' - ? { serverBase } - : { ...routerConfig, serverBase }, - }); - } - return { entrypoint, plugins }; - }, - checkEntryPoint({ path, entry }) { - return { path, entry: entry || isRouteEntry(path) }; - }, - config() { - return { - source: { - include: [ - // react-router v6 is no longer support ie 11 - // so we need to compile these packages to ensure the compatibility - // https://github.com/remix-run/react-router/commit/f6df0697e1b2064a2b3a12e8b39577326fdd945b - /node_modules\/react-router/, - /node_modules\/react-router-dom/, - /node_modules\/@remix-run\/router/, - ], - }, - }; - }, - async modifyEntrypoints({ entrypoints }) { - const newEntryPoints = await handleModifyEntrypoints(api, entrypoints); - return { entrypoints: newEntryPoints }; - }, - async generateEntryCode({ entrypoints }) { - await handleGeneratorEntryCode(api, entrypoints); - }, - addRuntimeExports() { - const userConfig = api.useResolvedConfigContext(); - const { internalDirectory, metaName } = api.useAppContext(); + api._internalRuntimePlugins(({ entrypoint, plugins }) => { + const { packageName, serverRoutes, metaName } = api.getAppContext(); + const serverBase = serverRoutes + .filter( + (route: ServerRoute) => route.entryName === entrypoint.entryName, + ) + .map(route => route.urlPath) + .sort((a, b) => (a.length - b.length > 0 ? -1 : 1)); + const userConfig = api.getNormalizedConfig(); + const routerConfig = getEntryOptions( + entrypoint.entryName, + entrypoint.isMainEntry, + userConfig.runtime, + userConfig.runtimeByEntries, + packageName, + )?.router; + if (routerConfig && !isV5(userConfig)) { + plugins.push({ + name: 'router', + path: `@${metaName}/runtime/router`, + config: + typeof routerConfig === 'boolean' + ? { serverBase } + : { ...routerConfig, serverBase }, + }); + } - const pluginsExportsUtils = createRuntimeExportsUtils( - internalDirectory, - 'plugins', - ); - if (!isV5(userConfig)) { - pluginsExportsUtils.addExport( - `export { default as router } from '@${metaName}/runtime/router'`, - ); - } - }, - async fileChange(e) { - await handleFileChange(api, e); - }, + return { entrypoint, plugins }; + }); + api.checkEntryPoint(({ path, entry }) => { + return { path, entry: entry || isRouteEntry(path) }; + }); + api.config(() => { + return { + source: { + include: [ + // react-router v6 is no longer support ie 11 + // so we need to compile these packages to ensure the compatibility + // https://github.com/remix-run/react-router/commit/f6df0697e1b2064a2b3a12e8b39577326fdd945b + /node_modules\/react-router/, + /node_modules\/react-router-dom/, + /node_modules\/@remix-run\/router/, + ], + }, + }; + }); + api.modifyEntrypoints(async ({ entrypoints }) => { + const newEntryPoints = await handleModifyEntrypoints(api, entrypoints); + return { entrypoints: newEntryPoints }; + }); + api.generateEntryCode(async ({ entrypoints }) => { + await handleGeneratorEntryCode(api, entrypoints); + }); + api.addRuntimeExports(() => { + const userConfig = api.useResolvedConfigContext(); + const { internalDirectory, metaName } = api.useAppContext(); - async modifyFileSystemRoutes({ entrypoint, routes }) { - nestedRoutes[entrypoint.entryName] = routes; - nestedRoutesForServer[entrypoint.entryName] = filterRoutesForServer( - routes as (NestedRouteForCli | PageRoute)[], + const pluginsExportsUtils = createRuntimeExportsUtils( + internalDirectory, + 'plugins', + ); + if (!isV5(userConfig)) { + pluginsExportsUtils.addExport( + `export { default as router } from '@${metaName}/runtime/router'`, ); + } + }); + api.onFileChanged(async e => { + await handleFileChange(api, e); + }); - return { - entrypoint, - routes, - }; - }, + api.modifyFileSystemRoutes(({ entrypoint, routes }) => { + nestedRoutes[entrypoint.entryName] = routes; + nestedRoutesForServer[entrypoint.entryName] = filterRoutesForServer( + routes as (NestedRouteForCli | PageRoute)[], + ); - async beforeGenerateRoutes({ entrypoint, code }) { - const { distDirectory } = api.useAppContext(); + return { + entrypoint, + routes, + }; + }); - await fs.outputJSON( - path.resolve(distDirectory, NESTED_ROUTE_SPEC_FILE), - nestedRoutesForServer, - ); + api.onBeforeGenerateRoutes(async ({ entrypoint, code }) => { + const { distDirectory } = api.getAppContext(); + + await fs.outputJSON( + path.resolve(distDirectory, NESTED_ROUTE_SPEC_FILE), + nestedRoutesForServer, + ); - return { - entrypoint, - code, - }; - }, - }; + return { + entrypoint, + code, + }; + }); }, }); From 7b2745875beedfecfb3548d62df6355f4c0d7f03 Mon Sep 17 00:00:00 2001 From: caohuilin Date: Thu, 12 Dec 2024 11:21:43 +0800 Subject: [PATCH 04/11] feat: migrate state plugin --- .../runtime/plugin-runtime/src/cli/index.ts | 4 +- .../plugin-runtime/src/state/cli/index.ts | 66 +++++++++---------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/packages/runtime/plugin-runtime/src/cli/index.ts b/packages/runtime/plugin-runtime/src/cli/index.ts index 5e99b3cba969..cbee0d583c96 100644 --- a/packages/runtime/plugin-runtime/src/cli/index.ts +++ b/packages/runtime/plugin-runtime/src/cli/index.ts @@ -31,8 +31,8 @@ export const runtimePlugin = (params?: { usePlugins: params?.plugins || [ ssrPlugin(), routerPlugin(), - statePlugin() as any, - documentPlugin(), + statePlugin(), + documentPlugin() as any, ], setup: api => { api.checkEntryPoint(({ path, entry }) => { diff --git a/packages/runtime/plugin-runtime/src/state/cli/index.ts b/packages/runtime/plugin-runtime/src/state/cli/index.ts index 22ec50563763..33700e3fd71f 100644 --- a/packages/runtime/plugin-runtime/src/state/cli/index.ts +++ b/packages/runtime/plugin-runtime/src/state/cli/index.ts @@ -1,48 +1,46 @@ -import type { AppTools, CliPlugin } from '@modern-js/app-tools'; +import type { AppTools, CliPluginFuture } from '@modern-js/app-tools'; import { createRuntimeExportsUtils, getEntryOptions } from '@modern-js/utils'; const PLUGIN_IDENTIFIER = 'state'; -export const statePlugin = (): CliPlugin => ({ +export const statePlugin = (): CliPluginFuture => ({ name: '@modern-js/plugin-state', required: ['@modern-js/runtime'], setup: api => { - return { - _internalRuntimePlugins({ entrypoint, plugins }) { - const { entryName, isMainEntry } = entrypoint; - const userConfig = api.useResolvedConfigContext(); - const { packageName, metaName } = api.useAppContext(); + api._internalRuntimePlugins(({ entrypoint, plugins }) => { + const { entryName, isMainEntry } = entrypoint; + const userConfig = api.useResolvedConfigContext(); + const { packageName, metaName } = api.useAppContext(); - const stateConfig = getEntryOptions( - entryName, - isMainEntry, - userConfig.runtime, - userConfig.runtimeByEntries, - packageName, - )?.state; - if (stateConfig) { - plugins.push({ - name: PLUGIN_IDENTIFIER, - path: `@${metaName}/runtime/model`, - config: typeof stateConfig === 'boolean' ? {} : stateConfig, - }); - } - return { entrypoint, plugins }; - }, - addRuntimeExports() { - const { internalDirectory, metaName } = api.useAppContext(); + const stateConfig = getEntryOptions( + entryName, + isMainEntry, + userConfig.runtime, + userConfig.runtimeByEntries, + packageName, + )?.state; + if (stateConfig) { + plugins.push({ + name: PLUGIN_IDENTIFIER, + path: `@${metaName}/runtime/model`, + config: typeof stateConfig === 'boolean' ? {} : stateConfig, + }); + } + return { entrypoint, plugins }; + }); + api.addRuntimeExports(() => { + const { internalDirectory, metaName } = api.useAppContext(); - const pluginsExportsUtils = createRuntimeExportsUtils( - internalDirectory, - 'plugins', - ); - pluginsExportsUtils.addExport( - `export { default as state } from '@${metaName}/runtime/model'`, - ); - }, - }; + const pluginsExportsUtils = createRuntimeExportsUtils( + internalDirectory, + 'plugins', + ); + pluginsExportsUtils.addExport( + `export { default as state } from '@${metaName}/runtime/model'`, + ); + }); }, }); From a396479cd8aea3a6fd05ef5779edd18b40430565 Mon Sep 17 00:00:00 2001 From: caohuilin Date: Thu, 12 Dec 2024 11:37:11 +0800 Subject: [PATCH 05/11] feat: migrate document plugin --- packages/runtime/plugin-runtime/package.json | 1 - .../runtime/plugin-runtime/src/cli/index.ts | 2 +- .../plugin-runtime/src/document/cli/index.ts | 91 +++++++++---------- .../plugin-runtime/src/state/cli/index.ts | 2 +- .../tests/document/cli.test.tsx | 77 ++++++++++------ 5 files changed, 98 insertions(+), 75 deletions(-) diff --git a/packages/runtime/plugin-runtime/package.json b/packages/runtime/plugin-runtime/package.json index 60d56f7e1bf1..5742c6c27268 100644 --- a/packages/runtime/plugin-runtime/package.json +++ b/packages/runtime/plugin-runtime/package.json @@ -221,7 +221,6 @@ }, "devDependencies": { "@modern-js/app-tools": "workspace:*", - "@modern-js/core": "workspace:*", "@remix-run/web-fetch": "^4.1.3", "@rsbuild/core": "1.1.10", "@scripts/build": "workspace:*", diff --git a/packages/runtime/plugin-runtime/src/cli/index.ts b/packages/runtime/plugin-runtime/src/cli/index.ts index cbee0d583c96..363da67e5901 100644 --- a/packages/runtime/plugin-runtime/src/cli/index.ts +++ b/packages/runtime/plugin-runtime/src/cli/index.ts @@ -32,7 +32,7 @@ export const runtimePlugin = (params?: { ssrPlugin(), routerPlugin(), statePlugin(), - documentPlugin() as any, + documentPlugin(), ], setup: api => { api.checkEntryPoint(({ path, entry }) => { diff --git a/packages/runtime/plugin-runtime/src/document/cli/index.ts b/packages/runtime/plugin-runtime/src/document/cli/index.ts index 3d14c489ae88..d43f7760f83a 100644 --- a/packages/runtime/plugin-runtime/src/document/cli/index.ts +++ b/packages/runtime/plugin-runtime/src/document/cli/index.ts @@ -1,7 +1,7 @@ import path from 'path'; import type { AppTools, - CliPlugin, + CliPluginFuture, NormalizedConfig, } from '@modern-js/app-tools'; import type { Entrypoint } from '@modern-js/types/cli'; @@ -64,7 +64,7 @@ export const getDocumenByEntryName = function ( return docFile || undefined; }; -export const documentPlugin = (): CliPlugin => ({ +export const documentPlugin = (): CliPluginFuture> => ({ name: '@modern-js/plugin-document', pre: ['@modern-js/plugin-analyze'], @@ -92,7 +92,7 @@ export const documentPlugin = (): CliPlugin => ({ templateParameters: Record, ) => { const { entrypoints, internalDirectory, appDirectory } = - api.useAppContext(); + api.getAppContext(); // search the document.[tsx|jsx|js|ts] under entry const documentFilePath = getDocumenByEntryName( entrypoints, @@ -105,10 +105,10 @@ export const documentPlugin = (): CliPlugin => ({ } return async ({ htmlWebpackPlugin }: { [option: string]: any }) => { - const config = api.useResolvedConfigContext(); + const config = api.getNormalizedConfig(); const documentParams = getDocParams({ - config, + config: config as NormalizedConfig, entryName, templateParameters, }); @@ -186,7 +186,7 @@ export const documentPlugin = (): CliPlugin => ({ debug("entry %s's document jsx rendered html: %o", entryName, html); // htmlWebpackPlugin.tags - const { partialsByEntrypoint } = api.useAppContext(); + const { partialsByEntrypoint } = api.getAppContext(); const scripts = [ htmlWebpackPlugin.tags.headTags .filter((item: any) => item.tagName === 'script') @@ -300,52 +300,51 @@ export const documentPlugin = (): CliPlugin => ({ return finalHtml; }; }; - return { - config: () => { - const userConfig = api.useConfigContext(); - if (userConfig.tools?.htmlPlugin === false) { - return {}; - } + api.config(() => { + const userConfig = api.getConfig(); - return { - tools: { - htmlPlugin: (options, entry) => { - // just for reuse the baseParames calculate by builder: - // https://github.com/web-infra-dev/modern.js/blob/1abb452a87ae1adbcf8da47d62c05da39cbe4d69/packages/builder/builder-webpack-provider/src/plugins/html.ts#L69-L103 - const hackParameters: Record = - typeof options?.templateParameters === 'function' - ? options?.templateParameters( - {} as any, - {} as any, - {} as any, - {} as any, - ) - : { ...options?.templateParameters }; + if (userConfig.tools?.htmlPlugin === false) { + return {}; + } - const templateContent = documentEntry( - entry.entryName, - // options, - hackParameters, - ); + return { + tools: { + htmlPlugin: (options, entry) => { + // just for reuse the baseParames calculate by builder: + // https://github.com/web-infra-dev/modern.js/blob/1abb452a87ae1adbcf8da47d62c05da39cbe4d69/packages/builder/builder-webpack-provider/src/plugins/html.ts#L69-L103 + const hackParameters: Record = + typeof options?.templateParameters === 'function' + ? options?.templateParameters( + {} as any, + {} as any, + {} as any, + {} as any, + ) + : { ...options?.templateParameters }; - const documentHtmlOptions = templateContent - ? { - templateContent, - // Note: the behavior of inject/modify tags in afterTemplateExecution hook will not take effect - inject: false, - } - : {}; + const templateContent = documentEntry( + entry.entryName, + // options, + hackParameters, + ); - return { - ...options, - ...documentHtmlOptions, - }; - }, + const documentHtmlOptions = templateContent + ? { + templateContent, + // Note: the behavior of inject/modify tags in afterTemplateExecution hook will not take effect + inject: false, + } + : {}; + + return { + ...options, + ...documentHtmlOptions, + }; }, - }; - }, - }; + }, + }; + }); }, }); diff --git a/packages/runtime/plugin-runtime/src/state/cli/index.ts b/packages/runtime/plugin-runtime/src/state/cli/index.ts index 33700e3fd71f..fd972b1d648c 100644 --- a/packages/runtime/plugin-runtime/src/state/cli/index.ts +++ b/packages/runtime/plugin-runtime/src/state/cli/index.ts @@ -3,7 +3,7 @@ import { createRuntimeExportsUtils, getEntryOptions } from '@modern-js/utils'; const PLUGIN_IDENTIFIER = 'state'; -export const statePlugin = (): CliPluginFuture => ({ +export const statePlugin = (): CliPluginFuture> => ({ name: '@modern-js/plugin-state', required: ['@modern-js/runtime'], diff --git a/packages/runtime/plugin-runtime/tests/document/cli.test.tsx b/packages/runtime/plugin-runtime/tests/document/cli.test.tsx index eced202f0fc9..d80ba49348ae 100644 --- a/packages/runtime/plugin-runtime/tests/document/cli.test.tsx +++ b/packages/runtime/plugin-runtime/tests/document/cli.test.tsx @@ -1,46 +1,70 @@ import { existsSync } from 'fs'; import path from 'path'; -import { type CliPlugin, type IAppContext, manager } from '@modern-js/core'; +import { + type CLIPluginAPI, + type Plugin, + createPluginManager, +} from '@modern-js/plugin-v2'; +import { createContext, initPluginAPI } from '@modern-js/plugin-v2/cli'; +import type { + AppTools, + AppToolsContext, + AppToolsHooks, +} from '@modern-js/app-tools'; import { getBundleEntry } from '../../../../solutions/app-tools/src/plugins/analyze/getBundleEntry'; import { documentPlugin, getDocumenByEntryName } from '../../src/document/cli'; describe('plugin runtime cli', () => { - const main = manager.clone().usePlugin(documentPlugin as CliPlugin); - let runner: any; - + let pluginAPI: CLIPluginAPI; + const setup = async ({ appDirectory }: { appDirectory: string }) => { + const pluginManager = createPluginManager(); + pluginManager.addPlugins([documentPlugin() as Plugin]); + const plugins = pluginManager.getPlugins(); + const context = await createContext({ + appContext: { + appDirectory, + plugins, + } as any, + config: {}, + normalizedConfig: { plugins: [] } as any, + }); + pluginAPI = initPluginAPI({ + context, + pluginManager, + }); + context.pluginAPI = pluginAPI; + for (const plugin of plugins) { + await plugin.setup(pluginAPI); + } + }; beforeAll(async () => { - runner = await main.init(); + await setup({ appDirectory: path.join(__dirname, './feature') }); }); - it('plugin is defined', () => { expect(documentPlugin).toBeDefined(); }); it('plugin-document cli config is defined', async () => { - const config = await runner.config(); + const hooks = pluginAPI.getHooks(); + const config = await hooks.config.call(); expect(config.find((item: any) => item.tools)).toBeTruthy(); expect(config.find((item: any) => item.tools.htmlPlugin)).toBeTruthy(); }); it('plugin-document htmlPlugin can return the right', async () => { - const mockAPI = { - useAppContext: jest.fn((): any => ({ - internalDirectory: path.join(__dirname, './feature'), - appDirectory: path.join(__dirname, './feature'), - entrypoints: [ - { - entryName: 'main', - absoluteEntryDir: path.join(__dirname, './feature'), - }, - ], - })), - }; - const cloned = manager.clone(mockAPI); - cloned.usePlugin(documentPlugin as CliPlugin); - const runner2 = await cloned.init(); - const config = await runner2.config(); - + pluginAPI.updateAppContext({ + internalDirectory: path.join(__dirname, './feature'), + appDirectory: path.join(__dirname, './feature'), + entrypoints: [ + { + entryName: 'main', + absoluteEntryDir: path.join(__dirname, './feature'), + }, + ], + }); + const hooks = pluginAPI.getHooks(); + const config = await hooks.config.call(); const { htmlPlugin } = ( config.find((item: any) => item.tools.htmlPlugin)! as any ).tools; @@ -77,12 +101,13 @@ describe('plugin runtime cli', () => { ).toBeTruthy(); }); it('when user config set empty entries and disableDefaultEntries true, should get the ', async () => { + const hooks: any = pluginAPI.getHooks(); const entries = await getBundleEntry( - runner, + hooks, { internalDirectory: path.join(__dirname, './feature'), appDirectory: path.join(__dirname, './feature'), - } as IAppContext, + } as AppToolsContext<'shared'>, { source: { disableDefaultEntries: true, From 8150660ae75dc0075b4572e9ca41f6c7c7a6c558 Mon Sep 17 00:00:00 2001 From: caohuilin Date: Thu, 12 Dec 2024 11:56:15 +0800 Subject: [PATCH 06/11] fix: unit test --- .../runtime/plugin-router-v5/package.json | 1 + .../plugin-router-v5/tests/index.test.ts | 53 +++++++++++++++---- .../tests/document/cli.test.tsx | 6 +-- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/packages/runtime/plugin-router-v5/package.json b/packages/runtime/plugin-router-v5/package.json index d77b579595de..a36b7b114d72 100644 --- a/packages/runtime/plugin-router-v5/package.json +++ b/packages/runtime/plugin-router-v5/package.json @@ -62,6 +62,7 @@ }, "dependencies": { "@modern-js/plugin": "workspace:*", + "@modern-js/plugin-v2": "workspace:*", "@modern-js/runtime-utils": "workspace:*", "@modern-js/types": "workspace:*", "@modern-js/utils": "workspace:*", diff --git a/packages/runtime/plugin-router-v5/tests/index.test.ts b/packages/runtime/plugin-router-v5/tests/index.test.ts index 922340dca282..595a2591f6ca 100644 --- a/packages/runtime/plugin-router-v5/tests/index.test.ts +++ b/packages/runtime/plugin-router-v5/tests/index.test.ts @@ -1,5 +1,7 @@ -import { AppContext, manager } from '@modern-js/core'; -import RuntimePlugin from '@modern-js/runtime/cli'; +import type { AppTools } from '@modern-js/app-tools'; +import { createPluginManager } from '@modern-js/plugin-v2'; +import { createContext, initPluginAPI } from '@modern-js/plugin-v2/cli'; +import runtimePlugin from '@modern-js/runtime/cli'; import plugin, { useHistory, useParams } from '../src'; import cliPlugin from '../src/cli'; @@ -12,20 +14,51 @@ describe('plugin-router-legacy', () => { }); describe('cli-router-legacy', () => { - const main = manager.clone().usePlugin(RuntimePlugin, cliPlugin as any); - let runner: any; - - beforeAll(async () => { - runner = await main.init(); - }); + const setup = async () => { + const pluginManager = createPluginManager(); + pluginManager.addPlugins([runtimePlugin(), cliPlugin() as Plugin]); + const plugins = pluginManager.getPlugins(); + const context = await createContext({ + appContext: { + plugins, + } as any, + config: {}, + normalizedConfig: { plugins: [] } as any, + }); + const pluginAPI = { + ...initPluginAPI({ + context, + pluginManager, + }), + checkEntryPoint: ({ path, entry }: any) => { + return { path, entry }; + }, + modifyEntrypoints: ({ entrypoints }: any) => { + return { entrypoints }; + }, + generateEntryCode: async ({ entrypoints }: any) => {}, + _internalRuntimePlugins: ({ entrypoint, plugins }: any) => { + return { entrypoint, plugins }; + }, + addRuntimeExports: () => {}, + modifyFileSystemRoutes: () => {}, + onBeforeGenerateRoutes: () => {}, + }; + context.pluginAPI = pluginAPI; + for (const plugin of plugins) { + await plugin.setup(pluginAPI); + } + return pluginAPI; + }; test('should plugin-router-legacy defined', async () => { expect(cliPlugin).toBeDefined(); }); it('plugin-router-legacy cli config is defined', async () => { - AppContext.set({ metaName: 'modern-js' } as any); - const config = await runner.config(); + const api = await setup(); + api.updateAppContext({ metaName: 'modern-js' } as any); + const config = await api.getHooks().config.call(); expect( config.find( (item: any) => item.source.alias['@modern-js/runtime/plugins'], diff --git a/packages/runtime/plugin-runtime/tests/document/cli.test.tsx b/packages/runtime/plugin-runtime/tests/document/cli.test.tsx index d80ba49348ae..fa678dc35d22 100644 --- a/packages/runtime/plugin-runtime/tests/document/cli.test.tsx +++ b/packages/runtime/plugin-runtime/tests/document/cli.test.tsx @@ -7,11 +7,7 @@ import { } from '@modern-js/plugin-v2'; import { createContext, initPluginAPI } from '@modern-js/plugin-v2/cli'; -import type { - AppTools, - AppToolsContext, - AppToolsHooks, -} from '@modern-js/app-tools'; +import type { AppTools, AppToolsContext } from '@modern-js/app-tools'; import { getBundleEntry } from '../../../../solutions/app-tools/src/plugins/analyze/getBundleEntry'; import { documentPlugin, getDocumenByEntryName } from '../../src/document/cli'; From 5cee46c8cef1565900bc3994c916d5cd1ecfb091 Mon Sep 17 00:00:00 2001 From: caohuilin Date: Thu, 12 Dec 2024 12:00:45 +0800 Subject: [PATCH 07/11] docs: changeset --- .changeset/witty-hotels-collect.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/witty-hotels-collect.md diff --git a/.changeset/witty-hotels-collect.md b/.changeset/witty-hotels-collect.md new file mode 100644 index 000000000000..bb637c03af22 --- /dev/null +++ b/.changeset/witty-hotels-collect.md @@ -0,0 +1,7 @@ +--- +'@modern-js/runtime': patch +--- + +feat: migrate runtime cli plugin to new cli plugin + +feat: runtime CLI 插件迁移到新的 CLI 插件 From 5c95f72cb02ae5b67e85111e1eb54c0c309be97b Mon Sep 17 00:00:00 2001 From: caohuilin Date: Thu, 12 Dec 2024 12:05:12 +0800 Subject: [PATCH 08/11] feat: migrate router v5 plugin --- .../runtime/plugin-router-v5/src/cli/index.ts | 120 +++++++++--------- .../plugin-router-v5/tests/index.test.ts | 2 +- 2 files changed, 60 insertions(+), 62 deletions(-) diff --git a/packages/runtime/plugin-router-v5/src/cli/index.ts b/packages/runtime/plugin-router-v5/src/cli/index.ts index 16ba925ed631..c9cc4c3e0d4c 100644 --- a/packages/runtime/plugin-router-v5/src/cli/index.ts +++ b/packages/runtime/plugin-router-v5/src/cli/index.ts @@ -1,4 +1,4 @@ -import type { AppTools, CliPlugin } from '@modern-js/app-tools'; +import type { AppTools, CliPluginFuture } from '@modern-js/app-tools'; import { createRuntimeExportsUtils, getEntryOptions, @@ -7,74 +7,72 @@ import { import './types'; import type { ServerRoute } from '@modern-js/types'; -export const routerPlugin = (): CliPlugin => ({ +export const routerPlugin = (): CliPluginFuture => ({ name: '@modern-js/plugin-router-v5', required: ['@modern-js/runtime'], setup: api => { let routerExportsUtils: any; - return { - _internalRuntimePlugins({ entrypoint, plugins }) { - const userConfig = api.useResolvedConfigContext(); - const { serverRoutes, metaName, packageName } = api.useAppContext(); - if (isV5(userConfig)) { - const routerConfig = getEntryOptions( - entrypoint.entryName, - entrypoint.isMainEntry, - userConfig.runtime, - userConfig.runtimeByEntries, - packageName, - )?.router; - const serverBase = serverRoutes - .filter( - (route: ServerRoute) => route.entryName === entrypoint.entryName, - ) - .map(route => route.urlPath) - .sort((a, b) => (a.length - b.length > 0 ? -1 : 1)); - plugins.push({ - name: 'router', - path: `@${metaName}/plugin-router-v5/runtime`, - config: - typeof routerConfig === 'boolean' - ? { serverBase } - : { ...routerConfig, serverBase }, - }); - } - return { entrypoint, plugins }; - }, - config() { - const { internalDirectory, metaName } = api.useAppContext(); - // .modern-js/.runtime-exports/router (legacy) - routerExportsUtils = createRuntimeExportsUtils( - internalDirectory, - 'router', - ); + api._internalRuntimePlugins(({ entrypoint, plugins }) => { + const userConfig = api.getNormalizedConfig(); + const { serverRoutes, metaName, packageName } = api.getAppContext(); + if (isV5(userConfig)) { + const routerConfig = getEntryOptions( + entrypoint.entryName, + entrypoint.isMainEntry, + userConfig.runtime, + userConfig.runtimeByEntries, + packageName, + )?.router; + const serverBase = serverRoutes + .filter( + (route: ServerRoute) => route.entryName === entrypoint.entryName, + ) + .map(route => route.urlPath) + .sort((a, b) => (a.length - b.length > 0 ? -1 : 1)); + plugins.push({ + name: 'router', + path: `@${metaName}/plugin-router-v5/runtime`, + config: + typeof routerConfig === 'boolean' + ? { serverBase } + : { ...routerConfig, serverBase }, + }); + } + return { entrypoint, plugins }; + }); + api.config(() => { + const { internalDirectory, metaName } = api.getAppContext(); + // .modern-js/.runtime-exports/router (legacy) + routerExportsUtils = createRuntimeExportsUtils( + internalDirectory, + 'router', + ); - return { - source: { - alias: { - [`@${metaName}/runtime/router-v5`]: routerExportsUtils.getPath(), - }, + return { + source: { + alias: { + [`@${metaName}/runtime/router-v5`]: routerExportsUtils.getPath(), }, - }; - }, - addRuntimeExports() { - const userConfig = api.useResolvedConfigContext(); - const { internalDirectory, metaName } = api.useAppContext(); - const pluginsExportsUtils = createRuntimeExportsUtils( - internalDirectory, - 'plugins', + }, + }; + }); + api.addRuntimeExports(() => { + const userConfig = api.getNormalizedConfig(); + const { internalDirectory, metaName } = api.getAppContext(); + const pluginsExportsUtils = createRuntimeExportsUtils( + internalDirectory, + 'plugins', + ); + if (isV5(userConfig)) { + pluginsExportsUtils.addExport( + `export { default as router } from '@${metaName}/plugin-router-v5/runtime'`, + ); + routerExportsUtils?.addExport( + `export * from '@${metaName}/plugin-router-v5/runtime'`, ); - if (isV5(userConfig)) { - pluginsExportsUtils.addExport( - `export { default as router } from '@${metaName}/plugin-router-v5/runtime'`, - ); - routerExportsUtils?.addExport( - `export * from '@${metaName}/plugin-router-v5/runtime'`, - ); - } - }, - }; + } + }); }, }); diff --git a/packages/runtime/plugin-router-v5/tests/index.test.ts b/packages/runtime/plugin-router-v5/tests/index.test.ts index 595a2591f6ca..84003b0413c4 100644 --- a/packages/runtime/plugin-router-v5/tests/index.test.ts +++ b/packages/runtime/plugin-router-v5/tests/index.test.ts @@ -16,7 +16,7 @@ describe('plugin-router-legacy', () => { describe('cli-router-legacy', () => { const setup = async () => { const pluginManager = createPluginManager(); - pluginManager.addPlugins([runtimePlugin(), cliPlugin() as Plugin]); + pluginManager.addPlugins([runtimePlugin(), cliPlugin()]); const plugins = pluginManager.getPlugins(); const context = await createContext({ appContext: { From e3ca27ed40f4441ebb912b6e4b0282d7dd937c94 Mon Sep 17 00:00:00 2001 From: caohuilin Date: Thu, 12 Dec 2024 12:18:30 +0800 Subject: [PATCH 09/11] feat: migrate garfish plugin --- packages/runtime/plugin-garfish/package.json | 2 +- .../runtime/plugin-garfish/src/cli/code.ts | 10 +- .../runtime/plugin-garfish/src/cli/index.ts | 367 +++++++++--------- .../solutions/app-tools/src/compat/index.ts | 8 +- 4 files changed, 184 insertions(+), 203 deletions(-) diff --git a/packages/runtime/plugin-garfish/package.json b/packages/runtime/plugin-garfish/package.json index cac5fb822fad..3de9364285eb 100644 --- a/packages/runtime/plugin-garfish/package.json +++ b/packages/runtime/plugin-garfish/package.json @@ -71,7 +71,7 @@ "test": "jest --passWithNoTests" }, "dependencies": { - "@modern-js/plugin": "workspace:*", + "@modern-js/plugin-v2": "workspace:*", "@modern-js/runtime-utils": "workspace:*", "@modern-js/utils": "workspace:*", "@swc/helpers": "0.5.13", diff --git a/packages/runtime/plugin-garfish/src/cli/code.ts b/packages/runtime/plugin-garfish/src/cli/code.ts index 4e246bab0de6..9f15aa64e6b8 100644 --- a/packages/runtime/plugin-garfish/src/cli/code.ts +++ b/packages/runtime/plugin-garfish/src/cli/code.ts @@ -1,10 +1,10 @@ import path from 'path'; import type { AppTools, - IAppContext, + AppToolsContext, + AppToolsFeatureHooks, NormalizedConfig, } from '@modern-js/app-tools'; -import type { MaybeAsync } from '@modern-js/plugin'; import type { Entrypoint } from '@modern-js/types'; import { fs } from '@modern-js/utils'; import * as template from './template'; @@ -15,9 +15,9 @@ export const ENTRY_BOOTSTRAP_FILE_NAME = 'bootstrap.jsx'; export const generateCode = async ( entrypoints: Entrypoint[], - appContext: IAppContext, + appContext: AppToolsContext<'shared'>, config: NormalizedConfig, - appendEntryCode: (input: { entrypoint: Entrypoint }) => MaybeAsync, + hooks: AppToolsFeatureHooks<'shared'>, ) => { const { mountId } = config.html; const { enableAsyncEntry } = config.source; @@ -27,7 +27,7 @@ export const generateCode = async ( entrypoints.map(async entrypoint => { const { entryName, isAutoMount, entry, customEntry, customBootstrap } = entrypoint; - const appendCode = await appendEntryCode({ entrypoint }); + const appendCode = await hooks.appendEntryCode.call({ entrypoint }); if (isAutoMount) { // index.jsx diff --git a/packages/runtime/plugin-garfish/src/cli/index.ts b/packages/runtime/plugin-garfish/src/cli/index.ts index 6aa533d9db41..5c143ddc1717 100644 --- a/packages/runtime/plugin-garfish/src/cli/index.ts +++ b/packages/runtime/plugin-garfish/src/cli/index.ts @@ -1,6 +1,10 @@ -import type { AppTools, CliPlugin } from '@modern-js/app-tools'; +import type { + AppNormalizedConfig, + AppTools, + CliPluginFuture, +} from '@modern-js/app-tools'; import type { CliHookCallbacks, useConfigContext } from '@modern-js/core'; -import { type AsyncWorkflow, createAsyncWorkflow } from '@modern-js/plugin'; +import { createCollectAsyncHook } from '@modern-js/plugin-v2'; import type { Entrypoint } from '@modern-js/types'; import { createRuntimeExportsUtils, getEntryOptions } from '@modern-js/utils'; import { logger } from '../util'; @@ -35,211 +39,194 @@ export function getDefaultMicroFrontedConfig( }; } -const appendEntryCode = createAsyncWorkflow< - { entrypoint: Entrypoint }, - string ->(); +type AppendEntryCodeFn = (params: { + entrypoint: Entrypoint; + code: string; +}) => string | Promise; -export const garfishPlugin = (): CliPlugin< - AppTools & { - hooks: { - appendEntryCode: AsyncWorkflow<{ entrypoint: Entrypoint }, string>; - }; - } -> => ({ +export const garfishPlugin = (): CliPluginFuture> => ({ name: '@modern-js/plugin-garfish', pre: ['@modern-js/runtime'], - registerHook: { - appendEntryCode, + registryHooks: { + appendEntryCode: createCollectAsyncHook(), }, setup: api => { - return { - _internalRuntimePlugins({ entrypoint, plugins }) { - const userConfig = api.useResolvedConfigContext(); - const { packageName, metaName } = api.useAppContext(); - const runtimeConfig = getEntryOptions( - entrypoint.entryName, - entrypoint.isMainEntry, - userConfig.runtime, - userConfig.runtimeByEntries, - packageName, - ); - if (runtimeConfig?.masterApp) { - plugins.push({ - name: 'garfish', - path: `@${metaName}/plugin-garfish/runtime`, - config: runtimeConfig?.masterApp || {}, - }); - } - return { entrypoint, plugins }; - }, - resolvedConfig: async config => { - const { resolved } = config; - const { masterApp, router } = getRuntimeConfig(resolved); - const nConfig = { - resolved: { - ...resolved, - }, - }; - if (masterApp) { - const useConfig = api.useConfigContext(); - const baseUrl = useConfig?.server?.baseUrl; - if (Array.isArray(baseUrl)) { - throw new Error( - 'Now Micro-Front-End mode dose not support multiple baseUrl, you can set it as a string', - ); - } - // basename does not exist use router's basename - setRuntimeConfig( - nConfig.resolved, - 'masterApp', - Object.assign( - typeof masterApp === 'object' ? { ...masterApp } : {}, - { - basename: - baseUrl || - router?.historyOptions?.basename || - router?.basename || - '/', - }, - ), - ); - } - logger(`resolvedConfig`, { - output: nConfig.resolved.output, - runtime: nConfig.resolved.runtime, - deploy: nConfig.resolved.deploy, - server: nConfig.resolved.server, + api._internalRuntimePlugins(({ entrypoint, plugins }) => { + const userConfig = api.getNormalizedConfig(); + const { packageName, metaName } = api.getAppContext(); + const runtimeConfig = getEntryOptions( + entrypoint.entryName, + entrypoint.isMainEntry, + userConfig.runtime, + userConfig.runtimeByEntries, + packageName, + ); + if (runtimeConfig?.masterApp) { + plugins.push({ + name: 'garfish', + path: `@${metaName}/plugin-garfish/runtime`, + config: runtimeConfig?.masterApp || {}, }); - return nConfig; - }, - config() { - const useConfig = api.useConfigContext(); - const { metaName, packageName } = api.useAppContext(); - logger('useConfig', useConfig); - - let disableCssExtract = useConfig.output?.disableCssExtract || false; - - // When the micro-frontend application js entry, there is no need to extract css, close cssExtract - if (useConfig.deploy?.microFrontend) { - const { enableHtmlEntry } = getDefaultMicroFrontedConfig( - useConfig.deploy?.microFrontend, + } + return { entrypoint, plugins }; + }); + api.modifyResolvedConfig(config => { + const { masterApp, router } = getRuntimeConfig(config); + if (masterApp) { + const useConfig = api.getConfig(); + const baseUrl = useConfig?.server?.baseUrl; + if (Array.isArray(baseUrl)) { + throw new Error( + 'Now Micro-Front-End mode dose not support multiple baseUrl, you can set it as a string', ); - if (!enableHtmlEntry) { - disableCssExtract = true; - } } - - return { - output: { - disableCssExtract, + // basename does not exist use router's basename + setRuntimeConfig( + config, + 'masterApp', + Object.assign(typeof masterApp === 'object' ? { ...masterApp } : {}, { + basename: + baseUrl || + router?.historyOptions?.basename || + router?.basename || + '/', + }), + ); + } + logger(`resolvedConfig`, { + output: config.output, + runtime: config.runtime, + deploy: config.deploy, + server: config.server, + }); + return config; + }); + api.config(() => { + const useConfig = api.getConfig(); + const { metaName, packageName } = api.getAppContext(); + logger('useConfig', useConfig); + + let disableCssExtract = useConfig.output?.disableCssExtract || false; + + // When the micro-frontend application js entry, there is no need to extract css, close cssExtract + if (useConfig.deploy?.microFrontend) { + const { enableHtmlEntry } = getDefaultMicroFrontedConfig( + useConfig.deploy?.microFrontend, + ); + if (!enableHtmlEntry) { + disableCssExtract = true; + } + } + + return { + output: { + disableCssExtract, + }, + source: { + alias: { + [`@${metaName}/runtime/garfish`]: `@${metaName}/plugin-garfish/runtime`, }, - source: { - alias: { - [`@${metaName}/runtime/garfish`]: `@${metaName}/plugin-garfish/runtime`, + }, + tools: { + devServer: { + headers: { + 'Access-Control-Allow-Origin': '*', }, }, - tools: { - devServer: { - headers: { - 'Access-Control-Allow-Origin': '*', - }, - }, - bundlerChain: (chain, { env, CHAIN_ID, bundler }) => { - // add comments avoid sourcemap abnormal - if (bundler.BannerPlugin) { - chain - .plugin('garfish-banner') - .use(bundler.BannerPlugin, [{ banner: 'Micro front-end' }]); + bundlerChain: (chain, { env, CHAIN_ID, bundler }) => { + // add comments avoid sourcemap abnormal + if (bundler.BannerPlugin) { + chain + .plugin('garfish-banner') + .use(bundler.BannerPlugin, [{ banner: 'Micro front-end' }]); + } + + const resolveOptions = api.getNormalizedConfig(); + if (resolveOptions?.deploy?.microFrontend) { + chain.output.libraryTarget('umd'); + + const DEFAULT_ASSET_PREFIX = '/'; + + // Only override assetPrefix when using the default asset prefix, + // this allows user or other plugins to set asset prefix. + const resolvedAssetPrefix = resolveOptions.dev?.assetPrefix; + const isUsingDefaultAssetPrefix = + !useConfig.dev?.assetPrefix && + (!resolvedAssetPrefix || + resolvedAssetPrefix === DEFAULT_ASSET_PREFIX); + + if ( + isUsingDefaultAssetPrefix && + resolveOptions?.server?.port && + env === 'development' + ) { + chain.output.publicPath( + `//localhost:${resolveOptions.server.port}/`, + ); } - const resolveOptions = api.useResolvedConfigContext(); - if (resolveOptions?.deploy?.microFrontend) { - chain.output.libraryTarget('umd'); - - const DEFAULT_ASSET_PREFIX = '/'; - - // Only override assetPrefix when using the default asset prefix, - // this allows user or other plugins to set asset prefix. - const resolvedAssetPrefix = resolveOptions.dev?.assetPrefix; - const isUsingDefaultAssetPrefix = - !useConfig.dev?.assetPrefix && - (!resolvedAssetPrefix || - resolvedAssetPrefix === DEFAULT_ASSET_PREFIX); - - if ( - isUsingDefaultAssetPrefix && - resolveOptions?.server?.port && - env === 'development' - ) { - chain.output.publicPath( - `//localhost:${resolveOptions.server.port}/`, - ); - } - - const { enableHtmlEntry, externalBasicLibrary } = - getDefaultMicroFrontedConfig( - resolveOptions.deploy?.microFrontend, - ); - // external - if (externalBasicLibrary) { - chain.externals(externals); - } - // use html mode - if (!enableHtmlEntry) { - chain.output.filename('index.js'); - chain.plugins.delete(`${CHAIN_ID.PLUGIN.HTML}-main`); - chain.optimization.runtimeChunk(false); - chain.optimization.splitChunks({ - chunks: 'async', - }); - } + const { enableHtmlEntry, externalBasicLibrary } = + getDefaultMicroFrontedConfig( + resolveOptions.deploy?.microFrontend, + ); + // external + if (externalBasicLibrary) { + chain.externals(externals); } - const uniqueName = chain.output.get('uniqueName'); - if (!uniqueName) { - chain.output.uniqueName(packageName); + // use html mode + if (!enableHtmlEntry) { + chain.output.filename('index.js'); + chain.plugins.delete(`${CHAIN_ID.PLUGIN.HTML}-main`); + chain.optimization.runtimeChunk(false); + chain.optimization.splitChunks({ + chunks: 'async', + }); } - const resolveConfig = chain.toConfig(); - logger('bundlerConfig', { - output: resolveConfig.output, - externals: resolveConfig.externals, - env, - alias: resolveConfig.resolve?.alias, - plugins: resolveConfig.plugins, - }); - }, + } + const uniqueName = chain.output.get('uniqueName'); + if (!uniqueName) { + chain.output.uniqueName(packageName); + } + const resolveConfig = chain.toConfig(); + logger('bundlerConfig', { + output: resolveConfig.output, + externals: resolveConfig.externals, + env, + alias: resolveConfig.resolve?.alias, + plugins: resolveConfig.plugins, + }); }, - }; - }, - addRuntimeExports() { - const config = api.useResolvedConfigContext(); - const { masterApp } = getRuntimeConfig(config); - const { internalDirectory, metaName } = api.useAppContext(); - const pluginsExportsUtils = createRuntimeExportsUtils( - internalDirectory, - 'plugins', + }, + }; + }); + api.addRuntimeExports(() => { + const config = api.getNormalizedConfig(); + const { masterApp } = getRuntimeConfig(config); + const { internalDirectory, metaName } = api.useAppContext(); + const pluginsExportsUtils = createRuntimeExportsUtils( + internalDirectory, + 'plugins', + ); + if (masterApp) { + const addExportStatement = `export { default as garfish, default as masterApp } from '@${metaName}/plugin-garfish/runtime'`; + logger('exportStatement', addExportStatement); + pluginsExportsUtils.addExport(addExportStatement); + } + }); + api.generateEntryCode(async ({ entrypoints }) => { + const resolveOptions = api.getNormalizedConfig(); + if (resolveOptions?.deploy?.microFrontend) { + const appContext = api.getAppContext(); + const resolvedConfig = api.getNormalizedConfig(); + const hooks = api.getHooks(); + await generateCode( + entrypoints, + appContext, + resolvedConfig as AppNormalizedConfig, + hooks, ); - if (masterApp) { - const addExportStatement = `export { default as garfish, default as masterApp } from '@${metaName}/plugin-garfish/runtime'`; - logger('exportStatement', addExportStatement); - pluginsExportsUtils.addExport(addExportStatement); - } - }, - async generateEntryCode({ entrypoints }) { - const resolveOptions = api.useResolvedConfigContext(); - if (resolveOptions?.deploy?.microFrontend) { - const appContext = api.useAppContext(); - const resolvedConfig = api.useResolvedConfigContext(); - const { appendEntryCode } = api.useHookRunners(); - await generateCode( - entrypoints, - appContext, - resolvedConfig, - appendEntryCode, - ); - } - }, - }; + } + }); }, }); diff --git a/packages/solutions/app-tools/src/compat/index.ts b/packages/solutions/app-tools/src/compat/index.ts index 857d58553e08..9fa13febd501 100644 --- a/packages/solutions/app-tools/src/compat/index.ts +++ b/packages/solutions/app-tools/src/compat/index.ts @@ -1,12 +1,7 @@ -import { createAsyncHook, createCollectAsyncHook } from '@modern-js/plugin-v2'; -import type { Entrypoint } from '@modern-js/types'; +import { createAsyncHook } from '@modern-js/plugin-v2'; import type { AppTools, CliPluginFuture } from '../types'; import { getHookRunners } from './hooks'; -type AppendEntryCodeFn = (params: { - entrypoint: Entrypoint; - code: string; -}) => string | Promise; type JestConfigFn = ( utils: any, next: (utils: any) => any, @@ -39,7 +34,6 @@ export const compatPlugin = (): CliPluginFuture> => ({ }; }, registryHooks: { - appendEntryCode: createCollectAsyncHook(), jestConfig: createAsyncHook(), afterTest: createAsyncHook(), }, From 775c702930f598ff1faec2fce728b759af09b998 Mon Sep 17 00:00:00 2001 From: caohuilin Date: Wed, 18 Dec 2024 18:40:14 +0800 Subject: [PATCH 10/11] fix: garfish test case --- .../tests/__snapshots__/cli.test.tsx.snap | 46 ---- .../runtime/plugin-garfish/tests/cli.test.tsx | 215 ------------------ pnpm-lock.yaml | 13 +- 3 files changed, 8 insertions(+), 266 deletions(-) diff --git a/packages/runtime/plugin-garfish/tests/__snapshots__/cli.test.tsx.snap b/packages/runtime/plugin-garfish/tests/__snapshots__/cli.test.tsx.snap index b9aa7991686d..87f67e957046 100644 --- a/packages/runtime/plugin-garfish/tests/__snapshots__/cli.test.tsx.snap +++ b/packages/runtime/plugin-garfish/tests/__snapshots__/cli.test.tsx.snap @@ -29,49 +29,3 @@ exports[`plugin-garfish cli cli set runtime features config 1`] = ` }, } `; - -exports[`plugin-garfish cli webpack config close external and use js entry 1`] = ` -{ - "externals": { - "react": "react", - "react-dom": "react-dom", - }, - "optimization": { - "runtimeChunk": false, - "splitChunks": { - "chunks": "async", - }, - }, - "output": { - "filename": "index.js", - "libraryTarget": "umd", - "publicPath": "//localhost:8080/", - "uniqueName": undefined, - }, - "plugins": [ - BannerPlugin { - "params": { - "banner": "Micro front-end", - }, - }, - ], -} -`; - -exports[`plugin-garfish cli webpack config default micro config 1`] = ` -{ - "output": { - "libraryTarget": "umd", - "publicPath": "//localhost:8080/", - "uniqueName": undefined, - }, - "plugins": [ - HTMLWebpackPlugin {}, - BannerPlugin { - "params": { - "banner": "Micro front-end", - }, - }, - ], -} -`; diff --git a/packages/runtime/plugin-garfish/tests/cli.test.tsx b/packages/runtime/plugin-garfish/tests/cli.test.tsx index 4c5f23d3384e..786f7c65a946 100644 --- a/packages/runtime/plugin-garfish/tests/cli.test.tsx +++ b/packages/runtime/plugin-garfish/tests/cli.test.tsx @@ -1,53 +1,7 @@ import '@testing-library/jest-dom'; -import { manager, CliPlugin } from '@modern-js/core'; -import WebpackChain from '@modern-js/utils/webpack-chain'; -import type { AppUserConfig } from '@modern-js/app-tools'; -import { garfishPlugin, externals } from '../src/cli'; -import type { UseConfig } from '../src/cli'; import { getRuntimeConfig, setRuntimeConfig } from '../src/cli/utils'; -const CHAIN_ID = { - PLUGIN: { - HTML: 'html', - }, -}; - describe('plugin-garfish cli', () => { - test('cli garfish basename', async () => { - expect(garfishPlugin().name).toBe('@modern-js/plugin-garfish'); - - const main = manager.clone().usePlugin(garfishPlugin as CliPlugin); - const runner = await main.init(); - await runner.prepare(); - const configHistoryOptions: any = await runner.resolvedConfig({ - resolved: { - runtime: { - router: { - historyOptions: { basename: '/test' }, - }, - masterApp: {}, - }, - }, - } as any); - - expect(configHistoryOptions.resolved.runtime.masterApp.basename).toBe( - '/test', - ); - - const configHistory: any = await runner.resolvedConfig({ - resolved: { - runtime: { - router: { - basename: '/test2', - }, - masterApp: {}, - }, - }, - } as any); - - expect(configHistory.resolved.runtime.masterApp.basename).toBe('/test2'); - }); - test('cli get runtime config', () => { const runtimeConfig = getRuntimeConfig({ runtime: { @@ -105,173 +59,4 @@ describe('plugin-garfish cli', () => { expect(runtimeConfig.runtime).toMatchSnapshot(); }); - - test('webpack config close external and use js entry', async () => { - const resolveConfig: any = { - deploy: { - microFrontend: { - externalBasicLibrary: true, - enableHtmlEntry: false, - }, - }, - server: { - port: 8080, - }, - }; - - const main = manager - .clone({ - useResolvedConfigContext: () => resolveConfig, - }) - .usePlugin(garfishPlugin as CliPlugin); - - const runner = await main.init(); - await runner.prepare(); - const config: any = await runner.config(); - const webpackConfig = new WebpackChain(); - - function HTMLWebpackPlugin() {} - webpackConfig.plugin('html-main').use(HTMLWebpackPlugin); - - config[0].tools.bundlerChain(webpackConfig, { - webpack: jest.fn(), - env: 'development', - CHAIN_ID, - bundler: { - BannerPlugin: class { - params: any; - - constructor(params: any) { - this.params = params; - } - }, - } - }); - - const generateConfig = webpackConfig.toConfig(); - expect(generateConfig).toMatchSnapshot(); - expect(generateConfig).toMatchObject({ - output: { - libraryTarget: 'umd', - publicPath: '//localhost:8080/', - filename: 'index.js', - }, - externals, - optimization: { runtimeChunk: false, splitChunks: { chunks: 'async' } }, - }); - }); - - test('webpack config default micro config', async () => { - const resolveConfig: any = { - deploy: { - microFrontend: true, - }, - server: { - port: '8080', - }, - }; - - const main = manager - .clone({ - useResolvedConfigContext: () => resolveConfig, - }) - .usePlugin(garfishPlugin as CliPlugin); - const runner = await main.init(); - await runner.prepare(); - const config: any = await runner.config(); - const webpackConfig = new WebpackChain(); - function HTMLWebpackPlugin() {} - webpackConfig.plugin('html-main').use(HTMLWebpackPlugin); - - config[0].tools.bundlerChain(webpackConfig, { - webpack: jest.fn(), - env: 'development', - CHAIN_ID, - bundler: { - BannerPlugin: class { - params: any; - - constructor(params: any) { - this.params = params; - } - }, - } - }); - - const generateConfig = webpackConfig.toConfig(); - expect(config[0].tools.devServer).toMatchObject({ - headers: { - 'Access-Control-Allow-Origin': '*', - }, - }); - - expect(generateConfig).toMatchSnapshot(); - expect(generateConfig).toMatchObject({ - output: { - libraryTarget: 'umd', - publicPath: '//localhost:8080/', - }, - }); - expect(generateConfig.externals).toBeUndefined(); - expect(generateConfig.output!.filename).toBeUndefined(); - }); - - test('micro fronted default config disableCssExtract false', async () => { - const resolveConfig: Partial = { - deploy: { - microFrontend: {}, - }, - }; - - const main = manager - .clone({ - useResolvedConfigContext: () => resolveConfig as any, - useConfigContext: () => resolveConfig, - }) - .usePlugin(garfishPlugin as CliPlugin); - - const runner = await main.init(); - await runner.prepare(); - const config = (await runner.config()) as AppUserConfig[]; - expect(config[0].output!.disableCssExtract).toBe(false); - }); - - test('micro fronted js entry disableCssExtract true', async () => { - const resolveConfig: Partial = { - output: { - disableCssExtract: false, - }, - deploy: { - microFrontend: { - enableHtmlEntry: false, - }, - }, - }; - - const main = manager - .clone({ - useResolvedConfigContext: () => resolveConfig as any, - useConfigContext: () => resolveConfig, - }) - .usePlugin(garfishPlugin as CliPlugin); - const runner = await main.init(); - await runner.prepare(); - const config = (await runner.config()) as AppUserConfig[]; - expect(config[0].output!.disableCssExtract).toBe(true); - }); - - test('normal disableCssExtract false', async () => { - const resolveConfig: Partial = {}; - - const main = manager - .clone({ - useResolvedConfigContext: () => resolveConfig as any, - useConfigContext: () => resolveConfig, - }) - .usePlugin(garfishPlugin as CliPlugin); - const runner = await main.init(); - await runner.prepare(); - const config = (await runner.config()) as AppUserConfig[]; - expect(config[0].output!.disableCssExtract).toBe(false); - }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1056348168e9..b7ac9334a461 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2213,9 +2213,9 @@ importers: packages/runtime/plugin-garfish: dependencies: - '@modern-js/plugin': + '@modern-js/plugin-v2': specifier: workspace:* - version: link:../../toolkit/plugin + version: link:../../toolkit/plugin-v2 '@modern-js/runtime-utils': specifier: workspace:* version: link:../../toolkit/runtime-utils @@ -2310,6 +2310,9 @@ importers: '@modern-js/plugin': specifier: workspace:* version: link:../../toolkit/plugin + '@modern-js/plugin-v2': + specifier: workspace:* + version: link:../../toolkit/plugin-v2 '@modern-js/runtime-utils': specifier: workspace:* version: link:../../toolkit/runtime-utils @@ -2419,6 +2422,9 @@ importers: '@modern-js/plugin-data-loader': specifier: workspace:* version: link:../../cli/plugin-data-loader + '@modern-js/plugin-v2': + specifier: workspace:* + version: link:../../toolkit/plugin-v2 '@modern-js/runtime-utils': specifier: workspace:* version: link:../../toolkit/runtime-utils @@ -2471,9 +2477,6 @@ importers: '@modern-js/app-tools': specifier: workspace:* version: link:../../solutions/app-tools - '@modern-js/core': - specifier: workspace:* - version: link:../../cli/core '@remix-run/web-fetch': specifier: ^4.1.3 version: 4.4.2 From 739f0443db86f7d96c50ece76632e69da7099cac Mon Sep 17 00:00:00 2001 From: caohuilin Date: Wed, 18 Dec 2024 19:14:52 +0800 Subject: [PATCH 11/11] feat: add garfish unit test 1 --- .../runtime/plugin-garfish/tests/cli.test.tsx | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/packages/runtime/plugin-garfish/tests/cli.test.tsx b/packages/runtime/plugin-garfish/tests/cli.test.tsx index 786f7c65a946..3cff8e881a9f 100644 --- a/packages/runtime/plugin-garfish/tests/cli.test.tsx +++ b/packages/runtime/plugin-garfish/tests/cli.test.tsx @@ -1,7 +1,90 @@ import '@testing-library/jest-dom'; +import { type AppTools } from '@modern-js/app-tools'; +import { garfishPlugin } from '../src/cli'; import { getRuntimeConfig, setRuntimeConfig } from '../src/cli/utils'; +import { createPluginManager, Plugin } from '@modern-js/plugin-v2'; +import runtimePlugin from '@modern-js/runtime/cli'; +import { createContext, initPluginAPI } from '@modern-js/plugin-v2/cli'; + + +async function setup(config: any) { + const pluginManager = createPluginManager(); + + pluginManager.addPlugins([runtimePlugin() as Plugin, garfishPlugin() as Plugin]); + const plugins = pluginManager.getPlugins(); + const context = await createContext({ + appContext: { + plugins, + } as any, + config: {}, + normalizedConfig: { plugins: [] } as any, + }); + const pluginAPI = { + ...initPluginAPI({ + context, + pluginManager, + }), + checkEntryPoint: ({ path, entry }: any) => { + return { path, entry }; + }, + modifyEntrypoints: ({ entrypoints }: any) => { + return { entrypoints }; + }, + generateEntryCode: async ({ entrypoints }: any) => {}, + _internalRuntimePlugins: ({ entrypoint, plugins }: any) => { + return { entrypoint, plugins }; + }, + addRuntimeExports: () => {}, + modifyFileSystemRoutes: () => {}, + onBeforeGenerateRoutes: () => {}, + }; + pluginAPI.modifyResolvedConfig(() => { + return config + }) + for (const plugin of plugins) { + await plugin.setup(pluginAPI); + } + return pluginAPI; +} describe('plugin-garfish cli', () => { + test('cli garfish instance', async () => { + expect(garfishPlugin().name).toBe('@modern-js/plugin-garfish'); + }); + + test('test config historyOptions', async () => { + const pluginAPI = await setup({ + runtime: { + router: { + historyOptions: { basename: '/test' }, + }, + masterApp: {}, + }, + }); + const hooks = pluginAPI.getHooks(); + const configHistory: any = await hooks.modifyResolvedConfig.call({} as any); + expect(configHistory.runtime.masterApp.basename).toBe( + '/test', + ); + }) + + test('test config basename', async () => { + const pluginAPI = await setup({ + runtime: { + router: { + basename: '/test2', + }, + masterApp: {}, + }, + }); + const hooks = pluginAPI.getHooks(); + const configHistory: any = await hooks.modifyResolvedConfig.call({} as any); + console.log("===configHistory", configHistory) + expect(configHistory.runtime.masterApp.basename).toBe( + '/test2', + ); + }) + test('cli get runtime config', () => { const runtimeConfig = getRuntimeConfig({ runtime: {