Prepare the files for the codemod to upgrade to Flow 0.299.0 (#8262)

This commit is contained in:
Florian Rival
2026-02-12 17:25:57 +01:00
committed by GitHub
parent 2ca874f54d
commit e1ab2dad94
15 changed files with 10167 additions and 0 deletions
+1
View File
@@ -130,6 +130,7 @@
// Support for Flowtype (for newIDE):
"javascript.validate.enable": false,
"flow.useNPMPackagedFlow": true,
"flow.pathToFlow": "{workspaceFolder}/newIDE/app/node_modules/.bin/flow",
// Clang format styling (duplicated in scripts/CMakeClangUtils.txt)
"C_Cpp.clang_format_style": "{BasedOnStyle: Google, BinPackParameters: false, BinPackArguments: false}"
File diff suppressed because it is too large Load Diff
+570
View File
@@ -0,0 +1,570 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
declare class StyleSheet {
disabled: boolean;
+href: string;
+media: MediaList;
+ownerNode: Node;
+parentStyleSheet: ?StyleSheet;
+title: string;
+type: string;
}
declare class StyleSheetList {
@@iterator(): Iterator<StyleSheet>;
length: number;
[index: number]: StyleSheet;
}
declare class MediaList {
@@iterator(): Iterator<string>;
mediaText: string;
length: number;
item(index: number): ?string;
deleteMedium(oldMedium: string): void;
appendMedium(newMedium: string): void;
[index: number]: string;
}
declare class CSSStyleSheet extends StyleSheet {
+cssRules: CSSRuleList;
+ownerRule: ?CSSRule;
deleteRule(index: number): void;
insertRule(rule: string, index: number): number;
}
declare class CSSGroupingRule extends CSSRule {
+cssRules: CSSRuleList;
deleteRule(index: number): void;
insertRule(rule: string, index: number): number;
}
declare class CSSConditionRule extends CSSGroupingRule {
conditionText: string;
}
declare class CSSMediaRule extends CSSConditionRule {
+media: MediaList;
}
declare class CSSStyleRule extends CSSRule {
selectorText: string;
+style: CSSStyleDeclaration;
}
declare class CSSRule {
cssText: string;
+parentRule: ?CSSRule;
+parentStyleSheet: ?CSSStyleSheet;
+type: number;
static STYLE_RULE: number;
static MEDIA_RULE: number;
static FONT_FACE_RULE: number;
static PAGE_RULE: number;
static IMPORT_RULE: number;
static CHARSET_RULE: number;
static UNKNOWN_RULE: number;
static KEYFRAMES_RULE: number;
static KEYFRAME_RULE: number;
static NAMESPACE_RULE: number;
static COUNTER_STYLE_RULE: number;
static SUPPORTS_RULE: number;
static DOCUMENT_RULE: number;
static FONT_FEATURE_VALUES_RULE: number;
static VIEWPORT_RULE: number;
static REGION_STYLE_RULE: number;
}
declare class CSSKeyframeRule extends CSSRule {
keyText: string;
+style: CSSStyleDeclaration;
}
declare class CSSKeyframesRule extends CSSRule {
name: string;
+cssRules: CSSRuleList;
appendRule(rule: string): void;
deleteRule(select: string): void;
findRule(select: string): CSSKeyframeRule | null;
}
declare class CSSRuleList {
@@iterator(): Iterator<CSSRule>;
length: number;
item(index: number): ?CSSRule;
[index: number]: CSSRule;
}
declare class CSSStyleDeclaration {
@@iterator(): Iterator<string>;
/* DOM CSS Properties */
alignContent: string;
alignItems: string;
alignSelf: string;
all: string;
animation: string;
animationDelay: string;
animationDirection: string;
animationDuration: string;
animationFillMode: string;
animationIterationCount: string;
animationName: string;
animationPlayState: string;
animationTimingFunction: string;
backdropFilter: string;
webkitBackdropFilter: string;
backfaceVisibility: string;
background: string;
backgroundAttachment: string;
backgroundBlendMode: string;
backgroundClip: string;
backgroundColor: string;
backgroundImage: string;
backgroundOrigin: string;
backgroundPosition: string;
backgroundPositionX: string;
backgroundPositionY: string;
backgroundRepeat: string;
backgroundSize: string;
blockSize: string;
border: string;
borderBlockEnd: string;
borderBlockEndColor: string;
borderBlockEndStyle: string;
borderBlockEndWidth: string;
borderBlockStart: string;
borderBlockStartColor: string;
borderBlockStartStyle: string;
borderBlockStartWidth: string;
borderBottom: string;
borderBottomColor: string;
borderBottomLeftRadius: string;
borderBottomRightRadius: string;
borderBottomStyle: string;
borderBottomWidth: string;
borderCollapse: string;
borderColor: string;
borderImage: string;
borderImageOutset: string;
borderImageRepeat: string;
borderImageSlice: string;
borderImageSource: string;
borderImageWidth: string;
borderInlineEnd: string;
borderInlineEndColor: string;
borderInlineEndStyle: string;
borderInlineEndWidth: string;
borderInlineStart: string;
borderInlineStartColor: string;
borderInlineStartStyle: string;
borderInlineStartWidth: string;
borderLeft: string;
borderLeftColor: string;
borderLeftStyle: string;
borderLeftWidth: string;
borderRadius: string;
borderRight: string;
borderRightColor: string;
borderRightStyle: string;
borderRightWidth: string;
borderSpacing: string;
borderStyle: string;
borderTop: string;
borderTopColor: string;
borderTopLeftRadius: string;
borderTopRightRadius: string;
borderTopStyle: string;
borderTopWidth: string;
borderWidth: string;
bottom: string;
boxDecorationBreak: string;
boxShadow: string;
boxSizing: string;
breakAfter: string;
breakBefore: string;
breakInside: string;
captionSide: string;
clear: string;
clip: string;
clipPath: string;
color: string;
columns: string;
columnCount: string;
columnFill: string;
columnGap: string;
columnRule: string;
columnRuleColor: string;
columnRuleStyle: string;
columnRuleWidth: string;
columnSpan: string;
columnWidth: string;
contain: string;
content: string;
counterIncrement: string;
counterReset: string;
cursor: string;
direction: string;
display: string;
emptyCells: string;
filter: string;
flex: string;
flexBasis: string;
flexDirection: string;
flexFlow: string;
flexGrow: string;
flexShrink: string;
flexWrap: string;
float: string;
font: string;
fontFamily: string;
fontFeatureSettings: string;
fontKerning: string;
fontLanguageOverride: string;
fontSize: string;
fontSizeAdjust: string;
fontStretch: string;
fontStyle: string;
fontSynthesis: string;
fontVariant: string;
fontVariantAlternates: string;
fontVariantCaps: string;
fontVariantEastAsian: string;
fontVariantLigatures: string;
fontVariantNumeric: string;
fontVariantPosition: string;
fontWeight: string;
grad: string;
grid: string;
gridArea: string;
gridAutoColumns: string;
gridAutoFlow: string;
gridAutoPosition: string;
gridAutoRows: string;
gridColumn: string;
gridColumnStart: string;
gridColumnEnd: string;
gridRow: string;
gridRowStart: string;
gridRowEnd: string;
gridTemplate: string;
gridTemplateAreas: string;
gridTemplateRows: string;
gridTemplateColumns: string;
height: string;
hyphens: string;
imageRendering: string;
imageResolution: string;
imageOrientation: string;
imeMode: string;
inherit: string;
initial: string;
inlineSize: string;
isolation: string;
justifyContent: string;
left: string;
letterSpacing: string;
lineBreak: string;
lineHeight: string;
listStyle: string;
listStyleImage: string;
listStylePosition: string;
listStyleType: string;
margin: string;
marginBlockEnd: string;
marginBlockStart: string;
marginBottom: string;
marginInlineEnd: string;
marginInlineStart: string;
marginLeft: string;
marginRight: string;
marginTop: string;
marks: string;
mask: string;
maskType: string;
maxBlockSize: string;
maxHeight: string;
maxInlineSize: string;
maxWidth: string;
minBlockSize: string;
minHeight: string;
minInlineSize: string;
minWidth: string;
mixBlendMode: string;
mozTransform: string;
mozTransformOrigin: string;
mozTransitionDelay: string;
mozTransitionDuration: string;
mozTransitionProperty: string;
mozTransitionTimingFunction: string;
objectFit: string;
objectPosition: string;
offsetBlockEnd: string;
offsetBlockStart: string;
offsetInlineEnd: string;
offsetInlineStart: string;
opacity: string;
order: string;
orphans: string;
outline: string;
outlineColor: string;
outlineOffset: string;
outlineStyle: string;
outlineWidth: string;
overflow: string;
overflowWrap: string;
overflowX: string;
overflowY: string;
padding: string;
paddingBlockEnd: string;
paddingBlockStart: string;
paddingBottom: string;
paddingInlineEnd: string;
paddingInlineStart: string;
paddingLeft: string;
paddingRight: string;
paddingTop: string;
pageBreakAfter: string;
pageBreakBefore: string;
pageBreakInside: string;
perspective: string;
perspectiveOrigin: string;
pointerEvents: string;
position: string;
quotes: string;
rad: string;
resize: string;
right: string;
rubyAlign: string;
rubyMerge: string;
rubyPosition: string;
scrollBehavior: string;
scrollSnapCoordinate: string;
scrollSnapDestination: string;
scrollSnapPointsX: string;
scrollSnapPointsY: string;
scrollSnapType: string;
shapeImageThreshold: string;
shapeMargin: string;
shapeOutside: string;
tableLayout: string;
tabSize: string;
textAlign: string;
textAlignLast: string;
textCombineUpright: string;
textDecoration: string;
textDecorationColor: string;
textDecorationLine: string;
textDecorationStyle: string;
textIndent: string;
textOrientation: string;
textOverflow: string;
textRendering: string;
textShadow: string;
textTransform: string;
textUnderlinePosition: string;
top: string;
touchAction: string;
transform: string;
transformOrigin: string;
transformStyle: string;
transition: string;
transitionDelay: string;
transitionDuration: string;
transitionProperty: string;
transitionTimingFunction: string;
turn: string;
unicodeBidi: string;
unicodeRange: string;
userSelect: string;
verticalAlign: string;
visibility: string;
webkitOverflowScrolling: string;
webkitTransform: string;
webkitTransformOrigin: string;
webkitTransitionDelay: string;
webkitTransitionDuration: string;
webkitTransitionProperty: string;
webkitTransitionTimingFunction: string;
whiteSpace: string;
widows: string;
width: string;
willChange: string;
wordBreak: string;
wordSpacing: string;
wordWrap: string;
writingMode: string;
zIndex: string;
cssFloat: string;
cssText: string;
getPropertyPriority(property: string): string;
getPropertyValue(property:string): string;
item(index: number): string;
[index: number]: string;
length: number;
parentRule: CSSRule;
removeProperty(property: string): string;
setProperty(property: string, value: ?string, priority: ?string): void;
setPropertyPriority(property: string, priority: string): void;
}
declare class TransitionEvent extends Event {
elapsedTime: number; // readonly
pseudoElement: string; // readonly
propertyName: string; // readonly
}
type AnimationPlayState = 'idle' | 'running' | 'paused' | 'finished'
type AnimationReplaceState = 'active' | 'removed' | 'persisted'
type FillMode = 'none' | 'forwards' | 'backwards' | 'both' | 'auto'
type PlaybackDirection = 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'
type IterationCompositeOperation = 'replace' | 'accumulate'
type CompositeOperation = 'replace' | 'add' | 'accumulate'
type CompositeOperationOrAuto = CompositeOperation | 'auto'
declare class AnimationTimeline {
+currentTime: number | null;
}
type DocumentTimelineOptions = {
originTime?: DOMHighResTimeStamp;
...
}
declare class DocumentTimeline extends AnimationTimeline {
constructor(options?: DocumentTimelineOptions): void;
}
type EffectTiming = {
delay: number;
endDelay: number;
fill: FillMode;
iterationStart: number;
iterations: number;
duration: number | string;
direction: PlaybackDirection;
easing: string;
...
}
type OptionalEffectTiming = $Rest<EffectTiming, {...}>
type ComputedEffectTiming = EffectTiming & {
endTime: number;
activeDuration: number;
localTime: number | null;
progress: number | null;
currentIteration: number | null;
...
}
declare class AnimationEffect {
getTiming(): EffectTiming;
getComputedTiming(): ComputedEffectTiming;
updateTiming(timing?: OptionalEffectTiming): void;
}
type Keyframe = {
composite?: CompositeOperationOrAuto;
easing?: string;
offset?: number | null;
[property: string]: string | number | null | void;
...
}
type ComputedKeyframe = {
composite: CompositeOperationOrAuto;
computedOffset: number;
easing: string;
offset: number | null;
[property: string]: string | number | null | void;
...
}
type PropertyIndexedKeyframes = {
composite?: CompositeOperationOrAuto | CompositeOperationOrAuto[];
easing?: string | string[];
offset?: number | (number | null)[];
[property: string]: string | string[] | number | null | (number | null)[] | void;
...
}
type KeyframeEffectOptions = $Rest<EffectTiming, {...}> & {
iterationComposite?: IterationCompositeOperation;
composite?: CompositeOperation;
...
}
declare class KeyframeEffect extends AnimationEffect {
constructor(
target: Element | null,
keyframes: Keyframe[] | PropertyIndexedKeyframes | null,
options?: number | KeyframeEffectOptions,
): void;
constructor(source: KeyframeEffect): void;
target: Element | null;
iterationComposite: IterationCompositeOperation;
composite: CompositeOperation;
getKeyframes(): ComputedKeyframe[];
setKeyframes(keyframes: Keyframe[] | PropertyIndexedKeyframes | null): void;
}
declare class Animation extends EventTarget {
constructor(effect?: AnimationEffect | null, timeline?: AnimationTimeline | null): void;
id: string;
effect: AnimationEffect | null;
timeline: AnimationTimeline | null;
startTime: number | null;
currentTime: number | null;
playbackRate: number;
+playState: AnimationPlayState;
+replaceState: AnimationReplaceState;
+pending: boolean;
+ready: Promise<Animation>;
+finished: Promise<Animation>;
onfinish: ?((ev: AnimationPlaybackEvent) => mixed);
oncancel: ?((ev: AnimationPlaybackEvent) => mixed);
onremove: ?((ev: AnimationPlaybackEvent) => mixed);
cancel(): void;
finish(): void;
play(): void;
pause(): void;
updatePlaybackRate(playbackRate: number): void;
reverse(): void;
persist(): void;
commitStyles(): void;
}
type KeyframeAnimationOptions = KeyframeEffectOptions & {
id?: string;
...
}
type GetAnimationsOptions = {
subtree?: boolean;
...
}
interface Animatable {
animate(keyframes: Keyframe[] | PropertyIndexedKeyframes | null, options?: number | KeyframeAnimationOptions): Animation;
getAnimations(options?: GetAnimationsOptions): Animation[];
}
type AnimationPlaybackEvent$Init = Event$Init & {
currentTime?: number | null;
timelineTime?: number | null;
...
}
declare class AnimationPlaybackEvent extends Event {
constructor(type: string, animationEventInitDict?: AnimationPlaybackEvent$Init): void;
+currentTime: number | null;
+timelineTime: number | null;
}
File diff suppressed because it is too large Load Diff
+127
View File
@@ -0,0 +1,127 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// Implemented by window & worker
declare interface IDBEnvironment {
indexedDB: IDBFactory;
}
type IDBDirection = 'next' | 'nextunique' | 'prev' | 'prevunique';
// Implemented by window.indexedDB & worker.indexedDB
declare interface IDBFactory {
open(name: string, version?: number): IDBOpenDBRequest;
deleteDatabase(name: string): IDBOpenDBRequest;
cmp(a: any, b: any): -1|0|1;
}
declare interface IDBRequest extends EventTarget {
result: IDBObjectStore;
error: Error;
source: ?(IDBIndex | IDBObjectStore | IDBCursor);
transaction: IDBTransaction;
readyState: 'pending'|'done';
onerror: (err: any) => mixed;
onsuccess: (e: any) => mixed;
}
declare interface IDBOpenDBRequest extends IDBRequest {
onblocked: (e: any) => mixed;
onupgradeneeded: (e: any) => mixed;
}
declare interface IDBDatabase extends EventTarget {
close(): void;
createObjectStore(name: string, options?: {
keyPath?: ?(string|string[]),
autoIncrement?: bool,
...
}): IDBObjectStore;
deleteObjectStore(name: string): void;
transaction(storeNames: string|string[], mode?: 'readonly'|'readwrite'|'versionchange'): IDBTransaction;
name: string;
version: number;
objectStoreNames: string[];
onabort: (e: any) => mixed;
onclose: (e: any) => mixed;
onerror: (e: any) => mixed;
onversionchange: (e: any) => mixed;
}
declare interface IDBTransaction extends EventTarget {
abort(): void;
db: IDBDatabase;
error: Error;
mode: 'readonly'|'readwrite'|'versionchange';
name: string;
objectStore(name: string): IDBObjectStore;
onabort: (e: any) => mixed;
oncomplete: (e: any) => mixed;
onerror: (e: any) => mixed;
}
declare interface IDBObjectStore {
add(value: any, key?: any): IDBRequest;
autoIncrement: bool;
clear(): IDBRequest;
createIndex(indexName: string, keyPath: string|string[], optionalParameter?: {
unique?: bool,
multiEntry?: bool,
...
}): IDBIndex;
count(keyRange?: any|IDBKeyRange): IDBRequest;
delete(key: any): IDBRequest;
deleteIndex(indexName: string): void;
get(key: any): IDBRequest;
index(indexName: string): IDBIndex;
indexNames: string[];
name: string;
keyPath: any;
openCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
openKeyCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
put(value: any, key?: any): IDBRequest;
transaction : IDBTransaction;
}
declare interface IDBIndex extends EventTarget {
count(key?: any|IDBKeyRange): IDBRequest;
get(key: any|IDBKeyRange): IDBRequest;
getKey(key: any|IDBKeyRange): IDBRequest;
openCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
openKeyCursor(range?: any|IDBKeyRange, direction?: IDBDirection): IDBRequest;
name: string;
objectStore: IDBObjectStore;
keyPath: any;
multiEntry: bool;
unique: bool;
}
declare interface IDBKeyRange {
bound(lower: any, upper: any, lowerOpen?: bool, upperOpen?: bool): IDBKeyRange;
only(value: any): IDBKeyRange;
lowerBound(bound: any, open?: bool): IDBKeyRange;
upperBound(bound: any, open?: bool): IDBKeyRange;
lower: any;
upper: any;
lowerOpen: bool;
upperOpen: bool;
}
declare interface IDBCursor {
advance(count: number): void;
continue(key?: any): void;
delete(): IDBRequest;
update(newValue: any): IDBRequest;
source: IDBObjectStore|IDBIndex;
direction: IDBDirection;
key: any;
primaryKey: any;
}
declare interface IDBCursorWithValue extends IDBCursor {
value: any;
}
+196
View File
@@ -0,0 +1,196 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
declare var Intl: {
Collator: Class<Intl$Collator>,
DateTimeFormat: Class<Intl$DateTimeFormat>,
NumberFormat: Class<Intl$NumberFormat>,
PluralRules: ?Class<Intl$PluralRules>,
getCanonicalLocales?: (locales?: Intl$Locales) => Intl$Locale[],
...
}
type Intl$Locale = string
type Intl$Locales = Intl$Locale | Intl$Locale[]
declare class Intl$Collator {
constructor (
locales?: Intl$Locales,
options?: Intl$CollatorOptions
): Intl$Collator;
static (
locales?: Intl$Locales,
options?: Intl$CollatorOptions
): Intl$Collator;
compare (string, string): number;
resolvedOptions (): {
locale: Intl$Locale,
usage: 'sort' | 'search',
sensitivity: 'base' | 'accent' | 'case' | 'variant',
ignorePunctuation: boolean,
collation: string,
numeric: boolean,
caseFirst?: 'upper' | 'lower' | 'false',
...
};
static supportedLocalesOf (locales?: Intl$Locales): Intl$Locale[];
}
declare type Intl$CollatorOptions = {
localeMatcher?: 'lookup' | 'best fit',
usage?: 'sort' | 'search',
sensitivity?: 'base' | 'accent' | 'case' | 'variant',
ignorePunctuation?: boolean,
numeric?: boolean,
caseFirst?: 'upper' | 'lower' | 'false',
...
}
type FormatToPartsType = | 'day' | 'dayPeriod' | 'era' | 'hour' | 'literal'
| 'minute' | 'month' | 'second' | 'timeZoneName' | 'weekday' | 'year';
declare class Intl$DateTimeFormat {
constructor (
locales?: Intl$Locales,
options?: Intl$DateTimeFormatOptions
): Intl$DateTimeFormat;
static (
locales?: Intl$Locales,
options?: Intl$DateTimeFormatOptions
): Intl$DateTimeFormat;
format (value?: Date | number): string;
formatToParts (value?: Date | number): Array<{
type: FormatToPartsType,
value: string,
...
}>;
resolvedOptions (): {
locale: Intl$Locale,
calendar: string,
numberingSystem: string,
timeZone?: string,
hour12: boolean,
weekday?: 'narrow' | 'short' | 'long',
era?: 'narrow' | 'short' | 'long',
year?: 'numeric' | '2-digit',
month?: 'numeric' | '2-digit' | 'narrow' | 'short' | 'long',
day?: 'numeric' | '2-digit',
hour?: 'numeric' | '2-digit',
minute?: 'numeric' | '2-digit',
second?: 'numeric' | '2-digit',
timeZoneName?: 'short' | 'long',
...
};
static supportedLocalesOf (locales?: Intl$Locales): Intl$Locale[];
}
declare type Intl$DateTimeFormatOptions = {
localeMatcher?: 'lookup' | 'best fit',
timeZone?: string,
hour12?: boolean,
formatMatcher?: 'basic' | 'best fit',
weekday?: 'narrow' | 'short' | 'long',
era?: 'narrow' | 'short' | 'long',
year?: 'numeric' | '2-digit',
month?: 'numeric' | '2-digit' | 'narrow' | 'short' | 'long',
day?: 'numeric' | '2-digit',
hour?: 'numeric' | '2-digit',
minute?: 'numeric' | '2-digit',
second?: 'numeric' | '2-digit',
timeZoneName?: 'short' | 'long',
...
}
declare class Intl$NumberFormat {
constructor (
locales?: Intl$Locales,
options?: Intl$NumberFormatOptions
): Intl$NumberFormat;
static (
locales?: Intl$Locales,
options?: Intl$NumberFormatOptions
): Intl$NumberFormat;
format (number): string;
resolvedOptions (): {
locale: Intl$Locale,
numberingSystem: string,
style: 'decimal' | 'currency' | 'percent',
currency?: string,
currencyDisplay?: 'symbol' | 'code' | 'name',
useGrouping: boolean,
minimumIntegerDigits?: number,
minimumFractionDigits?: number,
maximumFractionDigits?: number,
minimumSignificantDigits?: number,
maximumSignificantDigits?: number,
...
};
static supportedLocalesOf (locales?: Intl$Locales): Intl$Locale[];
}
declare type Intl$NumberFormatOptions = {
localeMatcher?: 'lookup' | 'best fit',
style?: 'decimal' | 'currency' | 'percent',
currency?: string,
currencyDisplay?: 'symbol' | 'code' | 'name',
useGrouping?: boolean,
minimumIntegerDigits?: number,
minimumFractionDigits?: number,
maximumFractionDigits?: number,
minimumSignificantDigits?: number,
maximumSignificantDigits?: number,
...
}
declare class Intl$PluralRules {
constructor (
locales?: Intl$Locales,
options?: Intl$PluralRulesOptions
): Intl$PluralRules;
select (number): Intl$PluralRule;
resolvedOptions (): {
locale: Intl$Locale,
type: 'cardinal' | 'ordinal',
minimumIntegerDigits?: number,
minimumFractionDigits?: number,
maximumFractionDigits?: number,
minimumSignificantDigits?: number,
maximumSignificantDigits?: number,
pluralCategories: Intl$PluralRule[],
...
};
static supportedLocalesOf (locales?: Intl$Locales): Intl$Locale[];
}
type Intl$PluralRule = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'
declare type Intl$PluralRulesOptions = {
localeMatcher?: 'lookup' | 'best fit',
type?: 'cardinal' | 'ordinal',
minimumIntegerDigits?: number,
minimumFractionDigits?: number,
maximumFractionDigits?: number,
minimumSignificantDigits?: number,
maximumSignificantDigits?: number,
...
}
+692
View File
@@ -0,0 +1,692 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @nolint
* @format
*/
// The react-dom, react-dom/server and react-dom/test-utils modules were moved to flow-typed.
declare class SyntheticEvent<+T: EventTarget = EventTarget, +E: Event = Event> {
bubbles: boolean;
cancelable: boolean;
+currentTarget: T;
defaultPrevented: boolean;
eventPhase: number;
isDefaultPrevented(): boolean;
isPropagationStopped(): boolean;
isTrusted: boolean;
nativeEvent: E;
preventDefault(): void;
stopPropagation(): void;
// This should not be `T`. Use `currentTarget` instead. See:
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11508#issuecomment-256045682
+target: EventTarget;
timeStamp: number;
type: string;
persist(): void;
}
declare class SyntheticAnimationEvent<
+T: EventTarget = EventTarget,
> extends SyntheticEvent<T> {
animationName: string;
elapsedTime: number;
pseudoElement: string;
}
declare class SyntheticClipboardEvent<
+T: EventTarget = EventTarget,
> extends SyntheticEvent<T> {
clipboardData: any;
}
declare class SyntheticCompositionEvent<
+T: EventTarget = EventTarget,
> extends SyntheticEvent<T> {
data: any;
}
declare class SyntheticInputEvent<
+T: EventTarget = EventTarget,
> extends SyntheticEvent<T> {
+target: HTMLInputElement;
data: any;
}
declare class SyntheticUIEvent<
+T: EventTarget = EventTarget,
+E: Event = Event,
> extends SyntheticEvent<T, E> {
detail: number;
view: any;
}
declare class SyntheticFocusEvent<
+T: EventTarget = EventTarget,
> extends SyntheticUIEvent<T> {
relatedTarget: EventTarget;
}
declare class SyntheticKeyboardEvent<
+T: EventTarget = EventTarget,
> extends SyntheticUIEvent<T, KeyboardEvent> {
altKey: boolean;
charCode: number;
ctrlKey: boolean;
getModifierState(keyArg?: string): boolean;
key: string;
keyCode: number;
locale: string;
location: number;
metaKey: boolean;
repeat: boolean;
shiftKey: boolean;
which: number;
}
declare class SyntheticMouseEvent<
+T: EventTarget = EventTarget,
+E: Event = MouseEvent,
> extends SyntheticUIEvent<T, E> {
altKey: boolean;
button: number;
buttons: number;
clientX: number;
clientY: number;
ctrlKey: boolean;
getModifierState(keyArg: string): boolean;
metaKey: boolean;
pageX: number;
pageY: number;
relatedTarget: EventTarget;
screenX: number;
screenY: number;
shiftKey: boolean;
}
declare class SyntheticDragEvent<
+T: EventTarget = EventTarget,
> extends SyntheticMouseEvent<T, DragEvent> {
dataTransfer: any;
}
declare class SyntheticWheelEvent<
+T: EventTarget = EventTarget,
> extends SyntheticMouseEvent<T, WheelEvent> {
deltaMode: number;
deltaX: number;
deltaY: number;
deltaZ: number;
}
declare class SyntheticPointerEvent<
+T: EventTarget = EventTarget,
> extends SyntheticMouseEvent<T, PointerEvent> {
pointerId: number;
width: number;
height: number;
pressure: number;
tangentialPressure: number;
tiltX: number;
tiltY: number;
twist: number;
pointerType: string;
isPrimary: boolean;
}
declare class SyntheticTouchEvent<
+T: EventTarget = EventTarget,
> extends SyntheticUIEvent<T, TouchEvent> {
altKey: boolean;
changedTouches: any;
ctrlKey: boolean;
getModifierState: any;
metaKey: boolean;
shiftKey: boolean;
targetTouches: any;
touches: any;
}
declare class SyntheticTransitionEvent<
+T: EventTarget = EventTarget,
> extends SyntheticEvent<T> {
propertyName: string;
elapsedTime: number;
pseudoElement: string;
}
// prettier-ignore
declare type $JSXIntrinsics = {
// Catch-all for custom elements.
[string]: ReactDOM$HTMLElementJSXIntrinsic,
// HTML
a: {
instance: HTMLAnchorElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
abbr: ReactDOM$HTMLElementJSXIntrinsic,
address: ReactDOM$HTMLElementJSXIntrinsic,
area: ReactDOM$HTMLElementJSXIntrinsic,
article: ReactDOM$HTMLElementJSXIntrinsic,
aside: ReactDOM$HTMLElementJSXIntrinsic,
audio: {
instance: HTMLAudioElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
b: ReactDOM$HTMLElementJSXIntrinsic,
base: ReactDOM$HTMLElementJSXIntrinsic,
bdi: ReactDOM$HTMLElementJSXIntrinsic,
bdo: ReactDOM$HTMLElementJSXIntrinsic,
big: ReactDOM$HTMLElementJSXIntrinsic,
blockquote: ReactDOM$HTMLElementJSXIntrinsic,
body: ReactDOM$HTMLElementJSXIntrinsic,
br: {
instance: HTMLBRElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
button: {
instance: HTMLButtonElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
canvas: {
instance: HTMLCanvasElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
caption: {
instance: HTMLTableCaptionElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
cite: ReactDOM$HTMLElementJSXIntrinsic,
code: ReactDOM$HTMLElementJSXIntrinsic,
col: ReactDOM$HTMLElementJSXIntrinsic,
colgroup: ReactDOM$HTMLElementJSXIntrinsic,
data: ReactDOM$HTMLElementJSXIntrinsic,
datalist: ReactDOM$HTMLElementJSXIntrinsic,
dd: ReactDOM$HTMLElementJSXIntrinsic,
del: ReactDOM$HTMLElementJSXIntrinsic,
details: {
instance: HTMLDetailsElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
dfn: ReactDOM$HTMLElementJSXIntrinsic,
dialog: ReactDOM$HTMLElementJSXIntrinsic,
div: {
instance: HTMLDivElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
dl: {
instance: HTMLDListElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
dt: ReactDOM$HTMLElementJSXIntrinsic,
em: ReactDOM$HTMLElementJSXIntrinsic,
embed: ReactDOM$HTMLElementJSXIntrinsic,
fieldset: {
instance: HTMLFieldSetElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
figcaption: ReactDOM$HTMLElementJSXIntrinsic,
figure: ReactDOM$HTMLElementJSXIntrinsic,
footer: ReactDOM$HTMLElementJSXIntrinsic,
form: {
instance: HTMLFormElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
h1: {
instance: HTMLHeadingElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
h2: {
instance: HTMLHeadingElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
h3: {
instance: HTMLHeadingElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
h4: {
instance: HTMLHeadingElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
h5: {
instance: HTMLHeadingElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
h6: {
instance: HTMLHeadingElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
head: ReactDOM$HTMLElementJSXIntrinsic,
header: ReactDOM$HTMLElementJSXIntrinsic,
hgroup: ReactDOM$HTMLElementJSXIntrinsic,
hr: {
instance: HTMLHRElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
html: ReactDOM$HTMLElementJSXIntrinsic,
i: ReactDOM$HTMLElementJSXIntrinsic,
iframe: {
instance: HTMLIFrameElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
img: {
instance: HTMLImageElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
ins: ReactDOM$HTMLElementJSXIntrinsic,
kbd: ReactDOM$HTMLElementJSXIntrinsic,
keygen: ReactDOM$HTMLElementJSXIntrinsic,
label: {
instance: HTMLLabelElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
legend: {
instance: HTMLLegendElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
li: {
instance: HTMLLIElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
link: {
instance: HTMLLinkElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
main: ReactDOM$HTMLElementJSXIntrinsic,
map: ReactDOM$HTMLElementJSXIntrinsic,
mark: ReactDOM$HTMLElementJSXIntrinsic,
menu: ReactDOM$HTMLElementJSXIntrinsic,
menuitem: ReactDOM$HTMLElementJSXIntrinsic,
meta: {
instance: HTMLMetaElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
meter: ReactDOM$HTMLElementJSXIntrinsic,
nav: ReactDOM$HTMLElementJSXIntrinsic,
noscript: ReactDOM$HTMLElementJSXIntrinsic,
object: ReactDOM$HTMLElementJSXIntrinsic,
ol: {
instance: HTMLOListElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
optgroup: {
instance: HTMLOptGroupElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
option: {
instance: HTMLOptionElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
output: ReactDOM$HTMLElementJSXIntrinsic,
p: {
instance: HTMLParagraphElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
param: ReactDOM$HTMLElementJSXIntrinsic,
picture: ReactDOM$HTMLElementJSXIntrinsic,
pre: {
instance: HTMLPreElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
progress: ReactDOM$HTMLElementJSXIntrinsic,
q: ReactDOM$HTMLElementJSXIntrinsic,
rp: ReactDOM$HTMLElementJSXIntrinsic,
rt: ReactDOM$HTMLElementJSXIntrinsic,
ruby: ReactDOM$HTMLElementJSXIntrinsic,
s: ReactDOM$HTMLElementJSXIntrinsic,
samp: ReactDOM$HTMLElementJSXIntrinsic,
script: {
instance: HTMLScriptElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
section: ReactDOM$HTMLElementJSXIntrinsic,
small: ReactDOM$HTMLElementJSXIntrinsic,
source: {
instance: HTMLSourceElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
span: {
instance: HTMLSpanElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
strong: ReactDOM$HTMLElementJSXIntrinsic,
style: {
instance: HTMLStyleElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
sub: ReactDOM$HTMLElementJSXIntrinsic,
summary: ReactDOM$HTMLElementJSXIntrinsic,
sup: ReactDOM$HTMLElementJSXIntrinsic,
table: {
instance: HTMLTableElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
tbody: {
instance: HTMLTableSectionElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
td: {
instance: HTMLTableCellElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
tfoot: {
instance: HTMLTableSectionElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
th: {
instance: HTMLTableCellElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
thead: {
instance: HTMLTableSectionElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
time: ReactDOM$HTMLElementJSXIntrinsic,
title: ReactDOM$HTMLElementJSXIntrinsic,
tr: {
instance: HTMLTableRowElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
track: ReactDOM$HTMLElementJSXIntrinsic,
u: ReactDOM$HTMLElementJSXIntrinsic,
ul: {
instance: HTMLUListElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
'var': ReactDOM$HTMLElementJSXIntrinsic,
video: {
instance: HTMLVideoElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
wbr: ReactDOM$HTMLElementJSXIntrinsic,
// SVG
svg: ReactDOM$SVGElementJSXIntrinsic,
animate: ReactDOM$SVGElementJSXIntrinsic,
circle: ReactDOM$SVGElementJSXIntrinsic,
defs: ReactDOM$SVGElementJSXIntrinsic,
ellipse: ReactDOM$SVGElementJSXIntrinsic,
g: ReactDOM$SVGElementJSXIntrinsic,
image: ReactDOM$SVGElementJSXIntrinsic,
line: ReactDOM$SVGElementJSXIntrinsic,
linearGradient: ReactDOM$SVGElementJSXIntrinsic,
mask: ReactDOM$SVGElementJSXIntrinsic,
path: ReactDOM$SVGElementJSXIntrinsic,
pattern: ReactDOM$SVGElementJSXIntrinsic,
polygon: ReactDOM$SVGElementJSXIntrinsic,
polyline: ReactDOM$SVGElementJSXIntrinsic,
radialGradient: ReactDOM$SVGElementJSXIntrinsic,
rect: ReactDOM$SVGElementJSXIntrinsic,
stop: ReactDOM$SVGElementJSXIntrinsic,
symbol: ReactDOM$SVGElementJSXIntrinsic,
text: ReactDOM$SVGElementJSXIntrinsic,
tspan: ReactDOM$SVGElementJSXIntrinsic,
use: ReactDOM$SVGElementJSXIntrinsic,
// Elements React adds extra props for.
input: {
instance: HTMLInputElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
textarea: {
instance: HTMLTextAreaElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
select: {
instance: HTMLSelectElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
},
...
};
type ReactDOM$HTMLElementJSXIntrinsic = {
instance: HTMLElement,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
};
type ReactDOM$SVGElementJSXIntrinsic = {
instance: Element,
props: {
[key: string]: any,
children?: React$Node,
...
},
...
};
+222
View File
@@ -0,0 +1,222 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
type FrameType = 'auxiliary' | 'top-level' | 'nested' | 'none';
type VisibilityState = 'hidden' | 'visible' | 'prerender' | 'unloaded';
declare class WindowClient extends Client {
visibilityState: VisibilityState,
focused: boolean,
focus(): Promise<WindowClient>,
navigate(url: string): Promise<WindowClient>,
}
declare class Client {
id: string,
reserved: boolean,
url: string,
frameType: FrameType,
postMessage(message: any, transfer?: Iterator<any> | Array<any>): void,
}
declare class ExtendableEvent extends Event {
waitUntil(f: Promise<mixed>): void,
}
type ForeignFetchOptions = {
scopes: Iterator<string>,
origins: Iterator<string>,
...
};
declare class InstallEvent extends ExtendableEvent {
registerForeignFetch(options: ForeignFetchOptions): void,
}
declare class FetchEvent extends ExtendableEvent {
request: Request,
clientId: string,
isReload: boolean,
respondWith(response: Response | Promise<Response>): void,
preloadResponse: Promise<?Response>,
}
type ClientType = 'window' | 'worker' | 'sharedworker' | 'all';
type ClientQueryOptions = {
includeUncontrolled?: boolean,
includeReserved?: boolean,
type?: ClientType,
...
};
declare class Clients {
get(id: string): Promise<?Client>,
matchAll(options?: ClientQueryOptions): Promise<Array<Client>>,
openWindow(url: string): Promise<?WindowClient>,
claim(): Promise<void>,
}
type ServiceWorkerState = 'installing'
| 'installed'
| 'activating'
| 'activated'
| 'redundant';
declare class ServiceWorker extends EventTarget {
scriptURL: string,
state: ServiceWorkerState,
postMessage(message: any, transfer?: Iterator<any>): void,
onstatechange?: EventHandler,
}
declare class NavigationPreloadState {
enabled: boolean,
headerValue: string,
}
declare class NavigationPreloadManager {
enable: Promise<void>,
disable: Promise<void>,
setHeaderValue(value: string): Promise<void>,
getState: Promise<NavigationPreloadState>,
}
type PushSubscriptionOptions = {
userVisibleOnly?: boolean,
applicationServerKey?: string | ArrayBuffer | $ArrayBufferView,
...
}
declare class PushSubscriptionJSON {
endpoint: string,
expirationTime: number | null,
keys: { [string]: string, ... };
}
declare class PushSubscription {
+endpoint: string,
+expirationTime: number | null,
+options: PushSubscriptionOptions,
getKey(name: string): ArrayBuffer | null,
toJSON(): PushSubscriptionJSON,
unsubscribe(): Promise<boolean>,
}
declare class PushManager {
+supportedContentEncodings: Array<string>,
subscribe(options?: PushSubscriptionOptions): Promise<PushSubscription>,
getSubscription(): Promise<PushSubscription | null>,
permissionState(options?: PushSubscriptionOptions): Promise<'granted' | 'denied' | 'prompt'>,
}
type ServiceWorkerUpdateViaCache = 'imports' | 'all' | 'none';
declare class ServiceWorkerRegistration extends EventTarget {
+installing: ?ServiceWorker,
+waiting: ?ServiceWorker,
+active: ?ServiceWorker,
+navigationPreload: NavigationPreloadManager,
+scope: string,
+updateViaCache: ServiceWorkerUpdateViaCache,
+pushManager: PushManager,
update(): Promise<void>,
unregister(): Promise<boolean>,
onupdatefound?: EventHandler,
}
type WorkerType = 'classic' | 'module';
type RegistrationOptions = {
scope?: string,
type?: WorkerType,
updateViaCache?: ServiceWorkerUpdateViaCache,
...
};
declare class ServiceWorkerContainer extends EventTarget {
+controller: ?ServiceWorker,
+ready: Promise<ServiceWorkerRegistration>,
getRegistration(clientURL?: string): Promise<ServiceWorkerRegistration | void>,
getRegistrations(): Promise<Iterator<ServiceWorkerRegistration>>,
register(
scriptURL: string,
options?: RegistrationOptions
): Promise<ServiceWorkerRegistration>,
startMessages(): void,
oncontrollerchange?: EventHandler,
onmessage?: EventHandler,
onmessageerror?: EventHandler,
}
/**
* This feature has been removed from the Web standards.
*/
declare class ServiceWorkerMessageEvent extends Event {
data: any,
lastEventId: string,
origin: string,
ports: Array<MessagePort>,
source: ?(ServiceWorker | MessagePort),
}
declare class ExtendableMessageEvent extends ExtendableEvent {
data: any,
lastEventId: string,
origin: string,
ports: Array<MessagePort>,
source: ?(ServiceWorker | MessagePort),
}
type CacheQueryOptions = {
ignoreSearch?: boolean,
ignoreMethod?: boolean,
ignoreVary?: boolean,
cacheName?: string,
...
}
declare class Cache {
match(request: RequestInfo, options?: CacheQueryOptions): Promise<Response>,
matchAll(
request: RequestInfo,
options?: CacheQueryOptions
): Promise<Array<Response>>,
add(request: RequestInfo): Promise<void>,
addAll(requests: Array<RequestInfo>): Promise<void>,
put(request: RequestInfo, response: Response): Promise<void>,
delete(request: RequestInfo, options?: CacheQueryOptions): Promise<boolean>,
keys(
request?: RequestInfo,
options?: CacheQueryOptions
): Promise<Array<Request>>,
}
declare class CacheStorage {
match(request: RequestInfo, options?: CacheQueryOptions): Promise<Response>,
has(cacheName: string): Promise<true>,
open(cacheName: string): Promise<Cache>,
delete(cacheName: string): Promise<boolean>,
keys(): Promise<Array<string>>,
}
// Service worker global scope
// https://www.w3.org/TR/service-workers/#service-worker-global-scope
declare var clients: Clients;
declare var caches: CacheStorage;
declare var registration: ServiceWorkerRegistration;
declare function skipWaiting(): Promise<void>;
declare var onactivate: ?EventHandler;
declare var oninstall: ?EventHandler;
declare var onfetch: ?EventHandler;
declare var onforeignfetch: ?EventHandler;
declare var onmessage: ?EventHandler;
+139
View File
@@ -0,0 +1,139 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
type TextEncodeOptions = { options?: boolean, ... };
declare class TextEncoder {
encode(buffer: string, options?: TextEncodeOptions): Uint8Array,
}
declare class ReadableStreamController {
constructor(
stream: ReadableStream,
underlyingSource: UnderlyingSource,
size: number,
highWaterMark: number,
): void,
desiredSize: number,
close(): void,
enqueue(chunk: any): void,
error(error: Error): void,
}
declare class ReadableStreamBYOBRequest {
constructor(controller: ReadableStreamController, view: $TypedArray): void,
view: $TypedArray,
respond(bytesWritten: number): ?any,
respondWithNewView(view: $TypedArray): ?any,
}
declare class ReadableByteStreamController extends ReadableStreamController {
constructor(
stream: ReadableStream,
underlyingSource: UnderlyingSource,
highWaterMark: number,
): void,
byobRequest: ReadableStreamBYOBRequest,
}
declare class ReadableStreamReader {
constructor(stream: ReadableStream): void,
closed: boolean,
cancel(reason: string): void,
read(): Promise<{
value: ?any,
done: boolean,
...
}>,
releaseLock(): void,
}
declare interface UnderlyingSource {
autoAllocateChunkSize?: number,
type?: string,
start?: (controller: ReadableStreamController) => ?Promise<void>,
pull?: (controller: ReadableStreamController) => ?Promise<void>,
cancel?: (reason: string) => ?Promise<void>,
}
declare class TransformStream {
readable: ReadableStream,
writable: WritableStream,
};
type PipeToOptions = {
preventClose?: boolean,
preventAbort?: boolean,
preventCancel?: boolean,
...
};
type QueuingStrategy = {
highWaterMark: number,
size(chunk: ?any): number,
...
};
declare class ReadableStream {
constructor(
underlyingSource: ?UnderlyingSource,
queuingStrategy: ?QueuingStrategy,
): void,
locked: boolean,
cancel(reason: string): void,
getReader(): ReadableStreamReader,
pipeThrough(transform: TransformStream, options: ?any): void,
pipeTo(dest: WritableStream, options: ?PipeToOptions): Promise<void>,
tee(): [ReadableStream, ReadableStream],
};
declare interface WritableStreamController {
error(error: Error): void,
}
declare interface UnderlyingSink {
autoAllocateChunkSize?: number,
type?: string,
abort?: (reason: string) => ?Promise<void>,
close?: (controller: WritableStreamController) => ?Promise<void>,
start?: (controller: WritableStreamController) => ?Promise<void>,
write?: (chunk: any, controller: WritableStreamController) => ?Promise<void>,
}
declare interface WritableStreamWriter {
closed: Promise<any>,
desiredSize?: number,
ready: Promise<any>,
abort(reason: string): ?Promise<any>,
close(): Promise<any>,
releaseLock(): void,
write(chunk: any): Promise<any>,
}
declare class WritableStream {
constructor(
underlyingSink: ?UnderlyingSink,
queuingStrategy: QueuingStrategy,
): void,
locked: boolean,
abort(reason: string): void,
getWriter(): WritableStreamWriter,
}
+102
View File
@@ -0,0 +1,102 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// https://github.com/WebAssembly/design/blob/master/JS.md
// https://developer.mozilla.org/en-US/docs/WebAssembly
// https://github.com/WebAssembly/design/blob/master/Web.md
type BufferSource = $TypedArray | ArrayBuffer;
type ImportExportKind = 'function' | 'table' | 'memory' | 'global';
type ImportObject = Object;
type ResultObject = {
module: WebAssembly$Module,
instance: WebAssembly$Instance,
...
};
// https://github.com/WebAssembly/design/blob/master/JS.md#exported-function-exotic-objects
declare class ExportedFunctionExoticObject extends Function {
(): mixed;
}
declare class WebAssembly$Module {
constructor(bufferSource: BufferSource): void;
static exports(moduleObject: WebAssembly$Module): Array<{
name: string,
kind: ImportExportKind,
...
}>;
static imports(moduleObject: WebAssembly$Module): Array<{
name: string,
name: string,
kind: ImportExportKind,
...
}>;
static customSections(moduleObject: WebAssembly$Module, sectionName: string): Array<ArrayBuffer>;
}
declare class WebAssembly$Instance {
constructor(moduleObject: WebAssembly$Module, importObject?: ImportObject): void;
+exports: { [exportedFunction: string]: ExportedFunctionExoticObject, ... };
}
type MemoryDescriptor = {
initial: number,
maximum?: number,
...
};
declare class WebAssembly$Memory {
constructor(memoryDescriptor: MemoryDescriptor): void;
+buffer: ArrayBuffer;
grow(delta: number): number;
}
type TableDescriptor = {
element: 'anyfunc',
initial: number,
maximum?: number,
...
};
declare class WebAssembly$Table {
constructor(tableDescriptor: TableDescriptor): void;
+length: number;
grow(delta: number): number;
get(index: number): ExportedFunctionExoticObject;
set(index: number, value: ExportedFunctionExoticObject): void;
}
declare class WebAssembly$CompileError extends Error {}
declare class WebAssembly$LinkError extends Error {}
declare class WebAssembly$RuntimeError extends Error {}
declare function WebAssembly$instantiate(bufferSource: BufferSource, importObject?: ImportObject): Promise<ResultObject>;
declare function WebAssembly$instantiate(moduleObject: WebAssembly$Module, importObject?: ImportObject): Promise<WebAssembly$Instance>;
declare var WebAssembly: {
Module: typeof WebAssembly$Module,
Instance: typeof WebAssembly$Instance,
Memory: typeof WebAssembly$Memory,
Table: typeof WebAssembly$Table,
CompileError: typeof WebAssembly$CompileError,
LinkError: typeof WebAssembly$LinkError,
RuntimeError: typeof WebAssembly$RuntimeError,
validate(bufferSource: BufferSource): boolean,
compile(bufferSource: BufferSource): Promise<WebAssembly$Module>,
instantiate: typeof WebAssembly$instantiate,
// web embedding API
compileStreaming(source: Response | Promise<Response>): Promise<WebAssembly$Module>,
instantiateStreaming(source: Response | Promise<Response>, importObject?: ImportObject): Promise<ResultObject>,
...
}
+176
View File
@@ -0,0 +1,176 @@
#!/usr/bin/env python3
"""
Add $FlowFixMe comments to lines with Flow errors that can't be auto-fixed.
This script is idempotent - running it multiple times produces the same result.
"""
import subprocess
import json
import sys
import os
import re
from collections import defaultdict
APP_DIR = os.environ.get('APP_DIR', os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def get_flow_errors():
"""Run flow and get errors as JSON."""
result = subprocess.run(
['npx', 'flow', '--json', '--show-all-errors'],
capture_output=True, text=True, cwd=APP_DIR,
timeout=300
)
output = result.stdout
json_start = output.find('{"flowVersion"')
if json_start == -1:
json_start = result.stderr.find('{"flowVersion"')
if json_start != -1:
output = result.stderr
else:
print("ERROR: Could not find JSON in flow output")
return []
json_str = output[json_start:]
try:
data = json.loads(json_str)
return data.get('errors', [])
except json.JSONDecodeError as e:
print(f"ERROR: JSON parse error: {e}")
return []
def add_flowfixme_comments(errors):
"""Add $FlowFixMe comments to files with errors."""
# Group errors by file and line, collecting ALL error codes per line
file_errors = defaultdict(lambda: defaultdict(set))
for error in errors:
messages = error.get('message', [])
error_codes = error.get('error_codes', [])
if not messages:
continue
primary = messages[0]
loc = primary.get('loc', {})
source = loc.get('source', '')
line = loc.get('start', {}).get('line', 0)
if not source or not line:
continue
# Only fix files in src/ directory of the app
if APP_DIR + '/src/' not in source:
continue
error_code = error_codes[0] if error_codes else 'incompatible-type'
# Never rely on signature-verification-failure suppressions, as this
# degrades typed exports/imports too much.
if error_code == 'signature-verification-failure':
error_code = 'incompatible-type'
file_errors[source][line].add(error_code)
files_modified = 0
comments_added = 0
for filepath, line_errors in sorted(file_errors.items()):
if not os.path.isfile(filepath):
continue
try:
with open(filepath, 'r') as f:
lines = f.readlines()
except (IOError, UnicodeDecodeError):
continue
# Sort lines in reverse order so we can insert without offset issues
sorted_lines = sorted(line_errors.keys(), reverse=True)
modified = False
for line_num in sorted_lines:
error_codes = line_errors[line_num]
idx = line_num - 1 # 0-indexed
if idx < 0 or idx >= len(lines):
continue
# Collect existing $FlowFixMe codes above this line
existing_codes = set()
check_idx = idx - 1
while check_idx >= 0 and '$FlowFixMe' in lines[check_idx]:
# Extract the code from existing $FlowFixMe comment
match = re.search(r'\$FlowFixMe\[([^\]]+)\]', lines[check_idx])
if match:
existing_codes.add(match.group(1))
check_idx -= 1
# Also check if current line has inline $FlowFixMe
if '$FlowFixMe' in lines[idx]:
match = re.search(r'\$FlowFixMe\[([^\]]+)\]', lines[idx])
if match:
existing_codes.add(match.group(1))
# Determine which codes need to be added
needed_codes = error_codes - existing_codes
if not needed_codes:
continue
# Get the indentation of the error line
current_line = lines[idx]
indent = ''
for ch in current_line:
if ch in (' ', '\t'):
indent += ch
else:
break
# Add $FlowFixMe comments for each needed code
for code in sorted(needed_codes):
comment = f"{indent}// $FlowFixMe[{code}]\n"
lines.insert(idx, comment)
modified = True
comments_added += 1
if modified:
with open(filepath, 'w') as f:
f.writelines(lines)
files_modified += 1
print(f" Added {comments_added} $FlowFixMe comments across {files_modified} files")
return comments_added
def main():
print(" Getting Flow errors...")
errors = get_flow_errors()
print(f" Found {len(errors)} errors total")
if not errors:
print(" No errors to fix!")
return 0
src_errors = 0
for error in errors:
messages = error.get('message', [])
if messages:
loc = messages[0].get('loc', {})
source = loc.get('source', '')
if APP_DIR + '/src/' in source:
src_errors += 1
print(f" Found {src_errors} errors in src/ files")
if src_errors == 0:
print(" No src/ errors to fix!")
return 0
count = add_flowfixme_comments(errors)
return count
if __name__ == '__main__':
added = main()
sys.exit(0 if added >= 0 else 1)
+194
View File
@@ -0,0 +1,194 @@
#!/usr/bin/env python3
"""
Convert // $FlowFixMe[code] comments that are inside JSX to {/* $FlowFixMe[code] */} format.
This is needed because // comments don't work inside JSX element content.
"""
import json
import subprocess
import os
import re
from collections import defaultdict
APP_DIR = os.environ.get('APP_DIR', os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def looks_like_jsx_context(lines, code_idx):
"""
Heuristic to detect if a line is inside JSX where // comments are invalid.
"""
if code_idx < 0 or code_idx >= len(lines):
return False
for idx in range(code_idx, min(len(lines), code_idx + 4)):
stripped = lines[idx].lstrip()
if not stripped:
continue
if stripped.startswith('//') or stripped.startswith('/*') or stripped.startswith('*'):
continue
if (
stripped.startswith('<')
or stripped.startswith('</')
or stripped.startswith('<>')
or stripped.startswith('{<')
or stripped.startswith('(<')
):
return True
# JSX expression containers can start with "{" and contain JSX on
# following lines:
# {
# condition && (
# <Component />
# )
# }
if stripped.startswith('{'):
for look_ahead in range(idx + 1, min(len(lines), idx + 5)):
next_stripped = lines[look_ahead].lstrip()
if not next_stripped:
continue
if (
next_stripped.startswith('<')
or next_stripped.startswith('</')
or next_stripped.startswith('<>')
):
return True
if (
next_stripped.startswith('//')
or next_stripped.startswith('/*')
or next_stripped.startswith('*')
):
continue
break
# Common pattern:
# {
# condition && (
# <Component />
# )
# }
if stripped.startswith('(') and idx + 1 < len(lines):
next_stripped = lines[idx + 1].lstrip()
if (
next_stripped.startswith('<')
or next_stripped.startswith('</')
or next_stripped.startswith('<>')
):
return True
return False
return False
def get_flow_errors():
"""Run flow and get errors as JSON."""
result = subprocess.run(
['npx', 'flow', '--json', '--show-all-errors'],
capture_output=True, text=True, cwd=APP_DIR,
timeout=300
)
output = result.stdout
idx = output.find('{"flowVersion"')
if idx == -1:
return []
try:
data = json.loads(output[idx:])
return data.get('errors', [])
except json.JSONDecodeError:
return []
def main():
errors = get_flow_errors()
# Find errors where $FlowFixMe is on the line above but not suppressing
file_fixes = defaultdict(set)
for e in errors:
msgs = e.get('message', [])
codes = e.get('error_codes', [])
if not msgs:
continue
loc = msgs[0].get('loc', {})
source = loc.get('source', '')
line = loc.get('start', {}).get('line', 0)
if '/src/' not in source:
continue
code = codes[0] if codes else 'incompatible-type'
if not os.path.isfile(source):
continue
with open(source) as f:
lines = f.readlines()
if line < 2 or line > len(lines):
continue
above = lines[line - 2]
target = '// $FlowFixMe[' + code + ']'
if target in above:
file_fixes[source].add(line - 2)
# Convert the failing comments to JSX format
fixes_count = 0
reverted_count = 0
for filepath, fixme_indices in file_fixes.items():
with open(filepath) as f:
lines = f.readlines()
modified = False
converted_indices = set()
for idx in sorted(fixme_indices):
if idx >= len(lines):
continue
line = lines[idx]
m = re.match(r'^(\s*)// (\$FlowFixMe\[[^\]]+\])(.*?)$', line.rstrip())
if m:
indent = m.group(1)
fixme = m.group(2)
rest = m.group(3).strip()
if rest:
lines[idx] = f'{indent}{{/* {fixme} {rest} */}}\n'
else:
lines[idx] = f'{indent}{{/* {fixme} */}}\n'
modified = True
fixes_count += 1
converted_indices.add(idx)
# If a JSX-style FlowFixMe ended up outside JSX (can happen with noisy
# parse errors), restore it back to normal // comment syntax.
# Skip indices that were just converted from Flow error analysis -
# those are known to be in JSX context even if the heuristic can't tell.
for idx, line in enumerate(lines):
if idx in converted_indices:
continue
m = re.match(r'^(\s*)\{/\*\s*(\$FlowFixMe\[[^\]]+\])(?:\s+(.*?))?\s*\*/\}\s*$', line.rstrip())
if not m:
continue
if looks_like_jsx_context(lines, idx + 1):
continue
indent = m.group(1)
fixme = m.group(2)
rest = (m.group(3) or '').strip()
if rest:
lines[idx] = f'{indent}// {fixme} {rest}\n'
else:
lines[idx] = f'{indent}// {fixme}\n'
modified = True
reverted_count += 1
if modified:
with open(filepath, 'w') as f:
f.writelines(lines)
print(f"Converted {fixes_count} FlowFixMe comments to JSX format")
if reverted_count:
print(f"Reverted {reverted_count} non-JSX FlowFixMe comments back to // syntax")
if __name__ == '__main__':
main()
@@ -0,0 +1,215 @@
#!/usr/bin/env python3
"""
Fix arrow-function inline object return types that prettier 1.15 can't parse.
The Flow codemod may add return type annotations like:
const foo = (): { prop: Type, ... } => { ... }
Prettier 1.15 cannot parse (): { ... } => because it confuses the opening
brace of the return type with a function body.
This script detects files with prettier parse errors and, for each one,
extracts the inline object return type into a named type alias:
type _FooReturnType = { prop: Type, ... };
const foo = (): _FooReturnType => { ... }
The script is idempotent.
"""
import os
import re
import subprocess
import sys
APP_DIR = os.environ.get('APP_DIR', os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
SRC_DIR = os.path.join(APP_DIR, 'src')
def get_prettier_errors():
"""Run prettier --list-different and find files with syntax errors."""
result = subprocess.run(
['npx', 'prettier', '--list-different', 'src/!(locales)/**/*.js'],
capture_output=True, text=True, cwd=APP_DIR, timeout=120
)
# Combine stdout and stderr to find error lines
all_output = result.stdout + '\n' + result.stderr
error_files = set()
for line in all_output.split('\n'):
m = re.match(r'\[error\]\s+(src/\S+\.js):\s+SyntaxError', line)
if m:
error_files.add(os.path.join(APP_DIR, m.group(1)))
return error_files
def find_brace_end(content, start):
"""Find the matching closing brace for an opening brace at position start."""
depth = 0
i = start
while i < len(content):
ch = content[i]
if ch == '{':
depth += 1
elif ch == '}':
depth -= 1
if depth == 0:
return i
elif ch == "'" or ch == '"' or ch == '`':
# Skip strings
quote = ch
i += 1
while i < len(content) and content[i] != quote:
if content[i] == '\\':
i += 1
i += 1
elif ch == '/' and i + 1 < len(content):
if content[i + 1] == '/':
# Line comment
i = content.find('\n', i)
if i == -1:
return -1
elif content[i + 1] == '*':
# Block comment
i = content.find('*/', i + 2)
if i == -1:
return -1
i += 1
i += 1
return -1
def extract_identifier_name(content, pos):
"""Extract the identifier name before `= (` at the given position."""
# Walk backwards from `= (` to find the variable/const name
i = pos - 1
while i >= 0 and content[i] in ' \t':
i -= 1
if i < 0 or content[i] != '=':
return None
i -= 1
while i >= 0 and content[i] in ' \t':
i -= 1
if i < 0:
return None
end = i + 1
while i >= 0 and (content[i].isalnum() or content[i] == '_' or content[i] == '$'):
i -= 1
name = content[i + 1:end]
if name and name[0].isalpha():
return name
return None
def fix_file(filepath):
"""Fix arrow function return types in a single file."""
with open(filepath) as f:
content = f.read()
original = content
# Pattern: = (params): { ... } => {
# We need to find ): { where { starts a type annotation, not a body
# The type annotation ends with } =>
pattern = re.compile(r'(=\s*\([^)]*\))\s*:\s*\{')
insertions = [] # (position, type_name, type_body) to insert before the line
replacements = [] # (start, end, replacement)
used_names = set()
for m in pattern.finditer(content):
prefix = m.group(1) # e.g., '= (params)'
brace_start = content.index('{', m.start() + len(prefix))
brace_end = find_brace_end(content, brace_start)
if brace_end == -1:
continue
# Check if followed by => { (arrow function body)
after_brace = content[brace_end + 1:].lstrip()
if not after_brace.startswith('=>'):
continue
# Verify the type spans multiple lines (single-line types are ok for prettier)
type_text = content[brace_start:brace_end + 1]
if '\n' not in type_text:
continue
# Get the identifier name for the type alias
call_start = content.index('(', m.start())
name = extract_identifier_name(content, call_start)
if not name:
name = 'Anonymous'
# Create a unique type name
base_type_name = f'_{name[0].upper()}{name[1:]}ReturnType'
type_name = base_type_name
suffix = 2
while type_name in used_names:
type_name = f'{base_type_name}{suffix}'
suffix += 1
used_names.add(type_name)
# Find the line start for inserting the type declaration
line_start = content.rfind('\n', 0, m.start())
if line_start == -1:
line_start = 0
else:
line_start += 1
insertions.append((line_start, type_name, type_text))
# Replace ): { ...type... } => with ): TypeName =>
colon_pos = content.index(':', m.start() + len(prefix))
replacements.append((colon_pos, brace_end + 1, f': {type_name}'))
if not replacements:
return False
# Apply replacements in reverse order
combined = list(zip(insertions, replacements))
combined.sort(key=lambda x: x[1][0], reverse=True)
for (insert_pos, type_name, type_body), (rep_start, rep_end, rep_text) in combined:
content = content[:rep_start] + rep_text + content[rep_end:]
# Now insert type declarations (from bottom to top to maintain positions)
insertion_list = [(ins[0], ins[1], ins[2]) for ins, _ in combined]
insertion_list.sort(key=lambda x: x[0], reverse=True)
for insert_pos, type_name, type_body in insertion_list:
type_decl = f'type {type_name} = {type_body};\n'
content = content[:insert_pos] + type_decl + content[insert_pos:]
if content != original:
with open(filepath, 'w') as f:
f.write(content)
return True
return False
def main():
# First, find files that have prettier parse errors
error_files = get_prettier_errors()
if not error_files:
print(" No prettier parse errors found - no fixes needed.")
return
print(f" Found {len(error_files)} files with prettier parse errors")
fixed = 0
for filepath in sorted(error_files):
if fix_file(filepath):
fixed += 1
print(f" Fixed: {os.path.relpath(filepath, SRC_DIR)}")
print(f" Fixed {fixed} files")
# Verify no more errors
remaining = get_prettier_errors()
if remaining:
print(f" WARNING: {len(remaining)} files still have prettier parse errors:")
for f in sorted(remaining):
print(f" {os.path.relpath(f, SRC_DIR)}")
if __name__ == '__main__':
main()
+611
View File
@@ -0,0 +1,611 @@
#!/usr/bin/env python3
"""
Normalize Flow codemod syntax to Babel-compatible Flow syntax.
Flow 0.299 annotate-exports introduces syntax that older Babel/Flow tooling in this
project cannot parse:
- component(...) type annotations
- renders type operator
- as-casts
This script rewrites those constructs while preserving runtime behavior and as much
type information as possible:
- component(...) -> React.ComponentType<Props>
- React.AbstractComponent<Props, Instance> -> React.ComponentType<{...Props, ref?: React.RefSetter<Instance>}>
- renders T -> T (with renders any/mixed/empty/Fragment normalized to React.Node)
- expr as T -> (expr: T)
The script is idempotent.
"""
from __future__ import annotations
import json
import os
import pathlib
import re
import subprocess
import sys
from typing import Any, Dict, List, Optional, Tuple
APP_DIR = os.environ.get(
"APP_DIR",
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
)
SRC_DIR = pathlib.Path(APP_DIR) / "src"
FLOW_BIN = pathlib.Path(APP_DIR) / "node_modules" / ".bin" / "flow"
FLOW_AST_CMD = [str(FLOW_BIN), "ast"] if FLOW_BIN.exists() else ["npx", "flow", "ast"]
def _extract_json(output: str) -> Optional[Dict[str, Any]]:
start = output.find("{")
if start == -1:
return None
try:
return json.loads(output[start:])
except json.JSONDecodeError:
return None
def _run_flow_ast(relative_path: str, content_text: str) -> Optional[Dict[str, Any]]:
result = subprocess.run(
FLOW_AST_CMD + ["--path", relative_path],
input=content_text.encode("utf-8"),
capture_output=True,
cwd=APP_DIR,
)
stdout = result.stdout.decode("utf-8", errors="replace")
stderr = result.stderr.decode("utf-8", errors="replace")
data = _extract_json(stdout)
if data is not None:
return data
# Flow may occasionally emit JSON to stderr on failure.
return _extract_json(stderr)
def _walk(node: Any, visit, parent_type: Optional[str] = None) -> None:
if isinstance(node, dict):
node_type = node.get("type")
visit(node, parent_type)
for value in node.values():
_walk(value, visit, node_type)
elif isinstance(node, list):
for item in node:
_walk(item, visit, parent_type)
def _node_text(content: bytes, node: Dict[str, Any]) -> str:
start, end = node["range"]
return content[start:end].decode("utf-8")
def _is_render_node_like(type_text: str) -> bool:
normalized = "".join(type_text.split())
if normalized in {
"any",
"mixed",
"empty",
"void",
"null",
"Fragment",
"React.Node",
"React$Node",
"React.MixedElement",
}:
return True
return (
"React.Node" in normalized
or "React$Node" in normalized
or "React.MixedElement" in normalized
)
def _normalize_renders_target(type_text: str) -> str:
stripped = type_text.strip()
if stripped in {"any", "mixed", "empty", "Fragment"}:
return "React.Node"
return stripped
def _object_property_key_name(property_node: Dict[str, Any]) -> Optional[str]:
key = property_node.get("key")
if not isinstance(key, dict):
return None
key_type = key.get("type")
if key_type == "Identifier":
return key.get("name")
if key_type == "Literal":
return key.get("value")
return None
def _object_type_has_ref_property(object_type_node: Dict[str, Any]) -> bool:
for prop in object_type_node.get("properties", []):
if not isinstance(prop, dict):
continue
if prop.get("type") != "ObjectTypeProperty":
continue
if _object_property_key_name(prop) == "ref":
return True
return False
def _qualified_type_identifier_name(type_id_node: Any) -> Optional[str]:
if not isinstance(type_id_node, dict):
return None
node_type = type_id_node.get("type")
if node_type == "Identifier":
return type_id_node.get("name")
if node_type == "QualifiedTypeIdentifier":
left = _qualified_type_identifier_name(type_id_node.get("qualification"))
right = _qualified_type_identifier_name(type_id_node.get("id"))
if left and right:
return f"{left}.{right}"
return right
return None
def _is_react_abstract_component_type(type_id_node: Any) -> bool:
type_name = _qualified_type_identifier_name(type_id_node)
return type_name in {"React.AbstractComponent", "React$AbstractComponent"}
def _type_parameter_nodes(type_parameters_node: Any) -> List[Dict[str, Any]]:
if not isinstance(type_parameters_node, dict):
return []
params = type_parameters_node.get("params")
if not isinstance(params, list):
return []
return [param for param in params if isinstance(param, dict)]
def _is_ref_setter_type(type_text: str) -> bool:
stripped = type_text.strip()
return stripped.startswith("React.RefSetter<") or stripped.startswith(
"React$RefSetter<"
)
def _to_ref_setter_type(instance_type_text: str) -> str:
stripped = instance_type_text.strip()
if not stripped:
return "React.RefSetter<mixed>"
if _is_ref_setter_type(stripped):
return stripped.replace("React$RefSetter", "React.RefSetter")
return f"React.RefSetter<{stripped}>"
def _props_text_has_ref_property(props_type_text: str) -> bool:
return (
re.search(r"(^|[,{]\s*)\+?ref\??\s*:", props_type_text, re.MULTILINE)
is not None
)
def _append_ref_property_to_object_type(
props_type_text: str, ref_prop_type: str
) -> str:
original = props_type_text
stripped = original.rstrip()
trailing_whitespace = original[len(stripped) :]
if stripped.startswith("{|") and stripped.endswith("|}"):
open_token, close_token = "{|", "|}"
elif stripped.startswith("{") and stripped.endswith("}"):
open_token, close_token = "{", "}"
else:
return f"{{ ...{props_type_text.strip()}, +ref?: {ref_prop_type} }}"
inner = stripped[len(open_token) : -len(close_token)]
if _props_text_has_ref_property(inner):
return props_type_text
is_multiline = "\n" in stripped
inner_content = inner.strip()
if not is_multiline:
if inner_content:
separator = "," if not inner_content.endswith(",") else ""
return (
f"{open_token} {inner_content}{separator} +ref?: {ref_prop_type} "
f"{close_token}{trailing_whitespace}"
)
return (
f"{open_token} +ref?: {ref_prop_type} "
f"{close_token}{trailing_whitespace}"
)
# Multiline object type: place the new property on its own line and keep
# existing indentation style if possible.
closing_indent_match = re.search(r"\n([ \t]*)\s*$", inner)
closing_indent = closing_indent_match.group(1) if closing_indent_match else ""
property_indent_match = re.search(r"\n([ \t]+)\S", inner)
property_indent = (
property_indent_match.group(1)
if property_indent_match
else f"{closing_indent} "
)
inner_without_trailing_space = inner.rstrip()
if inner_content:
if not inner_without_trailing_space.endswith(","):
inner_without_trailing_space += ","
if not inner_without_trailing_space.endswith("\n"):
inner_without_trailing_space += "\n"
new_inner = (
f"{inner_without_trailing_space}"
f"{property_indent}+ref?: {ref_prop_type},\n"
f"{closing_indent}"
)
else:
new_inner = (
f"\n{property_indent}+ref?: {ref_prop_type},\n"
f"{closing_indent}"
)
return f"{open_token}{new_inner}{close_token}{trailing_whitespace}"
def _with_optional_ref_property(
props_type_text: str,
props_type_node: Optional[Dict[str, Any]],
instance_type_text: Optional[str],
) -> str:
if not instance_type_text:
return props_type_text
if _props_text_has_ref_property(props_type_text):
return props_type_text
ref_prop_type = _to_ref_setter_type(instance_type_text)
if props_type_node and props_type_node.get("type") == "ObjectTypeAnnotation":
if _object_type_has_ref_property(props_type_node):
return props_type_text
return _append_ref_property_to_object_type(props_type_text, ref_prop_type)
if props_type_text.strip() == "any":
return f"{{ +ref?: {ref_prop_type}, ... }}"
return f"{{ ...{props_type_text}, +ref?: {ref_prop_type} }}"
def _abstract_component_to_component_type(
node: Dict[str, Any], content: bytes
) -> Optional[str]:
if node.get("type") != "GenericTypeAnnotation":
return None
if not _is_react_abstract_component_type(node.get("id")):
return None
type_params = _type_parameter_nodes(node.get("typeParameters"))
if not type_params:
return "React.ComponentType<any>"
props_node = type_params[0]
props_type = (
_node_text(content, props_node).strip() if "range" in props_node else "any"
)
if not props_type:
props_type = "any"
instance_type = None
if len(type_params) >= 2:
instance_node = type_params[1]
if "range" in instance_node:
instance_type = _node_text(content, instance_node).strip()
props_type = _with_optional_ref_property(props_type, props_node, instance_type)
return f"React.ComponentType<{props_type}>"
def _extract_ref_type_from_component(
component_node: Dict[str, Any], content: bytes
) -> Optional[str]:
# Form: component(ref: Type, ...Props)
for param in component_node.get("params", []):
if not isinstance(param, dict):
continue
if param.get("name") == "ref":
type_annotation = param.get("typeAnnotation")
if isinstance(type_annotation, dict) and "range" in type_annotation:
return _node_text(content, type_annotation).strip()
# Form: component(...{ ...Props, +ref?: Type })
rest = component_node.get("rest")
if not isinstance(rest, dict):
return None
rest_type = rest.get("typeAnnotation")
if not isinstance(rest_type, dict):
return None
if rest_type.get("type") != "ObjectTypeAnnotation":
return None
for prop in rest_type.get("properties", []):
if not isinstance(prop, dict):
continue
if prop.get("type") != "ObjectTypeProperty":
continue
if _object_property_key_name(prop) != "ref":
continue
value = prop.get("value")
if isinstance(value, dict) and "range" in value:
return _node_text(content, value).strip()
return None
def _component_to_legacy_type(
component_node: Dict[str, Any], content: bytes
) -> str:
props_type = "any"
props_type_node: Optional[Dict[str, Any]] = None
rest = component_node.get("rest")
if isinstance(rest, dict):
rest_type = rest.get("typeAnnotation")
if isinstance(rest_type, dict) and "range" in rest_type:
props_type_node = rest_type
extracted_props = _node_text(content, rest_type).strip()
if extracted_props:
props_type = extracted_props
ref_type = _extract_ref_type_from_component(component_node, content)
renders_type = None
renders = component_node.get("rendersType")
if isinstance(renders, dict):
rendered = renders.get("typeAnnotation")
if isinstance(rendered, dict) and "range" in rendered:
renders_type = _node_text(content, rendered).strip()
ref_instance_type = ref_type
if not ref_instance_type and renders_type and not _is_render_node_like(renders_type):
# Non-React-node renders targets generally represent a component
# interface/instance type. In modern Flow this is represented with a
# `ref` prop typed with React.RefSetter<Instance>.
ref_instance_type = renders_type
props_type = _with_optional_ref_property(
props_type, props_type_node, ref_instance_type
)
return f"React.ComponentType<{props_type}>"
def _apply_replacements(
content: bytes, replacements: List[Tuple[int, int, str]]
) -> Tuple[bytes, int]:
valid: List[Tuple[int, int, bytes]] = []
for start, end, replacement in replacements:
if start >= end:
continue
replacement_bytes = replacement.encode("utf-8")
if content[start:end] == replacement_bytes:
continue
valid.append((start, end, replacement_bytes))
if not valid:
return content, 0
# Prefer outer nodes first when ranges overlap.
valid.sort(key=lambda item: (item[0], -(item[1] - item[0])))
non_overlapping: List[Tuple[int, int, bytes]] = []
current_end = -1
for start, end, replacement_bytes in valid:
if start < current_end:
continue
non_overlapping.append((start, end, replacement_bytes))
current_end = end
updated = bytearray(content)
for start, end, replacement_bytes in reversed(non_overlapping):
updated[start:end] = replacement_bytes
return bytes(updated), len(non_overlapping)
def _collect_component_and_renders_replacements(
ast: Dict[str, Any], content: bytes
) -> List[Tuple[int, int, str]]:
replacements: List[Tuple[int, int, str]] = []
def visit(node: Dict[str, Any], parent_type: Optional[str]) -> None:
node_type = node.get("type")
if node_type == "ComponentTypeAnnotation" and "range" in node:
start, end = node["range"]
replacements.append((start, end, _component_to_legacy_type(node, content)))
return
if node_type == "GenericTypeAnnotation" and "range" in node:
replacement = _abstract_component_to_component_type(node, content)
if replacement:
start, end = node["range"]
replacements.append((start, end, replacement))
if (
node_type == "TypeOperator"
and node.get("operator") == "renders"
and parent_type != "ComponentTypeAnnotation"
and "range" in node
):
type_annotation = node.get("typeAnnotation")
if isinstance(type_annotation, dict) and "range" in type_annotation:
rendered_type = _node_text(content, type_annotation)
replacements.append(
(
node["range"][0],
node["range"][1],
_normalize_renders_target(rendered_type),
)
)
_walk(ast, visit)
return replacements
def _collect_as_replacements(
ast: Dict[str, Any], content: bytes
) -> List[Tuple[int, int, str]]:
replacements: List[Tuple[int, int, str]] = []
def visit(node: Dict[str, Any], _parent_type: Optional[str]) -> None:
if node.get("type") != "AsExpression":
return
if "range" not in node:
return
expression = node.get("expression")
type_annotation = node.get("typeAnnotation")
if not isinstance(expression, dict) or "range" not in expression:
return
if not isinstance(type_annotation, dict) or "range" not in type_annotation:
return
start, end = node["range"]
expr_start, expr_end = expression["range"]
type_start, _type_end = type_annotation["range"]
type_text = _node_text(content, type_annotation)
leading = content[start:expr_start].decode("utf-8")
expr_core = content[expr_start:expr_end].decode("utf-8")
between = content[expr_end:type_start].decode("utf-8")
as_index = between.rfind(" as ")
if as_index == -1:
return
between_before_as = between[:as_index]
trailing_comment = ""
line_comment_index = between_before_as.rfind("//")
block_comment_index = between_before_as.rfind("/*")
if line_comment_index != -1:
trailing_comment = between_before_as[line_comment_index:].strip()
between_before_as = between_before_as[:line_comment_index]
elif block_comment_index != -1 and "*/" in between_before_as[block_comment_index:]:
trailing_comment = between_before_as[block_comment_index:].strip()
between_before_as = between_before_as[:block_comment_index]
expression_text = f"{leading}{expr_core}{between_before_as}".rstrip()
replacement = f"({expression_text}: {type_text})"
if trailing_comment:
replacement = f"{replacement} {trailing_comment}"
replacements.append((start, end, replacement))
_walk(ast, visit)
return replacements
def _remove_stale_prop_missing_fixmes(content: bytes) -> Tuple[bytes, int]:
try:
text = content.decode("utf-8")
except UnicodeDecodeError:
return content, 0
lines = text.splitlines(keepends=True)
keep_lines: List[str] = []
removed = 0
specific_fixme_re = re.compile(r"^\s*// \$FlowFixMe\[prop-missing\]\s*$")
any_fixme_re = re.compile(r"^\s*// \$FlowFixMe\[[^\]]+\]\s*$")
i = 0
while i < len(lines):
current_stripped = lines[i].strip()
if specific_fixme_re.match(current_stripped):
j = i + 1
inspected = 0
remove_current = False
while j < len(lines) and inspected < 8:
candidate = lines[j].strip()
inspected += 1
if not candidate:
j += 1
continue
if any_fixme_re.match(candidate):
j += 1
continue
if "React.ComponentType<" in lines[j]:
remove_current = True
break
if remove_current:
removed += 1
i += 1
continue
keep_lines.append(lines[i])
i += 1
if removed == 0:
return content, 0
return "".join(keep_lines).encode("utf-8"), removed
def _process_file(path: pathlib.Path) -> Tuple[bool, int]:
original = path.read_bytes()
# Quick pre-filter to avoid expensive AST parsing on unrelated files.
has_componenttype_prop_missing = (
b"$FlowFixMe[prop-missing]" in original and b"React.ComponentType<" in original
)
if (
b"component(" not in original
and b"renders " not in original
and b" as " not in original
and b"AbstractComponent" not in original
and b"React$AbstractComponent" not in original
and not has_componenttype_prop_missing
):
return False, 0
try:
text = original.decode("utf-8")
except UnicodeDecodeError:
return False, 0
relative_path = os.path.relpath(path, APP_DIR)
ast = _run_flow_ast(relative_path, text)
if ast is None:
return False, 0
content = original
total_replacements = 0
# Pass 1: component(...) + renders.
pass1_replacements = _collect_component_and_renders_replacements(ast, content)
content, applied = _apply_replacements(content, pass1_replacements)
total_replacements += applied
# Pass 2: "as" casts. Run iteratively to handle nested casts.
for _ in range(10):
if b" as " not in content:
break
try:
content_text = content.decode("utf-8")
except UnicodeDecodeError:
break
as_ast = _run_flow_ast(relative_path, content_text)
if as_ast is None:
break
as_replacements = _collect_as_replacements(as_ast, content)
content, applied = _apply_replacements(content, as_replacements)
total_replacements += applied
if applied == 0:
break
content, removed_fixmes = _remove_stale_prop_missing_fixmes(content)
total_replacements += removed_fixmes
if content != original:
path.write_bytes(content)
return True, total_replacements
return False, 0
def main() -> int:
files_modified = 0
replacements_applied = 0
for path in sorted(SRC_DIR.rglob("*.js")):
if not path.is_file():
continue
modified, replacements = _process_file(path)
if modified:
files_modified += 1
replacements_applied += replacements
print(f" Modified {files_modified} files ({replacements_applied} replacements)")
return 0
if __name__ == "__main__":
sys.exit(main())
+884
View File
@@ -0,0 +1,884 @@
#!/bin/bash
set -e
# Flow migration fix script (0.131 -> 0.299)
# This script is idempotent - it can be run multiple times safely.
# It fixes Flow type errors after upgrading from Flow 0.131 to 0.299.
#
# NOTE: We DO run "flow codemod annotate-exports", then normalize the
# generated modern syntax (component()/renders/as-casts) back to
# Babel-compatible Flow syntax.
#
# Usage: cd newIDE/app && bash scripts/fix-flow-errors.sh
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
export APP_DIR
cd "$APP_DIR"
echo "=== Flow Migration Fix Script ==="
echo "Working directory: $APP_DIR"
###############################################################################
# STEP 1: Ensure flow-libs directory exists
###############################################################################
echo ""
echo "--- Step 1: Ensuring flow-libs are in place ---"
if [ ! -d "$APP_DIR/flow-libs" ]; then
echo "ERROR: flow-libs directory not found."
exit 1
fi
echo " OK."
###############################################################################
# STEP 1b: Update flow-bin version to 0.299.0 in package.json and npm install
###############################################################################
echo ""
echo "--- Step 1b: Updating flow-bin version to 0.299.0 ---"
sed -i 's/"flow-bin": "0.131.0"/"flow-bin": "0.299.0"/' "$APP_DIR/package.json"
echo " Updated package.json. Running npm install..."
npm install
echo " Done."
###############################################################################
# STEP 2: Update .flowconfig
###############################################################################
echo ""
echo "--- Step 2: Updating .flowconfig ---"
if ! grep -q 'flow-libs' "$APP_DIR/.flowconfig"; then
sed -i '/^\[libs\]/a flow-libs' "$APP_DIR/.flowconfig"
fi
if ! grep -q '<PROJECT_ROOT>/flow-typed/\.\*' "$APP_DIR/.flowconfig"; then
sed -i '/^\[declarations\]/a <PROJECT_ROOT>/flow-typed/.*' "$APP_DIR/.flowconfig"
fi
if ! grep -q 'GDevelop.js/types/\.\*' "$APP_DIR/.flowconfig"; then
sed -i '/^\[declarations\]/a <PROJECT_ROOT>/../../GDevelop.js/types/.*' "$APP_DIR/.flowconfig"
fi
if ! grep -q '<PROJECT_ROOT>/flow-libs/\.\*' "$APP_DIR/.flowconfig"; then
sed -i '/^\[declarations\]/a <PROJECT_ROOT>/flow-libs/.*' "$APP_DIR/.flowconfig"
fi
if ! grep -q 'fbjs' "$APP_DIR/.flowconfig"; then
sed -i '/^\[declarations\]/a <PROJECT_ROOT>/node_modules/fbjs/.*' "$APP_DIR/.flowconfig"
fi
if ! grep -q '^\[lints\]' "$APP_DIR/.flowconfig"; then
echo -e "\n[lints]" >> "$APP_DIR/.flowconfig"
fi
echo " Done."
###############################################################################
# STEP 3: Fix deprecated syntax in src/ files
###############################################################################
echo ""
echo "--- Step 3: Fixing deprecated syntax ---"
# Fix existential type * -> any
find "$APP_DIR/src" -name "*.js" -type f -exec perl -pi -e '
s/(<[^>]*?)\*([,>])/${1}any${2}/g;
s/(<[^>]*?)\*([,>])/${1}any${2}/g;
s/& \*\b/\& any/g;
' {} +
# Fix %checks (removed in new Flow)
find "$APP_DIR/src" -name "*.js" -type f -exec sed -i 's/ %checks//g' {} +
# Fix React$ type names -> React. namespace
find "$APP_DIR/src" -name "*.js" -type f -exec sed -i \
-e 's/\bReact\$Element\b/React.Element/g' \
-e 's/\bReact\$Component\b/React.Component/g' \
-e 's/\bReact\$Node\b/React.Node/g' \
-e 's/\bReact\$Context\b/React.Context/g' \
-e 's/\bReact\$Ref\b/React.Ref/g' \
-e 's/\bReact\$Key\b/React.Key/g' \
-e 's/\bReact\$ElementRef\b/React.ElementRef/g' \
-e 's/\bReact\$ElementConfig\b/React.ElementConfig/g' \
-e 's/\bReact\$ComponentType\b/React.ComponentType/g' \
{} +
# Fix $PropertyType -> indexed access type
find "$APP_DIR/src" -name "*.js" -type f -exec perl -pi -e "
s/\\\$PropertyType<([^,]+),\s*'([^']+)'>/\$1['\$2']/g;
s/\\\$PropertyType<([^,]+),\s*\"([^\"]+)\">/\$1[\"\$2\"]/g;
" {} +
# Fix $FlowExpectedError -> $FlowFixMe[incompatible-type]
find "$APP_DIR/src" -name "*.js" -type f -exec perl -pi -e '
s/\$FlowExpectedError(?!\[)/\$FlowFixMe[incompatible-type]/g;
s/\$FlowExpectedError\[([^\]]+)\]/\$FlowFixMe[$1]/g;
' {} +
# Fix old $FlowFixMe without error codes
find "$APP_DIR/src" -name "*.js" -type f -exec perl -pi -e '
s/\$FlowFixMe(?!\[)/\$FlowFixMe[incompatible-type]/g;
' {} +
echo " Done."
###############################################################################
# STEP 4: Run annotate-exports and normalize syntax
###############################################################################
echo ""
echo "--- Step 4: Running Flow codemod annotate-exports ---"
npx flow stop 2>/dev/null || true
for i in $(seq 1 5); do
echo " Codemod iteration $i..."
set +e
CODEMOD_OUTPUT=$(npx flow codemod annotate-exports --write --default-any src 2>&1)
CODEMOD_EXIT=$?
set -e
echo "$CODEMOD_OUTPUT" | grep -E \
"Files changed:|Number of annotations added:|Number of sig\. ver\. errors:|Number of annotations required:" || true
if [ $CODEMOD_EXIT -ne 0 ]; then
echo " WARNING: annotate-exports exited with status $CODEMOD_EXIT"
break
fi
FILES_CHANGED=$(echo "$CODEMOD_OUTPUT" | sed -n 's/.*Files changed:[[:space:]]*\([0-9][0-9]*\).*/\1/p' | head -n 1)
if [ -z "$FILES_CHANGED" ]; then
echo " WARNING: Could not parse codemod output."
break
fi
if [ "$FILES_CHANGED" -eq 0 ]; then
echo " Codemod converged."
break
fi
done
echo " Normalizing codemod syntax to Babel-compatible Flow..."
python3 "$SCRIPT_DIR/fix-codemod-syntax.py"
# Fix arrow-function inline object return types that prettier 1.15 can't parse.
# The codemod may add return type annotations like:
# const foo = (): { prop: Type } => { ... }
# Prettier 1.15 can't parse the `: { ... } =>` pattern.
# Fix: extract the inline type into a named type alias, preserving typing.
echo " Fixing arrow-function return types for prettier compatibility..."
python3 "$SCRIPT_DIR/fix-arrow-return-types.py"
echo " Done."
###############################################################################
# STEP 4b: Fix codemod artifacts that cause parse errors or Flow issues
###############################################################################
echo ""
echo "--- Step 4b: Fixing codemod artifacts ---"
# 0. Remove stale FlowFixMe[prop-missing] comments that were previously added
# for invalid React.AbstractComponent output from older codemod runs.
find "$APP_DIR/src" -name "*.js" -type f -exec perl -0pi -e '
s/^[ \t]*\/\/ \$FlowFixMe\[prop-missing\]\n(?=(?:[ \t]*\/\/ \$FlowFixMe\[[^\]]+\]\n|[ \t]*\n)*[^\n]*React\.ComponentType<)//mg;
' {} +
# 1. Fix missing commas after ([]: Array<empty>) in object literals.
# The codemod turns "[]," into "([]: Array<empty>)" but loses the comma.
find "$APP_DIR/src" -name "*.js" -type f -exec perl -pi -e '
s/\(\[\]: Array<empty>\)(\s*\/\/.*)$/([]: Array<empty>),$1/g;
' {} +
# 2. Fix "new Array(n)" -> "new Array<number>(n)" to avoid underconstrained type
find "$APP_DIR/src" -name "*.js" -type f -exec perl -pi -e '
s/new Array\(([^)]+)\)\.fill\(0\)/new Array<number>($1).fill(0)/g;
' {} +
# 3. Fix !== null comparisons on number types -> != null
# (Flow 0.299 is strict about comparing non-nullable numbers to null)
find "$APP_DIR/src" -name "*.js" -type f -exec perl -pi -e '
s/selectedCompletionIndex !== null/selectedCompletionIndex != null/g;
' {} +
# 5. Generic type params like new Set<any>(), new Map<any,any>(), React.createRef<any>()
# are NOT parseable by prettier 1.15 (it treats < as comparison operator).
# These will get FlowFixMe[underconstrained-implicit-instantiation] automatically.
# 6. new Array(n) patterns beyond .fill(0) also can't use <any> with prettier 1.15.
# 7. axios calls with type params - skip, prettier 1.15 mangles method<Type>() syntax.
# These will get FlowFixMe[underconstrained-implicit-instantiation] automatically.
# 8. jest.fn() - leave as-is, prettier 1.15 can't parse jest.fn<any>()
# These will get FlowFixMe[underconstrained-implicit-instantiation] automatically.
# 9. Fix method-unbinding for common patterns: .push.apply -> spread syntax
# Handles both single-line and multi-line push.apply patterns.
python3 << 'PYEOF'
import os, re
src_dir = os.path.join(os.environ.get('APP_DIR', os.getcwd()), 'src')
fixed = 0
for root, dirs, files in os.walk(src_dir):
for fname in files:
if not fname.endswith('.js'):
continue
filepath = os.path.join(root, fname)
with open(filepath) as f:
content = f.read()
original = content
# Multi-line pattern: arr.push.apply(\n arr,\n items\n)
content = re.sub(
r'(\w+)\.push\.apply\(\s*\1\s*,\s*([^)]+?)\s*\)',
lambda m: f'{m.group(1)}.push(...{m.group(2).strip()})',
content,
flags=re.DOTALL)
if content != original:
with open(filepath, 'w') as f:
f.write(content)
fixed += 1
print(f" Converted {fixed} push.apply to spread syntax")
PYEOF
# 10. Fix import-type-as-value: convert value imports of type-only symbols to import type.
# I18n from @lingui/core is a type (interface) used only in type positions.
find "$APP_DIR/src" -name "*.js" -type f -exec perl -pi -e '
s/^(\/\/ \$FlowFixMe\[import-type-as-value\]\n)?import \{ I18n as I18nType \} from/import type { I18n as I18nType } from/g;
' {} +
# Fix TreeViewItemContent import-type-as-value: these are interfaces (type-only)
python3 << 'PYEOF'
import os, re
app_dir = os.environ.get('APP_DIR', os.getcwd())
src_dir = os.path.join(app_dir, 'src')
fixed = 0
for root, dirs, files in os.walk(src_dir):
for fname in files:
if not fname.endswith('.js'):
continue
filepath = os.path.join(root, fname)
with open(filepath) as f:
content = f.read()
original = content
# Remove FlowFixMe[import-type-as-value] above import lines where
# we can safely convert to import type
lines = content.split('\n')
new_lines = []
i = 0
while i < len(lines):
line = lines[i]
# Skip FlowFixMe[import-type-as-value] if the next line is an import we'll fix
if '$FlowFixMe[import-type-as-value]' in line:
# Check if this is a standalone FlowFixMe comment line
stripped = line.lstrip()
if stripped.startswith('// $FlowFixMe[import-type-as-value]'):
# Check if next line is an import statement
if i + 1 < len(lines) and 'import' in lines[i + 1]:
# Skip this FlowFixMe comment
i += 1
continue
# Check if this is inside an import block (destructured import)
# Look backwards for the import statement
is_in_import = False
for j in range(i - 1, max(i - 10, -1), -1):
if 'import' in lines[j] and '{' in lines[j]:
is_in_import = True
break
if '}' in lines[j] and 'from' in lines[j]:
break
if is_in_import:
# Skip this FlowFixMe
i += 1
continue
new_lines.append(line)
i += 1
content = '\n'.join(new_lines)
# Convert import { I18n as I18nType } to import type
content = re.sub(
r"import \{ I18n as I18nType \} from '@lingui/core'",
"import type { I18n as I18nType } from '@lingui/core'",
content)
if content != original:
with open(filepath, 'w') as f:
f.write(content)
fixed += 1
print(f" Fixed {fixed} import-type-as-value issues")
PYEOF
# 4. Fix inline object return types on multi-line arrow function params
# that fix-arrow-return-types.py couldn't handle (nested parens in destructured args).
# Pattern: ): { ... } => { (where the object return type causes prettier parse error)
python3 << 'PYEOF'
import os, re, subprocess
app_dir = os.environ.get('APP_DIR', os.getcwd())
# Find files with prettier parse errors
result = subprocess.run(
['npx', 'prettier', '--list-different', 'src/!(locales)/**/*.js'],
capture_output=True, text=True, cwd=app_dir, timeout=120
)
all_output = result.stdout + '\n' + result.stderr
error_files = set()
for line in all_output.split('\n'):
m = re.match(r'\[error\]\s+(src/\S+\.js):\s+SyntaxError', line)
if m:
error_files.add(os.path.join(app_dir, m.group(1)))
fixed = 0
for filepath in error_files:
if not os.path.isfile(filepath):
continue
with open(filepath) as f:
content = f.read()
original = content
# Find ): { ... } => patterns (multi-line object return types)
# This regex works on the whole content to find the ): { ... } => pattern
# We need to use a balanced-brace approach
pos = 0
replacements = []
while True:
idx = content.find('):', pos)
if idx == -1:
break
# Check if followed by whitespace then {
rest = content[idx+2:].lstrip()
if not rest.startswith('{'):
pos = idx + 2
continue
brace_start = content.index('{', idx + 2)
# Find matching close brace
depth = 0
i = brace_start
while i < len(content):
if content[i] == '{':
depth += 1
elif content[i] == '}':
depth -= 1
if depth == 0:
break
elif content[i] in ('"', "'", '`'):
q = content[i]
i += 1
while i < len(content) and content[i] != q:
if content[i] == '\\':
i += 1
i += 1
i += 1
if depth != 0:
pos = idx + 2
continue
brace_end = i
# Check if followed by => (arrow)
after = content[brace_end+1:].lstrip()
if not after.startswith('=>'):
pos = idx + 2
continue
type_text = content[brace_start:brace_end+1]
if '\n' not in type_text:
pos = idx + 2
continue
# Find function name for type alias
line_start = content.rfind('\n', 0, idx)
if line_start == -1:
line_start = 0
# Find export const/let/var name = ... pattern
preceding = content[max(0, line_start-200):idx+2]
name_match = re.search(r'(?:const|let|var|function)\s+(\w+)', preceding)
name = name_match.group(1) if name_match else 'Func'
type_name = f'_{name[0].upper()}{name[1:]}ReturnType'
# Check if type alias already exists
if f'type {type_name}' in content:
pos = idx + 2
continue
replacements.append((brace_start, brace_end+1, type_name, type_text))
pos = brace_end + 1
if not replacements:
continue
# Apply in reverse
for brace_start, brace_end, type_name, type_text in reversed(replacements):
content = content[:brace_start] + type_name + content[brace_end:]
# Insert type declarations at the top (after imports)
insert_lines = []
for _, _, type_name, type_text in replacements:
insert_lines.append(f'type {type_name} = {type_text};\n')
# Find insertion point (after last import)
last_import = 0
for m in re.finditer(r'^(?:import\s|from\s)', content, re.MULTILINE):
eol = content.find('\n', m.start())
# Handle multi-line imports
if '{' in content[m.start():eol] and '}' not in content[m.start():eol]:
eol = content.find('}', eol)
eol = content.find('\n', eol)
# Handle from '...' on next line
next_line_start = eol + 1
if next_line_start < len(content):
next_stripped = content[next_line_start:].lstrip()
if next_stripped.startswith("from ") or next_stripped.startswith("} from "):
eol = content.find('\n', next_line_start)
if eol > last_import:
last_import = eol
if last_import > 0:
insert_pos = last_import + 1
else:
insert_pos = 0
insert_text = '\n' + ''.join(insert_lines) + '\n'
content = content[:insert_pos] + insert_text + content[insert_pos:]
if content != original:
with open(filepath, 'w') as f:
f.write(content)
fixed += 1
print(f" Fixed {fixed} additional arrow-return-type files")
PYEOF
echo " Done."
###############################################################################
# STEP 5: Fix specific known issues
###############################################################################
echo ""
echo "--- Step 5: Fixing specific known issues ---"
# Fix GDevelop.js type files (if they exist and don't have FlowFixMe)
GD_TYPES="$APP_DIR/../../GDevelop.js/types"
if [ -d "$GD_TYPES" ]; then
python3 << 'PYEOF'
import os, re
gd_types = os.environ.get('GD_TYPES', '../../GDevelop.js/types')
for fname in ['gdbehaviorjsimplementation.js', 'gdbehaviorshareddatajsimplementation.js']:
filepath = os.path.join(gd_types, fname)
if not os.path.isfile(filepath):
continue
with open(filepath) as f:
content = f.read()
if '$FlowFixMe' in content:
continue
for method in ['getProperties(', 'updateProperty(', 'initializeContent(']:
content = content.replace(f' {method}', f' // $FlowFixMe[incompatible-type]\n {method}')
with open(filepath, 'w') as f:
f.write(content)
filepath = os.path.join(gd_types, 'libgdevelop.js')
if os.path.isfile(filepath):
with open(filepath) as f:
lines = f.readlines()
if '$FlowFixMe' not in ''.join(lines):
fixes = {19: 'cannot-resolve-name', 20: 'cannot-resolve-name', 39: 'cannot-resolve-name', 297: 'cannot-resolve-name'}
new_lines = []
for i, line in enumerate(lines):
if i + 1 in fixes:
new_lines.append(f' // $FlowFixMe[{fixes[i+1]}]\n')
new_lines.append(line)
with open(filepath, 'w') as f:
f.writelines(new_lines)
print(" Fixed GDevelop.js types")
PYEOF
fi
# Fix Theme/index.js - $PropertyType replacement may leave empty type or
# indexed access type (Theme['gdevelopTheme']) that prettier 1.15 can't parse.
# Use typeof DefaultLightTheme.gdevelopTheme which both Flow 0.299 and prettier understand.
THEME_FILE="$APP_DIR/src/UI/Theme/index.js"
python3 << 'PYEOF'
import os, re
theme_file = os.path.join(os.environ.get('APP_DIR', os.getcwd()), 'src/UI/Theme/index.js')
if os.path.isfile(theme_file):
with open(theme_file) as f:
content = f.read()
original = content
# Fix empty type from $PropertyType removal
content = content.replace("export type GDevelopTheme = ;",
"export type GDevelopTheme = typeof DefaultLightTheme.gdevelopTheme;")
# Fix indexed access type that prettier 1.15 can't parse
content = re.sub(
r"export type GDevelopTheme = Theme\['gdevelopTheme'\];",
"export type GDevelopTheme = typeof DefaultLightTheme.gdevelopTheme;",
content)
# Remove any stray FlowFixMe above the type export
content = re.sub(
r'(?://\s*\$FlowFixMe\[incompatible-type\]\s*\n|\{/\*\s*\$FlowFixMe\[incompatible-type\]\s*\*/\}\s*\n)'
r'(export type GDevelopTheme)',
r'\1', content)
# Remove any stale prettier-ignore before the line
content = re.sub(
r'// prettier-ignore\n(export type GDevelopTheme)',
r'\1', content)
if content != original:
with open(theme_file, 'w') as f:
f.write(content)
print(" Fixed Theme/index.js")
else:
print(" Theme/index.js already correct")
PYEOF
# Fix known annotate-exports gaps and formatting-displacement issues.
python3 << 'PYEOF'
import os
import re
app_dir = os.environ.get('APP_DIR', os.getcwd())
# Regex-based replacements for signature-verification gaps
replacements = [
(
os.path.join(app_dir, 'src/EventsSheet/index.js'),
r'onResourceExternallyChanged = resourceInfo => \{',
'onResourceExternallyChanged = (resourceInfo: any) => {',
),
(
os.path.join(app_dir, 'src/Leaderboard/LeaderboardContext.js'),
r'deleteLeaderboardEntry: async entryId => \{\},',
'deleteLeaderboardEntry: async (entryId: any) => {},',
),
]
fixed = 0
for filepath, pattern, replacement in replacements:
if not os.path.isfile(filepath):
continue
with open(filepath) as f:
content = f.read()
updated = re.sub(pattern, replacement, content)
if updated != content:
with open(filepath, 'w') as f:
f.write(updated)
fixed += 1
# Fix PrivateGameTemplateStoreContext: add type annotation to empty object
# that prettier splits across lines (causing FlowFixMe displacement).
pgts_file = os.path.join(app_dir,
'src/AssetStore/PrivateGameTemplates/PrivateGameTemplateStoreContext.js')
if os.path.isfile(pgts_file):
with open(pgts_file) as f:
content = f.read()
# Add type annotation to the untyped empty object
updated = content.replace(
'const privateGameTemplateListingDatasById = {};',
'const privateGameTemplateListingDatasById: {[string]: any} = {};')
# Remove any stacked FlowFixMe[prop-missing] that accumulated from previous runs
updated = re.sub(
r'(\s*// \$FlowFixMe\[prop-missing\]\n)+(\s*privateGameTemplateListingDatasById\[)',
r'\2', updated)
# Also remove FlowFixMe[invalid-computed-prop] for the typed object (no longer needed)
updated = re.sub(
r'\s*// \$FlowFixMe\[invalid-computed-prop\]\n(\s*if \(privateGameTemplateListingDatasById\[)',
r'\n\1', updated)
if updated != content:
with open(pgts_file, 'w') as f:
f.write(updated)
fixed += 1
# Fix ConditionsActionsColumns.js - renderActionsList type needs style prop
cac_file = os.path.join(app_dir, 'src/EventsSheet/EventsTree/ConditionsActionsColumns.js')
if os.path.isfile(cac_file):
with open(cac_file) as f:
content = f.read()
updated = content.replace(
'renderActionsList: ({ className: string }) => React.Node,',
'renderActionsList: ({ style?: Object, className: string }) => React.Node,')
if updated != content:
with open(cac_file, 'w') as f:
f.write(updated)
fixed += 1
# Fix PreferencesContext.js - annotate missing params for signature-verification
pref_file = os.path.join(app_dir, 'src/MainFrame/Preferences/PreferencesContext.js')
if os.path.isfile(pref_file):
with open(pref_file) as f:
content = f.read()
updated = content
updated = updated.replace(
'getRecentProjectFiles: options => [],',
'getRecentProjectFiles: (options: any): any => [],')
updated = updated.replace(
'getEditorStateForProject: projectId => {},',
'getEditorStateForProject: (projectId: any): any => {},')
if updated != content:
with open(pref_file, 'w') as f:
f.write(updated)
fixed += 1
# Fix ShortcutsList.js - annotate commandName parameter
sc_file = os.path.join(app_dir, 'src/KeyboardShortcuts/ShortcutsList.js')
if os.path.isfile(sc_file):
with open(sc_file) as f:
content = f.read()
updated = content.replace(
'areaWiseCommands[areaName].map(commandName =>',
'areaWiseCommands[areaName].map((commandName: string) =>')
if updated != content:
with open(sc_file, 'w') as f:
f.write(updated)
fixed += 1
# Fix ProjectManager/index.js - useState(null) needs type param for ?gdLayout
pm_file = os.path.join(app_dir, 'src/ProjectManager/index.js')
if os.path.isfile(pm_file):
with open(pm_file) as f:
content = f.read()
updated = content
# Add type annotation to useState calls that should be ?gdLayout
updated = re.sub(
r'const \[editedPropertiesLayout, setEditedPropertiesLayout\] = React\.useState\(\s*\n\s*null\s*\n\s*\)',
'const [editedPropertiesLayout, setEditedPropertiesLayout] = React.useState<?gdLayout>(\n null\n )',
updated)
updated = re.sub(
r'const \[editedVariablesLayout, setEditedVariablesLayout\] = React\.useState\(\s*\n\s*null\s*\n\s*\)',
'const [editedVariablesLayout, setEditedVariablesLayout] = React.useState<?gdLayout>(\n null\n )',
updated)
if updated != content:
with open(pm_file, 'w') as f:
f.write(updated)
fixed += 1
# Fix CollisionMasksPreview.js - use type cast for inexact/exact mismatch
cmp_file = os.path.join(app_dir, 'src/ObjectEditor/Editors/SpriteEditor/CollisionMasksEditor/CollisionMasksPreview.js')
if os.path.isfile(cmp_file):
with open(cmp_file) as f:
content = f.read()
# Cast polygons to any to bypass inexact/exact mismatch
updated = content.replace(
'mapVector(polygons,',
'mapVector((polygons: any),')
if updated != content:
with open(cmp_file, 'w') as f:
f.write(updated)
fixed += 1
print(f" Fixed {fixed} known annotate-exports signature gaps and type issues")
PYEOF
echo " Done."
###############################################################################
# STEP 6: Run prettier formatting (pre-FlowFixMe)
###############################################################################
echo ""
echo "--- Step 6: Running prettier formatting (pre-FlowFixMe) ---"
# Format all source files before adding FlowFixMe.
# This ensures FlowFixMe comments will be placed at the correct post-format
# line numbers.
npm run format 2>&1 || true
echo " Done."
###############################################################################
# STEP 7: Add $FlowFixMe for all remaining errors (iterative)
###############################################################################
echo ""
echo "--- Step 7: Adding \$FlowFixMe for all errors (iterative) ---"
npx flow stop 2>/dev/null || true
PREV_ERROR_COUNT=""
for i in $(seq 1 15); do
echo " Iteration $i..."
# Add FlowFixMe comments for errors
FLOWFIXME_OUTPUT=$(python3 "$SCRIPT_DIR/add-flow-fixme.py" 2>&1 || true)
echo "$FLOWFIXME_OUTPUT" | grep -E "Added|Found|No" || true
# Convert // FlowFixMe in JSX context to {/* FlowFixMe */}
JSX_CONVERT_OUTPUT=$(python3 "$SCRIPT_DIR/convert-jsx-flowfixme.py" 2>&1 || true)
echo "$JSX_CONVERT_OUTPUT" | grep -E "Converted|Reverted" || true
npx flow stop 2>/dev/null || true
# Check remaining errors
set +e
ERROR_OUTPUT=$(npx flow 2>&1)
FLOW_EXIT=$?
set -e
if echo "$ERROR_OUTPUT" | grep -q "No errors"; then
echo " No more errors!"
break
fi
ERROR_COUNT=$(echo "$ERROR_OUTPUT" | grep "Found" | grep -o '[0-9]*' | head -1)
echo " Remaining errors: ${ERROR_COUNT:-unknown} (flow exit: $FLOW_EXIT)"
if echo "$FLOWFIXME_OUTPUT" | grep -q "Added 0 \$FlowFixMe comments" && \
[ -n "$ERROR_COUNT" ] && [ "$ERROR_COUNT" = "$PREV_ERROR_COUNT" ]; then
echo " No automatic progress in this iteration; stopping early."
break
fi
PREV_ERROR_COUNT="$ERROR_COUNT"
npx flow stop 2>/dev/null || true
done
###############################################################################
# STEP 8: Post-FlowFixMe format + fix cycle
###############################################################################
echo ""
echo "--- Step 8: Post-FlowFixMe format and fix cycle ---"
# After adding FlowFixMe comments, formatting may introduce new errors
# (e.g., FlowFixMe comments getting displaced from error lines).
# Run format + flow check + FlowFixMe in a cycle until stable.
for j in $(seq 1 3); do
echo " Format-fix cycle iteration $j..."
npm run format 2>&1 || true
npx flow stop 2>/dev/null || true
set +e
CYCLE_CHECK=$(npx flow 2>&1)
set -e
if echo "$CYCLE_CHECK" | grep -q "No errors"; then
echo " No errors after formatting - cycle complete!"
break
fi
CYCLE_ERRORS=$(echo "$CYCLE_CHECK" | grep "Found" | grep -o '[0-9]*' | head -1)
echo " $CYCLE_ERRORS errors after formatting, adding FlowFixMe..."
python3 "$SCRIPT_DIR/add-flow-fixme.py" 2>&1 || true
python3 "$SCRIPT_DIR/convert-jsx-flowfixme.py" 2>&1 || true
done
echo " Done."
###############################################################################
# STEP 9: Final format verification
###############################################################################
echo ""
echo "--- Step 9: Final format verification ---"
npm run format 2>&1 || true
npx flow stop 2>/dev/null || true
set +e
FINAL_CHECK=$(npx flow 2>&1)
set -e
if echo "$FINAL_CHECK" | grep -q "No errors"; then
echo " All clean!"
else
echo " WARNING: Still have errors after all cycles:"
echo "$FINAL_CHECK" | grep "Found" || true
fi
echo " Done."
###############################################################################
# STEP 10: Fix eslint-disable-next-line ordering with $FlowFixMe
###############################################################################
echo ""
echo "--- Step 10: Fixing eslint/FlowFixMe comment ordering ---"
# When $FlowFixMe is inserted between eslint-disable-next-line and the code,
# eslint-disable no longer applies (it targets the FlowFixMe comment instead).
# Fix: wrap the code with eslint-disable/eslint-enable block comments, so that
# both the FlowFixMe suppression and the eslint suppression work correctly.
python3 << 'PYEOF'
import os, re
src_dir = os.path.join(os.environ.get('APP_DIR', os.getcwd()), 'src')
fixed = 0
for root, dirs, files in os.walk(src_dir):
for fname in files:
if not fname.endswith('.js'):
continue
filepath = os.path.join(root, fname)
with open(filepath) as f:
lines = f.readlines()
modified = False
new_lines = []
i = 0
while i < len(lines):
# Pattern: eslint-disable-next-line, then one or more $FlowFixMe[...], then code
if (i + 2 < len(lines) and
'eslint-disable-next-line' in lines[i] and
'$FlowFixMe[' in lines[i + 1]):
m = re.search(r'eslint-disable-next-line\s+(.+)', lines[i])
if m:
rule = m.group(1).strip()
# Collect all consecutive FlowFixMe lines
fixme_lines = []
j = i + 1
while j < len(lines) and '$FlowFixMe[' in lines[j]:
fixme_lines.append(lines[j])
j += 1
if j < len(lines):
code_line = lines[j]
indent = ''
for ch in code_line:
if ch in (' ', '\t'):
indent += ch
else:
break
# Use eslint-disable/enable block wrapping FlowFixMe + code
new_lines.append(f'{indent}/* eslint-disable {rule} */\n')
for fl in fixme_lines:
new_lines.append(fl)
new_lines.append(code_line)
new_lines.append(f'{indent}/* eslint-enable {rule} */\n')
i = j + 1
modified = True
fixed += 1
continue
# Pattern: one or more $FlowFixMe[...], then eslint-disable-next-line, then code
elif (i + 2 < len(lines) and
'$FlowFixMe[' in lines[i] and
'eslint-disable-next-line' in lines[i + 1]):
# Find where the FlowFixMe block starts
fixme_start = i
# The eslint line is after one FlowFixMe; collect FlowFixMe after it too
m = re.search(r'eslint-disable-next-line\s+(.+)', lines[i + 1])
if m:
rule = m.group(1).strip()
j = i + 2
# Code line is right after eslint-disable-next-line
if j < len(lines):
code_line = lines[j]
indent = ''
for ch in code_line:
if ch in (' ', '\t'):
indent += ch
else:
break
new_lines.append(f'{indent}/* eslint-disable {rule} */\n')
new_lines.append(lines[i]) # Keep $FlowFixMe
new_lines.append(code_line)
new_lines.append(f'{indent}/* eslint-enable {rule} */\n')
i = j + 1
modified = True
fixed += 1
continue
new_lines.append(lines[i])
i += 1
if modified:
with open(filepath, 'w') as f:
f.writelines(new_lines)
print(f" Fixed {fixed} eslint/FlowFixMe orderings")
PYEOF
###############################################################################
# STEP 11: Final format after all fixes
###############################################################################
echo ""
echo "--- Step 11: Final format pass ---"
npm run format 2>&1 || true
echo " Done."
echo ""
echo "=== Flow Migration Fix Script Complete ==="
echo "Run 'npm run flow' and 'npm run build' to verify."