mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Merge 67c687ccda into sapling-pr-archive-josephsavona
This commit is contained in:
@@ -7,7 +7,6 @@
|
||||
|
||||
import {Resizable} from 're-resizable';
|
||||
import React, {
|
||||
useCallback,
|
||||
useId,
|
||||
unstable_ViewTransition as ViewTransition,
|
||||
unstable_addTransitionType as addTransitionType,
|
||||
@@ -66,7 +65,7 @@ function AccordionWindowItem({
|
||||
|
||||
const transitionName = `accordion-window-item-${id}`;
|
||||
|
||||
const toggleTabs = () => {
|
||||
const toggleTabs = (): void => {
|
||||
startTransition(() => {
|
||||
addTransitionType(EXPAND_ACCORDION_TRANSITION);
|
||||
const nextState = new Set(tabsOpen);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
import MonacoEditor, {loader, type Monaco} from '@monaco-editor/react';
|
||||
import {PluginOptions} from 'babel-plugin-react-compiler';
|
||||
import type {editor} from 'monaco-editor';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import React, {
|
||||
@@ -18,9 +17,8 @@ import React, {
|
||||
} from 'react';
|
||||
import {Resizable} from 're-resizable';
|
||||
import {useStore, useStoreDispatch} from '../StoreContext';
|
||||
import {monacoOptions} from './monacoOptions';
|
||||
import {monacoConfigOptions} from './monacoOptions';
|
||||
import {IconChevron} from '../Icons/IconChevron';
|
||||
import prettyFormat from 'pretty-format';
|
||||
import {CONFIG_PANEL_TRANSITION} from '../../lib/transitionTypes';
|
||||
|
||||
// @ts-expect-error - webpack asset/source loader handles .d.ts files as strings
|
||||
@@ -29,9 +27,9 @@ import compilerTypeDefs from 'babel-plugin-react-compiler/dist/index.d.ts';
|
||||
loader.config({monaco});
|
||||
|
||||
export default function ConfigEditor({
|
||||
appliedOptions,
|
||||
formattedAppliedConfig,
|
||||
}: {
|
||||
appliedOptions: PluginOptions | null;
|
||||
formattedAppliedConfig: string;
|
||||
}): React.ReactElement {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
@@ -49,7 +47,7 @@ export default function ConfigEditor({
|
||||
setIsExpanded(false);
|
||||
});
|
||||
}}
|
||||
appliedOptions={appliedOptions}
|
||||
formattedAppliedConfig={formattedAppliedConfig}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
@@ -71,10 +69,10 @@ export default function ConfigEditor({
|
||||
|
||||
function ExpandedEditor({
|
||||
onToggle,
|
||||
appliedOptions,
|
||||
formattedAppliedConfig,
|
||||
}: {
|
||||
onToggle: () => void;
|
||||
appliedOptions: PluginOptions | null;
|
||||
onToggle: (expanded: boolean) => void;
|
||||
formattedAppliedConfig: string;
|
||||
}): React.ReactElement {
|
||||
const store = useStore();
|
||||
const dispatchStore = useStoreDispatch();
|
||||
@@ -122,13 +120,6 @@ function ExpandedEditor({
|
||||
});
|
||||
};
|
||||
|
||||
const formattedAppliedOptions = appliedOptions
|
||||
? prettyFormat(appliedOptions, {
|
||||
printFunctionName: false,
|
||||
printBasicPrototype: false,
|
||||
})
|
||||
: 'Invalid configs';
|
||||
|
||||
return (
|
||||
<ViewTransition
|
||||
update={{[CONFIG_PANEL_TRANSITION]: 'slide-in', default: 'none'}}>
|
||||
@@ -158,7 +149,7 @@ function ExpandedEditor({
|
||||
Config Overrides
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex-1 rounded-lg overflow-hidden border border-gray-300">
|
||||
<div className="flex-1 border border-gray-300">
|
||||
<MonacoEditor
|
||||
path={'config.ts'}
|
||||
language={'typescript'}
|
||||
@@ -167,16 +158,7 @@ function ExpandedEditor({
|
||||
onChange={handleChange}
|
||||
loading={''}
|
||||
className="monaco-editor-config"
|
||||
options={{
|
||||
...monacoOptions,
|
||||
lineNumbers: 'off',
|
||||
renderLineHighlight: 'none',
|
||||
overviewRulerBorder: false,
|
||||
overviewRulerLanes: 0,
|
||||
fontSize: 12,
|
||||
scrollBeyondLastLine: false,
|
||||
glyphMargin: false,
|
||||
}}
|
||||
options={monacoConfigOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -186,23 +168,16 @@ function ExpandedEditor({
|
||||
Applied Configs
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex-1 rounded-lg overflow-hidden border border-gray-300">
|
||||
<div className="flex-1 border border-gray-300">
|
||||
<MonacoEditor
|
||||
path={'applied-config.js'}
|
||||
language={'javascript'}
|
||||
value={formattedAppliedOptions}
|
||||
value={formattedAppliedConfig}
|
||||
loading={''}
|
||||
className="monaco-editor-applied-config"
|
||||
options={{
|
||||
...monacoOptions,
|
||||
lineNumbers: 'off',
|
||||
renderLineHighlight: 'none',
|
||||
overviewRulerBorder: false,
|
||||
overviewRulerLanes: 0,
|
||||
fontSize: 12,
|
||||
scrollBeyondLastLine: false,
|
||||
...monacoConfigOptions,
|
||||
readOnly: true,
|
||||
glyphMargin: false,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -5,316 +5,17 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {parse as babelParse, ParseResult} from '@babel/parser';
|
||||
import * as HermesParser from 'hermes-parser';
|
||||
import * as t from '@babel/types';
|
||||
import BabelPluginReactCompiler, {
|
||||
CompilerError,
|
||||
import {
|
||||
CompilerErrorDetail,
|
||||
CompilerDiagnostic,
|
||||
Effect,
|
||||
ErrorCategory,
|
||||
parseConfigPragmaForTests,
|
||||
ValueKind,
|
||||
type Hook,
|
||||
PluginOptions,
|
||||
CompilerPipelineValue,
|
||||
parsePluginOptions,
|
||||
printReactiveFunctionWithOutlined,
|
||||
printFunctionWithOutlined,
|
||||
type LoggerEvent,
|
||||
} from 'babel-plugin-react-compiler';
|
||||
import {
|
||||
useDeferredValue,
|
||||
useMemo,
|
||||
unstable_ViewTransition as ViewTransition,
|
||||
} from 'react';
|
||||
import {useDeferredValue, useMemo, useState} from 'react';
|
||||
import {useStore} from '../StoreContext';
|
||||
import ConfigEditor from './ConfigEditor';
|
||||
import Input from './Input';
|
||||
import {
|
||||
CompilerOutput,
|
||||
CompilerTransformOutput,
|
||||
default as Output,
|
||||
PrintedCompilerPipelineValue,
|
||||
} from './Output';
|
||||
import {transformFromAstSync} from '@babel/core';
|
||||
|
||||
function parseInput(
|
||||
input: string,
|
||||
language: 'flow' | 'typescript',
|
||||
): ParseResult<t.File> {
|
||||
// Extract the first line to quickly check for custom test directives
|
||||
if (language === 'flow') {
|
||||
return HermesParser.parse(input, {
|
||||
babel: true,
|
||||
flow: 'all',
|
||||
sourceType: 'module',
|
||||
enableExperimentalComponentSyntax: true,
|
||||
});
|
||||
} else {
|
||||
return babelParse(input, {
|
||||
plugins: ['typescript', 'jsx'],
|
||||
sourceType: 'module',
|
||||
}) as ParseResult<t.File>;
|
||||
}
|
||||
}
|
||||
|
||||
function invokeCompiler(
|
||||
source: string,
|
||||
language: 'flow' | 'typescript',
|
||||
options: PluginOptions,
|
||||
): CompilerTransformOutput {
|
||||
const ast = parseInput(source, language);
|
||||
let result = transformFromAstSync(ast, source, {
|
||||
filename: '_playgroundFile.js',
|
||||
highlightCode: false,
|
||||
retainLines: true,
|
||||
plugins: [[BabelPluginReactCompiler, options]],
|
||||
ast: true,
|
||||
sourceType: 'module',
|
||||
configFile: false,
|
||||
sourceMaps: true,
|
||||
babelrc: false,
|
||||
});
|
||||
if (result?.ast == null || result?.code == null || result?.map == null) {
|
||||
throw new Error('Expected successful compilation');
|
||||
}
|
||||
return {
|
||||
code: result.code,
|
||||
sourceMaps: result.map,
|
||||
language,
|
||||
};
|
||||
}
|
||||
|
||||
const COMMON_HOOKS: Array<[string, Hook]> = [
|
||||
[
|
||||
'useFragment',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
noAlias: true,
|
||||
transitiveMixedData: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'usePaginationFragment',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
noAlias: true,
|
||||
transitiveMixedData: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'useRefetchableFragment',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
noAlias: true,
|
||||
transitiveMixedData: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'useLazyLoadQuery',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
noAlias: true,
|
||||
transitiveMixedData: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'usePreloadedQuery',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
noAlias: true,
|
||||
transitiveMixedData: true,
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
function parseOptions(
|
||||
source: string,
|
||||
mode: 'compiler' | 'linter',
|
||||
configOverrides: string,
|
||||
): PluginOptions {
|
||||
// Extract the first line to quickly check for custom test directives
|
||||
const pragma = source.substring(0, source.indexOf('\n'));
|
||||
|
||||
const parsedPragmaOptions = parseConfigPragmaForTests(pragma, {
|
||||
compilationMode: 'infer',
|
||||
environment:
|
||||
mode === 'linter'
|
||||
? {
|
||||
// enabled in compiler
|
||||
validateRefAccessDuringRender: false,
|
||||
// enabled in linter
|
||||
validateNoSetStateInRender: true,
|
||||
validateNoSetStateInEffects: true,
|
||||
validateNoJSXInTryStatements: true,
|
||||
validateNoImpureFunctionsInRender: true,
|
||||
validateStaticComponents: true,
|
||||
validateNoFreezingKnownMutableFunctions: true,
|
||||
validateNoVoidUseMemo: true,
|
||||
}
|
||||
: {
|
||||
/* use defaults for compiler mode */
|
||||
},
|
||||
});
|
||||
|
||||
// Parse config overrides from config editor
|
||||
let configOverrideOptions: any = {};
|
||||
const configMatch = configOverrides.match(/^\s*import.*?\n\n\((.*)\)/s);
|
||||
if (configOverrides.trim()) {
|
||||
if (configMatch && configMatch[1]) {
|
||||
const configString = configMatch[1].replace(/satisfies.*$/, '').trim();
|
||||
configOverrideOptions = new Function(`return (${configString})`)();
|
||||
} else {
|
||||
throw new Error('Invalid override format');
|
||||
}
|
||||
}
|
||||
|
||||
const opts: PluginOptions = parsePluginOptions({
|
||||
...parsedPragmaOptions,
|
||||
...configOverrideOptions,
|
||||
environment: {
|
||||
...parsedPragmaOptions.environment,
|
||||
...configOverrideOptions.environment,
|
||||
customHooks: new Map([...COMMON_HOOKS]),
|
||||
},
|
||||
});
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
function compile(
|
||||
source: string,
|
||||
mode: 'compiler' | 'linter',
|
||||
configOverrides: string,
|
||||
): [CompilerOutput, 'flow' | 'typescript', PluginOptions | null] {
|
||||
const results = new Map<string, Array<PrintedCompilerPipelineValue>>();
|
||||
const error = new CompilerError();
|
||||
const otherErrors: Array<CompilerErrorDetail | CompilerDiagnostic> = [];
|
||||
const upsert: (result: PrintedCompilerPipelineValue) => void = result => {
|
||||
const entry = results.get(result.name);
|
||||
if (Array.isArray(entry)) {
|
||||
entry.push(result);
|
||||
} else {
|
||||
results.set(result.name, [result]);
|
||||
}
|
||||
};
|
||||
let language: 'flow' | 'typescript';
|
||||
if (source.match(/\@flow/)) {
|
||||
language = 'flow';
|
||||
} else {
|
||||
language = 'typescript';
|
||||
}
|
||||
let transformOutput;
|
||||
|
||||
let baseOpts: PluginOptions | null = null;
|
||||
try {
|
||||
baseOpts = parseOptions(source, mode, configOverrides);
|
||||
} catch (err) {
|
||||
error.details.push(
|
||||
new CompilerErrorDetail({
|
||||
category: ErrorCategory.Config,
|
||||
reason: `Unexpected failure when transforming configs! \n${err}`,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (baseOpts) {
|
||||
try {
|
||||
const logIR = (result: CompilerPipelineValue): void => {
|
||||
switch (result.kind) {
|
||||
case 'ast': {
|
||||
break;
|
||||
}
|
||||
case 'hir': {
|
||||
upsert({
|
||||
kind: 'hir',
|
||||
fnName: result.value.id,
|
||||
name: result.name,
|
||||
value: printFunctionWithOutlined(result.value),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'reactive': {
|
||||
upsert({
|
||||
kind: 'reactive',
|
||||
fnName: result.value.id,
|
||||
name: result.name,
|
||||
value: printReactiveFunctionWithOutlined(result.value),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'debug': {
|
||||
upsert({
|
||||
kind: 'debug',
|
||||
fnName: null,
|
||||
name: result.name,
|
||||
value: result.value,
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const _: never = result;
|
||||
throw new Error(`Unhandled result ${result}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Add logger options to the parsed options
|
||||
const opts = {
|
||||
...baseOpts,
|
||||
logger: {
|
||||
debugLogIRs: logIR,
|
||||
logEvent: (_filename: string | null, event: LoggerEvent): void => {
|
||||
if (event.kind === 'CompileError') {
|
||||
otherErrors.push(event.detail);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
transformOutput = invokeCompiler(source, language, opts);
|
||||
} catch (err) {
|
||||
/**
|
||||
* error might be an invariant violation or other runtime error
|
||||
* (i.e. object shape that is not CompilerError)
|
||||
*/
|
||||
if (err instanceof CompilerError && err.details.length > 0) {
|
||||
error.merge(err);
|
||||
} else {
|
||||
/**
|
||||
* Handle unexpected failures by logging (to get a stack trace)
|
||||
* and reporting
|
||||
*/
|
||||
error.details.push(
|
||||
new CompilerErrorDetail({
|
||||
category: ErrorCategory.Invariant,
|
||||
reason: `Unexpected failure when transforming input! \n${err}`,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only include logger errors if there weren't other errors
|
||||
if (!error.hasErrors() && otherErrors.length !== 0) {
|
||||
otherErrors.forEach(e => error.details.push(e));
|
||||
}
|
||||
if (error.hasErrors()) {
|
||||
return [{kind: 'err', results, error}, language, baseOpts];
|
||||
}
|
||||
return [
|
||||
{kind: 'ok', results, transformOutput, errors: error.details},
|
||||
language,
|
||||
baseOpts,
|
||||
];
|
||||
}
|
||||
import {CompilerOutput, default as Output} from './Output';
|
||||
import {compile} from '../../lib/compilation';
|
||||
import prettyFormat from 'pretty-format';
|
||||
|
||||
export default function Editor(): JSX.Element {
|
||||
const store = useStore();
|
||||
@@ -327,6 +28,7 @@ export default function Editor(): JSX.Element {
|
||||
() => compile(deferredStore.source, 'linter', deferredStore.config),
|
||||
[deferredStore.source, deferredStore.config],
|
||||
);
|
||||
const [formattedAppliedConfig, setFormattedAppliedConfig] = useState('');
|
||||
|
||||
let mergedOutput: CompilerOutput;
|
||||
let errors: Array<CompilerErrorDetail | CompilerDiagnostic>;
|
||||
@@ -340,11 +42,22 @@ export default function Editor(): JSX.Element {
|
||||
mergedOutput = compilerOutput;
|
||||
errors = compilerOutput.error.details;
|
||||
}
|
||||
|
||||
if (appliedOptions) {
|
||||
const formatted = prettyFormat(appliedOptions, {
|
||||
printFunctionName: false,
|
||||
printBasicPrototype: false,
|
||||
});
|
||||
if (formatted !== formattedAppliedConfig) {
|
||||
setFormattedAppliedConfig(formatted);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex top-14">
|
||||
<div className="flex-shrink-0">
|
||||
<ConfigEditor appliedOptions={appliedOptions} />
|
||||
<ConfigEditor formattedAppliedConfig={formattedAppliedConfig} />
|
||||
</div>
|
||||
<div className="flex flex-1 min-w-0">
|
||||
<Input language={language} errors={errors} />
|
||||
|
||||
@@ -372,12 +372,18 @@ function TextTabContent({
|
||||
loading={''}
|
||||
options={{
|
||||
...monacoOptions,
|
||||
scrollbar: {
|
||||
vertical: 'hidden',
|
||||
},
|
||||
dimension: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
readOnly: true,
|
||||
lineNumbers: 'off',
|
||||
glyphMargin: false,
|
||||
// Undocumented see https://github.com/Microsoft/vscode/issues/30795#issuecomment-410998882
|
||||
lineDecorationsWidth: 0,
|
||||
lineNumbersMinChars: 0,
|
||||
overviewRulerLanes: 0,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -32,3 +32,14 @@ export const monacoOptions: Partial<EditorProps['options']> = {
|
||||
|
||||
tabSize: 2,
|
||||
};
|
||||
|
||||
export const monacoConfigOptions: Partial<EditorProps['options']> = {
|
||||
...monacoOptions,
|
||||
lineNumbers: 'off',
|
||||
renderLineHighlight: 'none',
|
||||
overviewRulerBorder: false,
|
||||
overviewRulerLanes: 0,
|
||||
fontSize: 12,
|
||||
scrollBeyondLastLine: false,
|
||||
glyphMargin: false,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {parse as babelParse, ParseResult} from '@babel/parser';
|
||||
import * as HermesParser from 'hermes-parser';
|
||||
import * as t from '@babel/types';
|
||||
import BabelPluginReactCompiler, {
|
||||
CompilerError,
|
||||
CompilerErrorDetail,
|
||||
CompilerDiagnostic,
|
||||
Effect,
|
||||
ErrorCategory,
|
||||
parseConfigPragmaForTests,
|
||||
ValueKind,
|
||||
type Hook,
|
||||
PluginOptions,
|
||||
CompilerPipelineValue,
|
||||
parsePluginOptions,
|
||||
printReactiveFunctionWithOutlined,
|
||||
printFunctionWithOutlined,
|
||||
type LoggerEvent,
|
||||
} from 'babel-plugin-react-compiler';
|
||||
import {transformFromAstSync} from '@babel/core';
|
||||
import type {
|
||||
CompilerOutput,
|
||||
CompilerTransformOutput,
|
||||
PrintedCompilerPipelineValue,
|
||||
} from '../components/Editor/Output';
|
||||
|
||||
function parseInput(
|
||||
input: string,
|
||||
language: 'flow' | 'typescript',
|
||||
): ParseResult<t.File> {
|
||||
// Extract the first line to quickly check for custom test directives
|
||||
if (language === 'flow') {
|
||||
return HermesParser.parse(input, {
|
||||
babel: true,
|
||||
flow: 'all',
|
||||
sourceType: 'module',
|
||||
enableExperimentalComponentSyntax: true,
|
||||
});
|
||||
} else {
|
||||
return babelParse(input, {
|
||||
plugins: ['typescript', 'jsx'],
|
||||
sourceType: 'module',
|
||||
}) as ParseResult<t.File>;
|
||||
}
|
||||
}
|
||||
|
||||
function invokeCompiler(
|
||||
source: string,
|
||||
language: 'flow' | 'typescript',
|
||||
options: PluginOptions,
|
||||
): CompilerTransformOutput {
|
||||
const ast = parseInput(source, language);
|
||||
let result = transformFromAstSync(ast, source, {
|
||||
filename: '_playgroundFile.js',
|
||||
highlightCode: false,
|
||||
retainLines: true,
|
||||
plugins: [[BabelPluginReactCompiler, options]],
|
||||
ast: true,
|
||||
sourceType: 'module',
|
||||
configFile: false,
|
||||
sourceMaps: true,
|
||||
babelrc: false,
|
||||
});
|
||||
if (result?.ast == null || result?.code == null || result?.map == null) {
|
||||
throw new Error('Expected successful compilation');
|
||||
}
|
||||
return {
|
||||
code: result.code,
|
||||
sourceMaps: result.map,
|
||||
language,
|
||||
};
|
||||
}
|
||||
|
||||
const COMMON_HOOKS: Array<[string, Hook]> = [
|
||||
[
|
||||
'useFragment',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
noAlias: true,
|
||||
transitiveMixedData: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'usePaginationFragment',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
noAlias: true,
|
||||
transitiveMixedData: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'useRefetchableFragment',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
noAlias: true,
|
||||
transitiveMixedData: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'useLazyLoadQuery',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
noAlias: true,
|
||||
transitiveMixedData: true,
|
||||
},
|
||||
],
|
||||
[
|
||||
'usePreloadedQuery',
|
||||
{
|
||||
valueKind: ValueKind.Frozen,
|
||||
effectKind: Effect.Freeze,
|
||||
noAlias: true,
|
||||
transitiveMixedData: true,
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
function parseOptions(
|
||||
source: string,
|
||||
mode: 'compiler' | 'linter',
|
||||
configOverrides: string,
|
||||
): PluginOptions {
|
||||
// Extract the first line to quickly check for custom test directives
|
||||
const pragma = source.substring(0, source.indexOf('\n'));
|
||||
|
||||
const parsedPragmaOptions = parseConfigPragmaForTests(pragma, {
|
||||
compilationMode: 'infer',
|
||||
environment:
|
||||
mode === 'linter'
|
||||
? {
|
||||
// enabled in compiler
|
||||
validateRefAccessDuringRender: false,
|
||||
// enabled in linter
|
||||
validateNoSetStateInRender: true,
|
||||
validateNoSetStateInEffects: true,
|
||||
validateNoJSXInTryStatements: true,
|
||||
validateNoImpureFunctionsInRender: true,
|
||||
validateStaticComponents: true,
|
||||
validateNoFreezingKnownMutableFunctions: true,
|
||||
validateNoVoidUseMemo: true,
|
||||
}
|
||||
: {
|
||||
/* use defaults for compiler mode */
|
||||
},
|
||||
});
|
||||
|
||||
// Parse config overrides from config editor
|
||||
let configOverrideOptions: any = {};
|
||||
const configMatch = configOverrides.match(/^\s*import.*?\n\n\((.*)\)/s);
|
||||
if (configOverrides.trim()) {
|
||||
if (configMatch && configMatch[1]) {
|
||||
const configString = configMatch[1].replace(/satisfies.*$/, '').trim();
|
||||
configOverrideOptions = new Function(`return (${configString})`)();
|
||||
} else {
|
||||
throw new Error('Invalid override format');
|
||||
}
|
||||
}
|
||||
|
||||
const opts: PluginOptions = parsePluginOptions({
|
||||
...parsedPragmaOptions,
|
||||
...configOverrideOptions,
|
||||
environment: {
|
||||
...parsedPragmaOptions.environment,
|
||||
...configOverrideOptions.environment,
|
||||
customHooks: new Map([...COMMON_HOOKS]),
|
||||
},
|
||||
});
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
export function compile(
|
||||
source: string,
|
||||
mode: 'compiler' | 'linter',
|
||||
configOverrides: string,
|
||||
): [CompilerOutput, 'flow' | 'typescript', PluginOptions | null] {
|
||||
const results = new Map<string, Array<PrintedCompilerPipelineValue>>();
|
||||
const error = new CompilerError();
|
||||
const otherErrors: Array<CompilerErrorDetail | CompilerDiagnostic> = [];
|
||||
const upsert: (result: PrintedCompilerPipelineValue) => void = result => {
|
||||
const entry = results.get(result.name);
|
||||
if (Array.isArray(entry)) {
|
||||
entry.push(result);
|
||||
} else {
|
||||
results.set(result.name, [result]);
|
||||
}
|
||||
};
|
||||
let language: 'flow' | 'typescript';
|
||||
if (source.match(/\@flow/)) {
|
||||
language = 'flow';
|
||||
} else {
|
||||
language = 'typescript';
|
||||
}
|
||||
let transformOutput;
|
||||
|
||||
let baseOpts: PluginOptions | null = null;
|
||||
try {
|
||||
baseOpts = parseOptions(source, mode, configOverrides);
|
||||
} catch (err) {
|
||||
error.details.push(
|
||||
new CompilerErrorDetail({
|
||||
category: ErrorCategory.Config,
|
||||
reason: `Unexpected failure when transforming configs! \n${err}`,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (baseOpts) {
|
||||
try {
|
||||
const logIR = (result: CompilerPipelineValue): void => {
|
||||
switch (result.kind) {
|
||||
case 'ast': {
|
||||
break;
|
||||
}
|
||||
case 'hir': {
|
||||
upsert({
|
||||
kind: 'hir',
|
||||
fnName: result.value.id,
|
||||
name: result.name,
|
||||
value: printFunctionWithOutlined(result.value),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'reactive': {
|
||||
upsert({
|
||||
kind: 'reactive',
|
||||
fnName: result.value.id,
|
||||
name: result.name,
|
||||
value: printReactiveFunctionWithOutlined(result.value),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'debug': {
|
||||
upsert({
|
||||
kind: 'debug',
|
||||
fnName: null,
|
||||
name: result.name,
|
||||
value: result.value,
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const _: never = result;
|
||||
throw new Error(`Unhandled result ${result}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Add logger options to the parsed options
|
||||
const opts = {
|
||||
...baseOpts,
|
||||
logger: {
|
||||
debugLogIRs: logIR,
|
||||
logEvent: (_filename: string | null, event: LoggerEvent): void => {
|
||||
if (event.kind === 'CompileError') {
|
||||
otherErrors.push(event.detail);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
transformOutput = invokeCompiler(source, language, opts);
|
||||
} catch (err) {
|
||||
/**
|
||||
* error might be an invariant violation or other runtime error
|
||||
* (i.e. object shape that is not CompilerError)
|
||||
*/
|
||||
if (err instanceof CompilerError && err.details.length > 0) {
|
||||
error.merge(err);
|
||||
} else {
|
||||
/**
|
||||
* Handle unexpected failures by logging (to get a stack trace)
|
||||
* and reporting
|
||||
*/
|
||||
error.details.push(
|
||||
new CompilerErrorDetail({
|
||||
category: ErrorCategory.Invariant,
|
||||
reason: `Unexpected failure when transforming input! \n${err}`,
|
||||
loc: null,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only include logger errors if there weren't other errors
|
||||
if (!error.hasErrors() && otherErrors.length !== 0) {
|
||||
otherErrors.forEach(e => error.details.push(e));
|
||||
}
|
||||
if (error.hasErrors()) {
|
||||
return [{kind: 'err', results, error}, language, baseOpts];
|
||||
}
|
||||
return [
|
||||
{kind: 'ok', results, transformOutput, errors: error.details},
|
||||
language,
|
||||
baseOpts,
|
||||
];
|
||||
}
|
||||
@@ -124,3 +124,11 @@
|
||||
::view-transition-group(.expand-accordion) {
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
/**
|
||||
* For some reason, the original Monaco editor is still visible to the
|
||||
* left of the DiffEditor. This is a workaround for better visual clarity.
|
||||
*/
|
||||
.monaco-diff-editor .editor.original{
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {NoUnusedDirectivesRule} from '../src/rules/ReactCompilerRule';
|
||||
import {normalizeIndent, testRule} from './shared-utils';
|
||||
|
||||
testRule('no unused directives rule', NoUnusedDirectivesRule, {
|
||||
valid: [],
|
||||
invalid: [
|
||||
{
|
||||
name: "Unused 'use no forget' directive is reported when no errors are present on components",
|
||||
code: normalizeIndent`
|
||||
function Component() {
|
||||
'use no forget';
|
||||
return <div>Hello world</div>
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: "Unused 'use no forget' directive",
|
||||
suggestions: [
|
||||
{
|
||||
output:
|
||||
// yuck
|
||||
'\nfunction Component() {\n \n return <div>Hello world</div>\n}\n',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Unused 'use no forget' directive is reported when no errors are present on non-components or hooks",
|
||||
code: normalizeIndent`
|
||||
function notacomponent() {
|
||||
'use no forget';
|
||||
return 1 + 1;
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: "Unused 'use no forget' directive",
|
||||
suggestions: [
|
||||
{
|
||||
output:
|
||||
// yuck
|
||||
'\nfunction notacomponent() {\n \n return 1 + 1;\n}\n',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -161,69 +161,21 @@ function makeRule(rule: LintRule): Rule.RuleModule {
|
||||
};
|
||||
}
|
||||
|
||||
export const NoUnusedDirectivesRule: Rule.RuleModule = {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
recommended: true,
|
||||
},
|
||||
fixable: 'code',
|
||||
hasSuggestions: true,
|
||||
// validation is done at runtime with zod
|
||||
schema: [{type: 'object', additionalProperties: true}],
|
||||
},
|
||||
create(context: Rule.RuleContext): Rule.RuleListener {
|
||||
const results = getReactCompilerResult(context);
|
||||
|
||||
for (const directive of results.unusedOptOutDirectives) {
|
||||
context.report({
|
||||
message: `Unused '${directive.directive}' directive`,
|
||||
loc: directive.loc,
|
||||
suggest: [
|
||||
{
|
||||
desc: 'Remove the directive',
|
||||
fix(fixer): Rule.Fix {
|
||||
return fixer.removeRange(directive.range);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
return {};
|
||||
},
|
||||
};
|
||||
|
||||
type RulesConfig = {
|
||||
[name: string]: {rule: Rule.RuleModule; severity: ErrorSeverity};
|
||||
};
|
||||
|
||||
export const allRules: RulesConfig = LintRules.reduce(
|
||||
(acc, rule) => {
|
||||
acc[rule.name] = {rule: makeRule(rule), severity: rule.severity};
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
'no-unused-directives': {
|
||||
rule: NoUnusedDirectivesRule,
|
||||
severity: ErrorSeverity.Error,
|
||||
},
|
||||
} as RulesConfig,
|
||||
);
|
||||
export const allRules: RulesConfig = LintRules.reduce((acc, rule) => {
|
||||
acc[rule.name] = {rule: makeRule(rule), severity: rule.severity};
|
||||
return acc;
|
||||
}, {} as RulesConfig);
|
||||
|
||||
export const recommendedRules: RulesConfig = LintRules.filter(
|
||||
rule => rule.recommended,
|
||||
).reduce(
|
||||
(acc, rule) => {
|
||||
acc[rule.name] = {rule: makeRule(rule), severity: rule.severity};
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
'no-unused-directives': {
|
||||
rule: NoUnusedDirectivesRule,
|
||||
severity: ErrorSeverity.Error,
|
||||
},
|
||||
} as RulesConfig,
|
||||
);
|
||||
).reduce((acc, rule) => {
|
||||
acc[rule.name] = {rule: makeRule(rule), severity: rule.severity};
|
||||
return acc;
|
||||
}, {} as RulesConfig);
|
||||
|
||||
export function mapErrorSeverityToESlint(
|
||||
severity: ErrorSeverity,
|
||||
|
||||
@@ -5,20 +5,18 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {transformFromAstSync, traverse} from '@babel/core';
|
||||
import {transformFromAstSync} from '@babel/core';
|
||||
import {parse as babelParse} from '@babel/parser';
|
||||
import {Directive, File} from '@babel/types';
|
||||
import {File} from '@babel/types';
|
||||
// @ts-expect-error: no types available
|
||||
import PluginProposalPrivateMethods from '@babel/plugin-proposal-private-methods';
|
||||
import BabelPluginReactCompiler, {
|
||||
parsePluginOptions,
|
||||
validateEnvironmentConfig,
|
||||
OPT_OUT_DIRECTIVES,
|
||||
type PluginOptions,
|
||||
} from 'babel-plugin-react-compiler/src';
|
||||
import {Logger, LoggerEvent} from 'babel-plugin-react-compiler/src/Entrypoint';
|
||||
import type {SourceCode} from 'eslint';
|
||||
import {SourceLocation} from 'estree';
|
||||
// @ts-expect-error: no types available
|
||||
import * as HermesParser from 'hermes-parser';
|
||||
import {isDeepStrictEqual} from 'util';
|
||||
@@ -45,17 +43,11 @@ const COMPILER_OPTIONS: PluginOptions = {
|
||||
}),
|
||||
};
|
||||
|
||||
export type UnusedOptOutDirective = {
|
||||
loc: SourceLocation;
|
||||
range: [number, number];
|
||||
directive: string;
|
||||
};
|
||||
export type RunCacheEntry = {
|
||||
sourceCode: string;
|
||||
filename: string;
|
||||
userOpts: PluginOptions;
|
||||
flowSuppressions: Array<{line: number; code: string}>;
|
||||
unusedOptOutDirectives: Array<UnusedOptOutDirective>;
|
||||
events: Array<LoggerEvent>;
|
||||
};
|
||||
|
||||
@@ -87,25 +79,6 @@ function getFlowSuppressions(
|
||||
return results;
|
||||
}
|
||||
|
||||
function filterUnusedOptOutDirectives(
|
||||
directives: ReadonlyArray<Directive>,
|
||||
): Array<UnusedOptOutDirective> {
|
||||
const results: Array<UnusedOptOutDirective> = [];
|
||||
for (const directive of directives) {
|
||||
if (
|
||||
OPT_OUT_DIRECTIVES.has(directive.value.value) &&
|
||||
directive.loc != null
|
||||
) {
|
||||
results.push({
|
||||
loc: directive.loc,
|
||||
directive: directive.value.value,
|
||||
range: [directive.start!, directive.end!],
|
||||
});
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function runReactCompilerImpl({
|
||||
sourceCode,
|
||||
filename,
|
||||
@@ -125,7 +98,6 @@ function runReactCompilerImpl({
|
||||
filename,
|
||||
userOpts,
|
||||
flowSuppressions: [],
|
||||
unusedOptOutDirectives: [],
|
||||
events: [],
|
||||
};
|
||||
const userLogger: Logger | null = options.logger;
|
||||
@@ -181,29 +153,6 @@ function runReactCompilerImpl({
|
||||
configFile: false,
|
||||
babelrc: false,
|
||||
});
|
||||
|
||||
if (results.events.filter(e => e.kind === 'CompileError').length === 0) {
|
||||
traverse(babelAST, {
|
||||
FunctionDeclaration(path) {
|
||||
path.node;
|
||||
results.unusedOptOutDirectives.push(
|
||||
...filterUnusedOptOutDirectives(path.node.body.directives),
|
||||
);
|
||||
},
|
||||
ArrowFunctionExpression(path) {
|
||||
if (path.node.body.type === 'BlockStatement') {
|
||||
results.unusedOptOutDirectives.push(
|
||||
...filterUnusedOptOutDirectives(path.node.body.directives),
|
||||
);
|
||||
}
|
||||
},
|
||||
FunctionExpression(path) {
|
||||
results.unusedOptOutDirectives.push(
|
||||
...filterUnusedOptOutDirectives(path.node.body.directives),
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
/* errors handled by injected logger */
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": ["plugin:react-hooks/recommended-legacy"],
|
||||
"extends": ["plugin:react-hooks/recommended-latest-legacy"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"react-hooks/exhaustive-deps": "error"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,3 +141,27 @@ function useHookInLoops() {
|
||||
useHook4();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiler Rules
|
||||
*/
|
||||
// Invalid: component factory
|
||||
function InvalidComponentFactory() {
|
||||
const DynamicComponent = () => <div>Hello</div>;
|
||||
// eslint-disable-next-line react-hooks/static-components
|
||||
return <DynamicComponent />;
|
||||
}
|
||||
|
||||
// Invalid: mutating globals
|
||||
function InvalidGlobals() {
|
||||
// eslint-disable-next-line react-hooks/immutability
|
||||
window.myGlobal = 42;
|
||||
return <div>Done</div>;
|
||||
}
|
||||
|
||||
// Invalid: useMemo with wrong deps - triggers preserve-manual-memoization
|
||||
function InvalidUseMemo({items}) {
|
||||
// eslint-disable-next-line react-hooks/preserve-manual-memoization, react-hooks/exhaustive-deps
|
||||
const sorted = useMemo(() => [...items].sort(), []);
|
||||
return <div>{sorted.length}</div>;
|
||||
}
|
||||
|
||||
@@ -2,15 +2,7 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@ampproject/remapping@^2.2.0":
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
|
||||
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.26.2":
|
||||
"@babel/code-frame@^7.0.0":
|
||||
version "7.26.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
|
||||
integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
|
||||
@@ -19,228 +11,11 @@
|
||||
js-tokens "^4.0.0"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
"@babel/compat-data@^7.26.5":
|
||||
version "7.26.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367"
|
||||
integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==
|
||||
|
||||
"@babel/core@^7.24.4":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9"
|
||||
integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.2.0"
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/generator" "^7.26.10"
|
||||
"@babel/helper-compilation-targets" "^7.26.5"
|
||||
"@babel/helper-module-transforms" "^7.26.0"
|
||||
"@babel/helpers" "^7.26.10"
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/traverse" "^7.26.10"
|
||||
"@babel/types" "^7.26.10"
|
||||
convert-source-map "^2.0.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.2"
|
||||
json5 "^2.2.3"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/generator@^7.26.10":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7"
|
||||
integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/types" "^7.26.10"
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.25"
|
||||
jsesc "^3.0.2"
|
||||
|
||||
"@babel/helper-annotate-as-pure@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4"
|
||||
integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==
|
||||
dependencies:
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-compilation-targets@^7.26.5":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8"
|
||||
integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.26.5"
|
||||
"@babel/helper-validator-option" "^7.25.9"
|
||||
browserslist "^4.24.0"
|
||||
lru-cache "^5.1.1"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.25.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71"
|
||||
integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.25.9"
|
||||
"@babel/helper-member-expression-to-functions" "^7.25.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.25.9"
|
||||
"@babel/helper-replace-supers" "^7.26.5"
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
|
||||
"@babel/traverse" "^7.26.9"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/helper-member-expression-to-functions@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3"
|
||||
integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-module-imports@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
|
||||
integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-module-transforms@^7.26.0":
|
||||
version "7.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae"
|
||||
integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
"@babel/traverse" "^7.25.9"
|
||||
|
||||
"@babel/helper-optimise-call-expression@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e"
|
||||
integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==
|
||||
dependencies:
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-plugin-utils@^7.25.9":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35"
|
||||
integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==
|
||||
|
||||
"@babel/helper-replace-supers@^7.26.5":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d"
|
||||
integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==
|
||||
dependencies:
|
||||
"@babel/helper-member-expression-to-functions" "^7.25.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.25.9"
|
||||
"@babel/traverse" "^7.26.5"
|
||||
|
||||
"@babel/helper-skip-transparent-expression-wrappers@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9"
|
||||
integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-string-parser@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
|
||||
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
|
||||
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
|
||||
|
||||
"@babel/helper-validator-option@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72"
|
||||
integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==
|
||||
|
||||
"@babel/helpers@^7.26.10":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.10.tgz#6baea3cd62ec2d0c1068778d63cb1314f6637384"
|
||||
integrity sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==
|
||||
dependencies:
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/types" "^7.26.10"
|
||||
|
||||
"@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749"
|
||||
integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==
|
||||
dependencies:
|
||||
"@babel/types" "^7.26.10"
|
||||
|
||||
"@babel/plugin-transform-private-methods@^7.24.4":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57"
|
||||
integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==
|
||||
dependencies:
|
||||
"@babel/helper-create-class-features-plugin" "^7.25.9"
|
||||
"@babel/helper-plugin-utils" "^7.25.9"
|
||||
|
||||
"@babel/template@^7.26.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2"
|
||||
integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/parser" "^7.26.9"
|
||||
"@babel/types" "^7.26.9"
|
||||
|
||||
"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380"
|
||||
integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/generator" "^7.26.10"
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/types" "^7.26.10"
|
||||
debug "^4.3.1"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259"
|
||||
integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.5":
|
||||
version "0.3.8"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142"
|
||||
integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==
|
||||
dependencies:
|
||||
"@jridgewell/set-array" "^1.2.1"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@jridgewell/resolve-uri@^3.1.0":
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
|
||||
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
|
||||
|
||||
"@jridgewell/set-array@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
|
||||
integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
|
||||
version "0.3.25"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
|
||||
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
acorn-jsx@^5.2.0:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
@@ -317,26 +92,11 @@ brace-expansion@^1.1.7:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
browserslist@^4.24.0:
|
||||
version "4.24.4"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b"
|
||||
integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001688"
|
||||
electron-to-chromium "^1.5.73"
|
||||
node-releases "^2.0.19"
|
||||
update-browserslist-db "^1.1.1"
|
||||
|
||||
callsites@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
caniuse-lite@^1.0.30001688:
|
||||
version "1.0.30001703"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz#977cb4920598c158f491ecf4f4f2cfed9e354718"
|
||||
integrity sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==
|
||||
|
||||
chalk@^2.1.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
@@ -400,11 +160,6 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
convert-source-map@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
||||
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
|
||||
|
||||
cross-spawn@^6.0.5:
|
||||
version "6.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57"
|
||||
@@ -416,7 +171,7 @@ cross-spawn@^6.0.5:
|
||||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
debug@^4.0.1, debug@^4.1.0, debug@^4.3.1:
|
||||
debug@^4.0.1:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
|
||||
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
||||
@@ -435,11 +190,6 @@ doctrine@^3.0.0:
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
electron-to-chromium@^1.5.73:
|
||||
version "1.5.114"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz#f2bb4fda80a7db4ea273565e75b0ebbe19af0ac3"
|
||||
integrity sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA==
|
||||
|
||||
emoji-regex@^7.0.1:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
|
||||
@@ -450,11 +200,6 @@ emoji-regex@^8.0.0:
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||
|
||||
escalade@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
|
||||
integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
|
||||
|
||||
escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
@@ -632,11 +377,6 @@ functional-red-black-tree@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||
integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==
|
||||
|
||||
gensync@^1.0.0-beta.2:
|
||||
version "1.0.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||
|
||||
glob-parent@^5.0.0:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||
@@ -656,11 +396,6 @@ glob@^7.1.3:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
globals@^11.1.0:
|
||||
version "11.12.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||
|
||||
globals@^12.1.0:
|
||||
version "12.4.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8"
|
||||
@@ -678,18 +413,6 @@ has-flag@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
|
||||
hermes-estree@0.25.1:
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480"
|
||||
integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==
|
||||
|
||||
hermes-parser@^0.25.1:
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1"
|
||||
integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==
|
||||
dependencies:
|
||||
hermes-estree "0.25.1"
|
||||
|
||||
iconv-lite@^0.4.24:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
@@ -787,11 +510,6 @@ js-yaml@^3.13.1:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
jsesc@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
|
||||
integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
|
||||
|
||||
json-schema-traverse@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
@@ -802,11 +520,6 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
||||
|
||||
json5@^2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
|
||||
levn@^0.3.0, levn@~0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
|
||||
@@ -820,13 +533,6 @@ lodash@^4.17.14, lodash@^4.17.19:
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
|
||||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
mimic-fn@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
@@ -871,11 +577,6 @@ nice-try@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
node-releases@^2.0.19:
|
||||
version "2.0.19"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314"
|
||||
integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
@@ -924,7 +625,7 @@ path-key@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
|
||||
integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==
|
||||
|
||||
picocolors@^1.0.0, picocolors@^1.1.1:
|
||||
picocolors@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
@@ -991,7 +692,7 @@ semver@^5.5.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
|
||||
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
|
||||
|
||||
semver@^6.1.2, semver@^6.3.1:
|
||||
semver@^6.1.2:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
@@ -1127,14 +828,6 @@ type-fest@^0.8.1:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
|
||||
|
||||
update-browserslist-db@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
|
||||
integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==
|
||||
dependencies:
|
||||
escalade "^3.2.0"
|
||||
picocolors "^1.1.1"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
@@ -1170,18 +863,3 @@ write@1.0.3:
|
||||
integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
|
||||
dependencies:
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
yallist@^3.0.2:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||
|
||||
zod-validation-error@^3.0.3:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6"
|
||||
integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==
|
||||
|
||||
zod@^3.22.4:
|
||||
version "3.24.2"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3"
|
||||
integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": ["plugin:react-hooks/recommended-legacy"],
|
||||
"extends": ["plugin:react-hooks/recommended-latest-legacy"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"react-hooks/exhaustive-deps": "error"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,3 +141,27 @@ function useHookInLoops() {
|
||||
useHook4();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiler Rules
|
||||
*/
|
||||
// Invalid: component factory
|
||||
function InvalidComponentFactory() {
|
||||
const DynamicComponent = () => <div>Hello</div>;
|
||||
// eslint-disable-next-line react-hooks/static-components
|
||||
return <DynamicComponent />;
|
||||
}
|
||||
|
||||
// Invalid: mutating globals
|
||||
function InvalidGlobals() {
|
||||
// eslint-disable-next-line react-hooks/immutability
|
||||
window.myGlobal = 42;
|
||||
return <div>Done</div>;
|
||||
}
|
||||
|
||||
// Invalid: useMemo with wrong deps - triggers preserve-manual-memoization
|
||||
function InvalidUseMemo({items}) {
|
||||
// eslint-disable-next-line react-hooks/preserve-manual-memoization, react-hooks/exhaustive-deps
|
||||
const sorted = useMemo(() => [...items].sort(), []);
|
||||
return <div>{sorted.length}</div>;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@ampproject/remapping@^2.2.0":
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
|
||||
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@babel/code-frame@7.12.11":
|
||||
version "7.12.11"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
|
||||
@@ -17,160 +9,11 @@
|
||||
dependencies:
|
||||
"@babel/highlight" "^7.10.4"
|
||||
|
||||
"@babel/code-frame@^7.26.2":
|
||||
version "7.26.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
|
||||
integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
js-tokens "^4.0.0"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
"@babel/compat-data@^7.26.5":
|
||||
version "7.26.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367"
|
||||
integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==
|
||||
|
||||
"@babel/core@^7.24.4":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9"
|
||||
integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.2.0"
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/generator" "^7.26.10"
|
||||
"@babel/helper-compilation-targets" "^7.26.5"
|
||||
"@babel/helper-module-transforms" "^7.26.0"
|
||||
"@babel/helpers" "^7.26.10"
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/traverse" "^7.26.10"
|
||||
"@babel/types" "^7.26.10"
|
||||
convert-source-map "^2.0.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.2"
|
||||
json5 "^2.2.3"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/generator@^7.26.10":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7"
|
||||
integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/types" "^7.26.10"
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.25"
|
||||
jsesc "^3.0.2"
|
||||
|
||||
"@babel/helper-annotate-as-pure@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4"
|
||||
integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==
|
||||
dependencies:
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-compilation-targets@^7.26.5":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8"
|
||||
integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.26.5"
|
||||
"@babel/helper-validator-option" "^7.25.9"
|
||||
browserslist "^4.24.0"
|
||||
lru-cache "^5.1.1"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.25.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71"
|
||||
integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.25.9"
|
||||
"@babel/helper-member-expression-to-functions" "^7.25.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.25.9"
|
||||
"@babel/helper-replace-supers" "^7.26.5"
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
|
||||
"@babel/traverse" "^7.26.9"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/helper-member-expression-to-functions@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3"
|
||||
integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-module-imports@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
|
||||
integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-module-transforms@^7.26.0":
|
||||
version "7.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae"
|
||||
integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
"@babel/traverse" "^7.25.9"
|
||||
|
||||
"@babel/helper-optimise-call-expression@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e"
|
||||
integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==
|
||||
dependencies:
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-plugin-utils@^7.25.9":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35"
|
||||
integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==
|
||||
|
||||
"@babel/helper-replace-supers@^7.26.5":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d"
|
||||
integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==
|
||||
dependencies:
|
||||
"@babel/helper-member-expression-to-functions" "^7.25.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.25.9"
|
||||
"@babel/traverse" "^7.26.5"
|
||||
|
||||
"@babel/helper-skip-transparent-expression-wrappers@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9"
|
||||
integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-string-parser@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
|
||||
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
|
||||
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
|
||||
|
||||
"@babel/helper-validator-option@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72"
|
||||
integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==
|
||||
|
||||
"@babel/helpers@^7.26.10":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.10.tgz#6baea3cd62ec2d0c1068778d63cb1314f6637384"
|
||||
integrity sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==
|
||||
dependencies:
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/types" "^7.26.10"
|
||||
|
||||
"@babel/highlight@^7.10.4":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.25.9.tgz#8141ce68fc73757946f983b343f1231f4691acc6"
|
||||
@@ -181,51 +24,6 @@
|
||||
js-tokens "^4.0.0"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
"@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749"
|
||||
integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==
|
||||
dependencies:
|
||||
"@babel/types" "^7.26.10"
|
||||
|
||||
"@babel/plugin-transform-private-methods@^7.24.4":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57"
|
||||
integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==
|
||||
dependencies:
|
||||
"@babel/helper-create-class-features-plugin" "^7.25.9"
|
||||
"@babel/helper-plugin-utils" "^7.25.9"
|
||||
|
||||
"@babel/template@^7.26.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2"
|
||||
integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/parser" "^7.26.9"
|
||||
"@babel/types" "^7.26.9"
|
||||
|
||||
"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380"
|
||||
integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/generator" "^7.26.10"
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/types" "^7.26.10"
|
||||
debug "^4.3.1"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259"
|
||||
integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
|
||||
"@eslint/eslintrc@^0.4.3":
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
|
||||
@@ -255,38 +53,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
||||
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.5":
|
||||
version "0.3.8"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142"
|
||||
integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==
|
||||
dependencies:
|
||||
"@jridgewell/set-array" "^1.2.1"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@jridgewell/resolve-uri@^3.1.0":
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
|
||||
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
|
||||
|
||||
"@jridgewell/set-array@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
|
||||
integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
|
||||
version "0.3.25"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
|
||||
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
acorn-jsx@^5.3.1:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
@@ -366,26 +132,11 @@ brace-expansion@^1.1.7:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
browserslist@^4.24.0:
|
||||
version "4.24.4"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b"
|
||||
integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001688"
|
||||
electron-to-chromium "^1.5.73"
|
||||
node-releases "^2.0.19"
|
||||
update-browserslist-db "^1.1.1"
|
||||
|
||||
callsites@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
caniuse-lite@^1.0.30001688:
|
||||
version "1.0.30001703"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz#977cb4920598c158f491ecf4f4f2cfed9e354718"
|
||||
integrity sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==
|
||||
|
||||
chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
@@ -432,11 +183,6 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
convert-source-map@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
||||
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
|
||||
|
||||
cross-spawn@^7.0.2:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
@@ -446,7 +192,7 @@ cross-spawn@^7.0.2:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
|
||||
debug@^4.0.1, debug@^4.1.1:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
|
||||
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
||||
@@ -465,11 +211,6 @@ doctrine@^3.0.0:
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
electron-to-chromium@^1.5.73:
|
||||
version "1.5.114"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz#f2bb4fda80a7db4ea273565e75b0ebbe19af0ac3"
|
||||
integrity sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA==
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
@@ -483,11 +224,6 @@ enquirer@^2.3.5:
|
||||
ansi-colors "^4.1.1"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
escalade@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
|
||||
integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
|
||||
|
||||
escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
@@ -667,11 +403,6 @@ functional-red-black-tree@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
|
||||
integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==
|
||||
|
||||
gensync@^1.0.0-beta.2:
|
||||
version "1.0.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||
|
||||
glob-parent@^5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||
@@ -691,11 +422,6 @@ glob@^7.1.3:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
globals@^11.1.0:
|
||||
version "11.12.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||
|
||||
globals@^13.6.0, globals@^13.9.0:
|
||||
version "13.24.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
|
||||
@@ -713,18 +439,6 @@ has-flag@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
|
||||
hermes-estree@0.25.1:
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480"
|
||||
integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==
|
||||
|
||||
hermes-parser@^0.25.1:
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1"
|
||||
integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==
|
||||
dependencies:
|
||||
hermes-estree "0.25.1"
|
||||
|
||||
ignore@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||
@@ -791,11 +505,6 @@ js-yaml@^3.13.1:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
jsesc@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
|
||||
integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
|
||||
|
||||
json-buffer@3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||
@@ -816,11 +525,6 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
||||
|
||||
json5@^2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
|
||||
keyv@^4.5.3:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||
@@ -846,13 +550,6 @@ lodash.truncate@^4.4.2:
|
||||
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
|
||||
integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
|
||||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
@@ -870,11 +567,6 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||
|
||||
node-releases@^2.0.19:
|
||||
version "2.0.19"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314"
|
||||
integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
@@ -911,7 +603,7 @@ path-key@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||
|
||||
picocolors@^1.0.0, picocolors@^1.1.1:
|
||||
picocolors@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
@@ -953,11 +645,6 @@ rimraf@^3.0.2:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
semver@^6.3.1:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.2.1:
|
||||
version "7.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
|
||||
@@ -1052,14 +739,6 @@ type-fest@^0.20.2:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
||||
|
||||
update-browserslist-db@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
|
||||
integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==
|
||||
dependencies:
|
||||
escalade "^3.2.0"
|
||||
picocolors "^1.1.1"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
@@ -1088,18 +767,3 @@ wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
yallist@^3.0.2:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||
|
||||
zod-validation-error@^3.0.3:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6"
|
||||
integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==
|
||||
|
||||
zod@^3.22.4:
|
||||
version "3.24.2"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3"
|
||||
integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": ["plugin:react-hooks/recommended-legacy"],
|
||||
"extends": ["plugin:react-hooks/recommended-latest-legacy"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"react-hooks/exhaustive-deps": "error"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,3 +141,27 @@ function useHookInLoops() {
|
||||
useHook4();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiler Rules
|
||||
*/
|
||||
// Invalid: component factory
|
||||
function InvalidComponentFactory() {
|
||||
const DynamicComponent = () => <div>Hello</div>;
|
||||
// eslint-disable-next-line react-hooks/static-components
|
||||
return <DynamicComponent />;
|
||||
}
|
||||
|
||||
// Invalid: mutating globals
|
||||
function InvalidGlobals() {
|
||||
// eslint-disable-next-line react-hooks/immutability
|
||||
window.myGlobal = 42;
|
||||
return <div>Done</div>;
|
||||
}
|
||||
|
||||
// Invalid: useMemo with wrong deps - triggers preserve-manual-memoization
|
||||
function InvalidUseMemo({items}) {
|
||||
// eslint-disable-next-line react-hooks/preserve-manual-memoization, react-hooks/exhaustive-deps
|
||||
const sorted = useMemo(() => [...items].sort(), []);
|
||||
return <div>{sorted.length}</div>;
|
||||
}
|
||||
|
||||
@@ -2,213 +2,6 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@ampproject/remapping@^2.2.0":
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
|
||||
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@babel/code-frame@^7.26.2":
|
||||
version "7.26.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
|
||||
integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
js-tokens "^4.0.0"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
"@babel/compat-data@^7.26.5":
|
||||
version "7.26.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367"
|
||||
integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==
|
||||
|
||||
"@babel/core@^7.24.4":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9"
|
||||
integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.2.0"
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/generator" "^7.26.10"
|
||||
"@babel/helper-compilation-targets" "^7.26.5"
|
||||
"@babel/helper-module-transforms" "^7.26.0"
|
||||
"@babel/helpers" "^7.26.10"
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/traverse" "^7.26.10"
|
||||
"@babel/types" "^7.26.10"
|
||||
convert-source-map "^2.0.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.2"
|
||||
json5 "^2.2.3"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/generator@^7.26.10":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7"
|
||||
integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/types" "^7.26.10"
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.25"
|
||||
jsesc "^3.0.2"
|
||||
|
||||
"@babel/helper-annotate-as-pure@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4"
|
||||
integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==
|
||||
dependencies:
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-compilation-targets@^7.26.5":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8"
|
||||
integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.26.5"
|
||||
"@babel/helper-validator-option" "^7.25.9"
|
||||
browserslist "^4.24.0"
|
||||
lru-cache "^5.1.1"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.25.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71"
|
||||
integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.25.9"
|
||||
"@babel/helper-member-expression-to-functions" "^7.25.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.25.9"
|
||||
"@babel/helper-replace-supers" "^7.26.5"
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
|
||||
"@babel/traverse" "^7.26.9"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/helper-member-expression-to-functions@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3"
|
||||
integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-module-imports@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
|
||||
integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-module-transforms@^7.26.0":
|
||||
version "7.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae"
|
||||
integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
"@babel/traverse" "^7.25.9"
|
||||
|
||||
"@babel/helper-optimise-call-expression@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e"
|
||||
integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==
|
||||
dependencies:
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-plugin-utils@^7.25.9":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35"
|
||||
integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==
|
||||
|
||||
"@babel/helper-replace-supers@^7.26.5":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d"
|
||||
integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==
|
||||
dependencies:
|
||||
"@babel/helper-member-expression-to-functions" "^7.25.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.25.9"
|
||||
"@babel/traverse" "^7.26.5"
|
||||
|
||||
"@babel/helper-skip-transparent-expression-wrappers@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9"
|
||||
integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
|
||||
"@babel/helper-string-parser@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
|
||||
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
|
||||
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
|
||||
|
||||
"@babel/helper-validator-option@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72"
|
||||
integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==
|
||||
|
||||
"@babel/helpers@^7.26.10":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.10.tgz#6baea3cd62ec2d0c1068778d63cb1314f6637384"
|
||||
integrity sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==
|
||||
dependencies:
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/types" "^7.26.10"
|
||||
|
||||
"@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749"
|
||||
integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==
|
||||
dependencies:
|
||||
"@babel/types" "^7.26.10"
|
||||
|
||||
"@babel/plugin-transform-private-methods@^7.24.4":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57"
|
||||
integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==
|
||||
dependencies:
|
||||
"@babel/helper-create-class-features-plugin" "^7.25.9"
|
||||
"@babel/helper-plugin-utils" "^7.25.9"
|
||||
|
||||
"@babel/template@^7.26.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2"
|
||||
integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/parser" "^7.26.9"
|
||||
"@babel/types" "^7.26.9"
|
||||
|
||||
"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380"
|
||||
integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/generator" "^7.26.10"
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/types" "^7.26.10"
|
||||
debug "^4.3.1"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259"
|
||||
integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
|
||||
"@eslint-community/eslint-utils@^4.2.0":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56"
|
||||
@@ -260,38 +53,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
|
||||
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.5":
|
||||
version "0.3.8"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142"
|
||||
integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==
|
||||
dependencies:
|
||||
"@jridgewell/set-array" "^1.2.1"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@jridgewell/resolve-uri@^3.1.0":
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
|
||||
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
|
||||
|
||||
"@jridgewell/set-array@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
|
||||
integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
|
||||
version "0.3.25"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
|
||||
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
@@ -368,26 +129,11 @@ brace-expansion@^1.1.7:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
browserslist@^4.24.0:
|
||||
version "4.24.4"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b"
|
||||
integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001688"
|
||||
electron-to-chromium "^1.5.73"
|
||||
node-releases "^2.0.19"
|
||||
update-browserslist-db "^1.1.1"
|
||||
|
||||
callsites@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
caniuse-lite@^1.0.30001688:
|
||||
version "1.0.30001703"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz#977cb4920598c158f491ecf4f4f2cfed9e354718"
|
||||
integrity sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==
|
||||
|
||||
chalk@^4.0.0:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
@@ -413,11 +159,6 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
convert-source-map@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
||||
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
|
||||
|
||||
cross-spawn@^7.0.2:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
@@ -427,7 +168,7 @@ cross-spawn@^7.0.2:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
debug@^4.1.0, debug@^4.3.1, debug@^4.3.2:
|
||||
debug@^4.3.1, debug@^4.3.2:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
|
||||
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
||||
@@ -446,16 +187,6 @@ doctrine@^3.0.0:
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
electron-to-chromium@^1.5.73:
|
||||
version "1.5.114"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz#f2bb4fda80a7db4ea273565e75b0ebbe19af0ac3"
|
||||
integrity sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA==
|
||||
|
||||
escalade@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
|
||||
integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
|
||||
|
||||
escape-string-regexp@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
@@ -611,11 +342,6 @@ fs.realpath@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
gensync@^1.0.0-beta.2:
|
||||
version "1.0.0-beta.2"
|
||||
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||
|
||||
glob-parent@^6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
|
||||
@@ -635,11 +361,6 @@ glob@^7.1.3:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
globals@^11.1.0:
|
||||
version "11.12.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||
|
||||
globals@^13.19.0:
|
||||
version "13.24.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
|
||||
@@ -657,18 +378,6 @@ has-flag@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
|
||||
hermes-estree@0.25.1:
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480"
|
||||
integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==
|
||||
|
||||
hermes-parser@^0.25.1:
|
||||
version "0.25.1"
|
||||
resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1"
|
||||
integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==
|
||||
dependencies:
|
||||
hermes-estree "0.25.1"
|
||||
|
||||
ignore@^5.2.0:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
|
||||
@@ -722,11 +431,6 @@ isexe@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
@@ -734,11 +438,6 @@ js-yaml@^4.1.0:
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
jsesc@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
|
||||
integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
|
||||
|
||||
json-buffer@3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||
@@ -754,11 +453,6 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
|
||||
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
||||
|
||||
json5@^2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
|
||||
keyv@^4.5.3:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||
@@ -786,13 +480,6 @@ lodash.merge@^4.6.2:
|
||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
|
||||
dependencies:
|
||||
yallist "^3.0.2"
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
@@ -817,11 +504,6 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||
|
||||
node-releases@^2.0.19:
|
||||
version "2.0.19"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314"
|
||||
integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
@@ -877,11 +559,6 @@ path-key@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||
|
||||
picocolors@^1.0.0, picocolors@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
|
||||
prelude-ls@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
@@ -921,11 +598,6 @@ run-parallel@^1.1.9:
|
||||
dependencies:
|
||||
queue-microtask "^1.2.2"
|
||||
|
||||
semver@^6.3.1:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||
@@ -974,14 +646,6 @@ type-fest@^0.20.2:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
||||
|
||||
update-browserslist-db@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
|
||||
integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==
|
||||
dependencies:
|
||||
escalade "^3.2.0"
|
||||
picocolors "^1.1.1"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
@@ -1006,22 +670,7 @@ wrappy@1:
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
yallist@^3.0.2:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zod-validation-error@^3.0.3:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6"
|
||||
integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==
|
||||
|
||||
zod@^3.22.4:
|
||||
version "3.24.2"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3"
|
||||
integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import {defineConfig} from 'eslint/config';
|
||||
import reactHooks from 'eslint-plugin-react-hooks';
|
||||
|
||||
console.log(reactHooks.configs['recommended-latest']);
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
languageOptions: {
|
||||
|
||||
@@ -141,3 +141,27 @@ function useHookInLoops() {
|
||||
useHook4();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiler Rules
|
||||
*/
|
||||
// Invalid: component factory
|
||||
function InvalidComponentFactory() {
|
||||
const DynamicComponent = () => <div>Hello</div>;
|
||||
// eslint-disable-next-line react-hooks/static-components
|
||||
return <DynamicComponent />;
|
||||
}
|
||||
|
||||
// Invalid: mutating globals
|
||||
function InvalidGlobals() {
|
||||
// eslint-disable-next-line react-hooks/immutability
|
||||
window.myGlobal = 42;
|
||||
return <div>Done</div>;
|
||||
}
|
||||
|
||||
// Invalid: useMemo with wrong deps - triggers preserve-manual-memoization
|
||||
function InvalidUseMemo({items}) {
|
||||
// eslint-disable-next-line react-hooks/preserve-manual-memoization, react-hooks/exhaustive-deps
|
||||
const sorted = useMemo(() => [...items].sort(), []);
|
||||
return <div>{sorted.length}</div>;
|
||||
}
|
||||
|
||||
+212
-205
@@ -2,212 +2,209 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@ampproject/remapping@^2.2.0":
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4"
|
||||
integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
|
||||
"@babel/code-frame@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
|
||||
integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@babel/code-frame@^7.26.2":
|
||||
version "7.26.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85"
|
||||
integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.27.1"
|
||||
js-tokens "^4.0.0"
|
||||
picocolors "^1.0.0"
|
||||
picocolors "^1.1.1"
|
||||
|
||||
"@babel/compat-data@^7.26.5":
|
||||
version "7.26.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367"
|
||||
integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==
|
||||
"@babel/compat-data@^7.27.2":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.4.tgz#96fdf1af1b8859c8474ab39c295312bfb7c24b04"
|
||||
integrity sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==
|
||||
|
||||
"@babel/core@^7.24.4":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9"
|
||||
integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.4.tgz#12a550b8794452df4c8b084f95003bce1742d496"
|
||||
integrity sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==
|
||||
dependencies:
|
||||
"@ampproject/remapping" "^2.2.0"
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/generator" "^7.26.10"
|
||||
"@babel/helper-compilation-targets" "^7.26.5"
|
||||
"@babel/helper-module-transforms" "^7.26.0"
|
||||
"@babel/helpers" "^7.26.10"
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/traverse" "^7.26.10"
|
||||
"@babel/types" "^7.26.10"
|
||||
"@babel/code-frame" "^7.27.1"
|
||||
"@babel/generator" "^7.28.3"
|
||||
"@babel/helper-compilation-targets" "^7.27.2"
|
||||
"@babel/helper-module-transforms" "^7.28.3"
|
||||
"@babel/helpers" "^7.28.4"
|
||||
"@babel/parser" "^7.28.4"
|
||||
"@babel/template" "^7.27.2"
|
||||
"@babel/traverse" "^7.28.4"
|
||||
"@babel/types" "^7.28.4"
|
||||
"@jridgewell/remapping" "^2.3.5"
|
||||
convert-source-map "^2.0.0"
|
||||
debug "^4.1.0"
|
||||
gensync "^1.0.0-beta.2"
|
||||
json5 "^2.2.3"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/generator@^7.26.10":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.10.tgz#a60d9de49caca16744e6340c3658dfef6138c3f7"
|
||||
integrity sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==
|
||||
"@babel/generator@^7.28.3":
|
||||
version "7.28.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e"
|
||||
integrity sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/types" "^7.26.10"
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.25"
|
||||
"@babel/parser" "^7.28.3"
|
||||
"@babel/types" "^7.28.2"
|
||||
"@jridgewell/gen-mapping" "^0.3.12"
|
||||
"@jridgewell/trace-mapping" "^0.3.28"
|
||||
jsesc "^3.0.2"
|
||||
|
||||
"@babel/helper-annotate-as-pure@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4"
|
||||
integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==
|
||||
"@babel/helper-annotate-as-pure@^7.27.3":
|
||||
version "7.27.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5"
|
||||
integrity sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==
|
||||
dependencies:
|
||||
"@babel/types" "^7.25.9"
|
||||
"@babel/types" "^7.27.3"
|
||||
|
||||
"@babel/helper-compilation-targets@^7.26.5":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8"
|
||||
integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==
|
||||
"@babel/helper-compilation-targets@^7.27.2":
|
||||
version "7.27.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d"
|
||||
integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.26.5"
|
||||
"@babel/helper-validator-option" "^7.25.9"
|
||||
"@babel/compat-data" "^7.27.2"
|
||||
"@babel/helper-validator-option" "^7.27.1"
|
||||
browserslist "^4.24.0"
|
||||
lru-cache "^5.1.1"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/helper-create-class-features-plugin@^7.25.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71"
|
||||
integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==
|
||||
"@babel/helper-create-class-features-plugin@^7.18.6":
|
||||
version "7.28.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz#3e747434ea007910c320c4d39a6b46f20f371d46"
|
||||
integrity sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.25.9"
|
||||
"@babel/helper-member-expression-to-functions" "^7.25.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.25.9"
|
||||
"@babel/helper-replace-supers" "^7.26.5"
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
|
||||
"@babel/traverse" "^7.26.9"
|
||||
"@babel/helper-annotate-as-pure" "^7.27.3"
|
||||
"@babel/helper-member-expression-to-functions" "^7.27.1"
|
||||
"@babel/helper-optimise-call-expression" "^7.27.1"
|
||||
"@babel/helper-replace-supers" "^7.27.1"
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.27.1"
|
||||
"@babel/traverse" "^7.28.3"
|
||||
semver "^6.3.1"
|
||||
|
||||
"@babel/helper-member-expression-to-functions@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3"
|
||||
integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==
|
||||
"@babel/helper-globals@^7.28.0":
|
||||
version "7.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
|
||||
integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
|
||||
|
||||
"@babel/helper-member-expression-to-functions@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz#ea1211276be93e798ce19037da6f06fbb994fa44"
|
||||
integrity sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
"@babel/traverse" "^7.27.1"
|
||||
"@babel/types" "^7.27.1"
|
||||
|
||||
"@babel/helper-module-imports@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
|
||||
integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==
|
||||
"@babel/helper-module-imports@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204"
|
||||
integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
"@babel/traverse" "^7.27.1"
|
||||
"@babel/types" "^7.27.1"
|
||||
|
||||
"@babel/helper-module-transforms@^7.26.0":
|
||||
version "7.26.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae"
|
||||
integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==
|
||||
"@babel/helper-module-transforms@^7.28.3":
|
||||
version "7.28.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6"
|
||||
integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/helper-module-imports" "^7.27.1"
|
||||
"@babel/helper-validator-identifier" "^7.27.1"
|
||||
"@babel/traverse" "^7.28.3"
|
||||
|
||||
"@babel/helper-optimise-call-expression@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e"
|
||||
integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==
|
||||
"@babel/helper-optimise-call-expression@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200"
|
||||
integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==
|
||||
dependencies:
|
||||
"@babel/types" "^7.25.9"
|
||||
"@babel/types" "^7.27.1"
|
||||
|
||||
"@babel/helper-plugin-utils@^7.25.9":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35"
|
||||
integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==
|
||||
"@babel/helper-plugin-utils@^7.18.6":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c"
|
||||
integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==
|
||||
|
||||
"@babel/helper-replace-supers@^7.26.5":
|
||||
version "7.26.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d"
|
||||
integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==
|
||||
"@babel/helper-replace-supers@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0"
|
||||
integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==
|
||||
dependencies:
|
||||
"@babel/helper-member-expression-to-functions" "^7.25.9"
|
||||
"@babel/helper-optimise-call-expression" "^7.25.9"
|
||||
"@babel/traverse" "^7.26.5"
|
||||
"@babel/helper-member-expression-to-functions" "^7.27.1"
|
||||
"@babel/helper-optimise-call-expression" "^7.27.1"
|
||||
"@babel/traverse" "^7.27.1"
|
||||
|
||||
"@babel/helper-skip-transparent-expression-wrappers@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9"
|
||||
integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==
|
||||
"@babel/helper-skip-transparent-expression-wrappers@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56"
|
||||
integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==
|
||||
dependencies:
|
||||
"@babel/traverse" "^7.25.9"
|
||||
"@babel/types" "^7.25.9"
|
||||
"@babel/traverse" "^7.27.1"
|
||||
"@babel/types" "^7.27.1"
|
||||
|
||||
"@babel/helper-string-parser@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
|
||||
integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
|
||||
"@babel/helper-string-parser@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
|
||||
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
|
||||
integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
|
||||
"@babel/helper-validator-identifier@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
|
||||
integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
|
||||
|
||||
"@babel/helper-validator-option@^7.25.9":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72"
|
||||
integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==
|
||||
"@babel/helper-validator-option@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f"
|
||||
integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==
|
||||
|
||||
"@babel/helpers@^7.26.10":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.10.tgz#6baea3cd62ec2d0c1068778d63cb1314f6637384"
|
||||
integrity sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==
|
||||
"@babel/helpers@^7.28.4":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827"
|
||||
integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==
|
||||
dependencies:
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/types" "^7.26.10"
|
||||
"@babel/template" "^7.27.2"
|
||||
"@babel/types" "^7.28.4"
|
||||
|
||||
"@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.10.tgz#e9bdb82f14b97df6569b0b038edd436839c57749"
|
||||
integrity sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==
|
||||
"@babel/parser@^7.24.4", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3", "@babel/parser@^7.28.4":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.4.tgz#da25d4643532890932cc03f7705fe19637e03fa8"
|
||||
integrity sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==
|
||||
dependencies:
|
||||
"@babel/types" "^7.26.10"
|
||||
"@babel/types" "^7.28.4"
|
||||
|
||||
"@babel/plugin-transform-private-methods@^7.24.4":
|
||||
version "7.25.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57"
|
||||
integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==
|
||||
"@babel/plugin-proposal-private-methods@^7.18.6":
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea"
|
||||
integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==
|
||||
dependencies:
|
||||
"@babel/helper-create-class-features-plugin" "^7.25.9"
|
||||
"@babel/helper-plugin-utils" "^7.25.9"
|
||||
"@babel/helper-create-class-features-plugin" "^7.18.6"
|
||||
"@babel/helper-plugin-utils" "^7.18.6"
|
||||
|
||||
"@babel/template@^7.26.9":
|
||||
version "7.26.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2"
|
||||
integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==
|
||||
"@babel/template@^7.27.2":
|
||||
version "7.27.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d"
|
||||
integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/parser" "^7.26.9"
|
||||
"@babel/types" "^7.26.9"
|
||||
"@babel/code-frame" "^7.27.1"
|
||||
"@babel/parser" "^7.27.2"
|
||||
"@babel/types" "^7.27.1"
|
||||
|
||||
"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.10.tgz#43cca33d76005dbaa93024fae536cc1946a4c380"
|
||||
integrity sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==
|
||||
"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.4.tgz#8d456101b96ab175d487249f60680221692b958b"
|
||||
integrity sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.26.2"
|
||||
"@babel/generator" "^7.26.10"
|
||||
"@babel/parser" "^7.26.10"
|
||||
"@babel/template" "^7.26.9"
|
||||
"@babel/types" "^7.26.10"
|
||||
"@babel/code-frame" "^7.27.1"
|
||||
"@babel/generator" "^7.28.3"
|
||||
"@babel/helper-globals" "^7.28.0"
|
||||
"@babel/parser" "^7.28.4"
|
||||
"@babel/template" "^7.27.2"
|
||||
"@babel/types" "^7.28.4"
|
||||
debug "^4.3.1"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9":
|
||||
version "7.26.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259"
|
||||
integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==
|
||||
"@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.4.tgz#0a4e618f4c60a7cd6c11cb2d48060e4dbe38ac3a"
|
||||
integrity sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.25.9"
|
||||
"@babel/helper-validator-identifier" "^7.25.9"
|
||||
"@babel/helper-string-parser" "^7.27.1"
|
||||
"@babel/helper-validator-identifier" "^7.27.1"
|
||||
|
||||
"@eslint-community/eslint-utils@^4.2.0":
|
||||
version "4.4.1"
|
||||
@@ -303,13 +300,20 @@
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.2.tgz#1860473de7dfa1546767448f333db80cb0ff2161"
|
||||
integrity sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.5":
|
||||
version "0.3.8"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142"
|
||||
integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==
|
||||
"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5":
|
||||
version "0.3.13"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
|
||||
integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
|
||||
dependencies:
|
||||
"@jridgewell/set-array" "^1.2.1"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@jridgewell/remapping@^2.3.5":
|
||||
version "2.3.5"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1"
|
||||
integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==
|
||||
dependencies:
|
||||
"@jridgewell/gen-mapping" "^0.3.5"
|
||||
"@jridgewell/trace-mapping" "^0.3.24"
|
||||
|
||||
"@jridgewell/resolve-uri@^3.1.0":
|
||||
@@ -317,20 +321,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
|
||||
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
|
||||
|
||||
"@jridgewell/set-array@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280"
|
||||
integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
|
||||
"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
|
||||
version "1.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
|
||||
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
|
||||
version "0.3.25"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0"
|
||||
integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
|
||||
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
|
||||
version "0.3.31"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
|
||||
integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
@@ -387,6 +386,11 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
baseline-browser-mapping@^2.8.9:
|
||||
version "2.8.10"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz#32eb5e253d633fa3fa3ffb1685fabf41680d9e8a"
|
||||
integrity sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
@@ -396,24 +400,25 @@ brace-expansion@^1.1.7:
|
||||
concat-map "0.0.1"
|
||||
|
||||
browserslist@^4.24.0:
|
||||
version "4.24.4"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b"
|
||||
integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==
|
||||
version "4.26.3"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.26.3.tgz#40fbfe2d1cd420281ce5b1caa8840049c79afb56"
|
||||
integrity sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001688"
|
||||
electron-to-chromium "^1.5.73"
|
||||
node-releases "^2.0.19"
|
||||
update-browserslist-db "^1.1.1"
|
||||
baseline-browser-mapping "^2.8.9"
|
||||
caniuse-lite "^1.0.30001746"
|
||||
electron-to-chromium "^1.5.227"
|
||||
node-releases "^2.0.21"
|
||||
update-browserslist-db "^1.1.3"
|
||||
|
||||
callsites@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
caniuse-lite@^1.0.30001688:
|
||||
version "1.0.30001703"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz#977cb4920598c158f491ecf4f4f2cfed9e354718"
|
||||
integrity sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==
|
||||
caniuse-lite@^1.0.30001746:
|
||||
version "1.0.30001746"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz#199d20f04f5369825e00ff7067d45d5dfa03aee7"
|
||||
integrity sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==
|
||||
|
||||
chalk@^4.0.0:
|
||||
version "4.1.2"
|
||||
@@ -454,7 +459,14 @@ cross-spawn@^7.0.6:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
debug@^4.1.0, debug@^4.3.1, debug@^4.3.2:
|
||||
debug@^4.1.0:
|
||||
version "4.4.3"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
|
||||
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
|
||||
dependencies:
|
||||
ms "^2.1.3"
|
||||
|
||||
debug@^4.3.1, debug@^4.3.2:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
|
||||
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
||||
@@ -466,10 +478,10 @@ deep-is@^0.1.3:
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
|
||||
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
|
||||
|
||||
electron-to-chromium@^1.5.73:
|
||||
version "1.5.114"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz#f2bb4fda80a7db4ea273565e75b0ebbe19af0ac3"
|
||||
integrity sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA==
|
||||
electron-to-chromium@^1.5.227:
|
||||
version "1.5.228"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz#38b849bc8714bd21fb64f5ad56bf8cfd8638e1e9"
|
||||
integrity sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==
|
||||
|
||||
escalade@^3.2.0:
|
||||
version "3.2.0"
|
||||
@@ -646,11 +658,6 @@ glob-parent@^6.0.2:
|
||||
dependencies:
|
||||
is-glob "^4.0.3"
|
||||
|
||||
globals@^11.1.0:
|
||||
version "11.12.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||
|
||||
globals@^14.0.0:
|
||||
version "14.0.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
|
||||
@@ -801,10 +808,10 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||
|
||||
node-releases@^2.0.19:
|
||||
version "2.0.19"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314"
|
||||
integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==
|
||||
node-releases@^2.0.21:
|
||||
version "2.0.21"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.21.tgz#f59b018bc0048044be2d4c4c04e4c8b18160894c"
|
||||
integrity sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==
|
||||
|
||||
optionator@^0.9.3:
|
||||
version "0.9.4"
|
||||
@@ -849,7 +856,7 @@ path-key@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||
|
||||
picocolors@^1.0.0, picocolors@^1.1.1:
|
||||
picocolors@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
@@ -905,7 +912,7 @@ type-check@^0.4.0, type-check@~0.4.0:
|
||||
dependencies:
|
||||
prelude-ls "^1.2.1"
|
||||
|
||||
update-browserslist-db@^1.1.1:
|
||||
update-browserslist-db@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
|
||||
integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==
|
||||
@@ -943,11 +950,11 @@ yocto-queue@^0.1.0:
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zod-validation-error@^3.0.3:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6"
|
||||
integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.5.3.tgz#85ba33290200d8db9f043621e284f40dddefb7e5"
|
||||
integrity sha512-OT5Y8lbUadqVZCsnyFaTQ4/O2mys4tj7PqhdbBCp7McPwvIEKfPtdA6QfPeFQK2/Rz5LgwmAXRJTugBNBi0btw==
|
||||
|
||||
zod@^3.22.4:
|
||||
version "3.24.2"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3"
|
||||
integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==
|
||||
version "3.25.76"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34"
|
||||
integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# `eslint-plugin-react-hooks`
|
||||
|
||||
This ESLint plugin enforces the [Rules of Hooks](https://react.dev/reference/rules/rules-of-hooks).
|
||||
|
||||
It is a part of the [Hooks API](https://react.dev/reference/react/hooks) for React.
|
||||
The official ESLint plugin for [React](https://react.dev) which enforces the [Rules of React](https://react.dev/reference/eslint-plugin-react-hooks) and other best practices.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
import type {Linter, Rule} from 'eslint';
|
||||
|
||||
import ExhaustiveDeps from './rules/ExhaustiveDeps';
|
||||
import {allRules} from './shared/ReactCompiler';
|
||||
import {
|
||||
allRules,
|
||||
mapErrorSeverityToESlint,
|
||||
recommendedRules,
|
||||
} from './shared/ReactCompiler';
|
||||
import RulesOfHooks from './rules/RulesOfHooks';
|
||||
|
||||
// All rules
|
||||
@@ -19,24 +23,56 @@ const rules = {
|
||||
),
|
||||
} satisfies Record<string, Rule.RuleModule>;
|
||||
|
||||
// Config rules
|
||||
const ruleConfigs = {
|
||||
// Basic hooks rules (for recommended config)
|
||||
const basicRuleConfigs = {
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
'react-hooks/exhaustive-deps': 'warn',
|
||||
} satisfies Linter.RulesRecord;
|
||||
} as const satisfies Linter.RulesRecord;
|
||||
|
||||
const compilerRuleConfigs = Object.fromEntries(
|
||||
Object.entries(recommendedRules).map(([name, ruleConfig]) => {
|
||||
return [
|
||||
`react-hooks/${name}` as const,
|
||||
mapErrorSeverityToESlint(ruleConfig.severity),
|
||||
] as const;
|
||||
}),
|
||||
) as Record<`react-hooks/${string}`, Linter.RuleEntry>;
|
||||
|
||||
// All rules including compiler rules (for recommended-latest config)
|
||||
const allRuleConfigs: Linter.RulesRecord = {
|
||||
...basicRuleConfigs,
|
||||
...compilerRuleConfigs,
|
||||
};
|
||||
|
||||
const plugin = {
|
||||
meta: {
|
||||
name: 'eslint-plugin-react-hooks',
|
||||
},
|
||||
configs: {},
|
||||
rules,
|
||||
configs: {} as {
|
||||
'recommended-legacy': {
|
||||
plugins: Array<string>;
|
||||
rules: Linter.RulesRecord;
|
||||
};
|
||||
'recommended-latest-legacy': {
|
||||
plugins: Array<string>;
|
||||
rules: Linter.RulesRecord;
|
||||
};
|
||||
'flat/recommended': Array<Linter.Config>;
|
||||
'recommended-latest': Array<Linter.Config>;
|
||||
recommended: Array<Linter.Config>;
|
||||
},
|
||||
};
|
||||
|
||||
Object.assign(plugin.configs, {
|
||||
'recommended-legacy': {
|
||||
plugins: ['react-hooks'],
|
||||
rules: ruleConfigs,
|
||||
rules: basicRuleConfigs,
|
||||
},
|
||||
|
||||
'recommended-latest-legacy': {
|
||||
plugins: ['react-hooks'],
|
||||
rules: allRuleConfigs,
|
||||
},
|
||||
|
||||
'flat/recommended': [
|
||||
@@ -44,7 +80,7 @@ Object.assign(plugin.configs, {
|
||||
plugins: {
|
||||
'react-hooks': plugin,
|
||||
},
|
||||
rules: ruleConfigs,
|
||||
rules: basicRuleConfigs,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -53,14 +89,18 @@ Object.assign(plugin.configs, {
|
||||
plugins: {
|
||||
'react-hooks': plugin,
|
||||
},
|
||||
rules: ruleConfigs,
|
||||
rules: allRuleConfigs,
|
||||
},
|
||||
],
|
||||
|
||||
recommended: {
|
||||
plugins: ['react-hooks'],
|
||||
rules: ruleConfigs,
|
||||
},
|
||||
recommended: [
|
||||
{
|
||||
plugins: {
|
||||
'react-hooks': plugin,
|
||||
},
|
||||
rules: basicRuleConfigs,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export default plugin;
|
||||
|
||||
@@ -160,38 +160,6 @@ function makeRule(rule: LintRule): Rule.RuleModule {
|
||||
};
|
||||
}
|
||||
|
||||
export const NoUnusedDirectivesRule: Rule.RuleModule = {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
recommended: true,
|
||||
},
|
||||
fixable: 'code',
|
||||
hasSuggestions: true,
|
||||
// validation is done at runtime with zod
|
||||
schema: [{type: 'object', additionalProperties: true}],
|
||||
},
|
||||
create(context: Rule.RuleContext): Rule.RuleListener {
|
||||
const results = getReactCompilerResult(context);
|
||||
|
||||
for (const directive of results.unusedOptOutDirectives) {
|
||||
context.report({
|
||||
message: `Unused '${directive.directive}' directive`,
|
||||
loc: directive.loc,
|
||||
suggest: [
|
||||
{
|
||||
desc: 'Remove the directive',
|
||||
fix(fixer): Rule.Fix {
|
||||
return fixer.removeRange(directive.range);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
return {};
|
||||
},
|
||||
};
|
||||
|
||||
type RulesConfig = {
|
||||
[name: string]: {rule: Rule.RuleModule; severity: ErrorSeverity};
|
||||
};
|
||||
@@ -201,12 +169,7 @@ export const allRules: RulesConfig = LintRules.reduce(
|
||||
acc[rule.name] = {rule: makeRule(rule), severity: rule.severity};
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
'no-unused-directives': {
|
||||
rule: NoUnusedDirectivesRule,
|
||||
severity: ErrorSeverity.Error,
|
||||
},
|
||||
} as RulesConfig,
|
||||
{} as RulesConfig,
|
||||
);
|
||||
|
||||
export const recommendedRules: RulesConfig = LintRules.filter(
|
||||
@@ -216,12 +179,7 @@ export const recommendedRules: RulesConfig = LintRules.filter(
|
||||
acc[rule.name] = {rule: makeRule(rule), severity: rule.severity};
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
'no-unused-directives': {
|
||||
rule: NoUnusedDirectivesRule,
|
||||
severity: ErrorSeverity.Error,
|
||||
},
|
||||
} as RulesConfig,
|
||||
{} as RulesConfig,
|
||||
);
|
||||
|
||||
export function mapErrorSeverityToESlint(
|
||||
|
||||
@@ -6,21 +6,19 @@
|
||||
*/
|
||||
/* eslint-disable no-for-of-loops/no-for-of-loops */
|
||||
|
||||
import {transformFromAstSync, traverse} from '@babel/core';
|
||||
import {transformFromAstSync} from '@babel/core';
|
||||
import {parse as babelParse} from '@babel/parser';
|
||||
import {Directive, File} from '@babel/types';
|
||||
import {File} from '@babel/types';
|
||||
// @ts-expect-error: no types available
|
||||
import PluginProposalPrivateMethods from '@babel/plugin-proposal-private-methods';
|
||||
import BabelPluginReactCompiler, {
|
||||
parsePluginOptions,
|
||||
validateEnvironmentConfig,
|
||||
OPT_OUT_DIRECTIVES,
|
||||
type PluginOptions,
|
||||
Logger,
|
||||
LoggerEvent,
|
||||
} from 'babel-plugin-react-compiler';
|
||||
import type {SourceCode} from 'eslint';
|
||||
import {SourceLocation} from 'estree';
|
||||
import * as HermesParser from 'hermes-parser';
|
||||
import {isDeepStrictEqual} from 'util';
|
||||
import type {ParseResult} from '@babel/parser';
|
||||
@@ -46,17 +44,11 @@ const COMPILER_OPTIONS: PluginOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
export type UnusedOptOutDirective = {
|
||||
loc: SourceLocation;
|
||||
range: [number, number];
|
||||
directive: string;
|
||||
};
|
||||
export type RunCacheEntry = {
|
||||
sourceCode: string;
|
||||
filename: string;
|
||||
userOpts: PluginOptions;
|
||||
flowSuppressions: Array<{line: number; code: string}>;
|
||||
unusedOptOutDirectives: Array<UnusedOptOutDirective>;
|
||||
events: Array<LoggerEvent>;
|
||||
};
|
||||
|
||||
@@ -88,24 +80,6 @@ function getFlowSuppressions(
|
||||
return results;
|
||||
}
|
||||
|
||||
function filterUnusedOptOutDirectives(
|
||||
directives: ReadonlyArray<Directive>,
|
||||
): Array<UnusedOptOutDirective> {
|
||||
const results: Array<UnusedOptOutDirective> = [];
|
||||
for (const directive of directives) {
|
||||
if (
|
||||
OPT_OUT_DIRECTIVES.has(directive.value.value) &&
|
||||
directive.loc != null
|
||||
) {
|
||||
results.push({
|
||||
loc: directive.loc,
|
||||
directive: directive.value.value,
|
||||
range: [directive.start!, directive.end!],
|
||||
});
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function runReactCompilerImpl({
|
||||
sourceCode,
|
||||
@@ -126,7 +100,6 @@ function runReactCompilerImpl({
|
||||
filename,
|
||||
userOpts,
|
||||
flowSuppressions: [],
|
||||
unusedOptOutDirectives: [],
|
||||
events: [],
|
||||
};
|
||||
const userLogger: Logger | null = options.logger;
|
||||
@@ -182,28 +155,6 @@ function runReactCompilerImpl({
|
||||
configFile: false,
|
||||
babelrc: false,
|
||||
});
|
||||
|
||||
if (results.events.filter(e => e.kind === 'CompileError').length === 0) {
|
||||
traverse(babelAST, {
|
||||
FunctionDeclaration(path) {
|
||||
results.unusedOptOutDirectives.push(
|
||||
...filterUnusedOptOutDirectives(path.node.body.directives),
|
||||
);
|
||||
},
|
||||
ArrowFunctionExpression(path) {
|
||||
if (path.node.body.type === 'BlockStatement') {
|
||||
results.unusedOptOutDirectives.push(
|
||||
...filterUnusedOptOutDirectives(path.node.body.directives),
|
||||
);
|
||||
}
|
||||
},
|
||||
FunctionExpression(path) {
|
||||
results.unusedOptOutDirectives.push(
|
||||
...filterUnusedOptOutDirectives(path.node.body.directives),
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
/* errors handled by injected logger */
|
||||
}
|
||||
|
||||
@@ -850,6 +850,7 @@ describe('Timeline profiler', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
// @reactVersion >= 19.1
|
||||
// @reactVersion < 19.2
|
||||
it('should process a sample createRoot render sequence', async () => {
|
||||
function App() {
|
||||
@@ -2158,6 +2159,7 @@ describe('Timeline profiler', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
// @reactVersion >= 19.1
|
||||
// @reactVersion < 19.2
|
||||
it('should process a sample createRoot render sequence', async () => {
|
||||
function App() {
|
||||
|
||||
@@ -974,12 +974,8 @@ describe('Store', () => {
|
||||
<Suspense name="three" rects={[{x:1,y:2,width:5,height:1}]}>
|
||||
`);
|
||||
|
||||
const rendererID = getRendererID();
|
||||
const rootID = store.getRootIDForElement(store.getElementIDAtIndex(0));
|
||||
await actAsync(() => {
|
||||
agent.overrideSuspenseMilestone({
|
||||
rendererID,
|
||||
rootID,
|
||||
suspendedSet: [
|
||||
store.getElementIDAtIndex(4),
|
||||
store.getElementIDAtIndex(8),
|
||||
@@ -1009,8 +1005,6 @@ describe('Store', () => {
|
||||
|
||||
await actAsync(() => {
|
||||
agent.overrideSuspenseMilestone({
|
||||
rendererID,
|
||||
rootID,
|
||||
suspendedSet: [],
|
||||
});
|
||||
});
|
||||
|
||||
+264
-14
@@ -8,7 +8,11 @@
|
||||
*/
|
||||
|
||||
import EventEmitter from '../events';
|
||||
import {SESSION_STORAGE_LAST_SELECTION_KEY, __DEBUG__} from '../constants';
|
||||
import {
|
||||
SESSION_STORAGE_LAST_SELECTION_KEY,
|
||||
UNKNOWN_SUSPENDERS_NONE,
|
||||
__DEBUG__,
|
||||
} from '../constants';
|
||||
import setupHighlighter from './views/Highlighter';
|
||||
import {
|
||||
initialize as setupTraceUpdates,
|
||||
@@ -26,8 +30,13 @@ import type {
|
||||
RendererID,
|
||||
RendererInterface,
|
||||
DevToolsHookSettings,
|
||||
InspectedElement,
|
||||
} from './types';
|
||||
import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types';
|
||||
import type {
|
||||
ComponentFilter,
|
||||
DehydratedData,
|
||||
ElementType,
|
||||
} from 'react-devtools-shared/src/frontend/types';
|
||||
import type {GroupItem} from './views/TraceUpdates/canvas';
|
||||
import {gte, isReactNativeEnvironment} from './utils';
|
||||
import {
|
||||
@@ -73,6 +82,13 @@ type InspectElementParams = {
|
||||
requestID: number,
|
||||
};
|
||||
|
||||
type InspectScreenParams = {
|
||||
forceFullData: boolean,
|
||||
id: number,
|
||||
path: Array<string | number> | null,
|
||||
requestID: number,
|
||||
};
|
||||
|
||||
type OverrideHookParams = {
|
||||
id: number,
|
||||
hookID: number,
|
||||
@@ -131,8 +147,6 @@ type OverrideSuspenseParams = {
|
||||
};
|
||||
|
||||
type OverrideSuspenseMilestoneParams = {
|
||||
rendererID: number,
|
||||
rootID: number,
|
||||
suspendedSet: Array<number>,
|
||||
};
|
||||
|
||||
@@ -141,6 +155,111 @@ type PersistedSelection = {
|
||||
path: Array<PathFrame>,
|
||||
};
|
||||
|
||||
function createEmptyInspectedScreen(
|
||||
arbitraryRootID: number,
|
||||
type: ElementType,
|
||||
): InspectedElement {
|
||||
const suspendedBy: DehydratedData = {
|
||||
cleaned: [],
|
||||
data: [],
|
||||
unserializable: [],
|
||||
};
|
||||
return {
|
||||
// invariants
|
||||
id: arbitraryRootID,
|
||||
type: type,
|
||||
// Properties we merge
|
||||
isErrored: false,
|
||||
errors: [],
|
||||
warnings: [],
|
||||
suspendedBy,
|
||||
suspendedByRange: null,
|
||||
// TODO: How to merge these?
|
||||
unknownSuspenders: UNKNOWN_SUSPENDERS_NONE,
|
||||
// Properties where merging doesn't make sense so we ignore them entirely in the UI
|
||||
rootType: null,
|
||||
plugins: {stylex: null},
|
||||
nativeTag: null,
|
||||
env: null,
|
||||
source: null,
|
||||
stack: null,
|
||||
rendererPackageName: null,
|
||||
rendererVersion: null,
|
||||
// These don't make sense for a Root. They're just bottom values.
|
||||
key: null,
|
||||
canEditFunctionProps: false,
|
||||
canEditHooks: false,
|
||||
canEditFunctionPropsDeletePaths: false,
|
||||
canEditFunctionPropsRenamePaths: false,
|
||||
canEditHooksAndDeletePaths: false,
|
||||
canEditHooksAndRenamePaths: false,
|
||||
canToggleError: false,
|
||||
canToggleSuspense: false,
|
||||
isSuspended: false,
|
||||
hasLegacyContext: false,
|
||||
context: null,
|
||||
hooks: null,
|
||||
props: null,
|
||||
state: null,
|
||||
owners: null,
|
||||
};
|
||||
}
|
||||
|
||||
function mergeRoots(
|
||||
left: InspectedElement,
|
||||
right: InspectedElement,
|
||||
suspendedByOffset: number,
|
||||
): void {
|
||||
const leftSuspendedByRange = left.suspendedByRange;
|
||||
const rightSuspendedByRange = right.suspendedByRange;
|
||||
|
||||
if (right.isErrored) {
|
||||
left.isErrored = true;
|
||||
}
|
||||
for (let i = 0; i < right.errors.length; i++) {
|
||||
left.errors.push(right.errors[i]);
|
||||
}
|
||||
for (let i = 0; i < right.warnings.length; i++) {
|
||||
left.warnings.push(right.warnings[i]);
|
||||
}
|
||||
|
||||
const leftSuspendedBy: DehydratedData = left.suspendedBy;
|
||||
const {data, cleaned, unserializable} = (right.suspendedBy: DehydratedData);
|
||||
const leftSuspendedByData = ((leftSuspendedBy.data: any): Array<mixed>);
|
||||
const rightSuspendedByData = ((data: any): Array<mixed>);
|
||||
for (let i = 0; i < rightSuspendedByData.length; i++) {
|
||||
leftSuspendedByData.push(rightSuspendedByData[i]);
|
||||
}
|
||||
for (let i = 0; i < cleaned.length; i++) {
|
||||
leftSuspendedBy.cleaned.push(
|
||||
[suspendedByOffset + cleaned[i][0]].concat(cleaned[i].slice(1)),
|
||||
);
|
||||
}
|
||||
for (let i = 0; i < unserializable.length; i++) {
|
||||
leftSuspendedBy.unserializable.push(
|
||||
[suspendedByOffset + unserializable[i][0]].concat(
|
||||
unserializable[i].slice(1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (rightSuspendedByRange !== null) {
|
||||
if (leftSuspendedByRange === null) {
|
||||
left.suspendedByRange = [
|
||||
rightSuspendedByRange[0],
|
||||
rightSuspendedByRange[1],
|
||||
];
|
||||
} else {
|
||||
if (rightSuspendedByRange[0] < leftSuspendedByRange[0]) {
|
||||
leftSuspendedByRange[0] = rightSuspendedByRange[0];
|
||||
}
|
||||
if (rightSuspendedByRange[1] > leftSuspendedByRange[1]) {
|
||||
leftSuspendedByRange[1] = rightSuspendedByRange[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default class Agent extends EventEmitter<{
|
||||
hideNativeHighlight: [],
|
||||
showNativeHighlight: [HostInstance],
|
||||
@@ -201,6 +320,7 @@ export default class Agent extends EventEmitter<{
|
||||
bridge.addListener('getProfilingStatus', this.getProfilingStatus);
|
||||
bridge.addListener('getOwnersList', this.getOwnersList);
|
||||
bridge.addListener('inspectElement', this.inspectElement);
|
||||
bridge.addListener('inspectScreen', this.inspectScreen);
|
||||
bridge.addListener('logElementToConsole', this.logElementToConsole);
|
||||
bridge.addListener('overrideError', this.overrideError);
|
||||
bridge.addListener('overrideSuspense', this.overrideSuspense);
|
||||
@@ -531,6 +651,138 @@ export default class Agent extends EventEmitter<{
|
||||
}
|
||||
};
|
||||
|
||||
inspectScreen: InspectScreenParams => void = ({
|
||||
requestID,
|
||||
id,
|
||||
forceFullData,
|
||||
path: screenPath,
|
||||
}) => {
|
||||
let inspectedScreen: InspectedElement | null = null;
|
||||
let found = false;
|
||||
// the suspendedBy index will be from the previously merged roots.
|
||||
// We need to keep track of how many suspendedBy we've already seen to know
|
||||
// to which renderer the index belongs.
|
||||
let suspendedByOffset = 0;
|
||||
let suspendedByPathIndex: number | null = null;
|
||||
// The path to hydrate for a specific renderer
|
||||
let rendererPath: InspectElementParams['path'] = null;
|
||||
if (screenPath !== null && screenPath.length > 1) {
|
||||
const secondaryCategory = screenPath[0];
|
||||
if (secondaryCategory !== 'suspendedBy') {
|
||||
throw new Error(
|
||||
'Only hydrating suspendedBy paths is supported. This is a bug.',
|
||||
);
|
||||
}
|
||||
if (typeof screenPath[1] !== 'number') {
|
||||
throw new Error(
|
||||
`Expected suspendedBy index to be a number. Received '${screenPath[1]}' instead. This is a bug.`,
|
||||
);
|
||||
}
|
||||
suspendedByPathIndex = screenPath[1];
|
||||
rendererPath = screenPath.slice(2);
|
||||
}
|
||||
|
||||
for (const rendererID in this._rendererInterfaces) {
|
||||
const renderer = ((this._rendererInterfaces[
|
||||
(rendererID: any)
|
||||
]: any): RendererInterface);
|
||||
let path: InspectElementParams['path'] = null;
|
||||
if (suspendedByPathIndex !== null && rendererPath !== null) {
|
||||
const suspendedByPathRendererIndex =
|
||||
suspendedByPathIndex - suspendedByOffset;
|
||||
const rendererHasRequestedSuspendedByPath =
|
||||
renderer.getElementAttributeByPath(id, [
|
||||
'suspendedBy',
|
||||
suspendedByPathRendererIndex,
|
||||
]) !== undefined;
|
||||
if (rendererHasRequestedSuspendedByPath) {
|
||||
path = ['suspendedBy', suspendedByPathRendererIndex].concat(
|
||||
rendererPath,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const inspectedRootsPayload = renderer.inspectElement(
|
||||
requestID,
|
||||
id,
|
||||
path,
|
||||
forceFullData,
|
||||
);
|
||||
switch (inspectedRootsPayload.type) {
|
||||
case 'hydrated-path':
|
||||
// The path will be relative to the Roots of this renderer. We adjust it
|
||||
// to be relative to all Roots of this implementation.
|
||||
inspectedRootsPayload.path[1] += suspendedByOffset;
|
||||
// TODO: Hydration logic is flawed since the Frontend path is not based
|
||||
// on the original backend data but rather its own representation of it (e.g. due to reorder).
|
||||
// So we can receive null here instead when hydration fails
|
||||
if (inspectedRootsPayload.value !== null) {
|
||||
for (
|
||||
let i = 0;
|
||||
i < inspectedRootsPayload.value.cleaned.length;
|
||||
i++
|
||||
) {
|
||||
inspectedRootsPayload.value.cleaned[i][1] += suspendedByOffset;
|
||||
}
|
||||
}
|
||||
this._bridge.send('inspectedScreen', inspectedRootsPayload);
|
||||
// If we hydrated a path, it must've been in a specific renderer so we can stop here.
|
||||
return;
|
||||
case 'full-data':
|
||||
const inspectedRoots = inspectedRootsPayload.value;
|
||||
if (inspectedScreen === null) {
|
||||
inspectedScreen = createEmptyInspectedScreen(
|
||||
inspectedRoots.id,
|
||||
inspectedRoots.type,
|
||||
);
|
||||
}
|
||||
mergeRoots(inspectedScreen, inspectedRoots, suspendedByOffset);
|
||||
const dehydratedSuspendedBy: DehydratedData =
|
||||
inspectedRoots.suspendedBy;
|
||||
const suspendedBy = ((dehydratedSuspendedBy.data: any): Array<mixed>);
|
||||
suspendedByOffset += suspendedBy.length;
|
||||
found = true;
|
||||
break;
|
||||
case 'no-change':
|
||||
found = true;
|
||||
const rootsSuspendedBy: Array<mixed> =
|
||||
(renderer.getElementAttributeByPath(id, ['suspendedBy']): any);
|
||||
suspendedByOffset += rootsSuspendedBy.length;
|
||||
break;
|
||||
case 'not-found':
|
||||
break;
|
||||
case 'error':
|
||||
// bail out and show the error
|
||||
// TODO: aggregate errors
|
||||
this._bridge.send('inspectedScreen', inspectedRootsPayload);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (inspectedScreen === null) {
|
||||
if (found) {
|
||||
this._bridge.send('inspectedScreen', {
|
||||
type: 'no-change',
|
||||
responseID: requestID,
|
||||
id,
|
||||
});
|
||||
} else {
|
||||
this._bridge.send('inspectedScreen', {
|
||||
type: 'not-found',
|
||||
responseID: requestID,
|
||||
id,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this._bridge.send('inspectedScreen', {
|
||||
type: 'full-data',
|
||||
responseID: requestID,
|
||||
id,
|
||||
value: inspectedScreen,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
logElementToConsole: ElementAndRendererID => void = ({id, rendererID}) => {
|
||||
const renderer = this._rendererInterfaces[rendererID];
|
||||
if (renderer == null) {
|
||||
@@ -567,17 +819,15 @@ export default class Agent extends EventEmitter<{
|
||||
};
|
||||
|
||||
overrideSuspenseMilestone: OverrideSuspenseMilestoneParams => void = ({
|
||||
rendererID,
|
||||
rootID,
|
||||
suspendedSet,
|
||||
}) => {
|
||||
const renderer = this._rendererInterfaces[rendererID];
|
||||
if (renderer == null) {
|
||||
console.warn(
|
||||
`Invalid renderer id "${rendererID}" to override suspense milestone`,
|
||||
);
|
||||
} else {
|
||||
renderer.overrideSuspenseMilestone(rootID, suspendedSet);
|
||||
for (const rendererID in this._rendererInterfaces) {
|
||||
const renderer = ((this._rendererInterfaces[
|
||||
(rendererID: any)
|
||||
]: any): RendererInterface);
|
||||
if (renderer.supportsTogglingSuspense) {
|
||||
renderer.overrideSuspenseMilestone(suspendedSet);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -739,7 +989,7 @@ export default class Agent extends EventEmitter<{
|
||||
if (renderer !== null) {
|
||||
const devRenderer = renderer.bundleType === 1;
|
||||
const enableSuspenseTab =
|
||||
devRenderer && gte(renderer.version, '19.2.0-canary');
|
||||
devRenderer && gte(renderer.version, '19.3.0-canary');
|
||||
if (enableSuspenseTab) {
|
||||
this._bridge.send('enableSuspenseTab');
|
||||
}
|
||||
|
||||
+125
-21
@@ -2420,7 +2420,6 @@ export function attach(
|
||||
!isProductionBuildOfRenderer && StrictModeBits !== 0 ? 1 : 0,
|
||||
);
|
||||
pushOperation(hasOwnerMetadata ? 1 : 0);
|
||||
pushOperation(supportsTogglingSuspense ? 1 : 0);
|
||||
|
||||
if (isProfiling) {
|
||||
if (displayNamesByRootID !== null) {
|
||||
@@ -4902,7 +4901,11 @@ export function attach(
|
||||
fiberInstance.data = nextFiber;
|
||||
if (
|
||||
mostRecentlyInspectedElement !== null &&
|
||||
mostRecentlyInspectedElement.id === fiberInstance.id &&
|
||||
(mostRecentlyInspectedElement.id === fiberInstance.id ||
|
||||
// If we're inspecting a Root, we inspect the Screen.
|
||||
// Invalidating any Root invalidates the Screen too.
|
||||
(mostRecentlyInspectedElement.type === ElementTypeRoot &&
|
||||
nextFiber.tag === HostRoot)) &&
|
||||
didFiberRender(prevFiber, nextFiber)
|
||||
) {
|
||||
// If this Fiber has updated, clear cached inspected data.
|
||||
@@ -6422,7 +6425,10 @@ export function attach(
|
||||
return inspectVirtualInstanceRaw(devtoolsInstance);
|
||||
}
|
||||
if (devtoolsInstance.kind === FIBER_INSTANCE) {
|
||||
return inspectFiberInstanceRaw(devtoolsInstance);
|
||||
const isRoot = devtoolsInstance.parent === null;
|
||||
return isRoot
|
||||
? inspectRootsRaw(devtoolsInstance.id)
|
||||
: inspectFiberInstanceRaw(devtoolsInstance);
|
||||
}
|
||||
(devtoolsInstance: FilteredFiberInstance); // assert exhaustive
|
||||
throw new Error('Unsupported instance kind');
|
||||
@@ -6590,9 +6596,6 @@ export function attach(
|
||||
rootType = fiberRoot._debugRootType;
|
||||
}
|
||||
|
||||
const isTimedOutSuspense =
|
||||
tag === SuspenseComponent && memoizedState !== null;
|
||||
|
||||
let isErrored = false;
|
||||
if (isErrorBoundary(fiber)) {
|
||||
// if the current inspected element is an error boundary,
|
||||
@@ -6664,7 +6667,7 @@ export function attach(
|
||||
if (
|
||||
fiberInstance.suspenseNode !== null &&
|
||||
fiberInstance.suspenseNode.hasUnknownSuspenders &&
|
||||
!isTimedOutSuspense
|
||||
!isSuspended
|
||||
) {
|
||||
// Something unknown threw to suspended this boundary. Let's figure out why that might be.
|
||||
if (renderer.bundleType === 0) {
|
||||
@@ -6702,7 +6705,7 @@ export function attach(
|
||||
supportsTogglingSuspense &&
|
||||
hasSuspenseBoundary &&
|
||||
// If it's showing the real content, we can always flip fallback.
|
||||
(!isTimedOutSuspense ||
|
||||
(!isSuspended ||
|
||||
// If it's showing fallback because we previously forced it to,
|
||||
// allow toggling it back to remove the fallback override.
|
||||
forceFallbackForFibers.has(fiber) ||
|
||||
@@ -6875,10 +6878,24 @@ export function attach(
|
||||
let currentlyInspectedPaths: Object = {};
|
||||
|
||||
function isMostRecentlyInspectedElement(id: number): boolean {
|
||||
return (
|
||||
mostRecentlyInspectedElement !== null &&
|
||||
mostRecentlyInspectedElement.id === id
|
||||
);
|
||||
if (mostRecentlyInspectedElement === null) {
|
||||
return false;
|
||||
}
|
||||
if (mostRecentlyInspectedElement.id === id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mostRecentlyInspectedElement.type === ElementTypeRoot) {
|
||||
// we inspected the screen recently. If we're inspecting another root, we're
|
||||
// still inspecting the screen.
|
||||
const instance = idToDevToolsInstanceMap.get(id);
|
||||
return (
|
||||
instance !== undefined &&
|
||||
instance.kind === FIBER_INSTANCE &&
|
||||
instance.parent === null
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isMostRecentlyInspectedElementCurrent(id: number): boolean {
|
||||
@@ -7060,8 +7077,8 @@ export function attach(
|
||||
if (!hasElementUpdatedSinceLastInspected) {
|
||||
if (path !== null) {
|
||||
let secondaryCategory: 'suspendedBy' | 'hooks' | null = null;
|
||||
if (path[0] === 'hooks') {
|
||||
secondaryCategory = 'hooks';
|
||||
if (path[0] === 'hooks' || path[0] === 'suspendedBy') {
|
||||
secondaryCategory = path[0];
|
||||
}
|
||||
|
||||
// If this element has not been updated since it was last inspected,
|
||||
@@ -7209,6 +7226,99 @@ export function attach(
|
||||
};
|
||||
}
|
||||
|
||||
function inspectRootsRaw(arbitraryRootID: number): InspectedElement | null {
|
||||
const roots = hook.getFiberRoots(rendererID);
|
||||
if (roots.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const inspectedRoots: InspectedElement = {
|
||||
// invariants
|
||||
id: arbitraryRootID,
|
||||
type: ElementTypeRoot,
|
||||
// Properties we merge
|
||||
isErrored: false,
|
||||
errors: [],
|
||||
warnings: [],
|
||||
suspendedBy: [],
|
||||
suspendedByRange: null,
|
||||
// TODO: How to merge these?
|
||||
unknownSuspenders: UNKNOWN_SUSPENDERS_NONE,
|
||||
// Properties where merging doesn't make sense so we ignore them entirely in the UI
|
||||
rootType: null,
|
||||
plugins: {stylex: null},
|
||||
nativeTag: null,
|
||||
env: null,
|
||||
source: null,
|
||||
stack: null,
|
||||
rendererPackageName: null,
|
||||
rendererVersion: null,
|
||||
// These don't make sense for a Root. They're just bottom values.
|
||||
key: null,
|
||||
canEditFunctionProps: false,
|
||||
canEditHooks: false,
|
||||
canEditFunctionPropsDeletePaths: false,
|
||||
canEditFunctionPropsRenamePaths: false,
|
||||
canEditHooksAndDeletePaths: false,
|
||||
canEditHooksAndRenamePaths: false,
|
||||
canToggleError: false,
|
||||
canToggleSuspense: false,
|
||||
isSuspended: false,
|
||||
hasLegacyContext: false,
|
||||
context: null,
|
||||
hooks: null,
|
||||
props: null,
|
||||
state: null,
|
||||
owners: null,
|
||||
};
|
||||
|
||||
let minSuspendedByRange = Infinity;
|
||||
let maxSuspendedByRange = -Infinity;
|
||||
roots.forEach(root => {
|
||||
const rootInstance = rootToFiberInstanceMap.get(root);
|
||||
if (rootInstance === undefined) {
|
||||
throw new Error(
|
||||
'Expected a root instance to exist for this Fiber root',
|
||||
);
|
||||
}
|
||||
const inspectedRoot = inspectFiberInstanceRaw(rootInstance);
|
||||
if (inspectedRoot === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inspectedRoot.isErrored) {
|
||||
inspectedRoots.isErrored = true;
|
||||
}
|
||||
for (let i = 0; i < inspectedRoot.errors.length; i++) {
|
||||
inspectedRoots.errors.push(inspectedRoot.errors[i]);
|
||||
}
|
||||
for (let i = 0; i < inspectedRoot.warnings.length; i++) {
|
||||
inspectedRoots.warnings.push(inspectedRoot.warnings[i]);
|
||||
}
|
||||
for (let i = 0; i < inspectedRoot.suspendedBy.length; i++) {
|
||||
inspectedRoots.suspendedBy.push(inspectedRoot.suspendedBy[i]);
|
||||
}
|
||||
const suspendedByRange = inspectedRoot.suspendedByRange;
|
||||
if (suspendedByRange !== null) {
|
||||
if (suspendedByRange[0] < minSuspendedByRange) {
|
||||
minSuspendedByRange = suspendedByRange[0];
|
||||
}
|
||||
if (suspendedByRange[1] > maxSuspendedByRange) {
|
||||
maxSuspendedByRange = suspendedByRange[1];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (minSuspendedByRange !== Infinity || maxSuspendedByRange !== -Infinity) {
|
||||
inspectedRoots.suspendedByRange = [
|
||||
minSuspendedByRange,
|
||||
maxSuspendedByRange,
|
||||
];
|
||||
}
|
||||
|
||||
return inspectedRoots;
|
||||
}
|
||||
|
||||
function logElementToConsole(id: number) {
|
||||
const result = isMostRecentlyInspectedElementCurrent(id)
|
||||
? mostRecentlyInspectedElement
|
||||
@@ -7867,13 +7977,9 @@ export function attach(
|
||||
|
||||
/**
|
||||
* Resets the all other roots of this renderer.
|
||||
* @param rootID The root that contains this milestone
|
||||
* @param suspendedSet List of IDs of SuspenseComponent Fibers
|
||||
*/
|
||||
function overrideSuspenseMilestone(
|
||||
rootID: FiberInstance['id'],
|
||||
suspendedSet: Array<FiberInstance['id']>,
|
||||
) {
|
||||
function overrideSuspenseMilestone(suspendedSet: Array<FiberInstance['id']>) {
|
||||
if (
|
||||
typeof setSuspenseHandler !== 'function' ||
|
||||
typeof scheduleUpdate !== 'function'
|
||||
@@ -7883,8 +7989,6 @@ export function attach(
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Allow overriding the timeline for the specified root.
|
||||
|
||||
const unsuspendedSet: Set<Fiber> = new Set(forceFallbackForFibers);
|
||||
|
||||
let resuspended = false;
|
||||
|
||||
+106
-1
@@ -412,7 +412,6 @@ export function attach(
|
||||
pushOperation(0); // Profiling flag
|
||||
pushOperation(0); // StrictMode supported?
|
||||
pushOperation(hasOwnerMetadata ? 1 : 0);
|
||||
pushOperation(supportsTogglingSuspense ? 1 : 0);
|
||||
|
||||
pushOperation(SUSPENSE_TREE_OPERATION_ADD);
|
||||
pushOperation(id);
|
||||
@@ -800,6 +799,20 @@ export function attach(
|
||||
return null;
|
||||
}
|
||||
|
||||
const rootID = internalInstanceToRootIDMap.get(internalInstance);
|
||||
if (rootID === undefined) {
|
||||
throw new Error('Expected to find root ID.');
|
||||
}
|
||||
const isRoot = rootID === id;
|
||||
return isRoot
|
||||
? inspectRootsRaw(rootID)
|
||||
: inspectInternalInstanceRaw(id, internalInstance);
|
||||
}
|
||||
|
||||
function inspectInternalInstanceRaw(
|
||||
id: number,
|
||||
internalInstance: InternalInstance,
|
||||
): InspectedElement | null {
|
||||
const {key} = getData(internalInstance);
|
||||
const type = getElementType(internalInstance);
|
||||
|
||||
@@ -903,6 +916,98 @@ export function attach(
|
||||
};
|
||||
}
|
||||
|
||||
function inspectRootsRaw(arbitraryRootID: number): InspectedElement | null {
|
||||
const roots =
|
||||
renderer.Mount._instancesByReactRootID ||
|
||||
renderer.Mount._instancesByContainerID;
|
||||
|
||||
const inspectedRoots: InspectedElement = {
|
||||
// invariants
|
||||
id: arbitraryRootID,
|
||||
type: ElementTypeRoot,
|
||||
// Properties we merge
|
||||
isErrored: false,
|
||||
errors: [],
|
||||
warnings: [],
|
||||
suspendedBy: [],
|
||||
suspendedByRange: null,
|
||||
// TODO: How to merge these?
|
||||
unknownSuspenders: UNKNOWN_SUSPENDERS_NONE,
|
||||
// Properties where merging doesn't make sense so we ignore them entirely in the UI
|
||||
rootType: null,
|
||||
plugins: {stylex: null},
|
||||
nativeTag: null,
|
||||
env: null,
|
||||
source: null,
|
||||
stack: null,
|
||||
// TODO: We could make the Frontend accept an array to display
|
||||
// a list of unique renderers contributing to this Screen.
|
||||
rendererPackageName: null,
|
||||
rendererVersion: null,
|
||||
// These don't make sense for a Root. They're just bottom values.
|
||||
key: null,
|
||||
canEditFunctionProps: false,
|
||||
canEditHooks: false,
|
||||
canEditFunctionPropsDeletePaths: false,
|
||||
canEditFunctionPropsRenamePaths: false,
|
||||
canEditHooksAndDeletePaths: false,
|
||||
canEditHooksAndRenamePaths: false,
|
||||
canToggleError: false,
|
||||
canToggleSuspense: false,
|
||||
isSuspended: false,
|
||||
hasLegacyContext: false,
|
||||
context: null,
|
||||
hooks: null,
|
||||
props: null,
|
||||
state: null,
|
||||
owners: null,
|
||||
};
|
||||
|
||||
let minSuspendedByRange = Infinity;
|
||||
let maxSuspendedByRange = -Infinity;
|
||||
|
||||
for (const rootKey in roots) {
|
||||
const internalInstance = roots[rootKey];
|
||||
const id = getID(internalInstance);
|
||||
const inspectedRoot = inspectInternalInstanceRaw(id, internalInstance);
|
||||
|
||||
if (inspectedRoot === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (inspectedRoot.isErrored) {
|
||||
inspectedRoots.isErrored = true;
|
||||
}
|
||||
for (let i = 0; i < inspectedRoot.errors.length; i++) {
|
||||
inspectedRoots.errors.push(inspectedRoot.errors[i]);
|
||||
}
|
||||
for (let i = 0; i < inspectedRoot.warnings.length; i++) {
|
||||
inspectedRoots.warnings.push(inspectedRoot.warnings[i]);
|
||||
}
|
||||
for (let i = 0; i < inspectedRoot.suspendedBy.length; i++) {
|
||||
inspectedRoots.suspendedBy.push(inspectedRoot.suspendedBy[i]);
|
||||
}
|
||||
const suspendedByRange = inspectedRoot.suspendedByRange;
|
||||
if (suspendedByRange !== null) {
|
||||
if (suspendedByRange[0] < minSuspendedByRange) {
|
||||
minSuspendedByRange = suspendedByRange[0];
|
||||
}
|
||||
if (suspendedByRange[1] > maxSuspendedByRange) {
|
||||
maxSuspendedByRange = suspendedByRange[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minSuspendedByRange !== Infinity || maxSuspendedByRange !== -Infinity) {
|
||||
inspectedRoots.suspendedByRange = [
|
||||
minSuspendedByRange,
|
||||
maxSuspendedByRange,
|
||||
];
|
||||
}
|
||||
|
||||
return inspectedRoots;
|
||||
}
|
||||
|
||||
function logElementToConsole(id: number): void {
|
||||
const result = inspectElementRaw(id);
|
||||
if (result === null) {
|
||||
|
||||
+1
-4
@@ -450,10 +450,7 @@ export type RendererInterface = {
|
||||
onErrorOrWarning?: OnErrorOrWarning,
|
||||
overrideError: (id: number, forceError: boolean) => void,
|
||||
overrideSuspense: (id: number, forceFallback: boolean) => void,
|
||||
overrideSuspenseMilestone: (
|
||||
rootID: number,
|
||||
suspendedSet: Array<number>,
|
||||
) => void,
|
||||
overrideSuspenseMilestone: (suspendedSet: Array<number>) => void,
|
||||
overrideValueAtPath: (
|
||||
type: Type,
|
||||
id: number,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import Agent from 'react-devtools-shared/src/backend/agent';
|
||||
import {hideOverlay, showOverlay} from './Highlighter';
|
||||
|
||||
import type {HostInstance} from 'react-devtools-shared/src/backend/types';
|
||||
import type {BackendBridge} from 'react-devtools-shared/src/bridge';
|
||||
import type {RendererInterface} from '../../types';
|
||||
|
||||
@@ -26,6 +27,7 @@ export default function setupHighlighter(
|
||||
): void {
|
||||
bridge.addListener('clearHostInstanceHighlight', clearHostInstanceHighlight);
|
||||
bridge.addListener('highlightHostInstance', highlightHostInstance);
|
||||
bridge.addListener('highlightHostInstances', highlightHostInstances);
|
||||
bridge.addListener('scrollToHostInstance', scrollToHostInstance);
|
||||
bridge.addListener('shutdown', stopInspectingHost);
|
||||
bridge.addListener('startInspectingHost', startInspectingHost);
|
||||
@@ -157,6 +159,52 @@ export default function setupHighlighter(
|
||||
hideOverlay(agent);
|
||||
}
|
||||
|
||||
function highlightHostInstances({
|
||||
displayName,
|
||||
hideAfterTimeout,
|
||||
elements,
|
||||
scrollIntoView,
|
||||
}: {
|
||||
displayName: string | null,
|
||||
hideAfterTimeout: boolean,
|
||||
elements: Array<{rendererID: number, id: number}>,
|
||||
scrollIntoView: boolean,
|
||||
}) {
|
||||
const nodes: Array<HostInstance> = [];
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
const {id, rendererID} = elements[i];
|
||||
const renderer = agent.rendererInterfaces[rendererID];
|
||||
if (renderer == null) {
|
||||
console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// In some cases fiber may already be unmounted
|
||||
if (!renderer.hasElementWithId(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const hostInstances = renderer.findHostInstancesForElementID(id);
|
||||
if (hostInstances !== null) {
|
||||
for (let j = 0; j < hostInstances.length; j++) {
|
||||
nodes.push(hostInstances[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodes.length > 0) {
|
||||
const node = nodes[0];
|
||||
// $FlowFixMe[method-unbinding]
|
||||
if (scrollIntoView && typeof node.scrollIntoView === 'function') {
|
||||
// If the node isn't visible show it before highlighting it.
|
||||
// We may want to reconsider this; it might be a little disruptive.
|
||||
node.scrollIntoView({block: 'nearest', inline: 'nearest'});
|
||||
}
|
||||
}
|
||||
|
||||
showOverlay(nodes, displayName, agent, hideAfterTimeout);
|
||||
}
|
||||
|
||||
function attemptScrollToHostInstance(
|
||||
renderer: RendererInterface,
|
||||
id: number,
|
||||
|
||||
+27
-1
@@ -95,7 +95,7 @@ export function inspectElement(
|
||||
id: number,
|
||||
path: InspectedElementPath | null,
|
||||
rendererID: number,
|
||||
shouldListenToPauseEvents: boolean = false,
|
||||
shouldListenToPauseEvents: boolean,
|
||||
): Promise<InspectedElementPayload> {
|
||||
const requestID = requestCounter++;
|
||||
const promise = getPromiseForRequestID<InspectedElementPayload>(
|
||||
@@ -117,6 +117,32 @@ export function inspectElement(
|
||||
return promise;
|
||||
}
|
||||
|
||||
export function inspectScreen(
|
||||
bridge: FrontendBridge,
|
||||
forceFullData: boolean,
|
||||
arbitraryRootID: number,
|
||||
path: InspectedElementPath | null,
|
||||
shouldListenToPauseEvents: boolean,
|
||||
): Promise<InspectedElementPayload> {
|
||||
const requestID = requestCounter++;
|
||||
const promise = getPromiseForRequestID<InspectedElementPayload>(
|
||||
requestID,
|
||||
'inspectedScreen',
|
||||
bridge,
|
||||
`Timed out while inspecting screen.`,
|
||||
shouldListenToPauseEvents,
|
||||
);
|
||||
|
||||
bridge.send('inspectScreen', {
|
||||
requestID,
|
||||
id: arbitraryRootID,
|
||||
path,
|
||||
forceFullData,
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
let storeAsGlobalCount = 0;
|
||||
|
||||
export function storeAsGlobal({
|
||||
|
||||
+16
-8
@@ -65,12 +65,6 @@ export const BRIDGE_PROTOCOL: Array<BridgeProtocol> = [
|
||||
{
|
||||
version: 2,
|
||||
minNpmVersion: '4.22.0',
|
||||
maxNpmVersion: '6.2.0',
|
||||
},
|
||||
// Version 3 adds supports-toggling-suspense bit to add-root
|
||||
{
|
||||
version: 3,
|
||||
minNpmVersion: '6.2.0',
|
||||
maxNpmVersion: null,
|
||||
},
|
||||
];
|
||||
@@ -92,6 +86,12 @@ type HighlightHostInstance = {
|
||||
openBuiltinElementsPanel: boolean,
|
||||
scrollIntoView: boolean,
|
||||
};
|
||||
type HighlightHostInstances = {
|
||||
elements: Array<ElementAndRendererID>,
|
||||
displayName: string | null,
|
||||
hideAfterTimeout: boolean,
|
||||
scrollIntoView: boolean,
|
||||
};
|
||||
|
||||
type ScrollToHostInstance = {
|
||||
...ElementAndRendererID,
|
||||
@@ -145,8 +145,6 @@ type OverrideSuspense = {
|
||||
};
|
||||
|
||||
type OverrideSuspenseMilestone = {
|
||||
rendererID: number,
|
||||
rootID: number,
|
||||
suspendedSet: Array<number>,
|
||||
};
|
||||
|
||||
@@ -167,6 +165,13 @@ type InspectElementParams = {
|
||||
requestID: number,
|
||||
};
|
||||
|
||||
type InspectScreenParams = {
|
||||
requestID: number,
|
||||
id: number,
|
||||
forceFullData: boolean,
|
||||
path: Array<number | string> | null,
|
||||
};
|
||||
|
||||
type StoreAsGlobalParams = {
|
||||
...ElementAndRendererID,
|
||||
count: number,
|
||||
@@ -199,6 +204,7 @@ export type BackendEvents = {
|
||||
fastRefreshScheduled: [],
|
||||
getSavedPreferences: [],
|
||||
inspectedElement: [InspectedElementPayload],
|
||||
inspectedScreen: [InspectedElementPayload],
|
||||
isReloadAndProfileSupportedByBackend: [boolean],
|
||||
operations: [Array<number>],
|
||||
ownersList: [OwnersList],
|
||||
@@ -243,7 +249,9 @@ type FrontendEvents = {
|
||||
getProfilingData: [{rendererID: RendererID}],
|
||||
getProfilingStatus: [],
|
||||
highlightHostInstance: [HighlightHostInstance],
|
||||
highlightHostInstances: [HighlightHostInstances],
|
||||
inspectElement: [InspectElementParams],
|
||||
inspectScreen: [InspectScreenParams],
|
||||
logElementToConsole: [ElementAndRendererID],
|
||||
overrideError: [OverrideError],
|
||||
overrideSuspense: [OverrideSuspense],
|
||||
|
||||
+42
-41
@@ -96,7 +96,6 @@ export type Capabilities = {
|
||||
supportsBasicProfiling: boolean,
|
||||
hasOwnerMetadata: boolean,
|
||||
supportsStrictMode: boolean,
|
||||
supportsTogglingSuspense: boolean,
|
||||
supportsAdvancedProfiling: AdvancedProfiling,
|
||||
};
|
||||
|
||||
@@ -506,14 +505,6 @@ export default class Store extends EventEmitter<{
|
||||
);
|
||||
}
|
||||
|
||||
supportsTogglingSuspense(rootID: Element['id']): boolean {
|
||||
const capabilities = this._rootIDToCapabilities.get(rootID);
|
||||
if (capabilities === undefined) {
|
||||
throw new Error(`No capabilities registered for root ${rootID}`);
|
||||
}
|
||||
return capabilities.supportsTogglingSuspense;
|
||||
}
|
||||
|
||||
// This build of DevTools supports the Timeline profiler.
|
||||
// This is a static flag, controlled by the Store config.
|
||||
get supportsTimeline(): boolean {
|
||||
@@ -898,38 +889,48 @@ export default class Store extends EventEmitter<{
|
||||
* @param uniqueSuspendersOnly Filters out boundaries without unique suspenders
|
||||
*/
|
||||
getSuspendableDocumentOrderSuspense(
|
||||
rootID: Element['id'] | void,
|
||||
uniqueSuspendersOnly: boolean,
|
||||
): $ReadOnlyArray<SuspenseNode['id']> {
|
||||
if (rootID === undefined) {
|
||||
return [];
|
||||
}
|
||||
const root = this.getElementByID(rootID);
|
||||
if (root === null) {
|
||||
return [];
|
||||
}
|
||||
if (!this.supportsTogglingSuspense(rootID)) {
|
||||
const roots = this.roots;
|
||||
if (roots.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const list: SuspenseNode['id'][] = [];
|
||||
const suspense = this.getSuspenseByID(rootID);
|
||||
if (suspense !== null) {
|
||||
const stack = [suspense];
|
||||
while (stack.length > 0) {
|
||||
const current = stack.pop();
|
||||
if (current === undefined) {
|
||||
continue;
|
||||
for (let i = 0; i < roots.length; i++) {
|
||||
const rootID = roots[i];
|
||||
const root = this.getElementByID(rootID);
|
||||
if (root === null) {
|
||||
continue;
|
||||
}
|
||||
// TODO: This includes boundaries that can't be suspended due to no support from the renderer.
|
||||
|
||||
const suspense = this.getSuspenseByID(rootID);
|
||||
if (suspense !== null) {
|
||||
if (list.length === 0) {
|
||||
// start with an arbitrary root that will allow inspection of the Screen
|
||||
list.push(suspense.id);
|
||||
}
|
||||
// Include the root even if we won't show it suspended (because that's just blank).
|
||||
// You should be able to see what suspended the shell.
|
||||
if (!uniqueSuspendersOnly || current.hasUniqueSuspenders) {
|
||||
list.push(current.id);
|
||||
}
|
||||
// Add children in reverse order to maintain document order
|
||||
for (let j = current.children.length - 1; j >= 0; j--) {
|
||||
const childSuspense = this.getSuspenseByID(current.children[j]);
|
||||
if (childSuspense !== null) {
|
||||
stack.push(childSuspense);
|
||||
|
||||
const stack = [suspense];
|
||||
while (stack.length > 0) {
|
||||
const current = stack.pop();
|
||||
if (current === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
(!uniqueSuspendersOnly || current.hasUniqueSuspenders) &&
|
||||
// Roots are already included as part of the Screen
|
||||
current.id !== rootID
|
||||
) {
|
||||
list.push(current.id);
|
||||
}
|
||||
// Add children in reverse order to maintain document order
|
||||
for (let j = current.children.length - 1; j >= 0; j--) {
|
||||
const childSuspense = this.getSuspenseByID(current.children[j]);
|
||||
if (childSuspense !== null) {
|
||||
stack.push(childSuspense);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1191,7 +1192,6 @@ export default class Store extends EventEmitter<{
|
||||
|
||||
let supportsStrictMode = false;
|
||||
let hasOwnerMetadata = false;
|
||||
let supportsTogglingSuspense = false;
|
||||
|
||||
// If we don't know the bridge protocol, guess that we're dealing with the latest.
|
||||
// If we do know it, we can take it into consideration when parsing operations.
|
||||
@@ -1204,9 +1204,6 @@ export default class Store extends EventEmitter<{
|
||||
|
||||
hasOwnerMetadata = operations[i] > 0;
|
||||
i++;
|
||||
|
||||
supportsTogglingSuspense = operations[i] > 0;
|
||||
i++;
|
||||
}
|
||||
|
||||
this._roots = this._roots.concat(id);
|
||||
@@ -1215,7 +1212,6 @@ export default class Store extends EventEmitter<{
|
||||
supportsBasicProfiling,
|
||||
hasOwnerMetadata,
|
||||
supportsStrictMode,
|
||||
supportsTogglingSuspense,
|
||||
supportsAdvancedProfiling,
|
||||
});
|
||||
|
||||
@@ -1561,7 +1557,12 @@ export default class Store extends EventEmitter<{
|
||||
if (name === null) {
|
||||
// The boundary isn't explicitly named.
|
||||
// Pick a sensible default.
|
||||
name = this._guessSuspenseName(element);
|
||||
if (parentID === 0) {
|
||||
// For Roots we use their display name.
|
||||
name = element.displayName;
|
||||
} else {
|
||||
name = this._guessSuspenseName(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -269,18 +269,21 @@ export default function InspectedElementWrapper(_: Props): React.Node {
|
||||
<ButtonIcon type="error" />
|
||||
</Toggle>
|
||||
)}
|
||||
{canToggleSuspense && (
|
||||
{canToggleSuspense || isSuspended ? (
|
||||
<Toggle
|
||||
isChecked={isSuspended}
|
||||
isDisabled={!canToggleSuspense}
|
||||
onChange={toggleSuspended}
|
||||
title={
|
||||
isSuspended
|
||||
? 'Unsuspend the selected component'
|
||||
? canToggleSuspense
|
||||
? 'Unsuspend the selected component'
|
||||
: 'This boundary is still suspended'
|
||||
: 'Suspend the selected component'
|
||||
}>
|
||||
<ButtonIcon type="suspend" />
|
||||
</Toggle>
|
||||
)}
|
||||
) : null}
|
||||
{store.supportsInspectMatchingDOMElement && (
|
||||
<Button
|
||||
onClick={highlightElement}
|
||||
|
||||
+11
-2
@@ -17,7 +17,11 @@ import NewKeyValue from './NewKeyValue';
|
||||
import {alphaSortEntries, serializeDataForCopy} from '../utils';
|
||||
import Store from '../../store';
|
||||
import styles from './InspectedElementSharedStyles.css';
|
||||
import {ElementTypeClass} from 'react-devtools-shared/src/frontend/types';
|
||||
import {
|
||||
ElementTypeClass,
|
||||
ElementTypeSuspense,
|
||||
ElementTypeActivity,
|
||||
} from 'react-devtools-shared/src/frontend/types';
|
||||
import {withPermissionsCheck} from 'react-devtools-shared/src/frontend/utils/withPermissionsCheck';
|
||||
|
||||
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
|
||||
@@ -50,7 +54,12 @@ export default function InspectedElementPropsTree({
|
||||
const canDeletePaths =
|
||||
type === ElementTypeClass || canEditFunctionPropsDeletePaths;
|
||||
const canEditValues =
|
||||
!readOnly && (type === ElementTypeClass || canEditFunctionProps);
|
||||
!readOnly &&
|
||||
(type === ElementTypeClass || canEditFunctionProps) &&
|
||||
// Make it read-only for Suspense to make it a bit cleaner. It's not
|
||||
// useful to edit children anyway.
|
||||
type !== ElementTypeSuspense &&
|
||||
type !== ElementTypeActivity;
|
||||
const canRenamePaths =
|
||||
type === ElementTypeClass || canEditFunctionPropsRenamePaths;
|
||||
|
||||
|
||||
Vendored
+11
@@ -322,6 +322,17 @@ export default function InspectedElementSuspendedBy({
|
||||
(suspendedBy == null || suspendedBy.length === 0) &&
|
||||
inspectedElement.unknownSuspenders === UNKNOWN_SUSPENDERS_NONE
|
||||
) {
|
||||
if (inspectedElement.isSuspended) {
|
||||
// If we're still suspended, show a place holder until the data loads.
|
||||
// We don't know what we're suspended by until it has loaded.
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.HeaderRow}>
|
||||
<div className={styles.Header}>suspended...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Vendored
-72
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {OptionsContext} from '../context';
|
||||
import EditableValue from './EditableValue';
|
||||
import Store from '../../store';
|
||||
import {ElementTypeSuspense} from 'react-devtools-shared/src/frontend/types';
|
||||
import styles from './InspectedElementSharedStyles.css';
|
||||
|
||||
import type {InspectedElement} from 'react-devtools-shared/src/frontend/types';
|
||||
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
|
||||
|
||||
type Props = {
|
||||
bridge: FrontendBridge,
|
||||
inspectedElement: InspectedElement,
|
||||
store: Store,
|
||||
};
|
||||
|
||||
export default function InspectedElementSuspenseToggle({
|
||||
bridge,
|
||||
inspectedElement,
|
||||
store,
|
||||
}: Props): React.Node {
|
||||
const {readOnly} = React.useContext(OptionsContext);
|
||||
|
||||
const {id, isSuspended, type} = inspectedElement;
|
||||
const canToggleSuspense = !readOnly && inspectedElement.canToggleSuspense;
|
||||
|
||||
if (type !== ElementTypeSuspense) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const toggleSuspense = (path: any, value: boolean) => {
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (rendererID !== null) {
|
||||
bridge.send('overrideSuspense', {
|
||||
id,
|
||||
rendererID,
|
||||
forceFallback: value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.HeaderRow}>
|
||||
<div className={styles.Header}>suspense</div>
|
||||
</div>
|
||||
<div className={styles.ToggleSuspenseRow}>
|
||||
<span className={styles.Name}>Suspended</span>
|
||||
{canToggleSuspense ? (
|
||||
// key is required to keep <EditableValue> and header row toggle button in sync
|
||||
<EditableValue
|
||||
key={isSuspended}
|
||||
overrideValue={toggleSuspense}
|
||||
path={['suspense', 'Suspended']}
|
||||
value={isSuspended}
|
||||
/>
|
||||
) : (
|
||||
<span className={styles.Value}>{isSuspended ? 'true' : 'false'}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+29
-17
@@ -17,7 +17,6 @@ import InspectedElementHooksTree from './InspectedElementHooksTree';
|
||||
import InspectedElementPropsTree from './InspectedElementPropsTree';
|
||||
import InspectedElementStateTree from './InspectedElementStateTree';
|
||||
import InspectedElementStyleXPlugin from './InspectedElementStyleXPlugin';
|
||||
import InspectedElementSuspenseToggle from './InspectedElementSuspenseToggle';
|
||||
import InspectedElementSuspendedBy from './InspectedElementSuspendedBy';
|
||||
import NativeStyleEditor from './NativeStyleEditor';
|
||||
import {enableStyleXFeatures} from 'react-devtools-feature-flags';
|
||||
@@ -25,6 +24,10 @@ import InspectedElementSourcePanel from './InspectedElementSourcePanel';
|
||||
import StackTraceView from './StackTraceView';
|
||||
import OwnerView from './OwnerView';
|
||||
import Skeleton from './Skeleton';
|
||||
import {
|
||||
ElementTypeSuspense,
|
||||
ElementTypeActivity,
|
||||
} from 'react-devtools-shared/src/frontend/types';
|
||||
|
||||
import styles from './InspectedElementView.css';
|
||||
|
||||
@@ -61,6 +64,7 @@ export default function InspectedElementView({
|
||||
rootType,
|
||||
source,
|
||||
nativeTag,
|
||||
type,
|
||||
} = inspectedElement;
|
||||
|
||||
const bridge = useContext(BridgeContext);
|
||||
@@ -75,6 +79,17 @@ export default function InspectedElementView({
|
||||
const showRenderedBy =
|
||||
showStack || showOwnersList || rendererLabel !== null || rootType !== null;
|
||||
|
||||
const propsSection = (
|
||||
<div className={styles.InspectedElementSection}>
|
||||
<InspectedElementPropsTree
|
||||
bridge={bridge}
|
||||
element={element}
|
||||
inspectedElement={inspectedElement}
|
||||
store={store}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={styles.InspectedElement}>
|
||||
@@ -86,22 +101,12 @@ export default function InspectedElementView({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.InspectedElementSection}>
|
||||
<InspectedElementPropsTree
|
||||
bridge={bridge}
|
||||
element={element}
|
||||
inspectedElement={inspectedElement}
|
||||
store={store}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.InspectedElementSection}>
|
||||
<InspectedElementSuspenseToggle
|
||||
bridge={bridge}
|
||||
inspectedElement={inspectedElement}
|
||||
store={store}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
// For Suspense and Activity we show the props further down.
|
||||
type !== ElementTypeSuspense && type !== ElementTypeActivity
|
||||
? propsSection
|
||||
: null
|
||||
}
|
||||
|
||||
<div className={styles.InspectedElementSection}>
|
||||
<InspectedElementStateTree
|
||||
@@ -166,6 +171,13 @@ export default function InspectedElementView({
|
||||
/>
|
||||
</div>
|
||||
|
||||
{
|
||||
// For Suspense and Activity we show the props below suspended by to give that more priority.
|
||||
type !== ElementTypeSuspense && type !== ElementTypeActivity
|
||||
? null
|
||||
: propsSection
|
||||
}
|
||||
|
||||
{showRenderedBy && (
|
||||
<div
|
||||
className={styles.InspectedElementSection}
|
||||
|
||||
-1
@@ -209,7 +209,6 @@ function updateTree(
|
||||
i++; // Profiling flag
|
||||
i++; // supportsStrictMode flag
|
||||
i++; // hasOwnerMetadata flag
|
||||
i++; // supportsTogglingSuspense flag
|
||||
|
||||
if (__DEBUG__) {
|
||||
debug('Add', `new root fiber ${id}`);
|
||||
|
||||
+4
-4
@@ -25,7 +25,7 @@ export default function SuspenseBreadcrumbs(): React$Node {
|
||||
const store = useContext(StoreContext);
|
||||
const treeDispatch = useContext(TreeDispatcherContext);
|
||||
const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
|
||||
const {selectedSuspenseID, selectedRootID, lineage} = useContext(
|
||||
const {selectedSuspenseID, lineage, roots} = useContext(
|
||||
SuspenseTreeStateContext,
|
||||
);
|
||||
|
||||
@@ -45,13 +45,13 @@ export default function SuspenseBreadcrumbs(): React$Node {
|
||||
// that rendered the whole screen. In laymans terms this is really "Initial Paint".
|
||||
// TODO: Once we add subtree selection, then the equivalent should be called
|
||||
// "Transition" since in that case it's really about a Transition within the page.
|
||||
selectedRootID !== null ? (
|
||||
roots.length > 0 ? (
|
||||
<li
|
||||
className={styles.SuspenseBreadcrumbsListItem}
|
||||
aria-current={selectedSuspenseID === selectedRootID}>
|
||||
aria-current="true">
|
||||
<button
|
||||
className={styles.SuspenseBreadcrumbsButton}
|
||||
onClick={handleClick.bind(null, selectedRootID)}
|
||||
onClick={handleClick.bind(null, roots[0])}
|
||||
type="button">
|
||||
Initial Paint
|
||||
</button>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
.SuspenseRectsContainer {
|
||||
padding: .25rem;
|
||||
cursor: pointer;
|
||||
outline: 1px solid var(--color-component-name);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.SuspenseRectsContainer[data-highlighted='true'] {
|
||||
background: var(--color-dimmest);
|
||||
}
|
||||
|
||||
.SuspenseRectsViewBox {
|
||||
@@ -28,6 +35,8 @@
|
||||
pointer-events: all;
|
||||
outline-style: solid;
|
||||
outline-width: 1px;
|
||||
border-radius: 0.125rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.SuspenseRectsScaledRect {
|
||||
@@ -42,7 +51,7 @@
|
||||
|
||||
/* highlight this boundary */
|
||||
.SuspenseRectsBoundary:hover:not(:has(.SuspenseRectsBoundary:hover)) > .SuspenseRectsRect, .SuspenseRectsBoundary[data-highlighted='true'] > .SuspenseRectsRect {
|
||||
background-color: var(--color-background-hover);
|
||||
background-color: var(--color-background-hover);
|
||||
}
|
||||
|
||||
.SuspenseRectsRect[data-highlighted='true'] {
|
||||
|
||||
+30
-7
@@ -278,11 +278,7 @@ function getDocumentBoundingRect(
|
||||
};
|
||||
}
|
||||
|
||||
function SuspenseRectsShell({
|
||||
rootID,
|
||||
}: {
|
||||
rootID: SuspenseNode['id'],
|
||||
}): React$Node {
|
||||
function SuspenseRectsRoot({rootID}: {rootID: SuspenseNode['id']}): React$Node {
|
||||
const store = useContext(StoreContext);
|
||||
const root = store.getSuspenseByID(rootID);
|
||||
if (root === null) {
|
||||
@@ -299,6 +295,9 @@ const ViewBox = createContext<Rect>((null: any));
|
||||
|
||||
function SuspenseRectsContainer(): React$Node {
|
||||
const store = useContext(StoreContext);
|
||||
const {inspectedElementID} = useContext(TreeStateContext);
|
||||
const treeDispatch = useContext(TreeDispatcherContext);
|
||||
const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
|
||||
// TODO: This relies on a full re-render of all children when the Suspense tree changes.
|
||||
const {roots} = useContext(SuspenseTreeStateContext);
|
||||
|
||||
@@ -312,14 +311,38 @@ function SuspenseRectsContainer(): React$Node {
|
||||
const width = '100%';
|
||||
const aspectRatio = `1 / ${heightScale}`;
|
||||
|
||||
function handleClick(event: SyntheticMouseEvent) {
|
||||
if (event.defaultPrevented) {
|
||||
// Already clicked on an inner rect
|
||||
return;
|
||||
}
|
||||
if (roots.length === 0) {
|
||||
// Nothing to select
|
||||
return;
|
||||
}
|
||||
const arbitraryRootID = roots[0];
|
||||
|
||||
event.preventDefault();
|
||||
treeDispatch({type: 'SELECT_ELEMENT_BY_ID', payload: arbitraryRootID});
|
||||
suspenseTreeDispatch({
|
||||
type: 'SET_SUSPENSE_LINEAGE',
|
||||
payload: arbitraryRootID,
|
||||
});
|
||||
}
|
||||
|
||||
const isRootSelected = roots.includes(inspectedElementID);
|
||||
|
||||
return (
|
||||
<div className={styles.SuspenseRectsContainer}>
|
||||
<div
|
||||
className={styles.SuspenseRectsContainer}
|
||||
onClick={handleClick}
|
||||
data-highlighted={isRootSelected}>
|
||||
<ViewBox.Provider value={boundingBox}>
|
||||
<div
|
||||
className={styles.SuspenseRectsViewBox}
|
||||
style={{aspectRatio, width}}>
|
||||
{roots.map(rootID => {
|
||||
return <SuspenseRectsShell key={rootID} rootID={rootID} />;
|
||||
return <SuspenseRectsRoot key={rootID} rootID={rootID} />;
|
||||
})}
|
||||
</div>
|
||||
</ViewBox.Provider>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.SuspenseScrubberBead, .SuspenseScrubberBeadSelected {
|
||||
.SuspenseScrubberBead {
|
||||
flex: 1;
|
||||
height: 0.5rem;
|
||||
background: var(--color-background-selected);
|
||||
@@ -51,9 +51,11 @@
|
||||
background: var(--color-background-selected);
|
||||
}
|
||||
|
||||
.SuspenseScrubberBeadTransition {
|
||||
background: var(--color-component-name);
|
||||
}
|
||||
|
||||
.SuspenseScrubberStepHighlight > .SuspenseScrubberBead,
|
||||
.SuspenseScrubberStepHighlight > .SuspenseScrubberBeadSelected,
|
||||
.SuspenseScrubberStep:hover > .SuspenseScrubberBead,
|
||||
.SuspenseScrubberStep:hover > .SuspenseScrubberBeadSelected {
|
||||
.SuspenseScrubberStep:hover > .SuspenseScrubberBead {
|
||||
height: 0.75rem;
|
||||
}
|
||||
|
||||
+30
-14
@@ -14,6 +14,8 @@ import {useRef} from 'react';
|
||||
|
||||
import styles from './SuspenseScrubber.css';
|
||||
|
||||
import Tooltip from '../Components/reach-ui/tooltip';
|
||||
|
||||
export default function SuspenseScrubber({
|
||||
min,
|
||||
max,
|
||||
@@ -53,24 +55,38 @@ export default function SuspenseScrubber({
|
||||
const steps = [];
|
||||
for (let index = min; index <= max; index++) {
|
||||
steps.push(
|
||||
<div
|
||||
<Tooltip
|
||||
key={index}
|
||||
className={
|
||||
styles.SuspenseScrubberStep +
|
||||
(highlight === index
|
||||
? ' ' + styles.SuspenseScrubberStepHighlight
|
||||
: '')
|
||||
}
|
||||
onPointerDown={handlePress.bind(null, index)}
|
||||
onMouseEnter={onHoverSegment.bind(null, index)}>
|
||||
label={
|
||||
index === min
|
||||
? // The first step in the timeline is always a Transition (Initial Paint).
|
||||
// TODO: Support multiple environments.
|
||||
'Initial Paint'
|
||||
: // TODO: Consider adding the name of this specific boundary if this step has only one.
|
||||
'Suspense'
|
||||
}>
|
||||
<div
|
||||
className={
|
||||
index <= value
|
||||
? styles.SuspenseScrubberBeadSelected
|
||||
: styles.SuspenseScrubberBead
|
||||
styles.SuspenseScrubberStep +
|
||||
(highlight === index
|
||||
? ' ' + styles.SuspenseScrubberStepHighlight
|
||||
: '')
|
||||
}
|
||||
/>
|
||||
</div>,
|
||||
onPointerDown={handlePress.bind(null, index)}
|
||||
onMouseEnter={onHoverSegment.bind(null, index)}>
|
||||
<div
|
||||
className={
|
||||
styles.SuspenseScrubberBead +
|
||||
(index === min
|
||||
? // The first step in the timeline is always a Transition (Initial Paint).
|
||||
// TODO: Support multiple environments.
|
||||
' ' + styles.SuspenseScrubberBeadTransition
|
||||
: '') +
|
||||
(index <= value ? ' ' + styles.SuspenseScrubberBeadSelected : '')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+22
-66
@@ -34,13 +34,13 @@ import {
|
||||
SuspenseTreeStateContext,
|
||||
} from './SuspenseTreeContext';
|
||||
import {StoreContext, OptionsContext} from '../context';
|
||||
import {TreeDispatcherContext} from '../Components/TreeContext';
|
||||
import {
|
||||
TreeDispatcherContext,
|
||||
TreeStateContext,
|
||||
} from '../Components/TreeContext';
|
||||
import Button from '../Button';
|
||||
import Toggle from '../Toggle';
|
||||
import typeof {
|
||||
SyntheticEvent,
|
||||
SyntheticPointerEvent,
|
||||
} from 'react-dom-bindings/src/events/SyntheticEvent';
|
||||
import typeof {SyntheticPointerEvent} from 'react-dom-bindings/src/events/SyntheticEvent';
|
||||
import SettingsModal from 'react-devtools-shared/src/devtools/views/Settings/SettingsModal';
|
||||
import SettingsModalContextToggle from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContextToggle';
|
||||
import {SettingsModalContextController} from 'react-devtools-shared/src/devtools/views/Settings/SettingsModalContext';
|
||||
@@ -71,20 +71,14 @@ function ToggleUniqueSuspenders() {
|
||||
const store = useContext(StoreContext);
|
||||
const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
|
||||
|
||||
const {selectedRootID: rootID, uniqueSuspendersOnly} = useContext(
|
||||
SuspenseTreeStateContext,
|
||||
);
|
||||
const {uniqueSuspendersOnly} = useContext(SuspenseTreeStateContext);
|
||||
|
||||
function handleToggleUniqueSuspenders() {
|
||||
const nextUniqueSuspendersOnly = !uniqueSuspendersOnly;
|
||||
const nextTimeline =
|
||||
rootID === null
|
||||
? []
|
||||
: // TODO: Handle different timeline modes (e.g. random order)
|
||||
store.getSuspendableDocumentOrderSuspense(
|
||||
rootID,
|
||||
nextUniqueSuspendersOnly,
|
||||
);
|
||||
// TODO: Handle different timeline modes (e.g. random order)
|
||||
const nextTimeline = store.getSuspendableDocumentOrderSuspense(
|
||||
nextUniqueSuspendersOnly,
|
||||
);
|
||||
suspenseTreeDispatch({
|
||||
type: 'SET_SUSPENSE_TIMELINE',
|
||||
payload: [nextTimeline, null, nextUniqueSuspendersOnly],
|
||||
@@ -101,55 +95,6 @@ function ToggleUniqueSuspenders() {
|
||||
);
|
||||
}
|
||||
|
||||
function SelectRoot() {
|
||||
const store = useContext(StoreContext);
|
||||
const {roots, selectedRootID, uniqueSuspendersOnly} = useContext(
|
||||
SuspenseTreeStateContext,
|
||||
);
|
||||
const treeDispatch = useContext(TreeDispatcherContext);
|
||||
const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
|
||||
|
||||
function handleChange(event: SyntheticEvent) {
|
||||
const newRootID = +event.currentTarget.value;
|
||||
// TODO: scrollIntoView both suspense rects and host instance.
|
||||
const nextTimeline = store.getSuspendableDocumentOrderSuspense(
|
||||
newRootID,
|
||||
uniqueSuspendersOnly,
|
||||
);
|
||||
suspenseTreeDispatch({
|
||||
type: 'SET_SUSPENSE_TIMELINE',
|
||||
payload: [nextTimeline, newRootID, uniqueSuspendersOnly],
|
||||
});
|
||||
if (nextTimeline.length > 0) {
|
||||
const milestone = nextTimeline[nextTimeline.length - 1];
|
||||
treeDispatch({type: 'SELECT_ELEMENT_BY_ID', payload: milestone});
|
||||
}
|
||||
}
|
||||
return (
|
||||
roots.length > 0 && (
|
||||
<select
|
||||
aria-label="Select Suspense Root"
|
||||
className={styles.SuspenseTimelineRootSwitcher}
|
||||
onChange={handleChange}
|
||||
value={selectedRootID === null ? -1 : selectedRootID}>
|
||||
<option disabled={true} value={-1}>
|
||||
----
|
||||
</option>
|
||||
{roots.map(rootID => {
|
||||
// TODO: Use name
|
||||
const name = '#' + rootID;
|
||||
// TODO: Highlight host on hover
|
||||
return (
|
||||
<option key={rootID} value={rootID}>
|
||||
{name}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function ToggleTreeList({
|
||||
dispatch,
|
||||
state,
|
||||
@@ -240,6 +185,18 @@ function SuspenseTab(_: {}) {
|
||||
treeListHorizontalFraction,
|
||||
} = state;
|
||||
|
||||
const {inspectedElementID} = useContext(TreeStateContext);
|
||||
const {timeline} = useContext(SuspenseTreeStateContext);
|
||||
const treeDispatch = useContext(TreeDispatcherContext);
|
||||
useLayoutEffect(() => {
|
||||
// If the inspected element is still null and we've loaded a timeline, we can set the initial selection.
|
||||
// TODO: This tab should use its own source of truth instead so we only show suspense boundaries.
|
||||
if (inspectedElementID === null && timeline.length > 0) {
|
||||
const milestone = timeline[timeline.length - 1];
|
||||
treeDispatch({type: 'SELECT_ELEMENT_BY_ID', payload: milestone});
|
||||
}
|
||||
}, [timeline, inspectedElementID]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const wrapperElement = wrapperTreeRef.current;
|
||||
|
||||
@@ -427,7 +384,6 @@ function SuspenseTab(_: {}) {
|
||||
<div className={styles.SuspenseBreadcrumbs}>
|
||||
<SuspenseBreadcrumbs />
|
||||
</div>
|
||||
<SelectRoot />
|
||||
<div className={styles.VRule} />
|
||||
<ToggleUniqueSuspenders />
|
||||
{!hideSettings && <SettingsModalContextToggle />}
|
||||
|
||||
+5
-37
@@ -9,7 +9,7 @@
|
||||
|
||||
import * as React from 'react';
|
||||
import {useContext, useEffect, useRef} from 'react';
|
||||
import {BridgeContext, StoreContext} from '../context';
|
||||
import {BridgeContext} from '../context';
|
||||
import {TreeDispatcherContext} from '../Components/TreeContext';
|
||||
import {useHighlightHostInstance, useScrollToHostInstance} from '../hooks';
|
||||
import {
|
||||
@@ -23,20 +23,15 @@ import ButtonIcon from '../ButtonIcon';
|
||||
|
||||
function SuspenseTimelineInput() {
|
||||
const bridge = useContext(BridgeContext);
|
||||
const store = useContext(StoreContext);
|
||||
const treeDispatch = useContext(TreeDispatcherContext);
|
||||
const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext);
|
||||
const {highlightHostInstance, clearHighlightHostInstance} =
|
||||
useHighlightHostInstance();
|
||||
const scrollToHostInstance = useScrollToHostInstance();
|
||||
|
||||
const {
|
||||
selectedRootID: rootID,
|
||||
timeline,
|
||||
timelineIndex,
|
||||
hoveredTimelineIndex,
|
||||
playing,
|
||||
} = useContext(SuspenseTreeStateContext);
|
||||
const {timeline, timelineIndex, hoveredTimelineIndex, playing} = useContext(
|
||||
SuspenseTreeStateContext,
|
||||
);
|
||||
|
||||
const min = 0;
|
||||
const max = timeline.length > 0 ? timeline.length - 1 : 0;
|
||||
@@ -112,24 +107,12 @@ function SuspenseTimelineInput() {
|
||||
// For now we just exclude it from deps since we don't lint those anyway.
|
||||
function changeTimelineIndex(newIndex: number) {
|
||||
// Synchronize timeline index with what is resuspended.
|
||||
if (rootID === null) {
|
||||
return;
|
||||
}
|
||||
const rendererID = store.getRendererIDForElement(rootID);
|
||||
if (rendererID === null) {
|
||||
console.error(
|
||||
`No renderer ID found for root element ${rootID} in suspense timeline.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
// We suspend everything after the current selection. The root isn't showing
|
||||
// anything suspended in the root. The step after that should have one less
|
||||
// thing suspended. I.e. the first suspense boundary should be unsuspended
|
||||
// when it's selected. This also lets you show everything in the last step.
|
||||
const suspendedSet = timeline.slice(timelineIndex + 1);
|
||||
bridge.send('overrideSuspenseMilestone', {
|
||||
rendererID,
|
||||
rootID,
|
||||
suspendedSet,
|
||||
});
|
||||
if (isInitialMount.current) {
|
||||
@@ -164,20 +147,6 @@ function SuspenseTimelineInput() {
|
||||
};
|
||||
}, [playing]);
|
||||
|
||||
if (rootID === null) {
|
||||
return (
|
||||
<div className={styles.SuspenseTimelineInput}>No root selected.</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!store.supportsTogglingSuspense(rootID)) {
|
||||
return (
|
||||
<div className={styles.SuspenseTimelineInput}>
|
||||
Can't step through Suspense in production apps.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (timeline.length === 0) {
|
||||
return (
|
||||
<div className={styles.SuspenseTimelineInput}>
|
||||
@@ -226,10 +195,9 @@ function SuspenseTimelineInput() {
|
||||
}
|
||||
|
||||
export default function SuspenseTimeline(): React$Node {
|
||||
const {selectedRootID} = useContext(SuspenseTreeStateContext);
|
||||
return (
|
||||
<div className={styles.SuspenseTimelineContainer}>
|
||||
<SuspenseTimelineInput key={selectedRootID} />
|
||||
<SuspenseTimelineInput />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+36
-84
@@ -7,10 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
import type {ReactContext} from 'shared/ReactTypes';
|
||||
import type {
|
||||
Element,
|
||||
SuspenseNode,
|
||||
} from 'react-devtools-shared/src/frontend/types';
|
||||
import type {SuspenseNode} from 'react-devtools-shared/src/frontend/types';
|
||||
import type Store from '../../store';
|
||||
|
||||
import * as React from 'react';
|
||||
@@ -27,13 +24,13 @@ import {StoreContext} from '../context';
|
||||
export type SuspenseTreeState = {
|
||||
lineage: $ReadOnlyArray<SuspenseNode['id']> | null,
|
||||
roots: $ReadOnlyArray<SuspenseNode['id']>,
|
||||
selectedRootID: SuspenseNode['id'] | null,
|
||||
selectedSuspenseID: SuspenseNode['id'] | null,
|
||||
timeline: $ReadOnlyArray<SuspenseNode['id']>,
|
||||
timelineIndex: number | -1,
|
||||
hoveredTimelineIndex: number | -1,
|
||||
uniqueSuspendersOnly: boolean,
|
||||
playing: boolean,
|
||||
autoSelect: boolean,
|
||||
};
|
||||
|
||||
type ACTION_SUSPENSE_TREE_MUTATION = {
|
||||
@@ -107,60 +104,28 @@ type Props = {
|
||||
children: React$Node,
|
||||
};
|
||||
|
||||
function getDefaultRootID(store: Store): Element['id'] | null {
|
||||
const designatedRootID = store.roots.find(rootID => {
|
||||
const suspense = store.getSuspenseByID(rootID);
|
||||
return (
|
||||
store.supportsTogglingSuspense(rootID) &&
|
||||
suspense !== null &&
|
||||
suspense.children.length > 1
|
||||
);
|
||||
});
|
||||
|
||||
return designatedRootID === undefined ? null : designatedRootID;
|
||||
}
|
||||
|
||||
function getInitialState(store: Store): SuspenseTreeState {
|
||||
let initialState: SuspenseTreeState;
|
||||
const uniqueSuspendersOnly = true;
|
||||
const selectedRootID = getDefaultRootID(store);
|
||||
// TODO: Default to nearest from inspected
|
||||
if (selectedRootID === null) {
|
||||
initialState = {
|
||||
selectedSuspenseID: null,
|
||||
lineage: null,
|
||||
roots: store.roots,
|
||||
selectedRootID,
|
||||
timeline: [],
|
||||
timelineIndex: -1,
|
||||
hoveredTimelineIndex: -1,
|
||||
uniqueSuspendersOnly,
|
||||
playing: false,
|
||||
};
|
||||
} else {
|
||||
const timeline = store.getSuspendableDocumentOrderSuspense(
|
||||
selectedRootID,
|
||||
uniqueSuspendersOnly,
|
||||
);
|
||||
const timelineIndex = timeline.length - 1;
|
||||
const selectedSuspenseID =
|
||||
timelineIndex === -1 ? null : timeline[timelineIndex];
|
||||
const lineage =
|
||||
selectedSuspenseID !== null
|
||||
? store.getSuspenseLineage(selectedSuspenseID)
|
||||
: [];
|
||||
initialState = {
|
||||
selectedSuspenseID,
|
||||
lineage,
|
||||
roots: store.roots,
|
||||
selectedRootID,
|
||||
timeline,
|
||||
timelineIndex,
|
||||
hoveredTimelineIndex: -1,
|
||||
uniqueSuspendersOnly,
|
||||
playing: false,
|
||||
};
|
||||
}
|
||||
const timeline =
|
||||
store.getSuspendableDocumentOrderSuspense(uniqueSuspendersOnly);
|
||||
const timelineIndex = timeline.length - 1;
|
||||
const selectedSuspenseID =
|
||||
timelineIndex === -1 ? null : timeline[timelineIndex];
|
||||
const lineage =
|
||||
selectedSuspenseID !== null
|
||||
? store.getSuspenseLineage(selectedSuspenseID)
|
||||
: [];
|
||||
const initialState: SuspenseTreeState = {
|
||||
selectedSuspenseID,
|
||||
lineage,
|
||||
roots: store.roots,
|
||||
timeline,
|
||||
timelineIndex,
|
||||
hoveredTimelineIndex: -1,
|
||||
uniqueSuspendersOnly,
|
||||
playing: false,
|
||||
autoSelect: true,
|
||||
};
|
||||
|
||||
return initialState;
|
||||
}
|
||||
@@ -209,29 +174,19 @@ function SuspenseTreeContextController({children}: Props): React.Node {
|
||||
selectedTimelineID = removedIDs.get(selectedTimelineID);
|
||||
}
|
||||
|
||||
let nextRootID = state.selectedRootID;
|
||||
if (selectedTimelineID !== null && selectedTimelineID !== 0) {
|
||||
nextRootID =
|
||||
store.getSuspenseRootIDForSuspense(selectedTimelineID);
|
||||
}
|
||||
if (nextRootID === null) {
|
||||
nextRootID = getDefaultRootID(store);
|
||||
}
|
||||
|
||||
const nextTimeline =
|
||||
nextRootID === null
|
||||
? []
|
||||
: // TODO: Handle different timeline modes (e.g. random order)
|
||||
store.getSuspendableDocumentOrderSuspense(
|
||||
nextRootID,
|
||||
state.uniqueSuspendersOnly,
|
||||
);
|
||||
// TODO: Handle different timeline modes (e.g. random order)
|
||||
const nextTimeline = store.getSuspendableDocumentOrderSuspense(
|
||||
state.uniqueSuspendersOnly,
|
||||
);
|
||||
|
||||
let nextTimelineIndex =
|
||||
selectedTimelineID === null || nextTimeline.length === 0
|
||||
? -1
|
||||
: nextTimeline.indexOf(selectedTimelineID);
|
||||
if (nextTimeline.length > 0 && nextTimelineIndex === -1) {
|
||||
if (
|
||||
nextTimeline.length > 0 &&
|
||||
(nextTimelineIndex === -1 || state.autoSelect)
|
||||
) {
|
||||
nextTimelineIndex = nextTimeline.length - 1;
|
||||
selectedSuspenseID = nextTimeline[nextTimelineIndex];
|
||||
}
|
||||
@@ -250,7 +205,6 @@ function SuspenseTreeContextController({children}: Props): React.Node {
|
||||
...state,
|
||||
lineage: nextLineage,
|
||||
roots: store.roots,
|
||||
selectedRootID: nextRootID,
|
||||
selectedSuspenseID,
|
||||
timeline: nextTimeline,
|
||||
timelineIndex: nextTimelineIndex,
|
||||
@@ -258,28 +212,24 @@ function SuspenseTreeContextController({children}: Props): React.Node {
|
||||
}
|
||||
case 'SELECT_SUSPENSE_BY_ID': {
|
||||
const selectedSuspenseID = action.payload;
|
||||
const selectedRootID =
|
||||
store.getSuspenseRootIDForSuspense(selectedSuspenseID);
|
||||
|
||||
return {
|
||||
...state,
|
||||
selectedSuspenseID,
|
||||
selectedRootID,
|
||||
playing: false, // pause
|
||||
autoSelect: false,
|
||||
};
|
||||
}
|
||||
case 'SET_SUSPENSE_LINEAGE': {
|
||||
const suspenseID = action.payload;
|
||||
const lineage = store.getSuspenseLineage(suspenseID);
|
||||
const selectedRootID =
|
||||
store.getSuspenseRootIDForSuspense(suspenseID);
|
||||
|
||||
return {
|
||||
...state,
|
||||
lineage,
|
||||
selectedSuspenseID: suspenseID,
|
||||
selectedRootID,
|
||||
playing: false, // pause
|
||||
autoSelect: false,
|
||||
};
|
||||
}
|
||||
case 'SET_SUSPENSE_TIMELINE': {
|
||||
@@ -316,8 +266,6 @@ function SuspenseTreeContextController({children}: Props): React.Node {
|
||||
...state,
|
||||
selectedSuspenseID: nextSelectedSuspenseID,
|
||||
lineage: nextLineage,
|
||||
selectedRootID:
|
||||
nextRootID === null ? state.selectedRootID : nextRootID,
|
||||
timeline: nextTimeline,
|
||||
timelineIndex: nextMilestoneIndex,
|
||||
uniqueSuspendersOnly: nextUniqueSuspendersOnly,
|
||||
@@ -336,6 +284,7 @@ function SuspenseTreeContextController({children}: Props): React.Node {
|
||||
selectedSuspenseID: nextSelectedSuspenseID,
|
||||
timelineIndex: nextTimelineIndex,
|
||||
playing: false, // pause
|
||||
autoSelect: false,
|
||||
};
|
||||
}
|
||||
case 'SUSPENSE_SKIP_TIMELINE_INDEX': {
|
||||
@@ -358,6 +307,7 @@ function SuspenseTreeContextController({children}: Props): React.Node {
|
||||
selectedSuspenseID: nextSelectedSuspenseID,
|
||||
timelineIndex: nextTimelineIndex,
|
||||
playing: false, // pause
|
||||
autoSelect: false,
|
||||
};
|
||||
}
|
||||
case 'SUSPENSE_PLAY_PAUSE': {
|
||||
@@ -384,6 +334,7 @@ function SuspenseTreeContextController({children}: Props): React.Node {
|
||||
selectedSuspenseID: nextSelectedSuspenseID,
|
||||
timelineIndex: nextTimelineIndex,
|
||||
playing: mode === 'toggle' ? !state.playing : mode === 'play',
|
||||
autoSelect: false,
|
||||
};
|
||||
}
|
||||
case 'SUSPENSE_PLAY_TICK': {
|
||||
@@ -440,6 +391,7 @@ function SuspenseTreeContextController({children}: Props): React.Node {
|
||||
selectedSuspenseID: nextSelectedSuspenseID,
|
||||
timelineIndex: nextTimelineIndex,
|
||||
playing: false, // pause
|
||||
autoSelect: false,
|
||||
};
|
||||
}
|
||||
case 'HOVER_TIMELINE_FOR_ID': {
|
||||
|
||||
+34
-10
@@ -353,20 +353,44 @@ export function useHighlightHostInstance(): {
|
||||
const highlightHostInstance = useCallback(
|
||||
(id: number, scrollIntoView?: boolean = false) => {
|
||||
const element = store.getElementByID(id);
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (element !== null && rendererID !== null) {
|
||||
if (element !== null) {
|
||||
const isRoot = element.parentID === 0;
|
||||
let displayName = element.displayName;
|
||||
if (displayName !== null && element.nameProp !== null) {
|
||||
displayName += ` name="${element.nameProp}"`;
|
||||
}
|
||||
bridge.send('highlightHostInstance', {
|
||||
displayName,
|
||||
hideAfterTimeout: false,
|
||||
id,
|
||||
openBuiltinElementsPanel: false,
|
||||
rendererID,
|
||||
scrollIntoView: scrollIntoView,
|
||||
});
|
||||
if (isRoot) {
|
||||
// Inspect screen
|
||||
const elements: Array<{rendererID: number, id: number}> = [];
|
||||
|
||||
for (let i = 0; i < store.roots.length; i++) {
|
||||
const rootID = store.roots[i];
|
||||
const rendererID = store.getRendererIDForElement(rootID);
|
||||
if (rendererID === null) {
|
||||
continue;
|
||||
}
|
||||
elements.push({rendererID, id: rootID});
|
||||
}
|
||||
|
||||
bridge.send('highlightHostInstances', {
|
||||
displayName,
|
||||
hideAfterTimeout: false,
|
||||
elements,
|
||||
scrollIntoView: scrollIntoView,
|
||||
});
|
||||
} else {
|
||||
const rendererID = store.getRendererIDForElement(id);
|
||||
if (rendererID !== null) {
|
||||
bridge.send('highlightHostInstance', {
|
||||
displayName,
|
||||
hideAfterTimeout: false,
|
||||
id,
|
||||
openBuiltinElementsPanel: false,
|
||||
rendererID,
|
||||
scrollIntoView: scrollIntoView,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[store, bridge],
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
convertInspectedElementBackendToFrontend,
|
||||
hydrateHelper,
|
||||
inspectElement as inspectElementAPI,
|
||||
inspectScreen as inspectScreenAPI,
|
||||
} from 'react-devtools-shared/src/backendAPI';
|
||||
import {fillInPath} from 'react-devtools-shared/src/hydration';
|
||||
|
||||
@@ -57,21 +58,31 @@ export function inspectElement(
|
||||
rendererID: number,
|
||||
shouldListenToPauseEvents: boolean = false,
|
||||
): Promise<InspectElementReturnType> {
|
||||
const {id} = element;
|
||||
const {id, parentID} = element;
|
||||
|
||||
// This could indicate that the DevTools UI has been closed and reopened.
|
||||
// The in-memory cache will be clear but the backend still thinks we have cached data.
|
||||
// In this case, we need to tell it to resend the full data.
|
||||
const forceFullData = !inspectedElementCache.has(id);
|
||||
const isRoot = parentID === 0;
|
||||
const promisedElement = isRoot
|
||||
? inspectScreenAPI(
|
||||
bridge,
|
||||
forceFullData,
|
||||
id,
|
||||
path,
|
||||
shouldListenToPauseEvents,
|
||||
)
|
||||
: inspectElementAPI(
|
||||
bridge,
|
||||
forceFullData,
|
||||
id,
|
||||
path,
|
||||
rendererID,
|
||||
shouldListenToPauseEvents,
|
||||
);
|
||||
|
||||
return inspectElementAPI(
|
||||
bridge,
|
||||
forceFullData,
|
||||
id,
|
||||
path,
|
||||
rendererID,
|
||||
shouldListenToPauseEvents,
|
||||
).then((data: any) => {
|
||||
return promisedElement.then((data: any) => {
|
||||
const {type} = data;
|
||||
|
||||
let inspectedElement;
|
||||
|
||||
-1
@@ -262,7 +262,6 @@ export function printOperationsArray(operations: Array<number>) {
|
||||
i++; // supportsProfiling
|
||||
i++; // supportsStrictMode
|
||||
i++; // hasOwnerMetadata
|
||||
i++; // supportsTogglingSuspense
|
||||
} else {
|
||||
const parentID = ((operations[i]: any): number);
|
||||
i++;
|
||||
|
||||
@@ -10832,4 +10832,56 @@ Unfortunately that previous paragraph wasn't quite long enough so I'll continue
|
||||
</html>,
|
||||
);
|
||||
});
|
||||
|
||||
it('not error when a suspended fallback segment directly inside another Suspense is abandoned', async () => {
|
||||
function SuspendForever() {
|
||||
React.use(new Promise(() => {}));
|
||||
}
|
||||
|
||||
let resolve = () => {};
|
||||
const suspendPromise = new Promise(r => {
|
||||
resolve = r;
|
||||
});
|
||||
function Suspend() {
|
||||
return React.use(suspendPromise);
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<html>
|
||||
<body>
|
||||
<Suspense fallback="outer">
|
||||
<Suspense fallback={<SuspendForever />}>
|
||||
<span>hello world</span>
|
||||
<span>
|
||||
<Suspend />
|
||||
</span>
|
||||
</Suspense>
|
||||
</Suspense>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
const {pipe} = renderToPipeableStream(<App />, {
|
||||
onError() {},
|
||||
});
|
||||
pipe(writable);
|
||||
});
|
||||
|
||||
await act(() => {
|
||||
resolve('!');
|
||||
});
|
||||
|
||||
expect(getVisibleChildren(document)).toEqual(
|
||||
<html>
|
||||
<head />
|
||||
<body>
|
||||
<span>hello world</span>
|
||||
<span>!</span>
|
||||
</body>
|
||||
</html>,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -645,6 +645,10 @@ export type FragmentInstanceType = {
|
||||
observeUsing: (observer: IntersectionObserver) => void,
|
||||
unobserveUsing: (observer: IntersectionObserver) => void,
|
||||
compareDocumentPosition: (otherNode: PublicInstance) => number,
|
||||
getRootNode(getRootNodeOptions?: {
|
||||
composed: boolean,
|
||||
}): Node | FragmentInstanceType,
|
||||
getClientRects: () => Array<DOMRect>,
|
||||
};
|
||||
|
||||
function FragmentInstance(this: FragmentInstanceType, fragmentFiber: Fiber) {
|
||||
@@ -754,6 +758,42 @@ function collectChildren(child: Fiber, collection: Array<Fiber>): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
// $FlowFixMe[prop-missing]
|
||||
FragmentInstance.prototype.getRootNode = function (
|
||||
this: FragmentInstanceType,
|
||||
getRootNodeOptions?: {composed: boolean},
|
||||
): Node | FragmentInstanceType {
|
||||
const parentHostFiber = getFragmentParentHostFiber(this._fragmentFiber);
|
||||
if (parentHostFiber === null) {
|
||||
return this;
|
||||
}
|
||||
const parentHostInstance = getPublicInstanceFromHostFiber(parentHostFiber);
|
||||
// $FlowFixMe[incompatible-use] Fabric PublicInstance is opaque
|
||||
const rootNode = (parentHostInstance.getRootNode(getRootNodeOptions): Node);
|
||||
return rootNode;
|
||||
};
|
||||
|
||||
// $FlowFixMe[prop-missing]
|
||||
FragmentInstance.prototype.getClientRects = function (
|
||||
this: FragmentInstanceType,
|
||||
): Array<DOMRect> {
|
||||
const rects: Array<DOMRect> = [];
|
||||
traverseFragmentInstance(this._fragmentFiber, collectClientRects, rects);
|
||||
return rects;
|
||||
};
|
||||
function collectClientRects(child: Fiber, rects: Array<DOMRect>): boolean {
|
||||
const instance = getPublicInstanceFromHostFiber(child);
|
||||
|
||||
// getBoundingClientRect is available on Fabric instances while getClientRects is not.
|
||||
// This should work as a substitute in this case because the only equivalent of a multi-rect
|
||||
// element in RN would be a nested Text component.
|
||||
// Since we only use top-level nodes here, we can assume that getBoundingClientRect is sufficient.
|
||||
// $FlowFixMe[method-unbinding]
|
||||
// $FlowFixMe[incompatible-use] Fabric PublicInstance is opaque
|
||||
rects.push(instance.getBoundingClientRect());
|
||||
return false;
|
||||
}
|
||||
|
||||
export function createFragmentInstance(
|
||||
fragmentFiber: Fiber,
|
||||
): FragmentInstanceType {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let act;
|
||||
let Activity;
|
||||
let React;
|
||||
let ReactServer;
|
||||
let ReactNoop;
|
||||
let ReactNoopFlightClient;
|
||||
let ReactNoopFlightServer;
|
||||
|
||||
describe('ActivityReactServer', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.mock('react', () => require('react/react.react-server'));
|
||||
ReactServer = require('react');
|
||||
Activity = ReactServer.Activity;
|
||||
ReactNoopFlightServer = require('react-noop-renderer/flight-server');
|
||||
|
||||
jest.resetModules();
|
||||
__unmockReact();
|
||||
React = require('react');
|
||||
ReactNoopFlightClient = require('react-noop-renderer/flight-client');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
const InternalTestUtils = require('internal-test-utils');
|
||||
act = InternalTestUtils.act;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('can be rendered in React Server', async () => {
|
||||
function App() {
|
||||
return ReactServer.createElement(
|
||||
Activity,
|
||||
{mode: 'hidden'},
|
||||
ReactServer.createElement('div', null, 'Hello, Dave!'),
|
||||
);
|
||||
}
|
||||
|
||||
const transport = ReactNoopFlightServer.render(
|
||||
ReactServer.createElement(App, null),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
const app = await ReactNoopFlightClient.read(transport);
|
||||
|
||||
ReactNoop.render(app);
|
||||
});
|
||||
|
||||
expect(ReactNoop).toMatchRenderedOutput(
|
||||
<div hidden={true}>Hello, Dave!</div>,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let act;
|
||||
let ViewTransition;
|
||||
let React;
|
||||
let ReactServer;
|
||||
let ReactNoop;
|
||||
let ReactNoopFlightClient;
|
||||
let ReactNoopFlightServer;
|
||||
|
||||
describe('ViewTransitionReactServer', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.mock('react', () => require('react/react.react-server'));
|
||||
ReactServer = require('react');
|
||||
ViewTransition = ReactServer.unstable_ViewTransition;
|
||||
ReactNoopFlightServer = require('react-noop-renderer/flight-server');
|
||||
|
||||
jest.resetModules();
|
||||
__unmockReact();
|
||||
React = require('react');
|
||||
ReactNoopFlightClient = require('react-noop-renderer/flight-client');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
const InternalTestUtils = require('internal-test-utils');
|
||||
act = InternalTestUtils.act;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
// @gate enableViewTransition || fb
|
||||
it('can be rendered in React Server', async () => {
|
||||
function App() {
|
||||
return ReactServer.createElement(
|
||||
ViewTransition,
|
||||
{},
|
||||
ReactServer.createElement('div', null, 'Hello, Dave!'),
|
||||
);
|
||||
}
|
||||
|
||||
const transport = ReactNoopFlightServer.render(
|
||||
ReactServer.createElement(App, null),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
const app = await ReactNoopFlightClient.read(transport);
|
||||
|
||||
ReactNoop.render(app);
|
||||
});
|
||||
|
||||
expect(ReactNoop).toMatchRenderedOutput(<div>Hello, Dave!</div>);
|
||||
});
|
||||
});
|
||||
+6
-2
@@ -342,7 +342,7 @@ type Segment = {
|
||||
// The context that this segment was created in.
|
||||
parentFormatContext: FormatContext,
|
||||
// If this segment represents a fallback, this is the content that will replace that fallback.
|
||||
+boundary: null | SuspenseBoundary,
|
||||
boundary: null | SuspenseBoundary,
|
||||
// used to discern when text separator boundaries are needed
|
||||
lastPushedText: boolean,
|
||||
textEmbedded: boolean,
|
||||
@@ -5681,6 +5681,10 @@ function flushSegment(
|
||||
return flushSubtree(request, destination, segment, hoistableState);
|
||||
}
|
||||
|
||||
// We're going to write the boundary. We don't need to maintain this reference since
|
||||
// we might reflush this segment at a later time (if it aborts and we inlined) but
|
||||
// we don't want to reflush the boundary
|
||||
segment.boundary = null;
|
||||
boundary.parentFlushed = true;
|
||||
// This segment is a Suspense boundary. We need to decide whether to
|
||||
// emit the content or the fallback now.
|
||||
@@ -5952,7 +5956,7 @@ function flushPartiallyCompletedSegment(
|
||||
segment: Segment,
|
||||
): boolean {
|
||||
if (segment.status === FLUSHED) {
|
||||
// We've already flushed this inline.
|
||||
// We've already flushed this inline
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ export {
|
||||
|
||||
export {
|
||||
Children,
|
||||
REACT_ACTIVITY_TYPE as Activity,
|
||||
REACT_FRAGMENT_TYPE as Fragment,
|
||||
REACT_PROFILER_TYPE as Profiler,
|
||||
REACT_STRICT_MODE_TYPE as StrictMode,
|
||||
@@ -83,6 +84,5 @@ export {
|
||||
// Experimental
|
||||
REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList,
|
||||
REACT_VIEW_TRANSITION_TYPE as unstable_ViewTransition,
|
||||
REACT_ACTIVITY_TYPE as unstable_Activity,
|
||||
captureOwnerStack, // DEV-only
|
||||
};
|
||||
|
||||
@@ -57,6 +57,7 @@ export {
|
||||
|
||||
export {
|
||||
Children,
|
||||
REACT_ACTIVITY_TYPE as Activity,
|
||||
REACT_FRAGMENT_TYPE as Fragment,
|
||||
REACT_PROFILER_TYPE as Profiler,
|
||||
REACT_STRICT_MODE_TYPE as StrictMode,
|
||||
@@ -82,5 +83,4 @@ export {
|
||||
// Experimental
|
||||
REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList,
|
||||
REACT_VIEW_TRANSITION_TYPE as unstable_ViewTransition,
|
||||
REACT_ACTIVITY_TYPE as Activity,
|
||||
};
|
||||
|
||||
@@ -12,10 +12,13 @@ export {default as __SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRA
|
||||
import {forEach, map, count, toArray, only} from './ReactChildren';
|
||||
import {captureOwnerStack as captureOwnerStackImpl} from './ReactOwnerStack';
|
||||
import {
|
||||
REACT_ACTIVITY_TYPE,
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_PROFILER_TYPE,
|
||||
REACT_STRICT_MODE_TYPE,
|
||||
REACT_SUSPENSE_TYPE,
|
||||
REACT_SUSPENSE_LIST_TYPE,
|
||||
REACT_VIEW_TRANSITION_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
import {
|
||||
cloneElement,
|
||||
@@ -45,6 +48,7 @@ if (__DEV__) {
|
||||
|
||||
export {
|
||||
Children,
|
||||
REACT_ACTIVITY_TYPE as Activity,
|
||||
REACT_FRAGMENT_TYPE as Fragment,
|
||||
REACT_PROFILER_TYPE as Profiler,
|
||||
REACT_STRICT_MODE_TYPE as StrictMode,
|
||||
@@ -65,4 +69,7 @@ export {
|
||||
useMemo,
|
||||
version,
|
||||
captureOwnerStack, // DEV-only
|
||||
// Experimental
|
||||
REACT_SUSPENSE_LIST_TYPE as unstable_SuspenseList,
|
||||
REACT_VIEW_TRANSITION_TYPE as unstable_ViewTransition,
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ export {default as __SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRA
|
||||
|
||||
import {forEach, map, count, toArray, only} from './ReactChildren';
|
||||
import {
|
||||
REACT_ACTIVITY_TYPE,
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_PROFILER_TYPE,
|
||||
REACT_STRICT_MODE_TYPE,
|
||||
@@ -40,6 +41,7 @@ const Children = {
|
||||
|
||||
export {
|
||||
Children,
|
||||
REACT_ACTIVITY_TYPE as Activity,
|
||||
REACT_FRAGMENT_TYPE as Fragment,
|
||||
REACT_PROFILER_TYPE as Profiler,
|
||||
REACT_STRICT_MODE_TYPE as StrictMode,
|
||||
|
||||
Reference in New Issue
Block a user