Skip to content

Commit

Permalink
Feat: optimize runtime size (#6848)
Browse files Browse the repository at this point in the history
* feat: optimize runtime size

* fix: log error instead of throw

* chore: fix test case

* chore: clean up useless params
  • Loading branch information
ClarkXia authored Apr 2, 2024
1 parent ee14962 commit 44ef63f
Show file tree
Hide file tree
Showing 23 changed files with 92 additions and 478 deletions.
4 changes: 1 addition & 3 deletions packages/ice/src/bundler/config/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async function buildCustomOutputs(
bundleOptions: Pick<BundlerOptions, 'userConfig' | 'appConfig' | 'routeManifest'>,
) {
const { userConfig, appConfig, routeManifest } = bundleOptions;
const { ssg, output: { distType, prependCode } } = userConfig;
const { ssg } = userConfig;
const routeType = appConfig?.router?.type;
const {
outputPaths = [],
Expand All @@ -51,8 +51,6 @@ async function buildCustomOutputs(
documentOnly: !ssg,
renderMode: ssg ? 'SSG' : undefined,
routeType: appConfig?.router?.type,
distType,
prependCode,
routeManifest,
});
if (routeType === 'memory' && userConfig?.routes?.injectInitialEntry) {
Expand Down
9 changes: 9 additions & 0 deletions packages/ice/src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ export const RUNTIME_EXPORTS = [
'usePageLifecycle',
'unstable_useDocumentData',
'dynamic',
// Document API
'Meta',
'Title',
'Links',
'Scripts',
'FirstChunkCache',
'Data',
'Main',
'usePageAssets',
],
alias: {
usePublicAppContext: 'useAppContext',
Expand Down
54 changes: 15 additions & 39 deletions packages/ice/src/createService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ import { Context } from 'build-scripts';
import type { CommandArgs, CommandName } from 'build-scripts';
import type { Config } from '@ice/shared-config/types';
import type { AppConfig } from '@ice/runtime/types';
import fse from 'fs-extra';
import webpack from '@ice/bundles/compiled/webpack/index.js';
import type {
DeclarationData,
PluginData,
ExtendsPluginAPI,
TargetDeclarationData,
} from './types/index.js';
import { DeclarationType } from './types/index.js';
import Generator from './service/runtimeGenerator.js';
import { createServerCompiler } from './service/serverCompiler.js';
import createWatch from './service/watchSource.js';
Expand All @@ -41,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 hasDocument from './utils/hasDocument.js';

const require = createRequire(import.meta.url);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
Expand Down Expand Up @@ -75,56 +73,35 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
let entryCode = 'render();';

const generatorAPI = {
addExport: (declarationData: Omit<DeclarationData, 'declarationType'>) => {
generator.addDeclaration('framework', {
...declarationData,
declarationType: DeclarationType.NORMAL,
});
addExport: (declarationData: DeclarationData) => {
generator.addDeclaration('framework', declarationData);
},
addTargetExport: (declarationData: Omit<TargetDeclarationData, 'declarationType'>) => {
generator.addDeclaration('framework', {
...declarationData,
declarationType: DeclarationType.TARGET,
});
addTargetExport: () => {
logger.error('`addTargetExport` is deprecated, please use `addExport` instead.');
},
addExportTypes: (declarationData: Omit<DeclarationData, 'declarationType'>) => {
generator.addDeclaration('frameworkTypes', {
...declarationData,
declarationType: DeclarationType.NORMAL,
});
addExportTypes: (declarationData: DeclarationData) => {
generator.addDeclaration('frameworkTypes', declarationData);
},
addRuntimeOptions: (declarationData: Omit<DeclarationData, 'declarationType'>) => {
generator.addDeclaration('runtimeOptions', {
...declarationData,
declarationType: DeclarationType.NORMAL,
});
addRuntimeOptions: (declarationData: DeclarationData) => {
generator.addDeclaration('runtimeOptions', declarationData);
},
removeRuntimeOptions: (removeSource: string | string[]) => {
generator.removeDeclaration('runtimeOptions', removeSource);
},
addRouteTypes: (declarationData: Omit<DeclarationData, 'declarationType'>) => {
generator.addDeclaration('routeConfigTypes', {
...declarationData,
declarationType: DeclarationType.NORMAL,
});
addRouteTypes: (declarationData: DeclarationData) => {
generator.addDeclaration('routeConfigTypes', declarationData);
},
addRenderFile: generator.addRenderFile,
addRenderTemplate: generator.addTemplateFiles,
addEntryCode: (callback: (originalCode: string) => string) => {
entryCode = callback(entryCode);
},
addEntryImportAhead: (declarationData: Pick<DeclarationData, 'source'>) => {
generator.addDeclaration('entry', {
...declarationData,
declarationType: DeclarationType.NORMAL,
});
generator.addDeclaration('entry', declarationData);
},
modifyRenderData: generator.modifyRenderData,
addDataLoaderImport: (declarationData: DeclarationData) => {
generator.addDeclaration('dataLoaderImport', {
...declarationData,
declarationType: DeclarationType.NORMAL,
});
generator.addDeclaration('dataLoaderImport', declarationData);
},
getExportList: (registerKey: string) => {
return generator.getExportList(registerKey);
Expand Down Expand Up @@ -239,7 +216,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt

// get userConfig after setup because of userConfig maybe modified by plugins
const { userConfig } = ctx;
const { routes: routesConfig, server, syntaxFeatures, polyfill, output: { distType } } = userConfig;
const { routes: routesConfig, server, syntaxFeatures, polyfill } = userConfig;

const coreEnvKeys = getCoreEnvKeys();

Expand Down Expand Up @@ -286,8 +263,7 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
// Enable react-router for web as default.
enableRoutes: true,
entryCode,
jsOutput: distType.includes('javascript'),
hasDocument: fse.existsSync(path.join(rootDir, 'src/document.tsx')) || fse.existsSync(path.join(rootDir, 'src/document.jsx')) || fse.existsSync(path.join(rootDir, 'src/document.js')),
hasDocument: hasDocument(rootDir),
dataLoader: userConfig.dataLoader,
routeImports,
routeDefinition,
Expand Down
27 changes: 1 addition & 26 deletions packages/ice/src/plugins/web/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,8 @@ import { logger } from '../../utils/logger.js';

const plugin: Plugin = () => ({
name: 'plugin-web',
setup: ({ registerTask, onHook, context, generator }) => {
setup: ({ registerTask, onHook, context }) => {
const { commandArgs, command, userConfig } = context;

generator.addTargetExport({
specifier: [
'Meta',
'Title',
'Links',
'Scripts',
'FirstChunkCache',
'Data',
'Main',
'usePageAssets',
],
types: [
'MetaType',
'TitleType',
'LinksType',
'ScriptsType',
'FirstChunkCacheType',
'DataType',
'MainType',
],
source: '@ice/runtime',
target: 'web',
});

const removeExportExprs = ['serverDataLoader', 'staticDataLoader'];
// Remove dataLoader exports only when build in production
// and configure to generate data-loader.js.
Expand Down
118 changes: 31 additions & 87 deletions packages/ice/src/service/runtimeGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import type {
RenderTemplate,
RenderData,
DeclarationData,
TargetDeclarationData,
Registration,
TemplateOptions,
} from '../types/generator.js';
Expand All @@ -37,76 +36,36 @@ interface Options {
templates?: (string | TemplateOptions)[];
}

function isDeclarationData(data: TargetDeclarationData | DeclarationData): data is DeclarationData {
return data.declarationType === 'normal';
}

function isTargetDeclarationData(data: TargetDeclarationData | DeclarationData): data is TargetDeclarationData {
return data.declarationType === 'target';
}

export function generateDeclaration(exportList: Array<TargetDeclarationData | DeclarationData>) {
const targetImportDeclarations: Array<string> = [];
export function generateDeclaration(exportList: DeclarationData[]) {
const importDeclarations: Array<string> = [];
const exportDeclarations: Array<string> = [];
const exportNames: Array<string> = [];
const variables: Map<string, string> = new Map();

let moduleId = 0;
exportList.forEach(data => {
// Deal with target.
if (isTargetDeclarationData(data)) {
const { specifier, source, target, types = [] } = data;
const isDefaultImport = !Array.isArray(specifier);
const specifiers = isDefaultImport ? [specifier] : specifier;
const arrTypes: Array<string> = Array.isArray(types) ? types : [types];

moduleId++;
const moduleName = `${target}Module${moduleId}`;
targetImportDeclarations.push(`if (import.meta.target === '${target}') {
${specifiers.map(item => `${item} = ${moduleName}.${item};`).join('\n ')}
}
`);

importDeclarations.push(`import ${isDefaultImport ? moduleName : `* as ${moduleName}`} from '${source}';`);

if (arrTypes.length) {
importDeclarations.push(`import type { ${arrTypes.join(', ')}} from '${source}';`);
}

specifiers.forEach((specifierStr, index) => {
if (!variables.has(specifierStr)) {
variables.set(specifierStr, arrTypes[index] || 'any');
const { specifier, source, alias, type } = data;
const isDefaultImport = !Array.isArray(specifier);
const specifiers = isDefaultImport ? [specifier] : specifier;
const symbol = type ? ';' : ',';

if (specifier) {
importDeclarations.push(`import ${type ? 'type ' : ''}${isDefaultImport ? specifier : `{ ${specifiers.map(specifierStr => ((alias && alias[specifierStr]) ? `${specifierStr} as ${alias[specifierStr]}` : specifierStr)).join(', ')} }`} from '${source}';`);

specifiers.forEach((specifierStr) => {
if (alias && alias[specifierStr]) {
exportDeclarations.push(`${alias[specifierStr]}${symbol}`);
exportNames.push(alias[specifierStr]);
} else {
exportDeclarations.push(`${specifierStr}${symbol}`);
exportNames.push(specifierStr);
}
});
} else if (isDeclarationData(data)) {
const { specifier, source, alias, type } = data;
const isDefaultImport = !Array.isArray(specifier);
const specifiers = isDefaultImport ? [specifier] : specifier;
const symbol = type ? ';' : ',';

if (specifier) {
importDeclarations.push(`import ${type ? 'type ' : ''}${isDefaultImport ? specifier : `{ ${specifiers.map(specifierStr => ((alias && alias[specifierStr]) ? `${specifierStr} as ${alias[specifierStr]}` : specifierStr)).join(', ')} }`} from '${source}';`);

specifiers.forEach((specifierStr) => {
if (alias && alias[specifierStr]) {
exportDeclarations.push(`${alias[specifierStr]}${symbol}`);
exportNames.push(alias[specifierStr]);
} else {
exportDeclarations.push(`${specifierStr}${symbol}`);
exportNames.push(specifierStr);
}
});
} else {
importDeclarations.push(`import '${source}';`);
}
} else {
importDeclarations.push(`import '${source}';`);
}
});

return {
targetImportStr: targetImportDeclarations.join('\n'),
importStr: importDeclarations.join('\n'),
targetExportStr: Array.from(variables.keys()).join(',\n '),
/**
* Add two whitespace character in order to get the formatted code. For example:
* export {
Expand All @@ -116,39 +75,30 @@ export function generateDeclaration(exportList: Array<TargetDeclarationData | De
*/
exportStr: exportDeclarations.join('\n '),
exportNames,
variablesStr: Array.from(variables.entries()).map(item => `let ${item[0]}: ${item[1]};`).join('\n'),
};
}

export function checkExportData(
currentList: (DeclarationData | TargetDeclarationData)[],
exportData: (DeclarationData | TargetDeclarationData) | (DeclarationData | TargetDeclarationData)[],
currentList: DeclarationData[],
exportData: DeclarationData | DeclarationData[],
apiName: string,
) {
(Array.isArray(exportData) ? exportData : [exportData]).forEach((data) => {
const exportNames = (Array.isArray(data.specifier) ? data.specifier : [data.specifier]).map((specifierStr) => {
if (isDeclarationData(data)) {
return data?.alias?.[specifierStr] || specifierStr;
} else {
return specifierStr;
}
return data?.alias?.[specifierStr] || specifierStr;
});
currentList.forEach((item) => {
if (isTargetDeclarationData(item)) return;

if (isDeclarationData(item)) {
const { specifier, alias, source } = item;
const { specifier, alias, source } = item;

// check exportName and specifier
const currentExportNames = (Array.isArray(specifier) ? specifier : [specifier]).map((specifierStr) => {
return alias?.[specifierStr] || specifierStr || source;
});
// check exportName and specifier
const currentExportNames = (Array.isArray(specifier) ? specifier : [specifier]).map((specifierStr) => {
return alias?.[specifierStr] || specifierStr || source;
});

if (currentExportNames.some((name) => exportNames.includes(name))) {
logger.error('specifier:', specifier, 'alias:', alias);
logger.error('duplicate with', data);
throw new Error(`duplicate export data added by ${apiName}`);
}
if (currentExportNames.some((name) => exportNames.includes(name))) {
logger.error('specifier:', specifier, 'alias:', alias);
logger.error('duplicate with', data);
throw new Error(`duplicate export data added by ${apiName}`);
}
});
});
Expand Down Expand Up @@ -253,18 +203,12 @@ export default class Generator {
importStr,
exportStr,
exportNames,
targetExportStr,
targetImportStr,
variablesStr,
} = generateDeclaration(exportList);
const [importStrKey, exportStrKey, targetImportStrKey, targetExportStrKey] = dataKeys;
const [importStrKey, exportStrKey] = dataKeys;
return {
[importStrKey]: importStr,
[exportStrKey]: exportStr,
exportNames,
variablesStr,
[targetImportStrKey]: targetImportStr,
[targetExportStrKey]: targetExportStr,
};
};

Expand Down
14 changes: 0 additions & 14 deletions packages/ice/src/types/generator.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,8 @@
export enum DeclarationType {
NORMAL = 'normal',
TARGET = 'target',
}

export interface DeclarationData {
specifier?: string | string[];
source: string;
type?: boolean;
alias?: Record<string, string>;
declarationType?: DeclarationType;
}

export interface TargetDeclarationData {
specifier: string | string[];
source: string;
target: string;
types?: string | string[];
declarationType?: DeclarationType;
}

export type RenderData = Record<string, any>;
Expand Down
Loading

0 comments on commit 44ef63f

Please sign in to comment.