Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add stable PrivateIdentifier based on estree spec #2933

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -60,7 +60,7 @@ interface NormalizedSelector {
}

type ValidatorFunction = (
node: TSESTree.Identifier | TSESTree.Literal,
node: TSESTree.Identifier | TSESTree.PrivateIdentifier | TSESTree.Literal,
modifiers?: Set<Modifiers>,
) => void;
type ParsedOptions = Record<SelectorsString, null | ValidatorFunction>;
Expand Down
Expand Up @@ -25,7 +25,9 @@ function createValidator(
type: SelectorsString,
context: Context,
allConfigs: NormalizedSelector[],
): (node: TSESTree.Identifier | TSESTree.Literal) => void {
): (
node: TSESTree.Identifier | TSESTree.PrivateIdentifier | TSESTree.Literal,
) => void {
// make sure the "highest priority" configs are checked first
const selectorType = Selectors[type];
const configs = allConfigs
Expand Down Expand Up @@ -70,11 +72,14 @@ function createValidator(
});

return (
node: TSESTree.Identifier | TSESTree.Literal,
node: TSESTree.Identifier | TSESTree.PrivateIdentifier | TSESTree.Literal,
modifiers: Set<Modifiers> = new Set<Modifiers>(),
): void => {
const originalName =
node.type === AST_NODE_TYPES.Identifier ? node.name : `${node.value}`;
node.type === AST_NODE_TYPES.Identifier ||
node.type === AST_NODE_TYPES.PrivateIdentifier
? node.name
: `${node.value}`;

// return will break the loop and stop checking configs
// it is only used when the name is known to have failed or succeeded a config.
Expand Down Expand Up @@ -178,7 +183,7 @@ function createValidator(
position: 'leading' | 'trailing',
config: NormalizedSelector,
name: string,
node: TSESTree.Identifier | TSESTree.Literal,
node: TSESTree.Identifier | TSESTree.PrivateIdentifier | TSESTree.Literal,
originalName: string,
): string | null {
const option =
Expand Down Expand Up @@ -299,7 +304,7 @@ function createValidator(
position: 'prefix' | 'suffix',
config: NormalizedSelector,
name: string,
node: TSESTree.Identifier | TSESTree.Literal,
node: TSESTree.Identifier | TSESTree.PrivateIdentifier | TSESTree.Literal,
originalName: string,
): string | null {
const affixes = config[position];
Expand Down Expand Up @@ -339,7 +344,7 @@ function createValidator(
function validateCustom(
config: NormalizedSelector,
name: string,
node: TSESTree.Identifier | TSESTree.Literal,
node: TSESTree.Identifier | TSESTree.PrivateIdentifier | TSESTree.Literal,
originalName: string,
): boolean {
const custom = config.custom;
Expand Down Expand Up @@ -372,7 +377,7 @@ function createValidator(
function validatePredefinedFormat(
config: NormalizedSelector,
name: string,
node: TSESTree.Identifier | TSESTree.Literal,
node: TSESTree.Identifier | TSESTree.PrivateIdentifier | TSESTree.Literal,
originalName: string,
): boolean {
const formats = config.format;
Expand Down
7 changes: 5 additions & 2 deletions packages/eslint-plugin/src/rules/naming-convention.ts
Expand Up @@ -621,11 +621,14 @@ function isGlobal(scope: TSESLint.Scope.Scope | null): boolean {
}

function requiresQuoting(
node: TSESTree.Identifier | TSESTree.Literal,
node: TSESTree.Identifier | TSESTree.PrivateIdentifier | TSESTree.Literal,
target: ScriptTarget | undefined,
): boolean {
const name =
node.type === AST_NODE_TYPES.Identifier ? node.name : `${node.value}`;
node.type === AST_NODE_TYPES.Identifier ||
node.type === AST_NODE_TYPES.PrivateIdentifier
? node.name
: `${node.value}`;
return util.requiresQuoting(name, target);
}

Expand Down
3 changes: 2 additions & 1 deletion packages/eslint-plugin/src/rules/no-unsafe-assignment.ts
Expand Up @@ -171,7 +171,8 @@ export default util.createRule({
let key: string;
if (receiverProperty.computed === false) {
key =
receiverProperty.key.type === AST_NODE_TYPES.Identifier
receiverProperty.key.type === AST_NODE_TYPES.Identifier ||
receiverProperty.key.type === AST_NODE_TYPES.PrivateIdentifier
? receiverProperty.key.name
: String(receiverProperty.key.value);
} else if (receiverProperty.key.type === AST_NODE_TYPES.Literal) {
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin/src/rules/prefer-for-of.ts
Expand Up @@ -43,7 +43,7 @@ export default util.createRule({
}

function isMatchingIdentifier(
node: TSESTree.Expression,
node: TSESTree.Expression | TSESTree.PrivateIdentifier,
name: string,
): boolean {
return node.type === AST_NODE_TYPES.Identifier && node.name === name;
Expand Down
5 changes: 4 additions & 1 deletion packages/eslint-plugin/src/util/misc.ts
Expand Up @@ -79,7 +79,10 @@ function getNameFromMember(
| TSESTree.TSPropertySignature,
sourceCode: TSESLint.SourceCode,
): string {
if (member.key.type === AST_NODE_TYPES.Identifier) {
if (
member.key.type === AST_NODE_TYPES.Identifier ||
member.key.type === AST_NODE_TYPES.PrivateIdentifier
) {
return member.key.name;
}
if (member.key.type === AST_NODE_TYPES.Literal) {
Expand Down
5 changes: 5 additions & 0 deletions packages/eslint-plugin/tests/rules/naming-convention.test.ts
Expand Up @@ -561,6 +561,8 @@ const cases: Cases = [
'class Ignored { private static readonly % = 1 }',
'class Ignored { abstract % = 1 }',
'class Ignored { declare % }',
'class Ignored { #% }',
'class Ignored { #% = 1 }',
],
options: {
selector: 'classProperty',
Expand Down Expand Up @@ -616,6 +618,7 @@ const cases: Cases = [
'class Ignored { private % = () => {} }',
'class Ignored { abstract %() }',
'class Ignored { declare %() }',
'class Ignored { #%() {} }',
],
options: {
selector: 'classMethod',
Expand All @@ -626,6 +629,7 @@ const cases: Cases = [
'const ignored = { %() {} };',
'const ignored = { "%"() {} };',
'const ignored = { %: () => {} };',
'const ignored = { #%: () => {} };',
],
options: {
selector: 'objectLiteralMethod',
Expand All @@ -636,6 +640,7 @@ const cases: Cases = [
'interface Ignored { %(): string }',
'interface Ignored { "%"(): string }',
'type Ignored = { %(): string }',
'type Ignored = { #%(): string }',
'type Ignored = { "%"(): string }',
],
options: {
Expand Down
12 changes: 12 additions & 0 deletions packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts
Expand Up @@ -95,6 +95,11 @@ class Foo {
`
class Foo {
private a = 1;
}
`,
`
class Foo {
#a = 1;
}
`,
'const x: Set<string> = new Set();',
Expand Down Expand Up @@ -150,6 +155,7 @@ const x = (1 as any), y = 1;
function foo(a = (1 as any)) {}
class Foo { constructor(private a = (1 as any)) {} }
class Foo { private a = (1 as any) }
class Foo { #a = (1 as any) }
`,
errors: [
{
Expand Down Expand Up @@ -182,6 +188,12 @@ class Foo { private a = (1 as any) }
column: 13,
endColumn: 35,
},
{
messageId: 'anyAssignment',
line: 7,
column: 13,
endColumn: 28,
},
],
}),
...batchedSingleLineTests({
Expand Down
@@ -0,0 +1,11 @@
class Foo {
#bar

constructor(bar) {
this.#bar = name;
}

get bar () {
return this.#bar
}
}
@@ -0,0 +1,5 @@
class Foo {
private #bar: string
public #bar: string
static #bar: string
}
@@ -0,0 +1,11 @@
class Foo {
#bar: string

constructor(name: string) {
this.#bar = name;
}

get bar () {
return this.#bar
}
}
@@ -0,0 +1,3 @@
class Foo {
get #foo() { }
}
@@ -0,0 +1,3 @@
class Foo {
#foo() { }
}
@@ -0,0 +1,3 @@
class Foo {
readonly #bar: string
}
1 change: 1 addition & 0 deletions packages/types/src/ast-node-types.ts
Expand Up @@ -61,6 +61,7 @@ enum AST_NODE_TYPES {
NewExpression = 'NewExpression',
ObjectExpression = 'ObjectExpression',
ObjectPattern = 'ObjectPattern',
PrivateIdentifier = 'PrivateIdentifier',
Program = 'Program',
Property = 'Property',
RestElement = 'RestElement',
Expand Down
11 changes: 9 additions & 2 deletions packages/types/src/ts-estree.ts
Expand Up @@ -204,6 +204,7 @@ export type Node =
| ObjectPattern
| Program
| Property
| PrivateIdentifier
| RestElement
| ReturnStatement
| SequenceExpression
Expand Down Expand Up @@ -483,6 +484,7 @@ export type PropertyName = PropertyNameComputed | PropertyNameNonComputed;
export type PropertyNameComputed = Expression;
export type PropertyNameNonComputed =
| Identifier
| PrivateIdentifier
| StringLiteral
| NumberLiteral;
export type Statement =
Expand Down Expand Up @@ -654,7 +656,7 @@ interface LiteralBase extends BaseNode {
/** this should not be directly used - instead use MemberExpressionComputedNameBase or MemberExpressionNonComputedNameBase */
interface MemberExpressionBase extends BaseNode {
object: LeftHandSideExpression;
property: Expression | Identifier;
property: Expression | Identifier | PrivateIdentifier;
computed: boolean;
optional: boolean;
}
Expand All @@ -665,7 +667,7 @@ interface MemberExpressionComputedNameBase extends MemberExpressionBase {
}

interface MemberExpressionNonComputedNameBase extends MemberExpressionBase {
property: Identifier;
property: Identifier | PrivateIdentifier;
computed: false;
}

Expand Down Expand Up @@ -1174,6 +1176,11 @@ export interface Program extends BaseNode {
tokens?: Token[];
}

export interface PrivateIdentifier extends BaseNode {
type: AST_NODE_TYPES.PrivateIdentifier;
name: string;
}

export interface PropertyComputedName extends PropertyBase {
key: PropertyNameComputed;
computed: true;
Expand Down
6 changes: 6 additions & 0 deletions packages/typescript-estree/src/convert.ts
Expand Up @@ -675,6 +675,12 @@ export class Converter {
});
}

case SyntaxKind.PrivateIdentifier:
return this.createNode<TSESTree.PrivateIdentifier>(node, {
type: AST_NODE_TYPES.PrivateIdentifier,
name: node.text.slice(1),
});

case SyntaxKind.WithStatement:
return this.createNode<TSESTree.WithStatement>(node, {
type: AST_NODE_TYPES.WithStatement,
Expand Down
Expand Up @@ -118,6 +118,7 @@ export interface EstreeToTsNodeTypes {
[AST_NODE_TYPES.ObjectPattern]:
| ts.ObjectLiteralExpression
| ts.ObjectBindingPattern;
[AST_NODE_TYPES.PrivateIdentifier]: ts.PrivateIdentifier;
[AST_NODE_TYPES.Program]: ts.SourceFile;
[AST_NODE_TYPES.Property]:
| ts.PropertyAssignment
Expand Down
1 change: 1 addition & 0 deletions packages/typescript-estree/src/ts-estree/ts-nodes.ts
Expand Up @@ -68,6 +68,7 @@ export type TSNode =
| ts.OmittedExpression
| ts.PartiallyEmittedExpression
| ts.PrefixUnaryExpression
| ts.PrivateIdentifier
| ts.PostfixUnaryExpression
| ts.NullLiteral
| ts.BooleanLiteral
Expand Down
Expand Up @@ -390,6 +390,12 @@ tester.addFixturePatternConfig('typescript/basics', {
* TODO: report this to babel
*/
'catch-clause-with-invalid-annotation',
/**
* [BABEL ERRORED, BUT TS-ESTREE DID NOT]
* Private elements cannot have an accessibility modifier ('private')
* TODO: Add error code from typescript
*/
'class-private-field-modifiers-error',
],
ignoreSourceType: [
/**
Expand Down
2 changes: 2 additions & 0 deletions packages/typescript-estree/tests/ast-alignment/parse.ts
Expand Up @@ -23,6 +23,8 @@ function parseWithBabelParser(text: string, jsx = true): any {
const babel: typeof babelParser = require('@babel/parser');
const plugins: ParserPlugin[] = [
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
'decorators-legacy',
'estree',
'typescript',
Expand Down
18 changes: 18 additions & 0 deletions packages/typescript-estree/tests/ast-alignment/utils.ts
Expand Up @@ -241,6 +241,24 @@ export function preprocessBabylonAST(ast: BabelTypes.File): any {
}
}
},
/**
* TS 3.8 private properties
* https://github.com/estree/estree/blob/master/experimental/class-features.md
*/
ClassPrivateProperty(node) {
node.type = AST_NODE_TYPES.ClassProperty;
node.computed = false;
node.declare = false;
},
ClassPrivateMethod(node) {
node.type = AST_NODE_TYPES.MethodDefinition;
node.computed = false;
},
PrivateName(node) {
node.type = AST_NODE_TYPES.PrivateIdentifier;
node.name = (node.id as any).name;
delete node.id;
},
},
);
}
Expand Down
Expand Up @@ -273,6 +273,8 @@ exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" e

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/javascript/classes/class-one-method-super.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/javascript/classes/class-private-field.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/javascript/classes/class-static-method.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/javascript/classes/class-static-method-named-prototype.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;
Expand Down Expand Up @@ -1757,6 +1759,16 @@ exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" e

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/class-multi-line-keyword-declare.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/class-private-field.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/class-private-field-modifiers-error.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/class-private-getter.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/class-private-method.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/class-readonly-private-field.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/class-with-accessibility-modifiers.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;

exports[`Parse all fixtures with "errorOnTypeScriptSyntacticAndSemanticIssues" enabled fixtures/typescript/basics/class-with-constructor-and-modifier.src 1`] = `"TEST OUTPUT: No semantic or syntactic issues found"`;
Expand Down