in packages/eui/scripts/eslint-plugin/i18n.js [111:387]
JSXOpeningElement(node) {
// only process <EuiI8n/> elements
if (node.name.type !== 'JSXIdentifier' || node.name.name !== 'EuiI18n')
return;
const jsxElement = node.parent;
const hasRenderProp = jsxElement.children.length > 0;
const attributes = attributesArrayToLookup(node.attributes);
// validate attribute types
if (attributes.hasOwnProperty('token')) {
// `token` must be a Literal
if (attributes.token.type !== 'Literal') {
context.report({
node,
loc: attributes.token.loc,
messageId: 'invalidTokenType',
data: { type: attributes.token.type },
});
return;
}
}
if (attributes.hasOwnProperty('default')) {
// default must be either a Literal of an ArrowFunctionExpression
const isLiteral = attributes.default.type === 'Literal';
const isArrowExpression =
attributes.default.type === 'JSXExpressionContainer' &&
attributes.default.expression.type === 'ArrowFunctionExpression';
if (!isLiteral && !isArrowExpression) {
context.report({
node,
loc: attributes.default.loc,
messageId: 'invalidDefaultType',
data: { type: attributes.default.expression.type },
});
return;
}
}
if (attributes.hasOwnProperty('tokens')) {
// tokens must be an array of Literals
if (attributes.tokens.type !== 'JSXExpressionContainer') {
context.report({
node,
loc: attributes.tokens.loc,
messageId: 'invalidTokensType',
data: { type: attributes.tokens.type },
});
return;
}
if (attributes.tokens.expression.type !== 'ArrayExpression') {
context.report({
node,
loc: attributes.tokens.loc,
messageId: 'invalidTokensType',
data: { type: attributes.tokens.expression.type },
});
return;
}
for (
let i = 0;
i < attributes.tokens.expression.elements.length;
i++
) {
const tokenNode = attributes.tokens.expression.elements[i];
if (
tokenNode.type !== 'Literal' ||
typeof tokenNode.value !== 'string'
) {
context.report({
node,
loc: tokenNode.loc,
messageId: 'invalidTokensType',
data: { type: tokenNode.type },
});
return;
}
}
}
if (attributes.hasOwnProperty('defaults')) {
// defaults must be an array of either Literals or ArrowFunctionExpressions
if (attributes.defaults.type !== 'JSXExpressionContainer') {
context.report({
node,
loc: attributes.defaults.loc,
messageId: 'invalidDefaultsType',
data: { type: attributes.defaults.type },
});
return;
}
if (attributes.defaults.expression.type !== 'ArrayExpression') {
context.report({
node,
loc: attributes.defaults.loc,
messageId: 'invalidDefaultsType',
data: { type: attributes.defaults.expression.type },
});
return;
}
for (
let i = 0;
i < attributes.defaults.expression.elements.length;
i++
) {
const defaultNode = attributes.defaults.expression.elements[i];
if (
defaultNode.type !== 'Literal' ||
typeof defaultNode.value !== 'string'
) {
context.report({
node,
loc: defaultNode.loc,
messageId: 'invalidDefaultsType',
data: { type: defaultNode.type },
});
return;
}
}
}
const hasMultipleTokens = attributes.hasOwnProperty('tokens');
if (!hasMultipleTokens) {
// validate token format
const tokenParts = attributes.token.value.split('.');
if (
tokenParts.length <= 1 ||
tokenParts[0] !== expectedTokenNamespace
) {
context.report({
node,
loc: attributes.token.loc,
messageId: 'invalidToken',
data: {
tokenValue: attributes.token.value,
tokenNamespace: expectedTokenNamespace,
},
});
}
// validate default string interpolation matches values
const valueNames = getDefinedValues(
attributes.values && attributes.values.expression
);
if (attributes.default.type === 'Literal') {
// default is a string literal
const expectedNames = getExpectedValueNames(
attributes.default.value
);
if (areSetsEqual(expectedNames, valueNames) === false) {
context.report({
node,
loc: attributes.values.loc,
messageId: 'mismatchedValues',
data: {
expected: formatSet(expectedNames),
provided: formatSet(valueNames),
},
});
}
} else {
// default is a function
// validate the destructured param defined by default function match the values
const defaultFn = attributes.default.expression;
const objProperties =
defaultFn.params && defaultFn.params[0]
? defaultFn.params[0].properties
: [];
const expectedNames = new Set(
objProperties.map((property) => property.key.name)
);
if (areSetsEqual(valueNames, expectedNames) === false) {
context.report({
node,
loc: attributes.values.loc,
messageId: 'mismatchedValues',
data: {
expected: formatSet(expectedNames),
provided: formatSet(valueNames),
},
});
}
}
} else {
// has multiple tokens
// validate their names
const tokens = attributes.tokens.expression.elements;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
const tokenParts = token.value.split('.');
if (
tokenParts.length <= 1 ||
tokenParts[0] !== expectedTokenNamespace
) {
context.report({
node,
loc: token.loc,
messageId: 'invalidToken',
data: {
tokenValue: token.value,
tokenNamespace: expectedTokenNamespace,
},
});
}
}
// validate the number of tokens equals the number of defaults
const defaults = attributes.defaults.expression.elements;
if (tokens.length !== defaults.length) {
context.report({
node,
loc: node.loc,
messageId: 'mismatchedTokensAndDefaults',
data: {
tokenLength: tokens.length,
defaultsLength: defaults.length,
},
});
}
}
if (hasRenderProp) {
// validate the render prop
const renderProp = getRenderPropFromChildren(jsxElement.children);
if (hasMultipleTokens) {
// multiple tokens, verify each token matches an array-destructured param
const params = renderProp.params[0].elements;
const tokens = attributes.tokens.expression.elements;
const paramsSet = new Set(params.map((element) => element.name));
const tokensSet = new Set(
tokens.map((element) =>
getExpectedParamNameFromToken(element.value)
)
);
if (areSetsEqual(paramsSet, tokensSet) === false) {
context.report({
node,
loc: node.loc,
messageId: 'tokenNamesNotUsedInRenderProp',
data: {
tokenNames: formatSet(tokensSet),
paramNames: formatSet(paramsSet),
},
});
}
} else {
// single token, single param should be a matching identifier
const param = renderProp.params[0];
const tokenName = getExpectedParamNameFromToken(
attributes.token.value
);
const paramName = param.name;
if (tokenName !== paramName) {
context.report({
node,
loc: node.loc,
messageId: 'tokenNamesNotUsedInRenderProp',
data: { tokenNames: tokenName, paramNames: paramName },
});
}
}
}
// debugger;
},