Skip to content

Commit

Permalink
Merge pull request #9 from lifeomic/interpolated-strings
Browse files Browse the repository at this point in the history
feat: prevent nested interpolation by default
  • Loading branch information
Swain Molster authored Dec 16, 2021
2 parents 3c82aad + 7c22428 commit 5feb20d
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 2 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ module.exports = {
#### `default-value`

- `translateFunctionNames`: an array of translation function names to validate. Default is `['t']`
- `allowKeyOnly`: whether to allow e.g. `t('just-the-key')`
- `allowKeyOnly`: whether to allow e.g. `t('just-the-key')`. Default is `false`.
- `allowNestingInterpolation`: Whether to allow e.g. `{ defaultValue: 'some string $t(interpolated)' }`. Default is `false`.
- `nestingPrefix`: Used when `allowNestingInterpolation` is `false` to identify interpolated variables. Default is `"$t("`.
42 changes: 42 additions & 0 deletions src/default-value/default-value.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ tester.run('default-value', defaultValueRule, {
someOtherFunction()
`,
},
{
code: `
t('key', {
defaultValue: 'some interpolated $t(var)'
})
`,
options: [{ allowNestingInterpolation: true }],
},
{
code: `
t('key', {
defaultValue: 'some interpolated $nest(var)'
})
`,
options: [
{
allowNestingInterpolation: true,
nestingPrefix: '$nest(',
},
],
},
],
invalid: [
{
Expand Down Expand Up @@ -100,5 +121,26 @@ tester.run('default-value', defaultValueRule, {
options: [{ translateFunctionNames: ['translate', 't'] }],
errors: [Errors.missingVariable('long'), Errors.missingVariable('short')],
},
{
code: `
t('key', {
defaultValue: 'some interpolated $t(var)'
})
`,
errors: [Errors.referenceInterpolation],
},
{
code: `
t('key', {
defaultValue: 'some interpolated $nest(var)'
})
`,
options: [
{
nestingPrefix: '$nest(',
},
],
errors: [Errors.referenceInterpolation],
},
],
});
31 changes: 30 additions & 1 deletion src/default-value/default-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export const Errors = {
'Translate function defaultValue must a string property on the second argument',
missingVariable: (named: string) =>
`Missing "${named}" in translate variables`,
referenceInterpolation:
'Detected an interpolated variable. Use a static string instead.',
};

const findDefaultValueOnObject = (node: ObjectExpression) => {
Expand All @@ -29,12 +31,16 @@ const findDefaultValueOnObject = (node: ObjectExpression) => {
export type DefaultValueOptions = {
translateFunctionNames: string[];
allowKeyOnly: boolean;
nestingPrefix: string;
allowNestingInterpolation: boolean;
};

const parseOptions = (context: Rule.RuleContext): DefaultValueOptions => {
const DEFAULT: DefaultValueOptions = {
translateFunctionNames: ['t'],
allowKeyOnly: false,
nestingPrefix: '$t(',
allowNestingInterpolation: false,
};

if (!context.options.length) {
Expand All @@ -61,6 +67,12 @@ export const defaultValueRule: Rule.RuleModule = {
allowKeyOnly: {
type: 'boolean',
},
nestingPrefix: {
type: 'string',
},
allowNestingInterpolation: {
type: 'boolean',
},
},
},
],
Expand All @@ -78,6 +90,7 @@ export const defaultValueRule: Rule.RuleModule = {
return;
}

// Detect key only scenario.
if (node.arguments.length < 2) {
if (options.allowKeyOnly && node.arguments.length === 1) {
return;
Expand All @@ -91,6 +104,7 @@ export const defaultValueRule: Rule.RuleModule = {

const secondArg = node.arguments[1];

// Enforce that second argument is an object.
if (secondArg.type !== 'ObjectExpression') {
context.report({
node,
Expand All @@ -99,6 +113,7 @@ export const defaultValueRule: Rule.RuleModule = {
return;
}

// Enforce that second argument contains a defaultValue.
const defaultValue = findDefaultValueOnObject(secondArg);
if (!defaultValue) {
context.report({
Expand All @@ -108,14 +123,28 @@ export const defaultValueRule: Rule.RuleModule = {
return;
}

// Enforce that all referenced variables are provided.
for (const variable of defaultValue.variableNames) {
const prop = findPropertyOnNode(secondArg, { named: variable });
if (!prop) {
context.report({
node,
message: Errors.missingVariable(variable),
});
return;
}
}

// Check for interpolated references.
if (!options.allowNestingInterpolation) {
const hasAnInterpolatedVariable = defaultValue.value.includes(
options.nestingPrefix,
);

if (hasAnInterpolatedVariable) {
context.report({
node,
message: Errors.referenceInterpolation,
});
}
}
},
Expand Down

0 comments on commit 5feb20d

Please sign in to comment.