eslint-plugin-lingui
Advanced tools
Comparing version 0.4.0 to 0.5.0
import { TSESTree } from '@typescript-eslint/utils'; | ||
/** | ||
* Queries for TemplateLiteral in TaggedTemplateExpression expressions: | ||
* | ||
* t`Hello` | ||
* msg`Hello` | ||
* defineMessage`Hello` | ||
*/ | ||
export declare const LinguiTaggedTemplateExpressionMessageQuery = ":matches(TaggedTemplateExpression[tag.name=t], TaggedTemplateExpression[tag.name=msg], TaggedTemplateExpression[tag.name=defineMessage]) TemplateLiteral"; | ||
/** | ||
* Queries for TemplateLiteral | StringLiteral in CallExpression expressions: | ||
* | ||
* t({message: ``}); t({message: ''}) | ||
* msg({message: ``}); msg({message: ''}) | ||
* defineMessage({message: ``}); defineMessage({message: ''}) | ||
*/ | ||
export declare const LinguiCallExpressionMessageQuery = ":matches(CallExpression[callee.name=t], CallExpression[callee.name=msg], CallExpression[callee.name=defineMessage]) :matches(TemplateLiteral, Literal)"; | ||
/** | ||
* Queries for Trans | ||
* | ||
* <Trans></Trans> | ||
*/ | ||
export declare const LinguiTransQuery = "JSXElement[openingElement.name.name=Trans]"; | ||
export declare function isUpperCase(str: string): boolean; | ||
@@ -9,6 +31,4 @@ export declare function isNativeDOMTag(str: string): boolean; | ||
export declare function getNearestAncestor<Type>(node: any, type: string): Type | null; | ||
export declare const isTTaggedTemplateExpression: (node: TSESTree.TaggedTemplateExpression | null) => boolean; | ||
export declare const getQuasisValue: (node: TSESTree.TemplateLiteral, trim?: boolean) => string; | ||
export declare const getText: (node: TSESTree.TemplateLiteral | TSESTree.Literal | TSESTree.JSXText) => string; | ||
export declare function hasAncestorWithName(node: TSESTree.JSXElement | TSESTree.TemplateLiteral | TSESTree.Literal | TSESTree.JSXText, name: string): boolean; | ||
export declare function isNodeTranslated(node: TSESTree.TemplateLiteral | TSESTree.Literal | TSESTree.JSXText): boolean; | ||
export declare function getIdentifierName(jsxTagNameExpression: TSESTree.JSXTagNameExpression): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getIdentifierName = exports.isNodeTranslated = exports.hasAncestorWithName = exports.getQuasisValue = exports.isTTaggedTemplateExpression = exports.getNearestAncestor = exports.isAllowedDOMAttr = exports.isLinguiTags = exports.containAllAttributes = exports.isSvgTag = exports.isNativeDOMTag = exports.isUpperCase = void 0; | ||
exports.getIdentifierName = exports.hasAncestorWithName = exports.getText = exports.getNearestAncestor = exports.isAllowedDOMAttr = exports.isLinguiTags = exports.containAllAttributes = exports.isSvgTag = exports.isNativeDOMTag = exports.isUpperCase = exports.LinguiTransQuery = exports.LinguiCallExpressionMessageQuery = exports.LinguiTaggedTemplateExpressionMessageQuery = void 0; | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const constants_1 = require("./constants"); | ||
/** | ||
* Queries for TemplateLiteral in TaggedTemplateExpression expressions: | ||
* | ||
* t`Hello` | ||
* msg`Hello` | ||
* defineMessage`Hello` | ||
*/ | ||
exports.LinguiTaggedTemplateExpressionMessageQuery = ':matches(TaggedTemplateExpression[tag.name=t], TaggedTemplateExpression[tag.name=msg], TaggedTemplateExpression[tag.name=defineMessage]) TemplateLiteral'; | ||
/** | ||
* Queries for TemplateLiteral | StringLiteral in CallExpression expressions: | ||
* | ||
* t({message: ``}); t({message: ''}) | ||
* msg({message: ``}); msg({message: ''}) | ||
* defineMessage({message: ``}); defineMessage({message: ''}) | ||
*/ | ||
exports.LinguiCallExpressionMessageQuery = ':matches(CallExpression[callee.name=t], CallExpression[callee.name=msg], CallExpression[callee.name=defineMessage]) :matches(TemplateLiteral, Literal)'; | ||
/** | ||
* Queries for Trans | ||
* | ||
* <Trans></Trans> | ||
*/ | ||
exports.LinguiTransQuery = 'JSXElement[openingElement.name.name=Trans]'; | ||
function isUpperCase(str) { | ||
@@ -54,28 +76,12 @@ return /^[A-Z_-]+$/.test(str); | ||
exports.getNearestAncestor = getNearestAncestor; | ||
const isTTaggedTemplateExpression = (node) => { | ||
switch (node === null || node === void 0 ? void 0 : node.type) { | ||
case utils_1.TSESTree.AST_NODE_TYPES.TaggedTemplateExpression: | ||
if (node.tag.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) { | ||
const tagName = node.tag.name; | ||
if (tagName === 't') { | ||
return true; | ||
} | ||
} | ||
default: | ||
return false; | ||
const getText = (node) => { | ||
if (node.type === utils_1.TSESTree.AST_NODE_TYPES.TemplateLiteral) { | ||
return node.quasis | ||
.map((quasis) => quasis.value.cooked) | ||
.join('') | ||
.trim(); | ||
} | ||
return node.value.toString().trim(); | ||
}; | ||
exports.isTTaggedTemplateExpression = isTTaggedTemplateExpression; | ||
const getQuasisValue = (node, trim = true) => { | ||
if (node.quasis) { | ||
const quasisCookedArray = node.quasis.map((quasis) => quasis.value.cooked); | ||
const cookedArrayString = quasisCookedArray.join(''); | ||
if (trim) { | ||
return cookedArrayString.trim(); | ||
} | ||
return cookedArrayString; | ||
} | ||
return ''; | ||
}; | ||
exports.getQuasisValue = getQuasisValue; | ||
exports.getText = getText; | ||
function hasAncestorWithName(node, name) { | ||
@@ -98,10 +104,2 @@ var _a; | ||
exports.hasAncestorWithName = hasAncestorWithName; | ||
function isNodeTranslated(node) { | ||
if (hasAncestorWithName(node, 'Trans')) { | ||
return true; | ||
} | ||
const taggedTemplate = getNearestAncestor(node, 'TaggedTemplateExpression'); | ||
return taggedTemplate ? (0, exports.isTTaggedTemplateExpression)(taggedTemplate) : false; | ||
} | ||
exports.isNodeTranslated = isNodeTranslated; | ||
function getIdentifierName(jsxTagNameExpression) { | ||
@@ -108,0 +106,0 @@ switch (jsxTagNameExpression.type) { |
@@ -31,3 +31,6 @@ "use strict"; | ||
return { | ||
'TemplateLiteral:exit'(node) { | ||
[`${helpers_1.LinguiTaggedTemplateExpressionMessageQuery}, ${helpers_1.LinguiCallExpressionMessageQuery}`](node) { | ||
if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Literal) { | ||
return; | ||
} | ||
const noneIdentifierExpressions = node.expressions | ||
@@ -42,8 +45,5 @@ ? node.expressions.filter((expression) => { | ||
: []; | ||
const taggedTemplate = (0, helpers_1.getNearestAncestor)(node, 'TaggedTemplateExpression'); | ||
if (noneIdentifierExpressions.length > 0 && | ||
taggedTemplate && | ||
(0, helpers_1.isTTaggedTemplateExpression)(taggedTemplate)) { | ||
if (noneIdentifierExpressions.length > 0) { | ||
context.report({ | ||
node: node, | ||
node, | ||
messageId: 'default', | ||
@@ -54,2 +54,11 @@ }); | ||
}, | ||
[`${helpers_1.LinguiTransQuery} JSXExpressionContainer:not([parent.type=JSXAttribute]) > :expression`](node) { | ||
const isIdentifier = node.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier; | ||
if (!isIdentifier) { | ||
context.report({ | ||
node, | ||
messageId: 'default', | ||
}); | ||
} | ||
}, | ||
}; | ||
@@ -56,0 +65,0 @@ }, |
@@ -6,2 +6,3 @@ "use strict"; | ||
const create_rule_1 = require("../create-rule"); | ||
const helpers_1 = require("../helpers"); | ||
exports.name = 'no-single-tag-to-translate'; | ||
@@ -30,7 +31,5 @@ exports.rule = (0, create_rule_1.createRule)({ | ||
return { | ||
'JSXClosingElement > JSXIdentifier[name=Trans]'(node) { | ||
var _a; | ||
const parentJSXElement = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent; | ||
[helpers_1.LinguiTransQuery](node) { | ||
// delete all spaces or breaks | ||
const filteredChildren = parentJSXElement.children.filter((child) => { | ||
const filteredChildren = node.children.filter((child) => { | ||
var _a; | ||
@@ -37,0 +36,0 @@ switch (child.type) { |
@@ -44,23 +44,13 @@ "use strict"; | ||
return { | ||
JSXElement(node) { | ||
var _a; | ||
const identifierName = (0, helpers_1.getIdentifierName)((_a = node === null || node === void 0 ? void 0 : node.openingElement) === null || _a === void 0 ? void 0 : _a.name); | ||
if (identifierName === 'Trans') { | ||
const isSomeJSXTextWithContent = node && hasSomeJSXTextWithContent(node.children); | ||
const hasIdProperty = node.openingElement.attributes.find((attr) => attr.type === 'JSXAttribute' && attr.name.name === 'id') !== undefined; | ||
if (!isSomeJSXTextWithContent && !hasIdProperty) { | ||
context.report({ | ||
node, | ||
messageId: 'asJsx', | ||
}); | ||
} | ||
'JSXElement[openingElement.name.name=Trans]'(node) { | ||
const hasIdProperty = node.openingElement.attributes.find((attr) => attr.type === 'JSXAttribute' && attr.name.name === 'id') !== undefined; | ||
if (!hasSomeJSXTextWithContent(node.children) && !hasIdProperty) { | ||
context.report({ | ||
node, | ||
messageId: 'asJsx', | ||
}); | ||
} | ||
return; | ||
}, | ||
'TemplateLiteral:exit'(node) { | ||
const taggedTemplate = (0, helpers_1.getNearestAncestor)(node, 'TaggedTemplateExpression'); | ||
const quasisValue = (0, helpers_1.getQuasisValue)(node); | ||
if (taggedTemplate && | ||
(0, helpers_1.isTTaggedTemplateExpression)(taggedTemplate) && | ||
(!quasisValue || !quasisValue.length)) { | ||
[`${helpers_1.LinguiTaggedTemplateExpressionMessageQuery}, ${helpers_1.LinguiCallExpressionMessageQuery}`](node) { | ||
if (!(0, helpers_1.getText)(node)) { | ||
context.report({ | ||
@@ -67,0 +57,0 @@ node, |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.rule = exports.name = void 0; | ||
const create_rule_1 = require("../create-rule"); | ||
const helpers_1 = require("../helpers"); | ||
const create_rule_1 = require("../create-rule"); | ||
exports.name = 'no-trans-inside-trans'; | ||
@@ -29,11 +29,7 @@ exports.rule = (0, create_rule_1.createRule)({ | ||
return { | ||
JSXElement(node) { | ||
var _a; | ||
const identifierName = (0, helpers_1.getIdentifierName)((_a = node === null || node === void 0 ? void 0 : node.openingElement) === null || _a === void 0 ? void 0 : _a.name); | ||
if (identifierName === 'Trans' && (0, helpers_1.hasAncestorWithName)(node, 'Trans')) { | ||
context.report({ | ||
node, | ||
messageId: 'default', | ||
}); | ||
} | ||
[`${helpers_1.LinguiTransQuery} ${helpers_1.LinguiTransQuery}`](node) { | ||
context.report({ | ||
node, | ||
messageId: 'default', | ||
}); | ||
return; | ||
@@ -40,0 +36,0 @@ }, |
@@ -6,3 +6,5 @@ import { ESLintUtils } from '@typescript-eslint/utils'; | ||
ignoreAttribute?: string[]; | ||
strictAttribute?: string[]; | ||
ignoreProperty?: string[]; | ||
ignoreMethodsOnTypes?: string[]; | ||
useTsTypes?: boolean; | ||
@@ -9,0 +11,0 @@ }; |
@@ -34,2 +34,8 @@ "use strict"; | ||
}, | ||
ignoreMethodsOnTypes: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
}, | ||
}, | ||
ignoreAttribute: { | ||
@@ -41,2 +47,8 @@ type: 'array', | ||
}, | ||
strictAttribute: { | ||
type: 'array', | ||
items: { | ||
type: 'string', | ||
}, | ||
}, | ||
ignoreProperty: { | ||
@@ -61,2 +73,6 @@ type: 'array', | ||
const { options: [option], } = context; | ||
let tsService; | ||
if (option === null || option === void 0 ? void 0 : option.useTsTypes) { | ||
tsService = utils_1.ESLintUtils.getParserServices(context, false); | ||
} | ||
const whitelists = [ | ||
@@ -80,2 +96,3 @@ /^[^A-Za-z]+$/, | ||
function isValidFunctionCall({ callee, }) { | ||
var _a; | ||
switch (callee.type) { | ||
@@ -85,8 +102,22 @@ case utils_1.TSESTree.AST_NODE_TYPES.MemberExpression: { | ||
callee.object.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) { | ||
if (calleeWhitelists.simple.indexOf(callee.property.name) !== -1) { | ||
if (calleeWhitelists.simple.includes(callee.property.name)) { | ||
return true; | ||
} | ||
const calleeName = `${callee.object.name}.${callee.property.name}`; | ||
return calleeWhitelists.complex.indexOf(calleeName) !== -1; | ||
if (calleeWhitelists.complex.includes(calleeName)) { | ||
return true; | ||
} | ||
} | ||
// use power of TS compiler to exclude call on specific types, such Map.get, Set.get and so on | ||
if (tsService && callee.property.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) { | ||
for (const ignore of ignoredMethodsOnTypes) { | ||
const [type, method] = ignore.split('.'); | ||
if (method === callee.property.name) { | ||
const typeObj = tsService.getTypeAtLocation(callee.object); | ||
if (type === ((_a = typeObj === null || typeObj === void 0 ? void 0 : typeObj.getSymbol()) === null || _a === void 0 ? void 0 : _a.name)) { | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
return false; | ||
@@ -98,3 +129,3 @@ } | ||
} | ||
return calleeWhitelists.simple.indexOf(callee.name) !== -1; | ||
return calleeWhitelists.simple.includes(callee.name); | ||
} | ||
@@ -113,4 +144,4 @@ case utils_1.TSESTree.AST_NODE_TYPES.CallExpression: { | ||
const ignoredJSXSymbols = ['←', ' ', '·']; | ||
const ignoredAttributes = (option && option.ignoreAttribute) || []; | ||
const userJSXAttrs = [ | ||
const strictAttributes = [...((option === null || option === void 0 ? void 0 : option.strictAttribute) || [])]; | ||
const ignoredAttributes = [ | ||
'className', | ||
@@ -122,6 +153,11 @@ 'styleName', | ||
'height', | ||
...ignoredAttributes, | ||
...((option === null || option === void 0 ? void 0 : option.ignoreAttribute) || []), | ||
]; | ||
const ignoredProperties = (option && option.ignoreProperty) || []; | ||
const userProperties = [ | ||
const ignoredMethodsOnTypes = [ | ||
'Map.get', | ||
'Map.has', | ||
'Set.has', | ||
...((option === null || option === void 0 ? void 0 : option.ignoreMethodsOnTypes) || []), | ||
]; | ||
const ignoredProperties = [ | ||
'className', | ||
@@ -134,3 +170,3 @@ 'styleName', | ||
'displayName', | ||
...ignoredProperties, | ||
...((option === null || option === void 0 ? void 0 : option.ignoreProperty) || []), | ||
]; | ||
@@ -141,3 +177,9 @@ //---------------------------------------------------------------------- | ||
const visited = new WeakSet(); | ||
function isString(node) { | ||
function isIgnoredSymbol(str) { | ||
return ignoredJSXSymbols.some((name) => name === str); | ||
} | ||
function isIgnoredJSXElement(node) { | ||
return ignoredJSXElements.some((name) => (0, helpers_1.hasAncestorWithName)(node, name)); | ||
} | ||
function isStringLiteral(node) { | ||
switch (node.type) { | ||
@@ -160,84 +202,5 @@ case utils_1.TSESTree.AST_NODE_TYPES.Literal: | ||
}; | ||
const onJSXAttribute = (node) => { | ||
var _a, _b; | ||
const parent = (0, helpers_1.getNearestAncestor)(node, 'JSXAttribute'); | ||
const attrName = getAttrName((_a = parent === null || parent === void 0 ? void 0 : parent.name) === null || _a === void 0 ? void 0 : _a.name); | ||
// allow <MyComponent className="active" /> | ||
if (userJSXAttrs.includes(getAttrName((_b = parent === null || parent === void 0 ? void 0 : parent.name) === null || _b === void 0 ? void 0 : _b.name))) { | ||
visited.add(node); | ||
return; | ||
} | ||
const jsxElement = (0, helpers_1.getNearestAncestor)(node, 'JSXOpeningElement'); | ||
const tagName = (0, helpers_1.getIdentifierName)(jsxElement === null || jsxElement === void 0 ? void 0 : jsxElement.name); | ||
const attributeNames = jsxElement === null || jsxElement === void 0 ? void 0 : jsxElement.attributes.map((attr) => { var _a; return attr.type === utils_1.TSESTree.AST_NODE_TYPES.JSXAttribute && getAttrName((_a = attr === null || attr === void 0 ? void 0 : attr.name) === null || _a === void 0 ? void 0 : _a.name); }); | ||
if ((0, helpers_1.isAllowedDOMAttr)(tagName, attrName, attributeNames)) { | ||
visited.add(node); | ||
} | ||
}; | ||
const onProperty = (node) => { | ||
var _a, _b, _c, _d, _e, _f, _g, _h; | ||
const { parent } = node; | ||
if (parent.type === utils_1.TSESTree.AST_NODE_TYPES.Property) { | ||
// if node is key of property, skip | ||
if ((parent === null || parent === void 0 ? void 0 : parent.key) === node) { | ||
visited.add(node); | ||
} | ||
// name if key is Identifier; value if key is Literal | ||
// dont care whether if this is computed or not | ||
if (((_a = parent === null || parent === void 0 ? void 0 : parent.key) === null || _a === void 0 ? void 0 : _a.type) === utils_1.TSESTree.AST_NODE_TYPES.Identifier && | ||
((0, helpers_1.isUpperCase)((_b = parent === null || parent === void 0 ? void 0 : parent.key) === null || _b === void 0 ? void 0 : _b.name) || userProperties.includes((_c = parent === null || parent === void 0 ? void 0 : parent.key) === null || _c === void 0 ? void 0 : _c.name))) { | ||
visited.add(node); | ||
} | ||
if (((_d = parent === null || parent === void 0 ? void 0 : parent.key) === null || _d === void 0 ? void 0 : _d.type) === utils_1.TSESTree.AST_NODE_TYPES.Literal && | ||
(0, helpers_1.isUpperCase)(`${(_e = parent === null || parent === void 0 ? void 0 : parent.key) === null || _e === void 0 ? void 0 : _e.value}`)) { | ||
visited.add(node); | ||
} | ||
if (((_f = parent === null || parent === void 0 ? void 0 : parent.value) === null || _f === void 0 ? void 0 : _f.type) === utils_1.TSESTree.AST_NODE_TYPES.Literal && | ||
(0, helpers_1.isUpperCase)(`${(_g = parent === null || parent === void 0 ? void 0 : parent.value) === null || _g === void 0 ? void 0 : _g.value}`)) { | ||
visited.add(node); | ||
} | ||
if (((_h = parent === null || parent === void 0 ? void 0 : parent.key) === null || _h === void 0 ? void 0 : _h.type) === utils_1.TSESTree.AST_NODE_TYPES.TemplateLiteral && | ||
(0, helpers_1.isUpperCase)((0, helpers_1.getQuasisValue)(parent === null || parent === void 0 ? void 0 : parent.key))) { | ||
visited.add(node); | ||
} | ||
} | ||
}; | ||
const onBinaryExpression = (node) => { | ||
if (node.parent.type === utils_1.TSESTree.AST_NODE_TYPES.BinaryExpression) { | ||
const { parent: { operator }, } = node; | ||
// allow name === 'String' | ||
if (operator !== '+') { | ||
visited.add(node); | ||
} | ||
} | ||
}; | ||
const onCallExpression = (node, parentName) => { | ||
const parent = utils_1.TSESTree.AST_NODE_TYPES.CallExpression === parentName | ||
? (0, helpers_1.getNearestAncestor)(node, parentName) | ||
: (0, helpers_1.getNearestAncestor)(node, parentName); | ||
if (isValidFunctionCall(parent)) | ||
visited.add(node); | ||
}; | ||
const onClassProperty = (node) => { | ||
const { parent } = node; | ||
if ((parent.type === utils_1.TSESTree.AST_NODE_TYPES.Property || | ||
parent.type === utils_1.TSESTree.AST_NODE_TYPES.PropertyDefinition || | ||
//@ts-ignore | ||
parent.type === 'ClassProperty') && | ||
parent.key.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) { | ||
if ((parent === null || parent === void 0 ? void 0 : parent.key) && ignoredClassProperties.includes(parent.key.name)) { | ||
visited.add(node); | ||
} | ||
} | ||
}; | ||
const processTextNode = (node, text) => { | ||
visited.add(node); | ||
const userJSXElement = [...ignoredJSXElements]; | ||
function isUserJSXElement(node) { | ||
return userJSXElement.some((name) => (0, helpers_1.hasAncestorWithName)(node, name)); | ||
} | ||
function isIgnoredSymbol(str) { | ||
return ignoredJSXSymbols.some((name) => name === str); | ||
} | ||
if (!text || match(text) || isUserJSXElement(node) || isIgnoredSymbol(text)) { | ||
if (!text || match(text) || isIgnoredJSXElement(node) || isIgnoredSymbol(text)) { | ||
return; | ||
@@ -247,3 +210,3 @@ } | ||
}; | ||
const literalVisitor = { | ||
const visitor = { | ||
'ImportDeclaration Literal'(node) { | ||
@@ -261,2 +224,5 @@ // allow (import abc form 'abc') | ||
}, | ||
JSXText(node) { | ||
processTextNode(node, `${node.value}`.trim()); | ||
}, | ||
'JSXElement > Literal'(node) { | ||
@@ -268,5 +234,26 @@ processTextNode(node, `${node.value}`.trim()); | ||
}, | ||
'JSXAttribute Literal'(node) { | ||
onJSXAttribute(node); | ||
'JSXElement > JSXExpressionContainer > TemplateLiteral'(node) { | ||
processTextNode(node, (0, helpers_1.getText)(node)); | ||
}, | ||
'JSXAttribute :matches(Literal,TemplateLiteral)'(node) { | ||
var _a; | ||
const parent = (0, helpers_1.getNearestAncestor)(node, utils_1.TSESTree.AST_NODE_TYPES.JSXAttribute); | ||
const attrName = getAttrName((_a = parent === null || parent === void 0 ? void 0 : parent.name) === null || _a === void 0 ? void 0 : _a.name); | ||
if (strictAttributes.includes(attrName)) { | ||
visited.add(node); | ||
context.report({ node, messageId: 'default', data: { message } }); | ||
return; | ||
} | ||
// allow <MyComponent className="active" /> | ||
if (ignoredAttributes.includes(attrName)) { | ||
visited.add(node); | ||
return; | ||
} | ||
const jsxElement = (0, helpers_1.getNearestAncestor)(node, utils_1.TSESTree.AST_NODE_TYPES.JSXOpeningElement); | ||
const tagName = (0, helpers_1.getIdentifierName)(jsxElement === null || jsxElement === void 0 ? void 0 : jsxElement.name); | ||
const attributeNames = jsxElement === null || jsxElement === void 0 ? void 0 : jsxElement.attributes.map((attr) => { var _a; return attr.type === utils_1.TSESTree.AST_NODE_TYPES.JSXAttribute && getAttrName((_a = attr === null || attr === void 0 ? void 0 : attr.name) === null || _a === void 0 ? void 0 : _a.name); }); | ||
if ((0, helpers_1.isAllowedDOMAttr)(tagName, attrName, attributeNames)) { | ||
visited.add(node); | ||
} | ||
}, | ||
'TSLiteralType Literal'(node) { | ||
@@ -277,9 +264,18 @@ // allow var a: Type['member']; | ||
// ───────────────────────────────────────────────────────────────── | ||
'ClassProperty > Literal, PropertyDefinition > Literal'(node) { | ||
onClassProperty(node); | ||
'ClassProperty > :matches(Literal,TemplateLiteral), PropertyDefinition > :matches(Literal,TemplateLiteral)'(node) { | ||
const { parent } = node; | ||
if ((parent.type === utils_1.TSESTree.AST_NODE_TYPES.Property || | ||
parent.type === utils_1.TSESTree.AST_NODE_TYPES.PropertyDefinition || | ||
//@ts-ignore | ||
parent.type === 'ClassProperty') && | ||
parent.key.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) { | ||
if ((parent === null || parent === void 0 ? void 0 : parent.key) && ignoredClassProperties.includes(parent.key.name)) { | ||
visited.add(node); | ||
} | ||
} | ||
}, | ||
'TSEnumMember > Literal'(node) { | ||
'TSEnumMember > :matches(Literal,TemplateLiteral)'(node) { | ||
visited.add(node); | ||
}, | ||
'VariableDeclarator > Literal'(node) { | ||
'VariableDeclarator > :matches(Literal,TemplateLiteral)'(node) { | ||
// allow statements like const A_B = "test" | ||
@@ -292,6 +288,31 @@ if (node.parent.type === utils_1.TSESTree.AST_NODE_TYPES.VariableDeclarator && | ||
}, | ||
'Property > Literal'(node) { | ||
onProperty(node); | ||
'Property > :matches(Literal,TemplateLiteral)'(node) { | ||
var _a, _b, _c, _d, _e, _f, _g, _h; | ||
const { parent } = node; | ||
if (parent.type === utils_1.TSESTree.AST_NODE_TYPES.Property) { | ||
// if node is key of property, skip | ||
if ((parent === null || parent === void 0 ? void 0 : parent.key) === node) { | ||
visited.add(node); | ||
} | ||
// name if key is Identifier; value if key is Literal | ||
// dont care whether if this is computed or not | ||
if (((_a = parent === null || parent === void 0 ? void 0 : parent.key) === null || _a === void 0 ? void 0 : _a.type) === utils_1.TSESTree.AST_NODE_TYPES.Identifier && | ||
((0, helpers_1.isUpperCase)((_b = parent === null || parent === void 0 ? void 0 : parent.key) === null || _b === void 0 ? void 0 : _b.name) || ignoredProperties.includes((_c = parent === null || parent === void 0 ? void 0 : parent.key) === null || _c === void 0 ? void 0 : _c.name))) { | ||
visited.add(node); | ||
} | ||
if (((_d = parent === null || parent === void 0 ? void 0 : parent.key) === null || _d === void 0 ? void 0 : _d.type) === utils_1.TSESTree.AST_NODE_TYPES.Literal && | ||
(0, helpers_1.isUpperCase)(`${(_e = parent === null || parent === void 0 ? void 0 : parent.key) === null || _e === void 0 ? void 0 : _e.value}`)) { | ||
visited.add(node); | ||
} | ||
if (((_f = parent === null || parent === void 0 ? void 0 : parent.value) === null || _f === void 0 ? void 0 : _f.type) === utils_1.TSESTree.AST_NODE_TYPES.Literal && | ||
(0, helpers_1.isUpperCase)(`${(_g = parent === null || parent === void 0 ? void 0 : parent.value) === null || _g === void 0 ? void 0 : _g.value}`)) { | ||
visited.add(node); | ||
} | ||
if (((_h = parent === null || parent === void 0 ? void 0 : parent.key) === null || _h === void 0 ? void 0 : _h.type) === utils_1.TSESTree.AST_NODE_TYPES.TemplateLiteral && | ||
(0, helpers_1.isUpperCase)((0, helpers_1.getText)(parent === null || parent === void 0 ? void 0 : parent.key))) { | ||
visited.add(node); | ||
} | ||
} | ||
}, | ||
'MemberExpression[computed=true] > Literal'(node) { | ||
'MemberExpression[computed=true] > :matches(Literal,TemplateLiteral)'(node) { | ||
// obj["key with space"] | ||
@@ -301,2 +322,4 @@ visited.add(node); | ||
"AssignmentExpression[left.type='MemberExpression'] > Literal"(node) { | ||
// options: { ignoreProperties: ['myProperty'] } | ||
// MyComponent.myProperty = "Hello" | ||
const assignmentExp = node.parent; | ||
@@ -306,21 +329,38 @@ const memberExp = assignmentExp.left; | ||
memberExp.property.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier && | ||
userProperties.includes(memberExp.property.name)) { | ||
ignoredProperties.includes(memberExp.property.name)) { | ||
visited.add(node); | ||
} | ||
}, | ||
'BinaryExpression > Literal'(node) { | ||
onBinaryExpression(node); | ||
'BinaryExpression > :matches(Literal,TemplateLiteral)'(node) { | ||
if (node.parent.type === utils_1.TSESTree.AST_NODE_TYPES.BinaryExpression) { | ||
const { parent: { operator }, } = node; | ||
// allow name === 'String' | ||
if (operator !== '+') { | ||
visited.add(node); | ||
} | ||
} | ||
}, | ||
'CallExpression Literal'(node) { | ||
onCallExpression(node, utils_1.TSESTree.AST_NODE_TYPES.CallExpression); | ||
'CallExpression :matches(Literal,TemplateLiteral)'(node) { | ||
const parent = (0, helpers_1.getNearestAncestor)(node, utils_1.TSESTree.AST_NODE_TYPES.CallExpression); | ||
if (isValidFunctionCall(parent)) { | ||
visited.add(node); | ||
return; | ||
} | ||
}, | ||
'NewExpression Literal'(node) { | ||
onCallExpression(node, utils_1.TSESTree.AST_NODE_TYPES.NewExpression); | ||
'NewExpression :matches(Literal,TemplateLiteral)'(node) { | ||
const parent = (0, helpers_1.getNearestAncestor)(node, utils_1.TSESTree.AST_NODE_TYPES.NewExpression); | ||
if (isValidFunctionCall(parent)) { | ||
visited.add(node); | ||
return; | ||
} | ||
}, | ||
'SwitchCase > Literal'(node) { | ||
'SwitchCase > :matches(Literal,TemplateLiteral)'(node) { | ||
visited.add(node); | ||
}, | ||
'TaggedTemplateExpression > TemplateLiteral Literal'(node) { | ||
'TaggedTemplateExpression > TemplateLiteral'(node) { | ||
visited.add(node); | ||
}, | ||
'TaggedTemplateExpression > TemplateLiteral :matches(Literal,TemplateLiteral)'(node) { | ||
visited.add(node); | ||
}, | ||
'Literal:exit'(node) { | ||
@@ -341,5 +381,4 @@ // visited and passed linting | ||
// | ||
if (option === null || option === void 0 ? void 0 : option.useTsTypes) { | ||
const services = utils_1.ESLintUtils.getParserServices(context); | ||
const typeObj = services.getTypeAtLocation(node.parent); | ||
if (tsService) { | ||
const typeObj = tsService.getTypeAtLocation(node.parent); | ||
// var a: 'abc' = 'abc' | ||
@@ -352,53 +391,6 @@ if (typeObj.isStringLiteral() && typeObj.symbol) { | ||
}, | ||
}; | ||
const templateLiteralVisitor = { | ||
'JSXElement > JSXExpressionContainer > TemplateLiteral'(node) { | ||
processTextNode(node, (0, helpers_1.getQuasisValue)(node)); | ||
}, | ||
'JSXAttribute TemplateLiteral'(node) { | ||
onJSXAttribute(node); | ||
}, | ||
'ClassProperty > TemplateLiteral'(node) { | ||
onClassProperty(node); | ||
}, | ||
'TSEnumMember > TemplateLiteral'(node) { | ||
visited.add(node); | ||
}, | ||
'VariableDeclarator > TemplateLiteral'(node) { | ||
// allow statements like const A_B = `test` | ||
if (node.parent.type === utils_1.TSESTree.AST_NODE_TYPES.VariableDeclarator && | ||
node.parent.id.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier && | ||
(0, helpers_1.isUpperCase)(node.parent.id.name)) { | ||
visited.add(node); | ||
} | ||
}, | ||
'MemberExpression[computed=true] > TemplateLiteral'(node) { | ||
// obj[`key with space`] | ||
visited.add(node); | ||
}, | ||
'Property > TemplateLiteral'(node) { | ||
onProperty(node); | ||
}, | ||
'BinaryExpression > TemplateLiteral'(node) { | ||
onBinaryExpression(node); | ||
}, | ||
'CallExpression TemplateLiteral'(node) { | ||
onCallExpression(node, utils_1.TSESTree.AST_NODE_TYPES.CallExpression); | ||
}, | ||
'NewExpression TemplateLiteral'(node) { | ||
onCallExpression(node, utils_1.TSESTree.AST_NODE_TYPES.NewExpression); | ||
}, | ||
'SwitchCase > TemplateLiteral'(node) { | ||
visited.add(node); | ||
}, | ||
'TaggedTemplateExpression > TemplateLiteral'(node) { | ||
visited.add(node); | ||
}, | ||
'TaggedTemplateExpression > TemplateLiteral TemplateLiteral'(node) { | ||
visited.add(node); | ||
}, | ||
'TemplateLiteral:exit'(node) { | ||
if (visited.has(node)) | ||
return; | ||
const quasisValue = (0, helpers_1.getQuasisValue)(node); | ||
const quasisValue = (0, helpers_1.getText)(node); | ||
if ((0, helpers_1.isUpperCase)(quasisValue)) | ||
@@ -411,7 +403,2 @@ return; | ||
}; | ||
const jsxTextLiteralVisitor = { | ||
JSXText(node) { | ||
processTextNode(node, `${node.value}`.trim()); | ||
}, | ||
}; | ||
function wrapVisitor(visitor) { | ||
@@ -423,3 +410,3 @@ const newVisitor = {}; | ||
// make sure node is string literal | ||
if (!isString(node)) | ||
if (!isStringLiteral(node)) | ||
return; | ||
@@ -431,7 +418,3 @@ old(node); | ||
} | ||
return { | ||
...wrapVisitor(literalVisitor), | ||
...wrapVisitor(templateLiteralVisitor), | ||
...wrapVisitor(jsxTextLiteralVisitor), | ||
}; | ||
return wrapVisitor(visitor); | ||
}, | ||
@@ -458,3 +441,3 @@ }); | ||
ignoreFunction.forEach((item) => { | ||
if (item.indexOf('.') !== -1) { | ||
if (item.includes('.')) { | ||
result.complex.push(item); | ||
@@ -461,0 +444,0 @@ } |
@@ -0,2 +1,4 @@ | ||
import { Scope, ScopeType } from '@typescript-eslint/scope-manager'; | ||
export declare function hasAncestorScope(node: Scope, types: ScopeType[]): boolean; | ||
export declare const name = "t-call-in-function"; | ||
export declare const rule: import("@typescript-eslint/utils/ts-eslint").RuleModule<"default", any[], import("../create-rule").ExtraRuleDocs, import("@typescript-eslint/utils/ts-eslint").RuleListener>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.rule = exports.name = void 0; | ||
const utils_1 = require("@typescript-eslint/utils"); | ||
const helpers_1 = require("../helpers"); | ||
exports.rule = exports.name = exports.hasAncestorScope = void 0; | ||
const scope_manager_1 = require("@typescript-eslint/scope-manager"); | ||
const create_rule_1 = require("../create-rule"); | ||
function hasAncestorScope(node, types) { | ||
let current = node; | ||
while (current) { | ||
if (types.includes(current.type)) { | ||
return true; | ||
} | ||
current = current.upper; | ||
} | ||
return false; | ||
} | ||
exports.hasAncestorScope = hasAncestorScope; | ||
exports.name = 't-call-in-function'; | ||
@@ -31,24 +41,4 @@ exports.rule = (0, create_rule_1.createRule)({ | ||
const sourceCode = (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode(); | ||
const visited = new WeakSet(); | ||
const handler = (node) => { | ||
if (!(0, helpers_1.isTTaggedTemplateExpression)(node)) { | ||
return; | ||
} | ||
visited.add(node); | ||
return; | ||
}; | ||
return { | ||
['FunctionDeclaration TaggedTemplateExpression'](node) { | ||
handler(node); | ||
}, | ||
['FunctionExpression TaggedTemplateExpression'](node) { | ||
handler(node); | ||
}, | ||
['ArrowFunctionExpression TaggedTemplateExpression'](node) { | ||
handler(node); | ||
}, | ||
['ClassDeclaration TaggedTemplateExpression'](node) { | ||
handler(node); | ||
}, | ||
['CallExpression:exit'](node) { | ||
'TaggedTemplateExpression[tag.name=t], CallExpression[callee.name=t]'(node) { | ||
const scope = sourceCode.getScope | ||
@@ -59,5 +49,3 @@ ? // available from ESLint v8.37.0 | ||
context.getScope(); | ||
if (scope.type === 'module' && | ||
node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier && | ||
node.callee.name === 't') { | ||
if (!hasAncestorScope(scope, [scope_manager_1.ScopeType.function, scope_manager_1.ScopeType.classFieldInitializer])) { | ||
context.report({ | ||
@@ -69,13 +57,2 @@ node, | ||
}, | ||
['TaggedTemplateExpression:exit'](node) { | ||
if (visited.has(node)) | ||
return; | ||
if (!(0, helpers_1.isTTaggedTemplateExpression)(node)) { | ||
return; | ||
} | ||
context.report({ | ||
node, | ||
messageId: 'default', | ||
}); | ||
}, | ||
}; | ||
@@ -82,0 +59,0 @@ }, |
@@ -5,3 +5,2 @@ export type RestrictionRule = { | ||
flags?: string; | ||
isOnlyForTranslation?: boolean; | ||
}; | ||
@@ -8,0 +7,0 @@ export type Option = { |
@@ -38,5 +38,2 @@ "use strict"; | ||
}, | ||
isOnlyForTranslation: { | ||
type: 'boolean', | ||
}, | ||
}, | ||
@@ -53,39 +50,25 @@ }, | ||
create: function (context) { | ||
var _a; | ||
const { options: [option], } = context; | ||
if (option && option.rules) { | ||
const { rules } = option; | ||
const rulePatterns = rules.map(({ patterns, message, flags, isOnlyForTranslation }) => ({ | ||
patterns: patterns.map((item) => new RegExp(item, flags)), | ||
message, | ||
isOnlyForTranslation, | ||
})); | ||
const onLiteral = (value, node) => { | ||
rulePatterns.forEach(({ patterns, message, isOnlyForTranslation }) => { | ||
if (isOnlyForTranslation && !(0, helpers_1.isNodeTranslated)(node)) { | ||
return; | ||
} | ||
if (patterns.some((item) => { | ||
return item.test(value); | ||
})) { | ||
if (!((_a = option === null || option === void 0 ? void 0 : option.rules) === null || _a === void 0 ? void 0 : _a.length)) { | ||
return {}; | ||
} | ||
const { rules } = option; | ||
const rulePatterns = rules.map(({ patterns, message, flags }) => ({ | ||
patterns: patterns.map((item) => new RegExp(item, flags)), | ||
message, | ||
})); | ||
return { | ||
[`${helpers_1.LinguiTaggedTemplateExpressionMessageQuery}, ${helpers_1.LinguiCallExpressionMessageQuery}, ${helpers_1.LinguiTransQuery} JSXText`](node) { | ||
const text = (0, helpers_1.getText)(node); | ||
rulePatterns.forEach(({ patterns, message }) => { | ||
if (patterns.some((item) => item.test(text))) { | ||
context.report({ node, messageId: 'default', data: { message: message } }); | ||
} | ||
}); | ||
}; | ||
return { | ||
'TemplateLiteral:exit'(node) { | ||
const quasisValue = (0, helpers_1.getQuasisValue)(node); | ||
onLiteral(quasisValue.trim(), node); | ||
return; | ||
}, | ||
'Literal, JSXText'(node) { | ||
if (node.value) { | ||
const trimed = node.value.toString().trim(); | ||
onLiteral(trimed, node); | ||
} | ||
return; | ||
}, | ||
}; | ||
} | ||
return; | ||
}, | ||
}; | ||
}, | ||
}); | ||
//# sourceMappingURL=text-restrictions.js.map |
{ | ||
"name": "eslint-plugin-lingui", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "ESLint plugin for Lingui", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
82063
1250