mirror of
https://github.com/openharmony/third_party_typescript_eslint.git
synced 2026-06-30 21:27:59 -04:00
feat(scope-manager): add support for JSX scope analysis (#2498)
Fixes #2455 And part of #2477 JSX is a first-class citizen of TS, so we should really support it as well. I was going to just rely upon `eslint-plugin-react`'s patch lint rules (`react/jsx-uses-react` and `react/jsx-uses-vars`), but that leaves gaps in our tooling. For example #2455, `consistent-type-imports` makes assumptions and can create invalid fixes for react without this change. We could add options to that lint rule for the factory, but that is kind-of a sub-par experience and future rule authors will likely run into similar problems. - Adds full scope analysis support for JSX. - Adds two new `parserOption`: - `jsxPragma` - the name to use for constructing JSX elements. Defaults to `"React"`. Will be auto detected from the tsconfig. - `jsxFragmentName` - the name that unnamed JSX fragments use. Defaults to `null` (i.e. assumes `React.Fragment`). Will be auto detected from the tsconfig.
This commit is contained in:
@@ -75,6 +75,7 @@
|
||||
"pluggable",
|
||||
"postprocess",
|
||||
"postprocessor",
|
||||
"preact",
|
||||
"Premade",
|
||||
"prettier's",
|
||||
"recurse",
|
||||
@@ -88,6 +89,7 @@
|
||||
"rulesets",
|
||||
"serializers",
|
||||
"superset",
|
||||
"transpiling",
|
||||
"thenables",
|
||||
"transpiles",
|
||||
"tsconfigs",
|
||||
|
||||
@@ -140,6 +140,65 @@ function predicate(arg: any): asserts arg is T {
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
code: `
|
||||
function Foo() {}
|
||||
<Foo />;
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
code: `
|
||||
type T = 1;
|
||||
function Foo() {}
|
||||
<Foo<T> />;
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
code: `
|
||||
const x = 1;
|
||||
function Foo() {}
|
||||
<Foo attr={x} />;
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
code: `
|
||||
const x = {};
|
||||
function Foo() {}
|
||||
<Foo {...x} />;
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
code: `
|
||||
const x = {};
|
||||
function Foo() {}
|
||||
<Foo>{x}</Foo>;
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
@@ -175,5 +234,107 @@ function predicate(arg: any): asserts arg is T {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: '<Foo />;',
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
messageId: 'undef',
|
||||
data: {
|
||||
name: 'Foo',
|
||||
},
|
||||
line: 1,
|
||||
column: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
function Foo() {}
|
||||
<Foo attr={x} />;
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
messageId: 'undef',
|
||||
data: {
|
||||
name: 'x',
|
||||
},
|
||||
line: 3,
|
||||
column: 12,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
function Foo() {}
|
||||
<Foo {...x} />;
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
messageId: 'undef',
|
||||
data: {
|
||||
name: 'x',
|
||||
},
|
||||
line: 3,
|
||||
column: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
function Foo() {}
|
||||
<Foo<T> />;
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
messageId: 'undef',
|
||||
data: {
|
||||
name: 'T',
|
||||
},
|
||||
line: 3,
|
||||
column: 6,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
function Foo() {}
|
||||
<Foo>{x}</Foo>;
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
messageId: 'undef',
|
||||
data: {
|
||||
name: 'x',
|
||||
},
|
||||
line: 3,
|
||||
column: 7,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -180,6 +180,51 @@ ruleTester.run('consistent-type-imports', rule, {
|
||||
`,
|
||||
options: [{ prefer: 'no-type-imports' }],
|
||||
},
|
||||
// https://github.com/typescript-eslint/typescript-eslint/issues/2455
|
||||
{
|
||||
code: `
|
||||
import React from 'react';
|
||||
|
||||
export const ComponentFoo: React.FC = () => {
|
||||
return <div>Foo Foo</div>;
|
||||
};
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
code: `
|
||||
import { h } from 'some-other-jsx-lib';
|
||||
|
||||
export const ComponentFoo: h.FC = () => {
|
||||
return <div>Foo Foo</div>;
|
||||
};
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
jsxPragma: 'h',
|
||||
},
|
||||
},
|
||||
{
|
||||
code: `
|
||||
import { Fragment } from 'react';
|
||||
|
||||
export const ComponentFoo: Fragment = () => {
|
||||
return <>Foo Foo</>;
|
||||
};
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
jsxFragmentName: 'Fragment',
|
||||
},
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
|
||||
@@ -803,6 +803,51 @@ export type Test<U> = U extends (arg: {
|
||||
? I
|
||||
: never;
|
||||
`,
|
||||
// https://github.com/typescript-eslint/typescript-eslint/issues/2455
|
||||
{
|
||||
code: `
|
||||
import React from 'react';
|
||||
|
||||
export const ComponentFoo: React.FC = () => {
|
||||
return <div>Foo Foo</div>;
|
||||
};
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
code: `
|
||||
import { h } from 'some-other-jsx-lib';
|
||||
|
||||
export const ComponentFoo: h.FC = () => {
|
||||
return <div>Foo Foo</div>;
|
||||
};
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
jsxPragma: 'h',
|
||||
},
|
||||
},
|
||||
{
|
||||
code: `
|
||||
import { Fragment } from 'react';
|
||||
|
||||
export const ComponentFoo: Fragment = () => {
|
||||
return <>Foo Foo</>;
|
||||
};
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
jsxFragmentName: 'Fragment',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
invalid: [
|
||||
@@ -1325,5 +1370,59 @@ type Foo = Array<Foo>;
|
||||
},
|
||||
],
|
||||
},
|
||||
// https://github.com/typescript-eslint/typescript-eslint/issues/2455
|
||||
{
|
||||
code: `
|
||||
import React from 'react';
|
||||
import { Fragment } from 'react';
|
||||
|
||||
export const ComponentFoo = () => {
|
||||
return <div>Foo Foo</div>;
|
||||
};
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
messageId: 'unusedVar',
|
||||
line: 3,
|
||||
data: {
|
||||
varName: 'Fragment',
|
||||
action: 'defined',
|
||||
additional: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
import React from 'react';
|
||||
import { h } from 'some-other-jsx-lib';
|
||||
|
||||
export const ComponentFoo = () => {
|
||||
return <div>Foo Foo</div>;
|
||||
};
|
||||
`,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
jsxPragma: 'h',
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
messageId: 'unusedVar',
|
||||
line: 2,
|
||||
data: {
|
||||
varName: 'React',
|
||||
action: 'defined',
|
||||
additional: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -54,6 +54,9 @@ interface ParserOptions {
|
||||
globalReturn?: boolean;
|
||||
};
|
||||
ecmaVersion?: number;
|
||||
|
||||
jsxPragma?: string;
|
||||
jsxFragmentName?: string | null;
|
||||
lib?: string[];
|
||||
|
||||
project?: string | string[];
|
||||
@@ -98,6 +101,27 @@ Accepts any valid ECMAScript version number:
|
||||
|
||||
Specifies the version of ECMAScript syntax you want to use. This is used by the parser to determine how to perform scope analysis, and it affects the default
|
||||
|
||||
### `parserOptions.jsxPragma`
|
||||
|
||||
Default `'React'`
|
||||
|
||||
The identifier that's used for JSX Elements creation (after transpilation).
|
||||
If you're using a library other than React (like `preact`), then you should change this value.
|
||||
|
||||
This should not be a member expression - just the root identifier (i.e. use `"React"` instead of `"React.createElement"`).
|
||||
|
||||
If you provide `parserOptions.project`, you do not need to set this, as it will automatically detected from the compiler.
|
||||
|
||||
### `parserOptions.jsxFragmentName`
|
||||
|
||||
Default `null`
|
||||
|
||||
The identifier that's used for JSX fragment elements (after transpilation).
|
||||
If `null`, assumes transpilation will always use a member of the configured `jsxPragma`.
|
||||
This should not be a member expression - just the root identifier (i.e. use `"h"` instead of `"h.Fragment"`).
|
||||
|
||||
If you provide `parserOptions.project`, you do not need to set this, as it will automatically detected from the compiler.
|
||||
|
||||
### `parserOptions.lib`
|
||||
|
||||
Default `['es2018']`
|
||||
@@ -105,7 +129,8 @@ Default `['es2018']`
|
||||
For valid options, see the [TypeScript compiler options](https://www.typescriptlang.org/tsconfig#lib).
|
||||
|
||||
Specifies the TypeScript `lib`s that are available. This is used by the scope analyser to ensure there are global variables declared for the types exposed by TypeScript.
|
||||
If you provide `parserOptions.project`, you do not need to set this, as it will automatically detected from the compiler
|
||||
|
||||
If you provide `parserOptions.project`, you do not need to set this, as it will automatically detected from the compiler.
|
||||
|
||||
### `parserOptions.project`
|
||||
|
||||
|
||||
@@ -5,9 +5,13 @@ import {
|
||||
TSESTreeOptions,
|
||||
visitorKeys,
|
||||
} from '@typescript-eslint/typescript-estree';
|
||||
import { analyze, ScopeManager } from '@typescript-eslint/scope-manager';
|
||||
import {
|
||||
analyze,
|
||||
AnalyzeOptions,
|
||||
ScopeManager,
|
||||
} from '@typescript-eslint/scope-manager';
|
||||
import debug from 'debug';
|
||||
import { ScriptTarget } from 'typescript';
|
||||
import { CompilerOptions, ScriptTarget } from 'typescript';
|
||||
|
||||
const log = debug('typescript-eslint:parser:parser');
|
||||
|
||||
@@ -33,8 +37,7 @@ function validateBoolean(
|
||||
}
|
||||
|
||||
const LIB_FILENAME_REGEX = /lib\.(.+)\.d\.ts/;
|
||||
function getLib(services: ParserServices): Lib[] {
|
||||
const compilerOptions = services.program.getCompilerOptions();
|
||||
function getLib(compilerOptions: CompilerOptions): Lib[] {
|
||||
if (compilerOptions.lib) {
|
||||
return compilerOptions.lib
|
||||
.map(lib => {
|
||||
@@ -100,6 +103,14 @@ function parseForESLint(
|
||||
useJSXTextNode: validateBoolean(options.useJSXTextNode, true),
|
||||
jsx: validateBoolean(options.ecmaFeatures.jsx),
|
||||
});
|
||||
const analyzeOptions: AnalyzeOptions = {
|
||||
ecmaVersion: options.ecmaVersion,
|
||||
globalReturn: options.ecmaFeatures.globalReturn,
|
||||
jsxPragma: options.jsxPragma,
|
||||
jsxFragmentName: options.jsxFragmentName,
|
||||
lib: options.lib,
|
||||
sourceType: options.sourceType,
|
||||
};
|
||||
|
||||
if (typeof options.filePath === 'string') {
|
||||
const tsx = options.filePath.endsWith('.tsx');
|
||||
@@ -123,18 +134,40 @@ function parseForESLint(
|
||||
const { ast, services } = parseAndGenerateServices(code, parserOptions);
|
||||
ast.sourceType = options.sourceType;
|
||||
|
||||
// automatically apply the libs configured for the program
|
||||
if (services.hasFullTypeInformation && options.lib == null) {
|
||||
options.lib = getLib(services);
|
||||
log('Resolved libs from program: %o', options.lib);
|
||||
if (services.hasFullTypeInformation) {
|
||||
// automatically apply the options configured for the program
|
||||
const compilerOptions = services.program.getCompilerOptions();
|
||||
if (analyzeOptions.lib == null) {
|
||||
analyzeOptions.lib = getLib(compilerOptions);
|
||||
log('Resolved libs from program: %o', analyzeOptions.lib);
|
||||
}
|
||||
if (parserOptions.jsx === true) {
|
||||
if (
|
||||
analyzeOptions.jsxPragma === undefined &&
|
||||
compilerOptions.jsxFactory != null
|
||||
) {
|
||||
// in case the user has specified something like "preact.h"
|
||||
const factory = compilerOptions.jsxFactory.split('.')[0].trim();
|
||||
analyzeOptions.jsxPragma = factory;
|
||||
log('Resolved jsxPragma from program: %s', analyzeOptions.jsxPragma);
|
||||
}
|
||||
if (
|
||||
analyzeOptions.jsxFragmentName === undefined &&
|
||||
compilerOptions.jsxFragmentFactory != null
|
||||
) {
|
||||
// in case the user has specified something like "preact.Fragment"
|
||||
const fragFactory = compilerOptions.jsxFragmentFactory
|
||||
.split('.')[0]
|
||||
.trim();
|
||||
analyzeOptions.jsxFragmentName = fragFactory;
|
||||
log(
|
||||
'Resolved jsxFragmentName from program: %s',
|
||||
analyzeOptions.jsxFragmentName,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const analyzeOptions = {
|
||||
ecmaVersion: options.ecmaVersion,
|
||||
globalReturn: options.ecmaFeatures.globalReturn,
|
||||
lib: options.lib,
|
||||
sourceType: options.sourceType,
|
||||
};
|
||||
const scopeManager = analyze(ast, analyzeOptions);
|
||||
|
||||
return { ast, services, scopeManager, visitorKeys };
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { TSESLint } from '@typescript-eslint/experimental-utils';
|
||||
import * as typescriptESTree from '@typescript-eslint/typescript-estree/dist/parser';
|
||||
import * as scopeManager from '@typescript-eslint/scope-manager/dist/analyze';
|
||||
import { parse, parseForESLint } from '../../src/parser';
|
||||
|
||||
describe('parser', () => {
|
||||
@@ -70,4 +71,43 @@ describe('parser', () => {
|
||||
warnOnUnsupportedTypeScriptVersion: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('analyze() should be called with options', () => {
|
||||
const code = 'const valid = true;';
|
||||
const spy = jest.spyOn(scopeManager, 'analyze');
|
||||
const config: TSESLint.ParserOptions = {
|
||||
loc: false,
|
||||
comment: false,
|
||||
range: false,
|
||||
tokens: false,
|
||||
sourceType: 'module' as const,
|
||||
ecmaVersion: 2018,
|
||||
ecmaFeatures: {
|
||||
globalReturn: false,
|
||||
jsx: false,
|
||||
},
|
||||
// scope-manager specific
|
||||
lib: ['dom.iterable'],
|
||||
jsxPragma: 'Foo',
|
||||
jsxFragmentName: 'Bar',
|
||||
// ts-estree specific
|
||||
filePath: 'isolated-file.src.ts',
|
||||
project: 'tsconfig.json',
|
||||
useJSXTextNode: false,
|
||||
errorOnUnknownASTType: false,
|
||||
errorOnTypeScriptSyntacticAndSemanticIssues: false,
|
||||
tsconfigRootDir: 'tests/fixtures/services',
|
||||
extraFileExtensions: ['.foo'],
|
||||
};
|
||||
parseForESLint(code, config);
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
expect(spy).toHaveBeenLastCalledWith(expect.anything(), {
|
||||
ecmaVersion: 2018,
|
||||
globalReturn: false,
|
||||
lib: ['dom.iterable'],
|
||||
jsxPragma: 'Foo',
|
||||
jsxFragmentName: 'Bar',
|
||||
sourceType: 'module',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,6 +55,21 @@ interface AnalyzeOptions {
|
||||
*/
|
||||
impliedStrict?: boolean;
|
||||
|
||||
/**
|
||||
* The identifier that's used for JSX Element creation (after transpilation).
|
||||
* This should not be a member expression - just the root identifier (i.e. use "React" instead of "React.createElement").
|
||||
* Defaults to `"React"`.
|
||||
*/
|
||||
jsxPragma?: string;
|
||||
|
||||
/**
|
||||
* The identifier that's used for JSX fragment elements (after transpilation).
|
||||
* If `null`, assumes transpilation will always use a member on `jsxFactory` (i.e. React.Fragment).
|
||||
* This should not be a member expression - just the root identifier (i.e. use "h" instead of "h.Fragment").
|
||||
* Defaults to `null`.
|
||||
*/
|
||||
jsxFragmentName?: string | null;
|
||||
|
||||
/**
|
||||
* The lib used by the project.
|
||||
* This automatically defines a type variable for any types provided by the configured TS libs.
|
||||
|
||||
@@ -33,6 +33,21 @@ interface AnalyzeOptions {
|
||||
*/
|
||||
impliedStrict?: boolean;
|
||||
|
||||
/**
|
||||
* The identifier that's used for JSX Element creation (after transpilation).
|
||||
* This should not be a member expression - just the root identifier (i.e. use "React" instead of "React.createElement").
|
||||
* Defaults to `"React"`.
|
||||
*/
|
||||
jsxPragma?: string;
|
||||
|
||||
/**
|
||||
* The identifier that's used for JSX fragment elements (after transpilation).
|
||||
* If `null`, assumes transpilation will always use a member on `jsxFactory` (i.e. React.Fragment).
|
||||
* This should not be a member expression - just the root identifier (i.e. use "h" instead of "h.Fragment").
|
||||
* Defaults to `null`.
|
||||
*/
|
||||
jsxFragmentName?: string | null;
|
||||
|
||||
/**
|
||||
* The lib used by the project.
|
||||
* This automatically defines a type variable for any types provided by the configured TS libs.
|
||||
@@ -53,6 +68,8 @@ const DEFAULT_OPTIONS: Required<AnalyzeOptions> = {
|
||||
ecmaVersion: 2018,
|
||||
globalReturn: false,
|
||||
impliedStrict: false,
|
||||
jsxPragma: 'React',
|
||||
jsxFragmentName: null,
|
||||
lib: ['es2018'],
|
||||
sourceType: 'script',
|
||||
};
|
||||
@@ -78,13 +95,16 @@ function analyze(
|
||||
const ecmaVersion =
|
||||
providedOptions?.ecmaVersion ?? DEFAULT_OPTIONS.ecmaVersion;
|
||||
const options: Required<AnalyzeOptions> = {
|
||||
childVisitorKeys:
|
||||
providedOptions?.childVisitorKeys ?? DEFAULT_OPTIONS.childVisitorKeys,
|
||||
ecmaVersion,
|
||||
globalReturn: providedOptions?.globalReturn ?? DEFAULT_OPTIONS.globalReturn,
|
||||
impliedStrict:
|
||||
providedOptions?.impliedStrict ?? DEFAULT_OPTIONS.impliedStrict,
|
||||
jsxPragma: providedOptions?.jsxPragma ?? DEFAULT_OPTIONS.jsxPragma,
|
||||
jsxFragmentName:
|
||||
providedOptions?.jsxFragmentName ?? DEFAULT_OPTIONS.jsxFragmentName,
|
||||
sourceType: providedOptions?.sourceType ?? DEFAULT_OPTIONS.sourceType,
|
||||
ecmaVersion,
|
||||
childVisitorKeys:
|
||||
providedOptions?.childVisitorKeys ?? DEFAULT_OPTIONS.childVisitorKeys,
|
||||
lib: providedOptions?.lib ?? [mapEcmaVersion(ecmaVersion)],
|
||||
};
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class Reference {
|
||||
* Identifier syntax node.
|
||||
* @public
|
||||
*/
|
||||
public readonly identifier: TSESTree.Identifier;
|
||||
public readonly identifier: TSESTree.Identifier | TSESTree.JSXIdentifier;
|
||||
/**
|
||||
* `true` if this writing reference is a variable initializer or a default value.
|
||||
* @public
|
||||
@@ -82,7 +82,7 @@ class Reference {
|
||||
}
|
||||
|
||||
constructor(
|
||||
identifier: TSESTree.Identifier,
|
||||
identifier: TSESTree.Identifier | TSESTree.JSXIdentifier,
|
||||
scope: Scope,
|
||||
flag: ReferenceFlag,
|
||||
writeExpr?: TSESTree.Node | null,
|
||||
|
||||
@@ -22,18 +22,26 @@ import { lib as TSLibraries } from '../lib';
|
||||
import { Scope, GlobalScope } from '../scope';
|
||||
|
||||
interface ReferencerOptions extends VisitorOptions {
|
||||
jsxPragma: string;
|
||||
jsxFragmentName: string | null;
|
||||
lib: Lib[];
|
||||
}
|
||||
|
||||
// Referencing variables and creating bindings.
|
||||
class Referencer extends Visitor {
|
||||
#isInnerMethodDefinition: boolean;
|
||||
#jsxPragma: string;
|
||||
#jsxFragmentName: string | null;
|
||||
#hasReferencedJsxFactory = false;
|
||||
#hasReferencedJsxFragmentFactory = false;
|
||||
#lib: Lib[];
|
||||
public readonly scopeManager: ScopeManager;
|
||||
|
||||
constructor(options: ReferencerOptions, scopeManager: ScopeManager) {
|
||||
super(options);
|
||||
this.scopeManager = scopeManager;
|
||||
this.#jsxPragma = options.jsxPragma;
|
||||
this.#jsxFragmentName = options.jsxFragmentName;
|
||||
this.#lib = options.lib;
|
||||
this.#isInnerMethodDefinition = false;
|
||||
}
|
||||
@@ -99,6 +107,46 @@ class Referencer extends Visitor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a variable named "name" in the upper scopes and adds a pseudo-reference from itself to itself
|
||||
*/
|
||||
private referenceInSomeUpperScope(name: string): boolean {
|
||||
let scope = this.scopeManager.currentScope;
|
||||
while (scope) {
|
||||
const variable = scope.set.get(name);
|
||||
if (!variable) {
|
||||
scope = scope.upper;
|
||||
continue;
|
||||
}
|
||||
|
||||
scope.referenceValue(variable.identifiers[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private referenceJsxPragma(): void {
|
||||
if (this.#hasReferencedJsxFactory) {
|
||||
return;
|
||||
}
|
||||
this.#hasReferencedJsxFactory = this.referenceInSomeUpperScope(
|
||||
this.#jsxPragma,
|
||||
);
|
||||
}
|
||||
|
||||
private referenceJsxFragment(): void {
|
||||
if (
|
||||
this.#jsxFragmentName === null ||
|
||||
this.#hasReferencedJsxFragmentFactory
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.#hasReferencedJsxFragmentFactory = this.referenceInSomeUpperScope(
|
||||
this.#jsxFragmentName,
|
||||
);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
// Visit helpers //
|
||||
///////////////////
|
||||
@@ -498,6 +546,34 @@ class Referencer extends Visitor {
|
||||
ImportVisitor.visit(this, node);
|
||||
}
|
||||
|
||||
protected JSXAttribute(node: TSESTree.JSXAttribute): void {
|
||||
this.visit(node.value);
|
||||
}
|
||||
|
||||
protected JSXFragment(node: TSESTree.JSXFragment): void {
|
||||
this.referenceJsxPragma();
|
||||
this.referenceJsxFragment();
|
||||
this.visitChildren(node);
|
||||
}
|
||||
|
||||
protected JSXIdentifier(node: TSESTree.JSXIdentifier): void {
|
||||
this.currentScope().referenceValue(node);
|
||||
}
|
||||
|
||||
protected JSXMemberExpression(node: TSESTree.JSXMemberExpression): void {
|
||||
this.visit(node.object);
|
||||
// we don't ever reference the property as it's always going to be a property on the thing
|
||||
}
|
||||
|
||||
protected JSXOpeningElement(node: TSESTree.JSXOpeningElement): void {
|
||||
this.referenceJsxPragma();
|
||||
this.visit(node.name);
|
||||
this.visitType(node.typeParameters);
|
||||
for (const attr of node.attributes) {
|
||||
this.visit(attr);
|
||||
}
|
||||
}
|
||||
|
||||
protected LabeledStatement(node: TSESTree.LabeledStatement): void {
|
||||
this.visit(node.body);
|
||||
}
|
||||
|
||||
@@ -446,7 +446,7 @@ abstract class ScopeBase<
|
||||
}
|
||||
|
||||
public referenceValue(
|
||||
node: TSESTree.Identifier,
|
||||
node: TSESTree.Identifier | TSESTree.JSXIdentifier,
|
||||
assign: ReferenceFlag = ReferenceFlag.Read,
|
||||
writeExpr?: TSESTree.Expression | null,
|
||||
maybeImplicitGlobal?: ReferenceImplicitGlobal | null,
|
||||
|
||||
@@ -41,6 +41,8 @@ const ALLOWED_OPTIONS: Map<string, ALLOWED_VALUE> = new Map<
|
||||
['ecmaVersion', ['number']],
|
||||
['globalReturn', ['boolean']],
|
||||
['impliedStrict', ['boolean']],
|
||||
['jsxPragma', ['string']],
|
||||
['jsxFragmentName', ['string']],
|
||||
['sourceType', ['string', new Set(['module', 'script'])]],
|
||||
]);
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
const x = {};
|
||||
|
||||
<Foo {...x} />;
|
||||
@@ -0,0 +1,65 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx attribute-spread 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
VariableDefinition$1 {
|
||||
name: Identifier<"x">,
|
||||
node: VariableDeclarator$1,
|
||||
},
|
||||
],
|
||||
name: "x",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"x">,
|
||||
init: true,
|
||||
isRead: false,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: true,
|
||||
resolved: Variable$1,
|
||||
writeExpr: ObjectExpression$2,
|
||||
},
|
||||
Reference$3 {
|
||||
identifier: Identifier<"x">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: false,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$3,
|
||||
isStrict: false,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
Reference$2 {
|
||||
identifier: JSXIdentifier$4,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: null,
|
||||
},
|
||||
Reference$3,
|
||||
],
|
||||
set: Map {
|
||||
"x" => Variable$1,
|
||||
},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,4 @@
|
||||
const x = 1;
|
||||
const attr = 2; // should be unreferenced
|
||||
|
||||
<Foo attr={x} />;
|
||||
@@ -0,0 +1,91 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx attribute 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
VariableDefinition$1 {
|
||||
name: Identifier<"x">,
|
||||
node: VariableDeclarator$1,
|
||||
},
|
||||
],
|
||||
name: "x",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"x">,
|
||||
init: true,
|
||||
isRead: false,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: true,
|
||||
resolved: Variable$1,
|
||||
writeExpr: Literal$2,
|
||||
},
|
||||
Reference$4 {
|
||||
identifier: Identifier<"x">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: false,
|
||||
},
|
||||
Variable$2 {
|
||||
defs: Array [
|
||||
VariableDefinition$2 {
|
||||
name: Identifier<"attr">,
|
||||
node: VariableDeclarator$3,
|
||||
},
|
||||
],
|
||||
name: "attr",
|
||||
references: Array [
|
||||
Reference$2 {
|
||||
identifier: Identifier<"attr">,
|
||||
init: true,
|
||||
isRead: false,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: true,
|
||||
resolved: Variable$2,
|
||||
writeExpr: Literal$4,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: false,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$5,
|
||||
isStrict: false,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
Reference$2,
|
||||
Reference$3 {
|
||||
identifier: JSXIdentifier$6,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: null,
|
||||
},
|
||||
Reference$4,
|
||||
],
|
||||
set: Map {
|
||||
"x" => Variable$1,
|
||||
"attr" => Variable$2,
|
||||
},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
Variable$2,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,3 @@
|
||||
const child = 1;
|
||||
|
||||
<Foo>{child}</Foo>;
|
||||
@@ -0,0 +1,73 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx children 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
VariableDefinition$1 {
|
||||
name: Identifier<"child">,
|
||||
node: VariableDeclarator$1,
|
||||
},
|
||||
],
|
||||
name: "child",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"child">,
|
||||
init: true,
|
||||
isRead: false,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: true,
|
||||
resolved: Variable$1,
|
||||
writeExpr: Literal$2,
|
||||
},
|
||||
Reference$3 {
|
||||
identifier: Identifier<"child">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: false,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$3,
|
||||
isStrict: false,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
Reference$2 {
|
||||
identifier: JSXIdentifier$4,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: null,
|
||||
},
|
||||
Reference$3,
|
||||
Reference$4 {
|
||||
identifier: JSXIdentifier$5,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: null,
|
||||
},
|
||||
],
|
||||
set: Map {
|
||||
"child" => Variable$1,
|
||||
},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,6 @@
|
||||
const X = {
|
||||
Foo() {},
|
||||
};
|
||||
const Foo = 1; // should be unreferenced
|
||||
|
||||
<X.Foo />;
|
||||
@@ -0,0 +1,103 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx component-namespaced 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
VariableDefinition$1 {
|
||||
name: Identifier<"X">,
|
||||
node: VariableDeclarator$1,
|
||||
},
|
||||
],
|
||||
name: "X",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"X">,
|
||||
init: true,
|
||||
isRead: false,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: true,
|
||||
resolved: Variable$1,
|
||||
writeExpr: ObjectExpression$2,
|
||||
},
|
||||
Reference$3 {
|
||||
identifier: JSXIdentifier$3,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: false,
|
||||
},
|
||||
Variable$2 {
|
||||
defs: Array [],
|
||||
name: "arguments",
|
||||
references: Array [],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
Variable$3 {
|
||||
defs: Array [
|
||||
VariableDefinition$2 {
|
||||
name: Identifier<"Foo">,
|
||||
node: VariableDeclarator$4,
|
||||
},
|
||||
],
|
||||
name: "Foo",
|
||||
references: Array [
|
||||
Reference$2 {
|
||||
identifier: Identifier<"Foo">,
|
||||
init: true,
|
||||
isRead: false,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: true,
|
||||
resolved: Variable$3,
|
||||
writeExpr: Literal$5,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: false,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$6,
|
||||
isStrict: false,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
Reference$2,
|
||||
Reference$3,
|
||||
],
|
||||
set: Map {
|
||||
"X" => Variable$1,
|
||||
"Foo" => Variable$3,
|
||||
},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
Variable$3,
|
||||
],
|
||||
},
|
||||
FunctionScope$2 {
|
||||
block: FunctionExpression$7,
|
||||
isStrict: false,
|
||||
references: Array [],
|
||||
set: Map {
|
||||
"arguments" => Variable$2,
|
||||
},
|
||||
type: "function",
|
||||
upper: GlobalScope$1,
|
||||
variables: Array [
|
||||
Variable$2,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,3 @@
|
||||
function Foo() {}
|
||||
|
||||
<Foo />;
|
||||
@@ -0,0 +1,66 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx component 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
FunctionNameDefinition$1 {
|
||||
name: Identifier<"Foo">,
|
||||
node: FunctionDeclaration$1,
|
||||
},
|
||||
],
|
||||
name: "Foo",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: JSXIdentifier$2,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: false,
|
||||
},
|
||||
Variable$2 {
|
||||
defs: Array [],
|
||||
name: "arguments",
|
||||
references: Array [],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$3,
|
||||
isStrict: false,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
],
|
||||
set: Map {
|
||||
"Foo" => Variable$1,
|
||||
},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
],
|
||||
},
|
||||
FunctionScope$2 {
|
||||
block: FunctionDeclaration$1,
|
||||
isStrict: false,
|
||||
references: Array [],
|
||||
set: Map {
|
||||
"arguments" => Variable$2,
|
||||
},
|
||||
type: "function",
|
||||
upper: GlobalScope$1,
|
||||
variables: Array [
|
||||
Variable$2,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,6 @@
|
||||
//// @sourceType = 'module'
|
||||
|
||||
import React from 'react';
|
||||
import { Fragment } from 'react'; // should be unreferenced
|
||||
|
||||
<></>;
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx factory default-jsxFragmentName 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$1 {
|
||||
name: Identifier<"React">,
|
||||
node: ImportDefaultSpecifier$1,
|
||||
},
|
||||
],
|
||||
name: "React",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"React">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
Variable$2 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$2 {
|
||||
name: Identifier<"Fragment">,
|
||||
node: ImportSpecifier$2,
|
||||
},
|
||||
],
|
||||
name: "Fragment",
|
||||
references: Array [],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$3,
|
||||
isStrict: false,
|
||||
references: Array [],
|
||||
set: Map {},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [],
|
||||
},
|
||||
ModuleScope$2 {
|
||||
block: Program$3,
|
||||
isStrict: true,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
],
|
||||
set: Map {
|
||||
"React" => Variable$1,
|
||||
"Fragment" => Variable$2,
|
||||
},
|
||||
type: "module",
|
||||
upper: GlobalScope$1,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
Variable$2,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
//// @sourceType = 'module'
|
||||
|
||||
import React from 'react';
|
||||
|
||||
<></>;
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx factory default-jsxPragma-fragment 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$1 {
|
||||
name: Identifier<"React">,
|
||||
node: ImportDefaultSpecifier$1,
|
||||
},
|
||||
],
|
||||
name: "React",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"React">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$2,
|
||||
isStrict: false,
|
||||
references: Array [],
|
||||
set: Map {},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [],
|
||||
},
|
||||
ModuleScope$2 {
|
||||
block: Program$2,
|
||||
isStrict: true,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
],
|
||||
set: Map {
|
||||
"React" => Variable$1,
|
||||
},
|
||||
type: "module",
|
||||
upper: GlobalScope$1,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,5 @@
|
||||
//// @sourceType = 'module'
|
||||
|
||||
import React from 'react';
|
||||
|
||||
<Foo />;
|
||||
@@ -0,0 +1,63 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx factory default-jsxPragma 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$1 {
|
||||
name: Identifier<"React">,
|
||||
node: ImportDefaultSpecifier$1,
|
||||
},
|
||||
],
|
||||
name: "React",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"React">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$2,
|
||||
isStrict: false,
|
||||
references: Array [],
|
||||
set: Map {},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [],
|
||||
},
|
||||
ModuleScope$2 {
|
||||
block: Program$2,
|
||||
isStrict: true,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
Reference$2 {
|
||||
identifier: JSXIdentifier$3,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: null,
|
||||
},
|
||||
],
|
||||
set: Map {
|
||||
"React" => Variable$1,
|
||||
},
|
||||
type: "module",
|
||||
upper: GlobalScope$1,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
//// @sourceType = 'module'
|
||||
//// @jsxFragmentName = 'Fragment'
|
||||
|
||||
import React from 'react'; // should be unreferenced
|
||||
import { Fragment } from 'preact';
|
||||
|
||||
<></>;
|
||||
@@ -0,0 +1,79 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx factory jsxFragmentName 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$1 {
|
||||
name: Identifier<"React">,
|
||||
node: ImportDefaultSpecifier$1,
|
||||
},
|
||||
],
|
||||
name: "React",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"React">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
Variable$2 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$2 {
|
||||
name: Identifier<"Fragment">,
|
||||
node: ImportSpecifier$2,
|
||||
},
|
||||
],
|
||||
name: "Fragment",
|
||||
references: Array [
|
||||
Reference$2 {
|
||||
identifier: Identifier<"Fragment">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$2,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$3,
|
||||
isStrict: false,
|
||||
references: Array [],
|
||||
set: Map {},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [],
|
||||
},
|
||||
ModuleScope$2 {
|
||||
block: Program$3,
|
||||
isStrict: true,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
Reference$2,
|
||||
],
|
||||
set: Map {
|
||||
"React" => Variable$1,
|
||||
"Fragment" => Variable$2,
|
||||
},
|
||||
type: "module",
|
||||
upper: GlobalScope$1,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
Variable$2,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
//// @sourceType = 'module'
|
||||
//// @jsxPragma = 'h'
|
||||
//// @jsxFragmentName = 'Fragment'
|
||||
|
||||
import React from 'react'; // should be unreferenced
|
||||
import { h, Fragment } from 'preact';
|
||||
|
||||
<></>;
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx factory jsxPragma-jsxFragmentName 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$1 {
|
||||
name: Identifier<"React">,
|
||||
node: ImportDefaultSpecifier$1,
|
||||
},
|
||||
],
|
||||
name: "React",
|
||||
references: Array [],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
Variable$2 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$2 {
|
||||
name: Identifier<"h">,
|
||||
node: ImportSpecifier$2,
|
||||
},
|
||||
],
|
||||
name: "h",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"h">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$2,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
Variable$3 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$3 {
|
||||
name: Identifier<"Fragment">,
|
||||
node: ImportSpecifier$3,
|
||||
},
|
||||
],
|
||||
name: "Fragment",
|
||||
references: Array [
|
||||
Reference$2 {
|
||||
identifier: Identifier<"Fragment">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$3,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$4,
|
||||
isStrict: false,
|
||||
references: Array [],
|
||||
set: Map {},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [],
|
||||
},
|
||||
ModuleScope$2 {
|
||||
block: Program$4,
|
||||
isStrict: true,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
Reference$2,
|
||||
],
|
||||
set: Map {
|
||||
"React" => Variable$1,
|
||||
"h" => Variable$2,
|
||||
"Fragment" => Variable$3,
|
||||
},
|
||||
type: "module",
|
||||
upper: GlobalScope$1,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
Variable$2,
|
||||
Variable$3,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,7 @@
|
||||
//// @sourceType = 'module'
|
||||
//// @jsxPragma = 'h'
|
||||
|
||||
import React from 'react'; // should be unreferenced
|
||||
import { h } from 'preact';
|
||||
|
||||
<Foo />;
|
||||
@@ -0,0 +1,77 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx factory jsxPragma 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$1 {
|
||||
name: Identifier<"React">,
|
||||
node: ImportDefaultSpecifier$1,
|
||||
},
|
||||
],
|
||||
name: "React",
|
||||
references: Array [],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
Variable$2 {
|
||||
defs: Array [
|
||||
ImportBindingDefinition$2 {
|
||||
name: Identifier<"h">,
|
||||
node: ImportSpecifier$2,
|
||||
},
|
||||
],
|
||||
name: "h",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"h">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$2,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$3,
|
||||
isStrict: false,
|
||||
references: Array [],
|
||||
set: Map {},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [],
|
||||
},
|
||||
ModuleScope$2 {
|
||||
block: Program$3,
|
||||
isStrict: true,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
Reference$2 {
|
||||
identifier: JSXIdentifier$4,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: null,
|
||||
},
|
||||
],
|
||||
set: Map {
|
||||
"React" => Variable$1,
|
||||
"h" => Variable$2,
|
||||
},
|
||||
type: "module",
|
||||
upper: GlobalScope$1,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
Variable$2,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,3 @@
|
||||
const child = 1;
|
||||
|
||||
<>{child}</>;
|
||||
@@ -0,0 +1,57 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx fragment-children 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
VariableDefinition$1 {
|
||||
name: Identifier<"child">,
|
||||
node: VariableDeclarator$1,
|
||||
},
|
||||
],
|
||||
name: "child",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"child">,
|
||||
init: true,
|
||||
isRead: false,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: true,
|
||||
resolved: Variable$1,
|
||||
writeExpr: Literal$2,
|
||||
},
|
||||
Reference$2 {
|
||||
identifier: Identifier<"child">,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: false,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$3,
|
||||
isStrict: false,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
Reference$2,
|
||||
],
|
||||
set: Map {
|
||||
"child" => Variable$1,
|
||||
},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1 @@
|
||||
<></>;
|
||||
@@ -0,0 +1,18 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx fragment 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$1,
|
||||
isStrict: false,
|
||||
references: Array [],
|
||||
set: Map {},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,3 @@
|
||||
type T = 1;
|
||||
|
||||
<Foo<T> />;
|
||||
@@ -0,0 +1,54 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx generic-type-param 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
TypeDefinition$1 {
|
||||
name: Identifier<"T">,
|
||||
node: TSTypeAliasDeclaration$1,
|
||||
},
|
||||
],
|
||||
name: "T",
|
||||
references: Array [
|
||||
Reference$2 {
|
||||
identifier: Identifier<"T">,
|
||||
isRead: true,
|
||||
isTypeReference: true,
|
||||
isValueReference: false,
|
||||
isWrite: false,
|
||||
resolved: Variable$1,
|
||||
},
|
||||
],
|
||||
isValueVariable: false,
|
||||
isTypeVariable: true,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$2,
|
||||
isStrict: false,
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: JSXIdentifier$3,
|
||||
isRead: true,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: false,
|
||||
resolved: null,
|
||||
},
|
||||
Reference$2,
|
||||
],
|
||||
set: Map {
|
||||
"T" => Variable$1,
|
||||
},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,2 @@
|
||||
const Foo = 1; // should be unreferenced
|
||||
<>Foo</>;
|
||||
@@ -0,0 +1,48 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`jsx text 1`] = `
|
||||
ScopeManager {
|
||||
variables: Array [
|
||||
Variable$1 {
|
||||
defs: Array [
|
||||
VariableDefinition$1 {
|
||||
name: Identifier<"Foo">,
|
||||
node: VariableDeclarator$1,
|
||||
},
|
||||
],
|
||||
name: "Foo",
|
||||
references: Array [
|
||||
Reference$1 {
|
||||
identifier: Identifier<"Foo">,
|
||||
init: true,
|
||||
isRead: false,
|
||||
isTypeReference: false,
|
||||
isValueReference: true,
|
||||
isWrite: true,
|
||||
resolved: Variable$1,
|
||||
writeExpr: Literal$2,
|
||||
},
|
||||
],
|
||||
isValueVariable: true,
|
||||
isTypeVariable: false,
|
||||
},
|
||||
],
|
||||
scopes: Array [
|
||||
GlobalScope$1 {
|
||||
block: Program$3,
|
||||
isStrict: false,
|
||||
references: Array [
|
||||
Reference$1,
|
||||
],
|
||||
set: Map {
|
||||
"Foo" => Variable$1,
|
||||
},
|
||||
type: "global",
|
||||
upper: null,
|
||||
variables: Array [
|
||||
Variable$1,
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -28,6 +28,8 @@ interface ParserOptions {
|
||||
ecmaVersion?: EcmaVersion;
|
||||
|
||||
// scope-manager specific
|
||||
jsxPragma?: string;
|
||||
jsxFragmentName?: string | null;
|
||||
lib?: Lib[];
|
||||
|
||||
// typescript-estree specific
|
||||
|
||||
Reference in New Issue
Block a user