Skip to content

Commit

Permalink
Improve TypeScript plugin error when the configuration is not statica…
Browse files Browse the repository at this point in the history
…lly analyzable (#42062)

As per
#41951 (comment),
this PR improves the TS plugin error message when a non-literal
configuration is used:

<img width="902" alt="CleanShot 2022-10-28 at 07 36 31@2x"
src="https://user-images.githubusercontent.com/3676859/198650771-026a0430-d5bc-4826-9aef-b1bfb7947c6f.png">

Also a bug is fixed so string template literals (``const revalidate =
`auto`;``) will no longer result in an error.

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm build && pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing
doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
  • Loading branch information
shuding committed Oct 30, 2022
1 parent d651e27 commit 1d58c0a
Showing 1 changed file with 43 additions and 36 deletions.
79 changes: 43 additions & 36 deletions packages/next/server/next-typescript.ts
Expand Up @@ -130,7 +130,7 @@ function getAPIDescription(api: string): string {
}

function removeStringQuotes(str: string): string {
return str.replace(/^['"]|['"]$/g, '')
return str.replace(/^['"`]|['"`]$/g, '')
}

export function createTSPlugin(modules: {
Expand Down Expand Up @@ -527,8 +527,16 @@ export function createTSPlugin(modules: {
} else if (API_DOCS[name.text]) {
// Check if the value is valid
const value = declarartion.initializer

if (value) {
if (value.kind === ts.SyntaxKind.StringLiteral) {
let displayedValue = ''
let errorMessage = ''
let isInvalid = false

if (
ts.isStringLiteral(value) ||
ts.isNoSubstitutionTemplateLiteral(value)
) {
const text = removeStringQuotes(value.getText())
const allowedValues = Object.keys(
API_DOCS[name.text].options
Expand All @@ -537,59 +545,58 @@ export function createTSPlugin(modules: {
.map(removeStringQuotes)

if (!allowedValues.includes(text)) {
prior.push({
file: source,
category: ts.DiagnosticCategory.Error,
code: 71003,
messageText: `"'${text}'" is not a valid value for the "${name.text}" option.`,
start: value.getStart(),
length: value.getEnd() - value.getStart(),
})
isInvalid = true
displayedValue = `'${text}'`
}
} else if (
value.kind === ts.SyntaxKind.NumericLiteral ||
(value.kind === ts.SyntaxKind.PrefixUnaryExpression &&
(value as any).operator ===
ts.SyntaxKind.MinusToken &&
(value as any).operand.kind ===
ts.SyntaxKind.NumericLiteral) ||
(value.kind === ts.SyntaxKind.Identifier &&
ts.isNumericLiteral(value) ||
(ts.isPrefixUnaryExpression(value) &&
ts.isMinusToken((value as any).operator) &&
(ts.isNumericLiteral((value as any).operand.kind) ||
(ts.isIdentifier((value as any).operand.kind) &&
(value as any).operand.kind.getText() ===
'Infinity'))) ||
(ts.isIdentifier(value) &&
value.getText() === 'Infinity')
) {
const v = value.getText()
if (API_DOCS[name.text].isValid?.(v) === false) {
prior.push({
file: source,
category: ts.DiagnosticCategory.Error,
code: 71003,
messageText: `"${v}" is not a valid value for the "${name.text}" option.`,
start: value.getStart(),
length: value.getEnd() - value.getStart(),
})
isInvalid = true
displayedValue = v
}
} else if (
value.kind === ts.SyntaxKind.TrueKeyword ||
value.kind === ts.SyntaxKind.FalseKeyword
) {
const v = value.getText()
if (API_DOCS[name.text].isValid?.(v) === false) {
prior.push({
file: source,
category: ts.DiagnosticCategory.Error,
code: 71003,
messageText: `"${v}" is not a valid value for the "${name.text}" option.`,
start: value.getStart(),
length: value.getEnd() - value.getStart(),
})
isInvalid = true
displayedValue = v
}
} else if (
// Other literals
ts.isBigIntLiteral(value) ||
ts.isArrayLiteralExpression(value) ||
ts.isObjectLiteralExpression(value) ||
ts.isRegularExpressionLiteral(value)
) {
isInvalid = true
displayedValue = value.getText()
} else {
// Not a literal, error because it's not statically analyzable
isInvalid = true
displayedValue = value.getText()
errorMessage = `"${displayedValue}" is not a valid value for the "${name.text}" option. The configuration must be statically analyzable.`
}

if (isInvalid) {
prior.push({
file: source,
category: ts.DiagnosticCategory.Error,
code: 71003,
messageText: `"${value.getText()}" is not a valid value for the "${
name.text
}" option.`,
messageText:
errorMessage ||
`"${displayedValue}" is not a valid value for the "${name.text}" option.`,
start: value.getStart(),
length: value.getEnd() - value.getStart(),
})
Expand Down

0 comments on commit 1d58c0a

Please sign in to comment.