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

static render micro optim #742

Merged
merged 5 commits into from
Jul 8, 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
2 changes: 2 additions & 0 deletions build/api/binding.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1838,6 +1838,8 @@ export namespace TokenRegExps {
entityIdentifier: RegExp;
const // (undocumented)
identifier: RegExp;
const // (undocumented)
dotSeparatedIdentifier: RegExp;
}

// @public (undocumented)
Expand Down
6 changes: 3 additions & 3 deletions build/api/react-repeater.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { EntityListAccessor } from '@contember/binding';
import { JSX as JSX_2 } from 'react/jsx-runtime';
import { default as React_2 } from 'react';
import { ReactNode } from 'react';
import { SugaredFieldProps } from '@contember/react-binding';
import { SugaredQualifiedEntityList } from '@contember/binding';
import { SugaredRelativeEntityList } from '@contember/binding';
import { SugaredRelativeSingleField } from '@contember/binding';

// @public (undocumented)
export const Repeater: React_2.NamedExoticComponent<RepeaterProps>;
Expand Down Expand Up @@ -83,13 +83,13 @@ export type RepeaterProps = RepeaterQualifiedProps | RepeaterRelativeProps;
// @public (undocumented)
export type RepeaterQualifiedProps = SugaredQualifiedEntityList & {
children?: React_2.ReactNode;
sortableBy?: SugaredFieldProps['field'];
sortableBy?: SugaredRelativeSingleField['field'];
};

// @public (undocumented)
export type RepeaterRelativeProps = SugaredRelativeEntityList & {
children?: React_2.ReactNode;
sortableBy?: SugaredFieldProps['field'];
sortableBy?: SugaredRelativeSingleField['field'];
};

// @public (undocumented)
Expand Down
9 changes: 5 additions & 4 deletions build/api/react-slate-editor-legacy.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { RenderElementProps } from 'slate-react';
import * as Slate from 'slate';
import { SugaredFieldProps } from '@contember/react-binding';
import { SugaredRelativeEntityList } from '@contember/react-binding';
import { SugaredRelativeSingleField } from '@contember/react-binding';

// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@contember/react-slate-editor-legacy" does not have an export "BlockRepeater"
// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "@contember/react-slate-editor-legacy" does not have an export "DiscriminatedBlocks"
Expand All @@ -57,17 +58,17 @@ export interface BlockEditorProps extends SugaredRelativeEntityList, CreateEdito
// (undocumented)
children?: ReactNode;
// (undocumented)
contentField: SugaredFieldProps['field'];
contentField: SugaredRelativeSingleField['field'];
// (undocumented)
embedContentDiscriminationField?: SugaredFieldProps['field'];
embedContentDiscriminationField?: SugaredRelativeSingleField['field'];
// (undocumented)
embedHandlers?: Iterable<EmbedHandler>;
// (undocumented)
embedReferenceDiscriminateBy?: SugaredDiscriminateBy;
// (undocumented)
monolithicReferencesMode?: boolean;
// (undocumented)
referenceDiscriminationField?: SugaredFieldProps['field'];
referenceDiscriminationField?: SugaredRelativeSingleField['field'];
// (undocumented)
referencesField?: SugaredRelativeEntityList | string;
// (undocumented)
Expand All @@ -77,7 +78,7 @@ export interface BlockEditorProps extends SugaredRelativeEntityList, CreateEdito
// (undocumented)
renderSortableBlock: OverrideRenderElementOptions['renderSortableBlock'];
// (undocumented)
sortableBy: SugaredFieldProps['field'];
sortableBy: SugaredRelativeSingleField['field'];
}

// @public (undocumented)
Expand Down
6 changes: 6 additions & 0 deletions packages/binding/src/core/MarkerMerger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ export class MarkerMerger {
if (original === fresh) {
return original
}
if (original.size === 0) {
return fresh
}
const newOriginal: Map<PlaceholderName, MeaningfulMarker> = new Map(original)
for (const [placeholderName, freshMarker] of fresh) {
const markerFromOriginal = newOriginal.get(placeholderName)
Expand All @@ -160,6 +163,9 @@ export class MarkerMerger {
if (original === fresh) {
return original
}
if (original.size === 0) {
return fresh
}
const newOriginal: EntityFieldPlaceholders = new Map(original)
for (const [fieldName, freshPlaceholders] of fresh) {
const placeholderFromOriginal = newOriginal.get(fieldName)
Expand Down
75 changes: 52 additions & 23 deletions packages/binding/src/queryLanguage/QueryLanguage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
import { Parser } from './Parser'
import { GraphQlLiteral } from '@contember/client'
import { ParsedHasManyRelation, ParsedHasOneRelation } from './ParserResults'
import { TokenRegExps } from './tokenList'

const emptyObject = Object.freeze({})

Expand Down Expand Up @@ -293,13 +294,17 @@ export class QueryLanguage {
}

private static augmentDesugaredHasOneRelationPath(
path: ParsedHasOneRelation[],
path: (ParsedHasOneRelation | string)[],
unsugarable: UnsugarableHasOneRelation,
environment: Environment,
): HasOneRelation[] {
return path.map((desugaredHasOneRelation, i) =>
// Unsugarable applies to the last
this.desugarHasOneRelation(desugaredHasOneRelation, i === path.length - 1 ? unsugarable : {}, environment),
this.desugarHasOneRelation(
typeof desugaredHasOneRelation === 'string' ? { field: desugaredHasOneRelation } : desugaredHasOneRelation,
i === path.length - 1 ? unsugarable : {},
environment,
),
)
}

Expand Down Expand Up @@ -636,19 +641,30 @@ export class QueryLanguage {
sugaredRelativeSingleEntity: string | SugaredRelativeSingleEntity,
environment: Environment,
): RelativeSingleEntity {
let field: SugaredRelativeSingleEntity['field']
let unsugarableEntity: Omit<SugaredRelativeSingleEntity, 'field'>
if (typeof sugaredRelativeSingleEntity === 'string') {
return this.desugarRelativeSingleEntity({ field: sugaredRelativeSingleEntity }, environment)
field = sugaredRelativeSingleEntity
unsugarableEntity = {}
} else {
field = sugaredRelativeSingleEntity.field
unsugarableEntity = sugaredRelativeSingleEntity
}

const { field, ...unsugarableEntity } = sugaredRelativeSingleEntity
let hasOneRelationPath: HasOneRelation[]
if (typeof field === 'string') {
const parsed = this.parseRelativeSingleEntity(field, environment)
hasOneRelationPath = this.augmentDesugaredHasOneRelationPath(
parsed.hasOneRelationPath,
unsugarableEntity,
environment,
)
// fast path
if (field.match(TokenRegExps.dotSeparatedIdentifier)) {
const parts = field.split('.')
hasOneRelationPath = this.augmentDesugaredHasOneRelationPath(parts, unsugarableEntity, environment)
} else {
const parsed = this.parseRelativeSingleEntity(field, environment)
hasOneRelationPath = this.augmentDesugaredHasOneRelationPath(
parsed.hasOneRelationPath,
unsugarableEntity,
environment,
)
}

} else {
hasOneRelationPath = this.desugarHasOneRelationPath(field, unsugarableEntity, environment)
Expand All @@ -663,18 +679,30 @@ export class QueryLanguage {
sugaredRelativeSingleField: string | SugaredRelativeSingleField,
environment: Environment,
): RelativeSingleField {
let field: SugaredRelativeSingleField['field']
let unsugarableField: Omit<SugaredRelativeSingleField, 'field'>
if (typeof sugaredRelativeSingleField === 'string') {
return this.desugarRelativeSingleField({ field: sugaredRelativeSingleField }, environment)
field = sugaredRelativeSingleField
unsugarableField = {}
} else {
field = sugaredRelativeSingleField.field
unsugarableField = sugaredRelativeSingleField
}

const { field, ...unsugarableField } = sugaredRelativeSingleField

let hasOneRelationPath: HasOneRelation[]
let fieldName: FieldName
if (typeof field === 'string') {
const parsed = this.parseRelativeSingleField(field, environment)
hasOneRelationPath = this.augmentDesugaredHasOneRelationPath(parsed.hasOneRelationPath, {}, environment)
fieldName = parsed.field
// fast path
if (field.match(TokenRegExps.dotSeparatedIdentifier)) {
const parts = field.split('.')
fieldName = parts.pop()!
hasOneRelationPath = this.augmentDesugaredHasOneRelationPath(parts, {}, environment)
} else {
const parsed = this.parseRelativeSingleField(field, environment)
hasOneRelationPath = this.augmentDesugaredHasOneRelationPath(parsed.hasOneRelationPath, {}, environment)
fieldName = parsed.field
}

} else {
hasOneRelationPath = this.desugarHasOneRelationPath(field.hasOneRelationPath, {}, environment)
fieldName = field.field
Expand All @@ -696,16 +724,17 @@ export class QueryLanguage {
sugaredRelativeEntityList: string | SugaredRelativeEntityList,
environment: Environment,
): RelativeEntityList {
let field: SugaredRelativeEntityList['field']
let unsugarableEntityList: Omit<SugaredRelativeEntityList, 'field'>

if (typeof sugaredRelativeEntityList === 'string') {
return this.desugarRelativeEntityList(
{
field: sugaredRelativeEntityList,
},
environment,
)
field = sugaredRelativeEntityList
unsugarableEntityList = {}
} else {
field = sugaredRelativeEntityList.field
unsugarableEntityList = sugaredRelativeEntityList
}

const { field, ...unsugarableEntityList } = sugaredRelativeEntityList
let hasOneRelationPath: HasOneRelation[]
let hasManyRelation: HasManyRelation
if (typeof field === 'string') {
Expand Down
2 changes: 2 additions & 0 deletions packages/binding/src/queryLanguage/tokenList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { createToken, Lexer } from 'chevrotain'
export namespace TokenRegExps {
export const entityIdentifier = /[A-Z_]\w*/
export const identifier = /[a-zA-Z_]\w*/

export const dotSeparatedIdentifier = /^[a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*$/
}

const identifier = createToken({
Expand Down
21 changes: 5 additions & 16 deletions packages/react-repeater/src/components/Repeater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,21 @@ import React, { useEffect, useMemo } from 'react'
import { verifySortableProp } from '../internal/verifySortableProp'
import { useCreateRepeaterMethods } from '../internal/useCreateRepeaterMethods'
import { RepeaterEntityListAccessorContext, RepeaterMethodsContext, RepeaterSortedEntitiesContext } from '../contexts'
import {
Component,
EntityListSubTree,
HasMany,
repairEntitiesOrder,
sortEntities,
SugaredField,
SugaredFieldProps,
useEntityList,
useEntityListSubTree,
useEnvironment,
} from '@contember/react-binding'
import { EntityListAccessor, QueryLanguage, SugaredQualifiedEntityList, SugaredRelativeEntityList } from '@contember/binding'
import { Component, EntityListSubTree, HasMany, repairEntitiesOrder, sortEntities, SugaredField, useEntityList, useEntityListSubTree, useEnvironment } from '@contember/react-binding'
import { EntityListAccessor, QueryLanguage, SugaredQualifiedEntityList, SugaredRelativeEntityList, SugaredRelativeSingleField } from '@contember/binding'

export type RepeaterRelativeProps =
& SugaredRelativeEntityList
& {
children?: React.ReactNode
sortableBy?: SugaredFieldProps['field']
sortableBy?: SugaredRelativeSingleField['field']
}

export type RepeaterQualifiedProps =
& SugaredQualifiedEntityList
& {
children?: React.ReactNode
sortableBy?: SugaredFieldProps['field']
sortableBy?: SugaredRelativeSingleField['field']
}

export type RepeaterProps =
Expand Down Expand Up @@ -90,7 +79,7 @@ const RepeaterQualified = Component(
interface RepeaterInnerProps {
accessor: EntityListAccessor
children: React.ReactNode
sortableBy?: SugaredFieldProps['field']
sortableBy?: SugaredRelativeSingleField['field']
}

const RepeaterInner = ({ sortableBy, accessor, children }: RepeaterInnerProps) => {
Expand Down
20 changes: 10 additions & 10 deletions packages/react-slate-editor-legacy/src/blockEditor/BlockEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import {
BindingError,
Component,
Environment,
Field,
FieldValue,
HasMany,
SugaredField,
SugaredFieldProps,
SugaredRelativeEntityList,
SugaredRelativeEntityList, SugaredRelativeSingleField,
TreeNodeEnvironmentFactory,
useDesugaredRelativeSingleField,
useEntity,
Expand Down Expand Up @@ -36,17 +36,17 @@ import { createEditor, CreateEditorPublicOptions, paragraphElementType } from '@

export interface BlockEditorProps extends SugaredRelativeEntityList, CreateEditorPublicOptions {

contentField: SugaredFieldProps['field']
sortableBy: SugaredFieldProps['field']
contentField: SugaredRelativeSingleField['field']
sortableBy: SugaredRelativeSingleField['field']
children?: ReactNode

referencesField?: SugaredRelativeEntityList | string
referenceDiscriminationField?: SugaredFieldProps['field']
referenceDiscriminationField?: SugaredRelativeSingleField['field']
monolithicReferencesMode?: boolean
renderReference?: ComponentType<ReferenceElementRendererProps>

embedReferenceDiscriminateBy?: SugaredDiscriminateBy
embedContentDiscriminationField?: SugaredFieldProps['field']
embedContentDiscriminationField?: SugaredRelativeSingleField['field']
embedHandlers?: Iterable<EmbedHandler>
renderSortableBlock: OverrideRenderElementOptions['renderSortableBlock']
}
Expand Down Expand Up @@ -196,11 +196,11 @@ const BlockEditorComponent: FunctionComponent<BlockEditorProps> = Component(
{...(typeof props.referencesField === 'string' ? { field: props.referencesField } : props.referencesField)}
initialEntityCount={0}
>
<SugaredField field={props.referenceDiscriminationField} />
<Field field={props.referenceDiscriminationField} />
{props.children}
{props.embedContentDiscriminationField && (
<>
<SugaredField field={props.embedContentDiscriminationField} />
<Field field={props.embedContentDiscriminationField} />
{embedHandlers.map((handler, i) => (
<Fragment key={i}>{handler.staticRender(environment)}</Fragment>
))}
Expand All @@ -212,8 +212,8 @@ const BlockEditorComponent: FunctionComponent<BlockEditorProps> = Component(
return (
<>
<HasMany field={props.field} initialEntityCount={0}>
<SugaredField field={props.sortableBy} />
<SugaredField field={props.contentField} />
<Field field={props.sortableBy} />
<Field field={props.contentField} />
{!props.monolithicReferencesMode && references}
</HasMany>
{props.monolithicReferencesMode && references}
Expand Down
Loading