mirror of https://github.com/alibaba/ice.git
Compare commits
11 Commits
e119d64de9
...
adb89f0c44
| Author | SHA1 | Date |
|---|---|---|
|
|
adb89f0c44 | |
|
|
1a08895dc5 | |
|
|
1a0a2c1f61 | |
|
|
8bc00a2115 | |
|
|
b03194e046 | |
|
|
89fec6198d | |
|
|
cb35145ff3 | |
|
|
3280dbf433 | |
|
|
7fdb3ae845 | |
|
|
f048b31327 | |
|
|
a5d13dcfd3 |
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@ice/app': patch
|
||||
---
|
||||
|
||||
feat: support lazy compile of routes
|
||||
|
|
@ -6,9 +6,11 @@ import type { Config } from '@ice/shared-config/types';
|
|||
import createMockMiddleware from '../../middlewares/mock/createMiddleware.js';
|
||||
import createRenderMiddleware from '../../middlewares/renderMiddleware.js';
|
||||
import createDataLoaderMiddleware from '../../middlewares/dataLoaderMiddleware.js';
|
||||
import createProxyModuleMiddleware from '../../middlewares/proxyModuleMiddleware.js';
|
||||
import type { UserConfig } from '../../types/userConfig.js';
|
||||
import type RouteManifest from '../../utils/routeManifest.js';
|
||||
import type { GetAppConfig } from '../../types/plugin.js';
|
||||
import type Generator from '../../service/runtimeGenerator.js';
|
||||
|
||||
interface SetupOptions {
|
||||
userConfig: UserConfig;
|
||||
|
|
@ -18,7 +20,9 @@ interface SetupOptions {
|
|||
excuteServerEntry: () => Promise<any>;
|
||||
mock: boolean;
|
||||
rootDir: string;
|
||||
open?: boolean | string;
|
||||
dataLoaderCompiler?: Compiler;
|
||||
generator?: Generator;
|
||||
}
|
||||
|
||||
function setupMiddlewares(middlewares: Parameters<DevServerConfiguration['setupMiddlewares']>[0], {
|
||||
|
|
@ -30,8 +34,10 @@ function setupMiddlewares(middlewares: Parameters<DevServerConfiguration['setupM
|
|||
mock,
|
||||
rootDir,
|
||||
dataLoaderCompiler,
|
||||
generator,
|
||||
open,
|
||||
}: SetupOptions) {
|
||||
const { ssr, ssg } = userConfig;
|
||||
const { ssr, ssg, routes } = userConfig;
|
||||
let renderMode: RenderMode;
|
||||
// If ssr is set to true, use ssr for preview.
|
||||
if (ssr) {
|
||||
|
|
@ -56,6 +62,21 @@ function setupMiddlewares(middlewares: Parameters<DevServerConfiguration['setupM
|
|||
middlewares.unshift(dataLoaderMiddleware);
|
||||
}
|
||||
|
||||
if (routes?.lazyCompile) {
|
||||
const proxyModuleMiddleware = createProxyModuleMiddleware({
|
||||
manifest: routeManifest.getNestedRoute(),
|
||||
rootDir,
|
||||
generator,
|
||||
defaultPath: typeof open === 'string' ? open : '/',
|
||||
});
|
||||
// @ts-ignore property of name is exist.
|
||||
const staticIndex = middlewares.findIndex(({ name }) => name === 'express-static');
|
||||
middlewares.splice(
|
||||
staticIndex, 0,
|
||||
proxyModuleMiddleware,
|
||||
);
|
||||
}
|
||||
|
||||
// @ts-ignore property of name is exist.
|
||||
const insertIndex = middlewares.findIndex(({ name }) => name === 'serve-index');
|
||||
middlewares.splice(
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ async function bundler(
|
|||
routeManifest,
|
||||
appConfig,
|
||||
hasDataLoader,
|
||||
generator,
|
||||
} = options;
|
||||
let compiler: MultiCompiler;
|
||||
let dataLoaderCompiler: Compiler;
|
||||
|
|
@ -63,6 +64,7 @@ async function bundler(
|
|||
hooksAPI,
|
||||
taskConfigs,
|
||||
rspackConfigs,
|
||||
generator,
|
||||
};
|
||||
if (command === 'start') {
|
||||
// @ts-expect-error dev-server has been pre-packed, so it will have different type.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const start = async ({
|
|||
compiler,
|
||||
appConfig,
|
||||
hooksAPI,
|
||||
generator,
|
||||
}: BuildOptions, dataLoaderCompiler?: Compiler) => {
|
||||
const { rootDir, applyHook, commandArgs, userConfig, extendsPluginAPI: { excuteServerEntry } } = context;
|
||||
const customMiddlewares = rspackConfigs[0].devServer?.setupMiddlewares;
|
||||
|
|
@ -32,8 +33,10 @@ const start = async ({
|
|||
taskConfig: webTaskConfig,
|
||||
excuteServerEntry,
|
||||
mock: commandArgs.mock,
|
||||
open: commandArgs.open,
|
||||
rootDir,
|
||||
dataLoaderCompiler,
|
||||
generator,
|
||||
});
|
||||
return customMiddlewares ? customMiddlewares(builtInMiddlewares, devServer) : builtInMiddlewares;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import type { ServerCompiler, GetAppConfig, GetRoutesConfig, GetDataloaderConfig
|
|||
import type { UserConfig } from '../types/userConfig.js';
|
||||
import type RouteManifest from '../utils/routeManifest.js';
|
||||
import type ServerRunner from '../service/ServerRunner.js';
|
||||
import type Generator from '../service/runtimeGenerator.js';
|
||||
|
||||
export type Context = DefaultContext<Config, ExtendsPluginAPI>;
|
||||
|
||||
|
|
@ -19,9 +20,11 @@ export interface BuildOptions {
|
|||
appConfig: BundlerOptions['appConfig'];
|
||||
hooksAPI: BundlerOptions['hooksAPI'];
|
||||
taskConfigs: BundlerOptions['taskConfigs'];
|
||||
generator: Generator;
|
||||
}
|
||||
|
||||
export interface BundlerOptions {
|
||||
generator: Generator;
|
||||
taskConfigs: TaskConfig<Config>[];
|
||||
spinner: ora.Ora;
|
||||
hooksAPI: {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export async function startDevServer(
|
|||
routeManifest,
|
||||
userConfig,
|
||||
appConfig,
|
||||
generator,
|
||||
} = options;
|
||||
const routePaths = routeManifest.getFlattenRoute().sort((a, b) =>
|
||||
// Sort by length, shortest path first.
|
||||
|
|
@ -40,12 +41,14 @@ export async function startDevServer(
|
|||
...defaultDevServerConfig,
|
||||
setupMiddlewares: (middlewares, devServer) => {
|
||||
const builtInMiddlewares = getMiddlewares(middlewares, {
|
||||
generator,
|
||||
userConfig,
|
||||
routeManifest,
|
||||
getAppConfig: hooksAPI.getAppConfig,
|
||||
taskConfig: webTaskConfig,
|
||||
excuteServerEntry,
|
||||
mock: commandArgs.mock,
|
||||
open: commandArgs.open,
|
||||
rootDir,
|
||||
});
|
||||
return customMiddlewares ? customMiddlewares(builtInMiddlewares, devServer) : builtInMiddlewares;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ 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';
|
||||
import { addLeadingSlash } from './utils/slash.js';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
|
@ -66,6 +67,10 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
|
|||
templates: [coreTemplate],
|
||||
});
|
||||
|
||||
if (commandArgs.open) {
|
||||
commandArgs.open = typeof commandArgs.open === 'string' ? addLeadingSlash(commandArgs.open) : commandArgs.open;
|
||||
}
|
||||
|
||||
const { addWatchEvent, removeWatchEvent } = createWatch({
|
||||
watchDir: rootDir,
|
||||
command,
|
||||
|
|
@ -221,6 +226,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
|
|||
const { userConfig } = ctx;
|
||||
const { routes: routesConfig, server, syntaxFeatures, polyfill } = userConfig;
|
||||
|
||||
|
||||
const coreEnvKeys = getCoreEnvKeys();
|
||||
|
||||
const routesInfo = await generateRoutesInfo(rootDir, routesConfig, routeManifest.getRoutesDefinitions());
|
||||
|
|
@ -252,6 +258,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
|
|||
const { routeImports, routeDefinition } = getRoutesDefinition({
|
||||
manifest: routesInfo.routes,
|
||||
lazy,
|
||||
compileRoutes: routesConfig?.lazyCompile && command === 'start' ? [commandArgs.open || '/'] : undefined,
|
||||
});
|
||||
const loaderExports = hasExportAppData || Boolean(routesInfo.loaders);
|
||||
const hasDataLoader = Boolean(userConfig.dataLoader) && loaderExports;
|
||||
|
|
@ -301,6 +308,9 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
|
|||
generator.addRenderFile('core/entry.server.ts.ejs', FALLBACK_ENTRY, { hydrate: false });
|
||||
}
|
||||
|
||||
if (routesConfig?.lazyCompile && command === 'start') {
|
||||
generator.addRenderFile('core/empty.tsx.ejs', 'empty.tsx');
|
||||
}
|
||||
if (typeof userConfig.dataLoader === 'object' && userConfig.dataLoader.fetcher) {
|
||||
const {
|
||||
packageName,
|
||||
|
|
@ -401,6 +411,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
|
|||
userConfig,
|
||||
configFile,
|
||||
hasDataLoader,
|
||||
generator,
|
||||
};
|
||||
try {
|
||||
if (command === 'test') {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import type { ExpressRequestHandler, Middleware } from 'webpack-dev-server';
|
||||
import type { NestedRouteManifest } from '@ice/route-manifest';
|
||||
import { getRoutesDefinition } from '../routes.js';
|
||||
import { RUNTIME_TMP_DIR } from '../constant.js';
|
||||
import type Generator from '../service/runtimeGenerator.js';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
interface Options {
|
||||
manifest: NestedRouteManifest[];
|
||||
generator: Generator;
|
||||
rootDir: string;
|
||||
defaultPath: string;
|
||||
}
|
||||
|
||||
export default function createRenderMiddleware(options: Options): Middleware {
|
||||
const {
|
||||
manifest,
|
||||
generator,
|
||||
rootDir,
|
||||
defaultPath,
|
||||
} = options;
|
||||
const accessedPath = new Set<string>(defaultPath);
|
||||
|
||||
const middleware: ExpressRequestHandler = async function (req, res, next) {
|
||||
if (req.path === '/proxy-module') {
|
||||
if (!accessedPath.has(req.query.pathname)) {
|
||||
accessedPath.add(req.query.pathname);
|
||||
const { routeImports, routeDefinition } = getRoutesDefinition({
|
||||
manifest,
|
||||
lazy: true,
|
||||
compileRoutes: Array.from(accessedPath),
|
||||
});
|
||||
const templateDir = path.join(__dirname, '../../templates/core/');
|
||||
generator.renderFile(
|
||||
path.join(templateDir, 'routes.tsx.ejs'),
|
||||
path.join(rootDir, RUNTIME_TMP_DIR, 'routes.tsx'),
|
||||
{ routeImports, routeDefinition },
|
||||
);
|
||||
}
|
||||
|
||||
res.send('');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
name: 'proxy-module',
|
||||
middleware,
|
||||
};
|
||||
}
|
||||
|
|
@ -69,10 +69,11 @@ interface GetDefinationOptions {
|
|||
lazy?: boolean;
|
||||
depth?: number;
|
||||
matchRoute?: (route: NestedRouteManifest) => boolean;
|
||||
compileRoutes?: string[];
|
||||
}
|
||||
|
||||
export function getRoutesDefinition(options: GetDefinationOptions) {
|
||||
const { manifest, lazy = false, depth = 0, matchRoute = () => true } = options;
|
||||
const { manifest, lazy = false, depth = 0, matchRoute = () => true, compileRoutes } = options;
|
||||
const routeImports: string[] = [];
|
||||
const routeDefinition = manifest.reduce((prev, route) => {
|
||||
if (!matchRoute(route)) {
|
||||
|
|
@ -80,10 +81,13 @@ export function getRoutesDefinition(options: GetDefinationOptions) {
|
|||
}
|
||||
const { children, path: routePath, index, componentName, file, id, layout, exports } = route;
|
||||
const componentPath = id.startsWith('__') ? file : getFilePath(file);
|
||||
|
||||
const proxyModule = './empty';
|
||||
let loadStatement = '';
|
||||
if (lazy) {
|
||||
loadStatement = `import(/* webpackChunkName: "p_${componentName}" */ '${formatPath(componentPath)}')`;
|
||||
const filePath = compileRoutes && !layout
|
||||
? (compileRoutes.includes(`/${routePath || ''}`) ? componentPath : proxyModule)
|
||||
: componentPath;
|
||||
loadStatement = `import(/* webpackChunkName: "p_${componentName}" */ '${formatPath(filePath)}')`;
|
||||
} else {
|
||||
const routeSpecifier = formatRouteSpecifier(id);
|
||||
routeImports.push(`import * as ${routeSpecifier} from '${formatPath(componentPath)}';`);
|
||||
|
|
@ -128,6 +132,7 @@ export function getRoutesDefinition(options: GetDefinationOptions) {
|
|||
lazy,
|
||||
depth: depth + 1,
|
||||
matchRoute,
|
||||
compileRoutes,
|
||||
});
|
||||
routeImports.push(...res.routeImports);
|
||||
routeProperties.push(`children: [${res.routeDefinition}]`);
|
||||
|
|
|
|||
|
|
@ -167,6 +167,10 @@ export interface UserConfig {
|
|||
* inject initial route path for each route html.
|
||||
*/
|
||||
injectInitialEntry?: boolean;
|
||||
/**
|
||||
* Enable lazy compile for routes.
|
||||
*/
|
||||
lazyCompile?: boolean;
|
||||
};
|
||||
/**
|
||||
* Add ice.js plugin to customize framework config.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
export const addLeadingSlash = (path: string) => {
|
||||
return path.charAt(0) === '/' ? path : `/${path}`;
|
||||
};
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useLocation } from 'ice';
|
||||
|
||||
const ProxyModule = () => {
|
||||
const location = useLocation();
|
||||
useEffect(() => {
|
||||
fetch(`/proxy-module?pathname=${location.pathname}`);
|
||||
}, []);
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default ProxyModule;
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
# @ice/miniapp-react-dom
|
||||
|
||||
## 1.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @ice/miniapp-runtime@1.2.5
|
||||
|
||||
## 1.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ice/miniapp-react-dom",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"description": "like react-dom, but for miniapps.",
|
||||
"type": "module",
|
||||
"types": "./esm/index.d.ts",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
## 1.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1a0a2c1f]
|
||||
- @ice/runtime@1.5.4
|
||||
|
||||
## 1.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ice/miniapp-runtime",
|
||||
"version": "1.2.4",
|
||||
"version": "1.2.5",
|
||||
"description": "ice runtime for miniapps.",
|
||||
"type": "module",
|
||||
"types": "./esm/index.d.ts",
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@
|
|||
"webpack-dev-server": "4.15.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ice/app": "^3.6.3",
|
||||
"@ice/runtime": "^1.5.3"
|
||||
"@ice/app": "^3.6.4",
|
||||
"@ice/runtime": "^1.5.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
## 1.2.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @ice/miniapp-runtime@1.2.5
|
||||
- @ice/miniapp-react-dom@1.1.5
|
||||
|
||||
## 1.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ice/plugin-miniapp",
|
||||
"version": "1.2.5",
|
||||
"version": "1.2.6",
|
||||
"description": "ice.js plugin for miniapp.",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
|
@ -50,8 +50,8 @@
|
|||
"sax": "^1.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ice/app": "^3.6.3",
|
||||
"@ice/runtime": "^1.5.3",
|
||||
"@ice/app": "^3.6.4",
|
||||
"@ice/runtime": "^1.5.4",
|
||||
"webpack": "^5.88.0"
|
||||
},
|
||||
"repository": {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
# @ice/runtime
|
||||
|
||||
## 1.5.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1a0a2c1f: feat: Add custom event for suspense data updates, and remove ice-suspense custom event.
|
||||
|
||||
## 1.5.3
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ice/runtime",
|
||||
"version": "1.5.3",
|
||||
"version": "1.5.4",
|
||||
"description": "Runtime module for ice.js",
|
||||
"type": "module",
|
||||
"types": "./esm/index.d.ts",
|
||||
|
|
|
|||
|
|
@ -156,11 +156,6 @@ export function withSuspense(Component) {
|
|||
<SuspenseContext.Provider value={suspenseState}>
|
||||
<Component {...componentProps} />
|
||||
<Data id={id} />
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `window.dispatchEvent(new CustomEvent('ice-suspense', { detail: { id: ${id ? `'${id}'` : undefined} } }));`,
|
||||
}}
|
||||
/>
|
||||
</SuspenseContext.Provider>
|
||||
</React.Suspense>
|
||||
);
|
||||
|
|
@ -174,7 +169,7 @@ function Data(props) {
|
|||
<script
|
||||
id={props.id && `suspense-script-${props.id}`}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `!function(){window['${LOADER}'] = window['${LOADER}'] || {};window['${LOADER}']['${props.id}'] = ${JSON.stringify(data)}}();`,
|
||||
__html: `!function(){window['${LOADER}'] = window['${LOADER}'] || {};window['${LOADER}']['${props.id}'] = ${JSON.stringify(data)}}();window.dispatchEvent(new CustomEvent('ice-suspense-data', { detail: { id: ${props.id ? `'${props.id}'` : undefined} } }));`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2157,10 +2157,10 @@ importers:
|
|||
version: 1.2.4
|
||||
devDependencies:
|
||||
'@ice/app':
|
||||
specifier: ^3.6.3
|
||||
specifier: ^3.6.4
|
||||
version: link:../ice
|
||||
'@ice/runtime':
|
||||
specifier: ^1.5.3
|
||||
specifier: ^1.5.4
|
||||
version: link:../runtime
|
||||
webpack:
|
||||
specifier: ^5.88.0
|
||||
|
|
|
|||
Loading…
Reference in New Issue