UNPKG

stencil-eslint-core

Version:

ESLint utils to help create custom rules for Stencil JS projects.

177 lines (172 loc) 6.06 kB
'use strict'; var ts = require('typescript'); var eslintUtils = require('eslint-utils'); var eslint = require('eslint'); const SyntaxKind = ts.SyntaxKind; function isPrivate(originalNode) { if (originalNode.modifiers) { return originalNode.modifiers.some((m) => (m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword)); } // detect private identifier (#) const firstChildNode = originalNode.getChildAt(0); return firstChildNode ? firstChildNode.kind === SyntaxKind.PrivateIdentifier : false; } function getDecorator(node, decoratorName) { if (decoratorName) { return node.decorators && node.decorators.find(isDecoratorNamed(decoratorName)); } return node.decorators ? node.decorators.filter((dec) => dec.expression) : []; } function parseDecorator(decorator) { if (decorator && decorator.expression && decorator.expression.type === 'CallExpression') { return decorator.expression.arguments.map((a) => { const parsed = eslintUtils.getStaticValue(a); return parsed ? parsed.value : undefined; }); } return []; } function decoratorName(dec) { return dec.expression && dec.expression.callee.name; } function isDecoratorNamed(propName) { return (dec) => { return decoratorName(dec) === propName; }; } function stencilComponentContext() { let componentNode; return { rules: { 'ClassDeclaration': (node) => { const component = getDecorator(node, 'Component'); if (component) { componentNode = component; } }, 'ClassDeclaration:exit': (node) => { if (componentNode === node) { componentNode = undefined; } } }, isComponent() { return !!componentNode; } }; } function getType(node) { return node.typeAnnotation.typeAnnotation.typeName.name; } const stencilDecorators = ['Component', 'Prop', 'State', 'Watch', 'Element', 'Method', 'Event', 'Listen']; const stencilLifecycle = [ 'connectedCallback', 'disconnectedCallback', 'componentWillLoad', 'componentDidLoad', 'componentWillRender', 'componentDidRender', 'componentShouldUpdate', 'componentWillUpdate', 'componentDidUpdate', 'render' ]; const TOKEN_TO_TEXT = { [SyntaxKind.OpenBraceToken]: '{', [SyntaxKind.CloseBraceToken]: '}', [SyntaxKind.OpenParenToken]: '(', [SyntaxKind.CloseParenToken]: ')', [SyntaxKind.OpenBracketToken]: '[', [SyntaxKind.CloseBracketToken]: ']', [SyntaxKind.DotToken]: '.', [SyntaxKind.DotDotDotToken]: '...', [SyntaxKind.SemicolonToken]: ',', [SyntaxKind.CommaToken]: ',', [SyntaxKind.LessThanToken]: '<', [SyntaxKind.GreaterThanToken]: '>', [SyntaxKind.LessThanEqualsToken]: '<=', [SyntaxKind.GreaterThanEqualsToken]: '>=', [SyntaxKind.EqualsEqualsToken]: '==', [SyntaxKind.ExclamationEqualsToken]: '!=', [SyntaxKind.EqualsEqualsEqualsToken]: '===', [SyntaxKind.InstanceOfKeyword]: 'instanceof', [SyntaxKind.ExclamationEqualsEqualsToken]: '!==', [SyntaxKind.EqualsGreaterThanToken]: '=>', [SyntaxKind.PlusToken]: '+', [SyntaxKind.MinusToken]: '-', [SyntaxKind.AsteriskToken]: '*', [SyntaxKind.AsteriskAsteriskToken]: '**', [SyntaxKind.SlashToken]: '/', [SyntaxKind.PercentToken]: '%', [SyntaxKind.PlusPlusToken]: '++', [SyntaxKind.MinusMinusToken]: '--', [SyntaxKind.LessThanLessThanToken]: '<<', [SyntaxKind.LessThanSlashToken]: '</', [SyntaxKind.GreaterThanGreaterThanToken]: '>>', [SyntaxKind.GreaterThanGreaterThanGreaterThanToken]: '>>>', [SyntaxKind.AmpersandToken]: '&', [SyntaxKind.BarToken]: '|', [SyntaxKind.CaretToken]: '^', [SyntaxKind.ExclamationToken]: '!', [SyntaxKind.TildeToken]: '~', [SyntaxKind.AmpersandAmpersandToken]: '&&', [SyntaxKind.BarBarToken]: '||', [SyntaxKind.QuestionToken]: '?', [SyntaxKind.ColonToken]: ':', [SyntaxKind.EqualsToken]: '=', [SyntaxKind.PlusEqualsToken]: '+=', [SyntaxKind.MinusEqualsToken]: '-=', [SyntaxKind.AsteriskEqualsToken]: '*=', [SyntaxKind.AsteriskAsteriskEqualsToken]: '**=', [SyntaxKind.SlashEqualsToken]: '/=', [SyntaxKind.PercentEqualsToken]: '%=', [SyntaxKind.LessThanLessThanEqualsToken]: '<<=', [SyntaxKind.GreaterThanGreaterThanEqualsToken]: '>>=', [SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken]: '>>>=', [SyntaxKind.AmpersandEqualsToken]: '&=', [SyntaxKind.BarEqualsToken]: '|=', [SyntaxKind.CaretEqualsToken]: '^=', [SyntaxKind.AtToken]: '@', [SyntaxKind.InKeyword]: 'in', [SyntaxKind.UniqueKeyword]: 'unique', [SyntaxKind.KeyOfKeyword]: 'keyof', [SyntaxKind.NewKeyword]: 'new', [SyntaxKind.ImportKeyword]: 'import', [SyntaxKind.ReadonlyKeyword]: 'readonly' }; /** * Returns the string form of the given TSToken SyntaxKind * @param kind the token's SyntaxKind * @returns the token applicable token as a string */ function getTextForTokenKind(kind) { return TOKEN_TO_TEXT[kind]; } const ruleTester = (projectPath) => new eslint.RuleTester({ parser: require.resolve("@typescript-eslint/parser"), parserOptions: { ecmaVersion: 2017, sourceType: 'module', ecmaFeatures: { jsx: true }, project: projectPath, env: { browser: true, node: true, es6: true } } }); exports.decoratorName = decoratorName; exports.getDecorator = getDecorator; exports.getTextForTokenKind = getTextForTokenKind; exports.getType = getType; exports.isDecoratorNamed = isDecoratorNamed; exports.isPrivate = isPrivate; exports.parseDecorator = parseDecorator; exports.ruleTester = ruleTester; exports.stencilComponentContext = stencilComponentContext; exports.stencilDecorators = stencilDecorators; exports.stencilLifecycle = stencilLifecycle;