Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support split page chunk in cjs format #6843

Merged
merged 6 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/spotty-dodos-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ice/app': patch
---

feat: support spilt page chunk in cjs format
8 changes: 8 additions & 0 deletions packages/ice/src/bundler/config/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import ServerRunnerPlugin from '../../webpack/ServerRunnerPlugin.js';
import { IMPORT_META_RENDERER, IMPORT_META_TARGET, WEB } from '../../constant.js';
import getServerCompilerPlugin from '../../utils/getServerCompilerPlugin.js';
import ReCompilePlugin from '../../webpack/ReCompilePlugin.js';
import getEntryPoints from '../../utils/getEntryPoints.js';
import { multipleServerEntry } from '../../utils/multipleEntry.js';
import type ServerRunner from '../../service/ServerRunner';
import type ServerCompileTask from '../../utils/ServerCompileTask.js';
import type { ServerCompiler, UserConfig } from '../../types';
Expand All @@ -28,6 +30,8 @@ interface ServerPluginOptions {
serverEntry?: string;
ensureRoutesConfig: () => Promise<void>;
userConfig?: UserConfig;
getFlattenRoutes?: () => string[];
command?: string;
}
export const getServerPlugin = ({
serverRunner,
Expand All @@ -39,6 +43,8 @@ export const getServerPlugin = ({
outputDir,
serverCompileTask,
userConfig,
getFlattenRoutes,
command,
}: ServerPluginOptions) => {
if (serverRunner) {
return new ServerRunnerPlugin(serverRunner, ensureRoutesConfig);
Expand All @@ -51,6 +57,8 @@ export const getServerPlugin = ({
serverCompileTask,
userConfig,
ensureRoutesConfig,
entryPoints: multipleServerEntry(userConfig, command)
? getEntryPoints(rootDir, getFlattenRoutes(), serverEntry) : undefined,
runtimeDefineVars: {
[IMPORT_META_TARGET]: JSON.stringify(target),
[IMPORT_META_RENDERER]: JSON.stringify('server'),
Expand Down
4 changes: 4 additions & 0 deletions packages/ice/src/bundler/rspack/getConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ const getConfig: GetConfig = async (context, options, rspack) => {
const {
rootDir,
userConfig,
command,
extendsPluginAPI: {
serverCompileTask,
getRoutesFile,
getFlattenRoutes,
},
} = context;
const { reCompile, ensureRoutesConfig } = getRouteExportConfig(rootDir);
Expand All @@ -56,6 +58,8 @@ const getConfig: GetConfig = async (context, options, rspack) => {
outputDir,
serverCompileTask,
userConfig,
getFlattenRoutes,
command,
}),
// Add ReCompile plugin when routes config changed.
getReCompilePlugin(reCompile, routeManifest),
Expand Down
3 changes: 3 additions & 0 deletions packages/ice/src/bundler/webpack/getWebpackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const getWebpackConfig: GetWebpackConfig = async (context, options) => {
configFilePath,
extendsPluginAPI: {
serverCompileTask,
getFlattenRoutes,
getRoutesFile,
generator,
},
Expand Down Expand Up @@ -96,6 +97,8 @@ const getWebpackConfig: GetWebpackConfig = async (context, options) => {
outputDir,
serverCompileTask,
userConfig,
command,
getFlattenRoutes,
});
if (serverCompilerPlugin) {
webpackConfig.plugins.push(serverCompilerPlugin);
Expand Down
16 changes: 15 additions & 1 deletion packages/ice/src/createService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import addPolyfills from './utils/runtimePolyfill.js';
import webpackBundler from './bundler/webpack/index.js';
import rspackBundler from './bundler/rspack/index.js';
import getDefaultTaskConfig from './plugins/task.js';
import { multipleServerEntry, renderMultiEntry } from './utils/multipleEntry.js';
import hasDocument from './utils/hasDocument.js';

const require = createRequire(import.meta.url);
Expand Down Expand Up @@ -246,7 +247,10 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
const iceRuntimePath = '@ice/runtime';
// Only when code splitting use the default strategy or set to `router`, the router will be lazy loaded.
const lazy = [true, 'chunks', 'page', 'page-vendors'].includes(userConfig.codeSplitting);
const { routeImports, routeDefinition } = getRoutesDefinition(routesInfo.routes, lazy);
const { routeImports, routeDefinition } = getRoutesDefinition({
manifest: routesInfo.routes,
lazy,
});
const loaderExports = hasExportAppData || Boolean(routesInfo.loaders);
const hasDataLoader = Boolean(userConfig.dataLoader) && loaderExports;
// add render data
Expand All @@ -270,6 +274,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
hasDataLoader,
routeImports,
routeDefinition,
routesFile: './routes',
});
dataCache.set('routes', JSON.stringify(routesInfo));
dataCache.set('hasExportAppData', hasExportAppData ? 'true' : '');
Expand Down Expand Up @@ -307,6 +312,15 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
});
}

if (multipleServerEntry(userConfig, command)) {
renderMultiEntry({
generator,
renderRoutes: routeManifest.getFlattenRoute(),
routesManifest: routesInfo.routes,
lazy,
});
}

// render template before webpack compile
const renderStart = new Date().getTime();
generator.render();
Expand Down
5 changes: 4 additions & 1 deletion packages/ice/src/getWatchEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ const getWatchEvents = (options: Options): WatchEvent[] => {
async (eventName: string) => {
if (eventName === 'add' || eventName === 'unlink' || eventName === 'change') {
const routesRenderData = await generateRoutesInfo(rootDir, routesConfig);
const { routeImports, routeDefinition } = getRoutesDefinition(routesRenderData.routes, lazyRoutes);
const { routeImports, routeDefinition } = getRoutesDefinition({
manifest: routesRenderData.routes,
lazy: lazyRoutes,
});
const stringifiedData = JSON.stringify(routesRenderData);
if (cache.get('routes') !== stringifiedData) {
cache.set('routes', stringifiedData);
Expand Down
22 changes: 19 additions & 3 deletions packages/ice/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,20 @@ function getFilePath(file: string) {
return formatPath(path.isAbsolute(file) ? file : `@/pages/${file}`.replace(new RegExp(`${path.extname(file)}$`), ''));
}

export function getRoutesDefinition(nestRouteManifest: NestedRouteManifest[], lazy = false, depth = 0) {
interface GetDefinationOptions {
manifest: NestedRouteManifest[];
lazy?: boolean;
depth?: number;
matchRoute?: (route: NestedRouteManifest) => boolean;
}

export function getRoutesDefinition(options: GetDefinationOptions) {
const { manifest, lazy = false, depth = 0, matchRoute = () => true } = options;
const routeImports: string[] = [];
const routeDefinition = nestRouteManifest.reduce((prev, route) => {
const routeDefinition = manifest.reduce((prev, route) => {
if (!matchRoute(route)) {
return prev;
}
const { children, path: routePath, index, componentName, file, id, layout, exports } = route;
const componentPath = id.startsWith('__') ? file : getFilePath(file);

Expand Down Expand Up @@ -107,7 +118,12 @@ export function getRoutesDefinition(nestRouteManifest: NestedRouteManifest[], la
routeProperties.push('layout: true,');
}
if (children) {
const res = getRoutesDefinition(children, lazy, depth + 1);
const res = getRoutesDefinition({
manifest: children,
lazy,
depth: depth + 1,
matchRoute,
});
routeImports.push(...res.routeImports);
routeProperties.push(`children: [${res.routeDefinition}]`);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/ice/src/types/userConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export interface UserConfig {
/**
* bundle server code as a single file
*/
bundle?: boolean;
bundle?: boolean | 'page';
/**
* ignore file when bundle server code, module return empty when match
*/
Expand Down
8 changes: 6 additions & 2 deletions packages/ice/src/utils/generateEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,13 @@ const writeFile = async (file: string, content: string) => {
await fse.writeFile(file, content);
};

function formatFilePath(routePath: string, type: 'js' | 'html' | 'js.map'): string {
export function escapeRoutePath(str: string) {
// Win32 do not support file name start with ':' and '*'.
return routePath === '/' ? `index.${type}` : `${routePath.replace(/\/(:|\*)/g, '/$')}.${type}`;
return str.replace(/\/(:|\*)/g, '/$');
}

function formatFilePath(routePath: string, type: 'js' | 'html' | 'js.map'): string {
return routePath === '/' ? `index.${type}` : `${escapeRoutePath(routePath)}.${type}`;
}

async function generateFilePath(
Expand Down
17 changes: 17 additions & 0 deletions packages/ice/src/utils/getEntryPoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as path from 'path';
import { RUNTIME_TMP_DIR } from '../constant.js';
import getServerEntry from './getServerEntry.js';
import { formatServerEntry } from './multipleEntry.js';

function getEntryPoints(rootDir: string, routes: string[], mainEntry: string) {
const serverEntry: Record<string, string> = {};
routes.forEach((route) => {
serverEntry[`pages${route === '/' ? '/index' : route}`] =
path.join(rootDir, RUNTIME_TMP_DIR, formatServerEntry(route));
});
serverEntry.index = getServerEntry(rootDir, mainEntry);

return serverEntry;
}

export default getEntryPoints;
6 changes: 4 additions & 2 deletions packages/ice/src/utils/getServerCompilerPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface Options {
serverCompileTask: ExtendsPluginAPI['serverCompileTask'];
ensureRoutesConfig: () => Promise<void>;
runtimeDefineVars: Record<string, string>;
entryPoints?: Record<string, string>;
}

function getServerCompilerPlugin(serverCompiler: ServerCompiler, options: Options) {
Expand All @@ -24,15 +25,16 @@ function getServerCompilerPlugin(serverCompiler: ServerCompiler, options: Option
serverCompileTask,
ensureRoutesConfig,
runtimeDefineVars,
entryPoints,
} = options;
const entryPoint = getServerEntry(rootDir, serverEntry);
const { ssg, ssr, server: { format } } = userConfig;
const isEsm = userConfig?.server?.format === 'esm';

return new ServerCompilerPlugin(
serverCompiler,
[
{
entryPoints: { index: entryPoint },
entryPoints: entryPoints || { index: getServerEntry(rootDir, serverEntry) },
outdir: path.join(outputDir, SERVER_OUTPUT_DIR),
splitting: isEsm,
format,
Expand Down
56 changes: 56 additions & 0 deletions packages/ice/src/utils/multipleEntry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import matchRoutes from '@ice/runtime/matchRoutes';
import type { NestedRouteManifest } from '@ice/route-manifest';
import type { CommandName } from 'build-scripts';
import { getRoutesDefinition } from '../routes.js';
import type Generator from '../service/runtimeGenerator.js';
import type { UserConfig } from '../types/userConfig.js';
import { escapeRoutePath } from './generateEntry.js';

interface Options {
renderRoutes: string[];
routesManifest: NestedRouteManifest[];
generator: Generator;
lazy: boolean;
}

export const multipleServerEntry = (userConfig: UserConfig, command: CommandName): boolean => {
return userConfig?.server?.bundle === 'page' &&
userConfig.server.format === 'cjs' &&
command === 'build';
};

export const formatRoutePath = (route: string) => {
return escapeRoutePath(route)
.replace(/^\//, '').replace(/\//g, '_');
};

export const formatServerEntry = (route: string) => {
return `server.entry.${formatRoutePath(route) || 'index'}.ts`;
};

export function renderMultiEntry(options: Options) {
const { renderRoutes, routesManifest, generator, lazy } = options;
renderRoutes.forEach((route) => {
const routeId = formatRoutePath(route);
generator.addRenderFile(
'core/entry.server.ts.ejs',
formatServerEntry(route),
{
routesFile: `./routes.${routeId}`,
},
);
// Generate route file for each route.
const matches = matchRoutes(routesManifest, route);
const { routeImports, routeDefinition } = getRoutesDefinition({
manifest: routesManifest,
lazy,
matchRoute: (routeItem) => {
return matches.some((match) => match.route.id === routeItem.id);
},
});
generator.addRenderFile('core/routes.tsx.ejs', `routes.${routeId}.tsx`, {
routeImports,
routeDefinition,
});
});
}
2 changes: 1 addition & 1 deletion packages/ice/templates/core/entry.server.ts.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { RenderToPipeableStreamOptions } from 'react-dom/server';
// @ts-ignore
import assetsManifest from 'virtual:assets-manifest.json';
<% if (hydrate) {-%>
import createRoutes from './routes';
import createRoutes from '<%- routesFile %>';
<% } else { -%>
import routesManifest from './route-manifest.json';
<% } -%>
Expand Down