Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix types in memo-cache as well as a codescan security issue",
"packageName": "@fluentui-react-native/framework",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix types in memo-cache as well as a codescan security issue",
"packageName": "@fluentui-react-native/framework-base",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix types in memo-cache as well as a codescan security issue",
"packageName": "@fluentui-react-native/icon",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix types in memo-cache as well as a codescan security issue",
"packageName": "@fluentui-react-native/memo-cache",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix types in memo-cache as well as a codescan security issue",
"packageName": "@fluentui-react-native/use-styling",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix types in memo-cache as well as a codescan security issue",
"packageName": "@fluentui-react-native/use-tokens",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix types in memo-cache as well as a codescan security issue",
"packageName": "@uifabricshared/foundation-compose",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix types in memo-cache as well as a codescan security issue",
"packageName": "@uifabricshared/foundation-tokens",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix types in memo-cache as well as a codescan security issue",
"packageName": "@uifabricshared/themed-settings",
"email": "[email protected]",
"dependentChangeType": "patch"
}
4 changes: 1 addition & 3 deletions packages/components/Icon/src/SvgIcon/useSvgIcon.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import type { ImageStyle } from 'react-native';

import { getMemoCache, mergeStyles } from '@fluentui-react-native/framework';

import type { SvgIconProps } from './SvgIcon.types';

const rasterImageStyleCache = getMemoCache<ImageStyle>();
const rasterImageStyleCache = getMemoCache();

export const useSvgIcon = (props: SvgIconProps): SvgIconProps => {
const { accessible, style, height, width, ...rest } = props;
Expand Down
4 changes: 2 additions & 2 deletions packages/components/Icon/src/legacy/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { Image, Platform, View } from 'react-native';
import type { ImageStyle, TextStyle } from 'react-native';

import { mergeStyles, useFluentTheme } from '@fluentui-react-native/framework';
import { stagedComponent, mergeProps, getMemoCache } from '@fluentui-react-native/framework';
import { stagedComponent, mergeProps, getMemoCache, getTypedMemoCache } from '@fluentui-react-native/framework';
import { Text } from '@fluentui-react-native/text';
import type { SvgProps } from 'react-native-svg';
import { SvgUri } from 'react-native-svg';

import type { IconProps, SvgIconProps, FontIconProps } from './Icon.types';

const rasterImageStyleCache = getMemoCache<ImageStyle>();
const rasterImageStyleCache = getTypedMemoCache<ImageStyle>();

function renderRasterImage(iconProps: IconProps) {
const { width, height, color } = iconProps;
Expand Down
14 changes: 4 additions & 10 deletions packages/deprecated/foundation-compose/src/useStyling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function _getHasToken<TProps, TSlotProps extends ISlotProps, TTokens extends obj
function useStylingCore<TProps, TSlotProps extends ISlotProps, TTokens extends object>(
props: TProps,
options: IStylingSettings<TSlotProps, TTokens>,
instanceMemoCache: GetMemoValue<TSlotProps, TSlotProps>,
instanceMemoCache: GetMemoValue,
lookupOverride?: IOverrideLookup,
): IWithTokens<TSlotProps, TTokens> {
// get the theme value from the context (or the default theme if it is not set)
Expand All @@ -50,20 +50,14 @@ function useStylingCore<TProps, TSlotProps extends ISlotProps, TTokens extends o
const { settings, getMemoValue } = getThemedSettings<ILocalSettings, ITheme>(
options.settings,
theme,
instanceMemoCache as GetMemoValue<any>,
instanceMemoCache as GetMemoValue,
lookupOverride,
_getSettingsFromTheme as any,
);

// finish by processing the tokens and turning IComponentSettings into ISlotProps (this removes things like _overrides)
return returnAsSlotProps(
processTokens<TSlotProps, TTokens, ITheme>(
props as unknown as TTokens,
theme,
settings as any,
options.resolvedTokens,
getMemoValue as GetMemoValue<any>,
),
processTokens<TSlotProps, TTokens, ITheme>(props as unknown as TTokens, theme, settings as any, options.resolvedTokens, getMemoValue),
) as IWithTokens<TSlotProps, TTokens>;
}

Expand All @@ -86,7 +80,7 @@ export function initializeStyling<
options.resolvedTokens = buildComponentTokens<TSlotProps, TTokens, ITheme>(styles, _getHasToken(slots));

// memo cache root for this component, keyed on options
const getMemoValue = getMemoCache<TSlotProps>(options);
const getMemoValue = getMemoCache(options);

// create a useStyling implementation for this component type (per type, not per instance)
return (props: TProps, lookupOverride?: IOverrideLookup) => {
Expand Down
12 changes: 3 additions & 9 deletions packages/deprecated/foundation-tokens/src/MockComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type IMockComponentFn<TProps, TSlotProps extends ISlotProps, TTokens> = (
props: TProps,
settings: IComponentSettings<TSlotProps> & { tokens?: TTokens },
theme: IMockTheme,
cache: GetMemoValue<any>,
cache: GetMemoValue,
recurse?: boolean,
) => TSlotProps | TProps;

Expand All @@ -36,7 +36,7 @@ export function stockFakeComponent(
props: IMockBaseProps,
_settings: ISlotProps,
_theme: IMockTheme,
_cache: GetMemoValue<any>,
_cache: GetMemoValue,
_recurse: boolean,
): IMockBaseProps {
return props;
Expand All @@ -56,13 +56,7 @@ export function mockCreate<TProps extends object, TSlotProps extends ISlotProps,
options.styles,
hasTokens,
);
const fn = (
props: TProps,
settings: TSlotProps & { tokens?: TTokens },
theme: IMockTheme,
cache: GetMemoValue<any>,
recurse?: boolean,
) => {
const fn = (props: TProps, settings: TSlotProps & { tokens?: TTokens }, theme: IMockTheme, cache: GetMemoValue, recurse?: boolean) => {
let newSettings = processTokens(props as any, theme, settings as any, resolvedTokens, cache);
if (recurse) {
Object.keys(slots).forEach((slotName: string) => {
Expand Down
6 changes: 3 additions & 3 deletions packages/deprecated/foundation-tokens/src/Token.function.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { GetMemoValue } from '@fluentui-react-native/framework-base';
import type { GetTypedMemoValue } from '@fluentui-react-native/framework-base';
import { mergeProps } from '@fluentui-react-native/framework-base';
import type { ISlotProps } from '@uifabricshared/foundation-settings';

Expand Down Expand Up @@ -82,7 +82,7 @@ function _getCachedPropsForSlot<TProps, TTokens, TTheme>(
tokenProps: ITokenPropInfo<TTokens>,
theme: TTheme,
slotName: string,
getMemoValue: GetMemoValue<TProps>,
getMemoValue: GetTypedMemoValue<TProps>,
keys: string[],
mappings: ITokensForSlot<TProps, TTokens, TTheme>,
finalizer?: IStyleFinalizer<TProps>,
Expand Down Expand Up @@ -160,7 +160,7 @@ export function buildComponentTokens<TSlotProps extends ISlotProps, TTokens, TTh
tokenProps: ITokenPropInfo<TTokens>,
theme: TTheme,
slotName: string,
getValue: GetMemoValue<IPropsForSlot>,
getValue: GetTypedMemoValue<IPropsForSlot>,
) => {
const keys = Object.getOwnPropertyNames(slotKeys);
return _getCachedPropsForSlot<IPropsForSlot, TTokens, TTheme>(props, tokenProps, theme, slotName, getValue, keys, mappings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type IGetCachedPropsForSlot<TProps, TTokens, TTheme> = (
tokenProps: ITokenPropInfo<TTokens>,
theme: TTheme,
slotName: string,
getMemoValue: GetMemoValue<TProps>,
getMemoValue: GetMemoValue,
) => TProps;

export type ICachedPropHandlers<TSlotProps extends object, TTokens, TTheme> = {
Expand Down
2 changes: 1 addition & 1 deletion packages/deprecated/foundation-tokens/src/Token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function processTokens<TSlotProps extends ISlotProps, TTokens extends obj
theme: TTheme,
slotProps: IComponentSettings<TSlotProps & { tokens?: TTokens }>,
tokenInfo: IComponentTokens<TSlotProps, TTokens, TTheme>,
cache: GetMemoValue<TSlotProps>,
cache: GetMemoValue,
): ISlotProps {
// merge in tokens and build up the cache key which are the tokens overridden by the user
slotProps = slotProps || {};
Expand Down
4 changes: 2 additions & 2 deletions packages/deprecated/themed-settings/src/CustomSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ export function mergeBaseSettings<TSettings extends IComponentSettings, TTheme>(
export function getThemedSettings<TSettings extends IComponentSettings, TTheme>(
customSettings: ISettingsEntry<TSettings, TTheme>[],
theme: TTheme,
memoValue: GetMemoValue<TSettings, TSettings>,
memoValue: GetMemoValue,
hasOverride?: IOverrideLookup,
getFromTheme?: IGetSettingsFromTheme<TSettings, TTheme>,
): { settings: TSettings | undefined; getMemoValue: GetMemoValue<TSettings, TSettings> } {
): { settings: TSettings | undefined; getMemoValue: GetMemoValue } {
// resolve the settings for this component, keyed on the theme
let [settings, getMemoValue] = memoValue(() => mergeBaseSettings(customSettings, theme, getFromTheme), [theme]);

Expand Down
4 changes: 2 additions & 2 deletions packages/experimental/Stack/src/Stack.styling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const tokensThatAreAlsoProps: (keyof StackTokenProps)[] = [

const nowrapProps: ViewProps = {};

const buildInnerProps = (tokenProps: StackTokens, theme: Theme, cache: GetMemoValue<ViewProps>) => {
const buildInnerProps = (tokenProps: StackTokens, theme: Theme, cache: GetMemoValue) => {
// if wrapping is disabled just return a fixed empty object without doing any additional work
if (!tokenProps.wrap) {
return nowrapProps;
Expand All @@ -54,7 +54,7 @@ const buildInnerProps = (tokenProps: StackTokens, theme: Theme, cache: GetMemoVa
const { horizontal, horizontalAlign, verticalAlign, padding } = tokenProps;
return !tokenProps.wrap
? nowrapProps
: cache(() => {
: cache<ViewProps>(() => {
const childrenGap = tokenProps.childrenGap || tokenProps.gap;
const { rowGap, columnGap } = parseGap(childrenGap, theme);

Expand Down
4 changes: 2 additions & 2 deletions packages/experimental/Stack/src/Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { ViewProps } from 'react-native';

import { filterViewProps } from '@fluentui-react-native/adapters';
import type { UseSlots } from '@fluentui-react-native/framework';
import { compose, withSlots, mergeProps, getMemoCache } from '@fluentui-react-native/framework';
import { compose, withSlots, mergeProps, getTypedMemoCache } from '@fluentui-react-native/framework';

import { stylingSettings } from './Stack.styling';
import { stackName } from './Stack.types';
Expand All @@ -23,7 +23,7 @@ declare global {
}
}

const mixinCache = getMemoCache<ViewProps>();
const mixinCache = getTypedMemoCache<ViewProps>();

/**
* ensures that the object reference of the ViewProps is the same if the values of horizontal and gap are the same, this
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { ViewStyle, ViewProps } from 'react-native';

import type { UseStylingOptions } from '@fluentui-react-native/framework';
import { getMemoCache } from '@fluentui-react-native/framework';
import { getTypedMemoCache } from '@fluentui-react-native/framework';

import { stackItemName } from './StackItem.types';
import type { StackItemTokens, StackItemProps, StackItemSlotProps } from './StackItem.types';

// styles are keyed on token values and are independent of themes or variants so just use a common cache
const localCache = getMemoCache<ViewProps>();
const localCache = getTypedMemoCache<ViewProps>();

const alignMap: { [key: string]: ViewStyle['alignSelf'] } = {
start: 'flex-start',
Expand Down
15 changes: 0 additions & 15 deletions packages/framework-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,6 @@
"import": "./lib/index.js",
"require": "./lib-commonjs/index.js",
"types": "./lib/index.d.ts"
},
"./immutable-merge": {
"import": "./lib/immutable-merge/Merge.js",
"require": "./lib-commonjs/immutable-merge/Merge.js",
"types": "./lib/immutable-merge/Merge.d.ts"
},
"./memo-cache": {
"import": "./lib/memo-cache/index.js",
"require": "./lib-commonjs/memo-cache/index.js",
"types": "./lib/memo-cache/index.d.ts"
},
"./merge-props": {
"import": "./lib/merge-props/index.js",
"require": "./lib-commonjs/merge-props/index.js",
"types": "./lib/merge-props/lib/index.d.ts"
}
},
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions packages/framework-base/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ export type {
} from './immutable-merge/Merge';

// memo-cache exports
export type { GetMemoValue } from './memo-cache/getMemoCache';
export { getMemoCache } from './memo-cache/getMemoCache';
export type { GetMemoValue, GetTypedMemoValue } from './memo-cache/getMemoCache';
export { getMemoCache, getTypedMemoCache } from './memo-cache/getMemoCache';
export { memoize } from './memo-cache/memoize';

// merge-props exports
Expand Down
42 changes: 17 additions & 25 deletions packages/framework-base/src/memo-cache/getCacheEntry.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,46 @@
export type CacheEntry<T, TGet = any> = {
/** signature for the object/function key values, used for memoization */
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
export type MemoObjectKey = object | Function;

export type CacheEntry<T = unknown> = {
/** stores the cached value if any */
value?: T;

/** entry used for undefined and null values, these both collapse to the same type */
empty?: CacheEntry<TGet>;
empty?: CacheEntry<T>;

/** entry used for the case where the array of args is null or length 0 */
noargs?: CacheEntry<TGet>;
noargs?: CacheEntry<T>;

/** all remaining non-object types are keyed as strings for lookups */
str?: { [key: string]: CacheEntry<TGet> };
str?: { [key: string]: CacheEntry<T> };

/** object types are keyed in a weak map on object identity */
obj?: WeakMap<object, TGet>;
obj?: WeakMap<MemoObjectKey, CacheEntry<T>>;
};

/**
* just wraps the common entry.foo = entry.foo || {} pattern
* @param entry - entry to ensure a key value for
* @param key - which key of that entry to ensure the value for
*/
function ensureAndReturn(entry: CacheEntry<any>, key: keyof CacheEntry<any>): CacheEntry<any> | { [key: string]: CacheEntry<any> } {
if ((key as string) === '__proto__' || (key as string) === 'constructor' || (key as string) === 'prototype') {
throw new Error('Invalid key');
}
return (entry[key] = entry[key] || {});
}

/**
* Step one level deeper in the cache, based on the key value from the current location
*
* @param entry - base entry to work from
* @param val - value to use as the key for progressing to the next level of the cache
*/
function jumpToCacheEntry(entry: CacheEntry<any>, val: any): CacheEntry<any> {
function jumpToCacheEntry(entry: CacheEntry, val: any): CacheEntry {
if (val === undefined || val === null) {
// undefined or null just routes directly to the empty object. This avoids the issues of string collisions with 'null' or 'undefined'
// when using the string key map, it also avoids creating the WeakMap (since null is technically typoef object), particularly in cases
// where null is just being set on non-object types.
return ensureAndReturn(entry, 'empty');
return (entry.empty ??= {});
}
if (typeof val === 'object' || typeof val === 'function') {
// objects and functions will be treated as key values in a WeakMap
const byObj = (entry.obj = entry.obj || new WeakMap<object, CacheEntry<any>>());
const byObj = (entry.obj ??= new WeakMap<MemoObjectKey, CacheEntry>());
return byObj.get(val) || byObj.set(val, {}).get(val);
}
// otherwise convert everything to a string and store it in the str object (using it as a map)
const key = val + '';
const byString = ensureAndReturn(entry, 'str');
return (byString[key] = byString[key] || {});
const byString = (entry.str ??= {});
return (byString[key] ??= {});
}

/**
Expand All @@ -57,11 +49,11 @@ function jumpToCacheEntry(entry: CacheEntry<any>, val: any): CacheEntry<any> {
* @param entry - entry to use as the base of the cache walk
* @param args - array of arguments to use to progress deeper into the cache
*/
export function getCacheEntry<T, TGet = any>(entry: CacheEntry<T>, args: any[]): CacheEntry<TGet> {
export function getCacheEntry(entry: CacheEntry, args: unknown[]): CacheEntry {
// in the case where the args array exists and is > 0 length:
// - walk the cache from entry, like a linked list, jumping to the next entry by key, building it up as you go
// - otherwise if there are no args just use the noargs branch
return args && args.length > 0
? (args.reduce((previous: CacheEntry<any>, arg: any) => jumpToCacheEntry(previous, arg), entry) as CacheEntry<TGet>)
: ensureAndReturn(entry, 'noargs');
? (args.reduce((previous: CacheEntry, arg: unknown) => jumpToCacheEntry(previous, arg), entry) as CacheEntry)
: (entry.noargs ??= {});
}
Loading