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

Refactor: use the same compiler when build data-loader #6804

Merged
merged 9 commits into from
Mar 19, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/khaki-parrots-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@ice/rspack-config': patch
'@ice/shared-config': patch
'@ice/bundles': patch
'@ice/runtime': patch
'@ice/app': patch
---

refactor: the compilation for data-loader
10 changes: 10 additions & 0 deletions packages/ice/src/bundler/config/middlewares.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { Compiler } from '@rspack/core';
import type { Configuration as DevServerConfiguration } from 'webpack-dev-server';
import type { TaskConfig } from 'build-scripts';
import type { RenderMode } from '@ice/runtime';
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 type { UserConfig } from '../../types/userConfig.js';
import type RouteManifest from '../../utils/routeManifest.js';
import type { GetAppConfig } from '../../types/plugin.js';
Expand All @@ -16,6 +18,7 @@ interface SetupOptions {
excuteServerEntry: () => Promise<any>;
mock: boolean;
rootDir: string;
dataLoaderCompiler?: Compiler;
}

function setupMiddlewares(middlewares: Parameters<DevServerConfiguration['setupMiddlewares']>[0], {
Expand All @@ -26,6 +29,7 @@ function setupMiddlewares(middlewares: Parameters<DevServerConfiguration['setupM
excuteServerEntry,
mock,
rootDir,
dataLoaderCompiler,
}: SetupOptions) {
const { ssr, ssg } = userConfig;
let renderMode: RenderMode;
Expand All @@ -46,6 +50,12 @@ function setupMiddlewares(middlewares: Parameters<DevServerConfiguration['setupM
routeManifest,
excuteServerEntry,
});

if (dataLoaderCompiler) {
const dataLoaderMiddleware = createDataLoaderMiddleware(dataLoaderCompiler);
middlewares.unshift(dataLoaderMiddleware);
}

// @ts-ignore property of name is exist.
const insertIndex = middlewares.findIndex(({ name }) => name === 'serve-index');
middlewares.splice(
Expand Down
61 changes: 49 additions & 12 deletions packages/ice/src/bundler/rspack/getConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as path from 'path';
import getRspackConfig from '@ice/rspack-config';
import type { TaskConfig } from 'build-scripts';
import type { Configuration, rspack as Rspack } from '@rspack/core';
import type { Config } from '@ice/shared-config/types';
import { getRouteExportConfig } from '../../service/config.js';
Expand All @@ -11,7 +13,6 @@ import {
} from '../../constant.js';
import { getReCompilePlugin, getServerPlugin, getSpinnerPlugin } from '../config/plugins.js';
import { getExpandedEnvs } from '../../utils/runtimeEnv.js';
import DataLoaderPlugin from '../../webpack/DataLoaderPlugin.js';
import type { BundlerOptions, Context } from '../types.js';

type GetConfig = (
Expand All @@ -33,16 +34,14 @@ const getConfig: GetConfig = async (context, options, rspack) => {
const {
rootDir,
userConfig,
getAllPlugin,
extendsPluginAPI: {
serverCompileTask,
getRoutesFile,
generator,
},
} = context;
const { reCompile, ensureRoutesConfig } = getRouteExportConfig(rootDir);
const getPlugins = (taskConfig: Config): Config['plugins'] => {
const { target, outputDir, useDataLoader, server } = taskConfig;
const { target, outputDir, server } = taskConfig;
return [
// Add spinner for webpack task.
getSpinnerPlugin(spinner),
Expand All @@ -60,14 +59,6 @@ const getConfig: GetConfig = async (context, options, rspack) => {
}),
// Add ReCompile plugin when routes config changed.
getReCompilePlugin(reCompile, routeManifest),
// Add DataLoader plugin.
useDataLoader && new DataLoaderPlugin({
serverCompiler,
target,
rootDir,
getAllPlugin,
frameworkExports: generator.getExportList('framework', target),
}),
].filter(Boolean) as Config['plugins'];
};
return await Promise.all(taskConfigs.map(async ({ config }) => {
Expand All @@ -91,5 +82,51 @@ const getConfig: GetConfig = async (context, options, rspack) => {
}));
};

type GetDataLoaderRspackConfig = (
context: Context,
task: TaskConfig<Config>,
rspack: typeof Rspack,
) => Promise<Configuration>;

export const getDataLoaderConfig: GetDataLoaderRspackConfig = async (context, task, rspack) => {
const {
rootDir,
extendsPluginAPI: {
generator,
},
} = context;
const { config } = task;
const frameworkExports = generator.getExportList('framework', config.target);
return await getRspackConfig({
rootDir,
rspack,
runtimeTmpDir: RUNTIME_TMP_DIR,
getExpandedEnvs,
runtimeDefineVars: {
[IMPORT_META_TARGET]: JSON.stringify(config.target),
[IMPORT_META_RENDERER]: JSON.stringify('client'),
},
localIdentName: config.cssModules?.localIdentName || (config.mode === 'development' ? CSS_MODULES_LOCAL_IDENT_NAME_DEV : CSS_MODULES_LOCAL_IDENT_NAME),
taskConfig: {
...config,
// Override task config for dataLoader.
assetsManifest: false,
entry: {
'data-loader': path.join(rootDir, RUNTIME_TMP_DIR, 'data-loader.ts'),
},
swcOptions: {
keepExports: ['dataLoader'],
},
splitChunks: false,
redirectImports: frameworkExports,
// Data loader should be hot reload in development mode.
fastRefresh: false,
devServer: {
hot: false,
},
name: 'dataLoader',
},
});
};

export default getConfig;
32 changes: 29 additions & 3 deletions packages/ice/src/bundler/rspack/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { MultiCompiler, rspack as Rspack } from '@rspack/core';
import type { MultiCompiler, Compiler, rspack as Rspack } from '@rspack/core';
import type { RspackDevServer } from '@rspack/dev-server';
import { logger } from '../../utils/logger.js';
import type { BundlerOptions, Context } from '../types.js';
import getConfig from './getConfig.js';
import { WEB } from '../../constant.js';
import getConfig, { getDataLoaderConfig } from './getConfig.js';
import start from './start.js';
import build from './build.js';

Expand All @@ -16,13 +17,38 @@ async function bundler(
hooksAPI,
routeManifest,
appConfig,
hasDataLoader,
} = options;
let compiler: MultiCompiler;
let dataLoaderCompiler: Compiler;
let devServer: RspackDevServer;
const { rspack } = await import('@ice/bundles/esm/rspack.js');
// Override the type of rspack, because of rspack is imported from pre-compiled bundle.
const rspackConfigs = await getConfig(context, options, rspack as unknown as typeof Rspack);
try {
if (hasDataLoader) {
const dataLoaderRspackConfig = await getDataLoaderConfig(
context,
taskConfigs.find(({ name }) => name === WEB),
rspack as unknown as typeof Rspack,
);
if (command === 'start') {
// Create a special compiler for dataLoader,
// it will be used in dev-server middleware.
// @ts-ignore
dataLoaderCompiler = rspack(dataLoaderRspackConfig);
} else if (command === 'build') {
// Build parrallel when build.
rspackConfigs.push(dataLoaderRspackConfig);
// Override the output options of clean to false,
// Otherwise, the output of previous build will be cleaned.
rspackConfigs.forEach((config) => {
if (config?.output?.clean) {
config.output.clean = false;
}
});
}
}
// @ts-ignore
compiler = rspack(rspackConfigs);
} catch (error) {
Expand All @@ -40,7 +66,7 @@ async function bundler(
};
if (command === 'start') {
// @ts-expect-error dev-server has been pre-packed, so it will have different type.
devServer = await start(buildOptions);
devServer = await start(buildOptions, dataLoaderCompiler);
} else if (command === 'build') {
await build(buildOptions);
}
Expand Down
4 changes: 3 additions & 1 deletion packages/ice/src/bundler/rspack/start.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Compiler } from '@rspack/core';
import type { Configuration as DevServerConfiguration } from '@rspack/dev-server';
import getDefaultServerConfig from '../config/defaultServerConfig.js';
import getMiddlewares from '../config/middlewares.js';
Expand All @@ -15,7 +16,7 @@ const start = async ({
compiler,
appConfig,
hooksAPI,
}: BuildOptions) => {
}: BuildOptions, dataLoaderCompiler?: Compiler) => {
const { rootDir, applyHook, commandArgs, userConfig, extendsPluginAPI: { excuteServerEntry } } = context;
const customMiddlewares = rspackConfigs[0].devServer?.setupMiddlewares;
const defaultConfig = await getDefaultServerConfig(rspackConfigs[0].devServer, commandArgs);
Expand All @@ -32,6 +33,7 @@ const start = async ({
excuteServerEntry,
mock: commandArgs.mock,
rootDir,
dataLoaderCompiler,
});
return customMiddlewares ? customMiddlewares(builtInMiddlewares, devServer) : builtInMiddlewares;
},
Expand Down
1 change: 1 addition & 0 deletions packages/ice/src/bundler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface BundlerOptions {
configFile: string;
userConfig: UserConfig;
routeManifest: RouteManifest;
hasDataLoader: boolean;
}

export type CompileResults = {
Expand Down
11 changes: 8 additions & 3 deletions packages/ice/src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ export const RUNTIME_EXPORTS = [
'useSuspenseData',
'usePublicAppContext',
'Await',
'defineDataLoader',
'defineServerDataLoader',
'defineStaticDataLoader',
'usePageLifecycle',
'unstable_useDocumentData',
],
Expand All @@ -78,6 +75,14 @@ export const RUNTIME_EXPORTS = [
},
source: '@ice/runtime',
},
{
specifier: [
'defineDataLoader',
'defineServerDataLoader',
'defineStaticDataLoader',
],
source: '@ice/runtime/data-loader',
},
];

export const CSS_MODULES_LOCAL_IDENT_NAME = '[local]_[hash:8]';
Expand Down
2 changes: 2 additions & 0 deletions packages/ice/src/createService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
dataCache.set('routes', JSON.stringify(routesInfo));
dataCache.set('hasExportAppData', hasExportAppData ? 'true' : '');

const hasDataLoader = Boolean(userConfig.dataLoader) && (hasExportAppData || Boolean(routesInfo.loaders));
// Render exports files if route component export dataLoader / pageConfig.
renderExportsTemplate(
{
Expand Down Expand Up @@ -386,6 +387,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
},
userConfig,
configFile,
hasDataLoader,
};
try {
if (command === 'test') {
Expand Down
68 changes: 68 additions & 0 deletions packages/ice/src/middlewares/dataLoaderMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { IncomingMessage } from 'http';
import { parse } from 'url';
import crypto from 'crypto';
import type { Compiler } from '@rspack/core';
import type { ExpressRequestHandler, Middleware } from 'webpack-dev-server';
import { logger } from '../utils/logger.js';

function etag(buf: Buffer) {
return crypto.createHash('sha256').update(buf).digest('hex');
}

export default function createDataLoaderMiddleware(compiler: Compiler): Middleware {
const watchOptions = compiler.options.watchOptions || {};
const watching = compiler.watch(watchOptions, (
error,
) => {
if (error) {
console.error(error);
}
});
const compileTask = new Promise((resolve, reject) => {
compiler.hooks.done.tap('data-loader-compiler', (stats) => {
const statsJson = stats.toJson({
all: false,
});
if (!stats.hasErrors() || !statsJson?.errors?.length) {
resolve(watching);
} else if (statsJson?.errors?.length > 0) {
logger.error('[data-loader] Compile failed:');
logger.log(statsJson?.errors);
reject(false);
}
});
});

const middleware: ExpressRequestHandler = async function (req: IncomingMessage, res: any, next: () => void) {
const { method, url } = req;
if (method !== 'GET') {
return next();
}
const publicPath = compiler.options.output?.publicPath
? `${compiler.options.output.publicPath.replace(/\/$/, '')}/`
: '/';
const filePath = parse(url || '').pathname;
const filename = filePath?.startsWith(publicPath) ? filePath.slice(publicPath.length) : filePath.slice(1);
// Mark sure the compiler is ready.
await compileTask;
const buffer = compiler.getAsset(filename);

if (!buffer) {
return next();
}
const calcEtag = etag(buffer);
const oldEtag = req.headers['if-none-match'];
// Only data-loader.js will be matched.
res.setHeader('Content-Type', 'text/javascript');
res.setHeader('ETag', calcEtag);
if (calcEtag === oldEtag) {
res.status(304).send();
} else {
res.send(buffer);
}
};
return {
name: 'data-loader-middleware',
middleware,
};
}
1 change: 0 additions & 1 deletion packages/ice/src/plugins/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ const getDefaultTaskConfig = ({ rootDir, command }): Config => {
'universal-env': envReplacement,
'@uni/env': envReplacement,
},

assetsManifest: true,
fastRefresh: command === 'start',
logging: process.env.WEBPACK_LOGGING || defaultLogging,
Expand Down
2 changes: 1 addition & 1 deletion packages/ice/src/utils/renderExportsTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ function renderExportsTemplate(
});
}

export default renderExportsTemplate;
export default renderExportsTemplate;
Loading
Loading