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

[Flight] Dedupe modules #20172

Merged
merged 1 commit into from Nov 11, 2020
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
3 changes: 3 additions & 0 deletions packages/react-noop-renderer/src/ReactNoopFlightServer.js
Expand Up @@ -45,6 +45,9 @@ const ReactNoopFlightServer = ReactFlightServer({
isModuleReference(reference: Object): boolean {
return reference.$$typeof === Symbol.for('react.module.reference');
},
getModuleKey(reference: Object): Object {
return reference;
},
resolveModuleMetaData(
config: void,
reference: {$$typeof: Symbol, value: any},
Expand Down
9 changes: 9 additions & 0 deletions packages/react-server/src/ReactFlightServer.js
Expand Up @@ -14,6 +14,7 @@ import type {
BundlerConfig,
ModuleMetaData,
ModuleReference,
ModuleKey,
} from './ReactFlightServerConfig';

import {
Expand All @@ -28,6 +29,7 @@ import {
processSymbolChunk,
processErrorChunk,
resolveModuleMetaData,
getModuleKey,
isModuleReference,
} from './ReactFlightServerConfig';

Expand Down Expand Up @@ -79,6 +81,7 @@ export type Request = {
completedJSONChunks: Array<Chunk>,
completedErrorChunks: Array<Chunk>,
writtenSymbols: Map<Symbol, number>,
writtenModules: Map<ModuleKey, number>,
flowing: boolean,
toJSON: (key: string, value: ReactModel) => ReactJSONValue,
};
Expand All @@ -101,6 +104,7 @@ export function createRequest(
completedJSONChunks: [],
completedErrorChunks: [],
writtenSymbols: new Map(),
writtenModules: new Map(),
flowing: false,
toJSON: function(key: string, value: ReactModel): ReactJSONValue {
return resolveModelToJSON(request, this, key, value);
Expand Down Expand Up @@ -425,6 +429,11 @@ export function resolveModelToJSON(
if (typeof value === 'object') {
if (isModuleReference(value)) {
const moduleReference: ModuleReference<any> = (value: any);
const moduleKey: ModuleKey = getModuleKey(moduleReference);
const existingId = request.writtenModules.get(moduleKey);
if (existingId !== undefined) {
return serializeByValueID(existingId);
}
try {
const moduleMetaData: ModuleMetaData = resolveModuleMetaData(
request.bundlerConfig,
Expand Down
Expand Up @@ -12,5 +12,7 @@ declare var $$$hostConfig: any;
export opaque type BundlerConfig = mixed; // eslint-disable-line no-undef
export opaque type ModuleReference<T> = mixed; // eslint-disable-line no-undef
export opaque type ModuleMetaData: any = mixed; // eslint-disable-line no-undef
export opaque type ModuleKey: any = mixed; // eslint-disable-line no-undef
export const isModuleReference = $$$hostConfig.isModuleReference;
export const getModuleKey = $$$hostConfig.getModuleKey;
export const resolveModuleMetaData = $$$hostConfig.resolveModuleMetaData;
Expand Up @@ -38,6 +38,14 @@ export function isModuleReference(reference: Object): boolean {
return reference instanceof JSResourceReference;
}

export type ModuleKey = ModuleReference<any>;

export function getModuleKey(reference: ModuleReference<any>): ModuleKey {
// We use the reference object itself as the key because we assume the
// object will be cached by the bundler runtime.
return reference;
}

export function resolveModuleMetaData<T>(
config: BundlerConfig,
resource: ModuleReference<T>,
Expand Down
Expand Up @@ -25,8 +25,14 @@ export type ModuleMetaData = {
name: string,
};

export type ModuleKey = string;

const MODULE_TAG = Symbol.for('react.module.reference');

export function getModuleKey(reference: ModuleReference<any>): ModuleKey {
return reference.name;
}

export function isModuleReference(reference: Object): boolean {
return reference.$$typeof === MODULE_TAG;
}
Expand Down
Expand Up @@ -38,6 +38,14 @@ export function isModuleReference(reference: Object): boolean {
return reference instanceof JSResourceReferenceImpl;
}

export type ModuleKey = ModuleReference<any>;

export function getModuleKey(reference: ModuleReference<any>): ModuleKey {
// We use the reference object itself as the key because we assume the
// object will be cached by the bundler runtime.
return reference;
}

export function resolveModuleMetaData<T>(
config: BundlerConfig,
resource: ModuleReference<T>,
Expand Down
1 change: 1 addition & 0 deletions scripts/jest/setupHostConfigs.js
Expand Up @@ -35,6 +35,7 @@ jest.mock('react-server/flight', () => {
jest.mock(shimServerFormatConfigPath, () => config);
jest.mock('react-server/src/ReactFlightServerBundlerConfigCustom', () => ({
isModuleReference: config.isModuleReference,
getModuleKey: config.getModuleKey,
resolveModuleMetaData: config.resolveModuleMetaData,
}));
jest.mock(shimFlightServerConfigPath, () =>
Expand Down