From acd8735bd49d184472a637985054ffa2fa6ac50e Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Sun, 30 Apr 2023 20:05:50 +0200 Subject: [PATCH] Update TS plugin type for `preferredRegion` (#49011) Related change: #49003. This PR makes sure that these related IntelliSense features are updated: ### Invalid CleanShot 2023-04-30 at 19 33 06@2x ### Region (`string`) CleanShot 2023-04-30 at 19 33 29@2x ### Regions (`string[]`) CleanShot 2023-04-30 at 19 34 20@2x ### Docs CleanShot 2023-04-30 at 19 34 41@2x --- .../src/server/typescript/rules/config.ts | 56 ++++++++++++++----- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/packages/next/src/server/typescript/rules/config.ts b/packages/next/src/server/typescript/rules/config.ts index b09d76b506c71..11a77b330d920 100644 --- a/packages/next/src/server/typescript/rules/config.ts +++ b/packages/next/src/server/typescript/rules/config.ts @@ -20,7 +20,7 @@ const API_DOCS: Record< link?: string type?: string isValid?: (value: string) => boolean - getHint?: (value: any) => string + getHint?: (value: any) => string | undefined } > = { dynamic: { @@ -61,14 +61,32 @@ const API_DOCS: Record< }, preferredRegion: { description: - 'Specify the perferred region that this layout or page should be deployed to. If the region option is not specified, it inherits the option from the nearest parent layout. The root defaults to `"auto"`.', + 'Specify the perferred region that this layout or page should be deployed to. If the region option is not specified, it inherits the option from the nearest parent layout. The root defaults to `"auto"`.\n\nYou can also specify a region, such as "iad1", or an array of regions, such as `["iad1", "sfo1"]`.', options: { '"auto"': - 'Next.js will first deploy to the `"home"` region. Then if it doesn’t detect any waterfall requests after a few requests, it can upgrade that route, to be deployed globally to `"edge"`. If it detects any waterfall requests after that, it can eventually downgrade back to `"home`".', + 'Next.js will first deploy to the `"home"` region. Then if it doesn’t detect any waterfall requests after a few requests, it can upgrade that route, to be deployed globally. If it detects any waterfall requests after that, it can eventually downgrade back to `"home`".', + '"global"': 'Prefer deploying globally.', '"home"': 'Prefer deploying to the Home region.', - '"edge"': 'Prefer deploying to the Edge globally.', }, link: 'https://beta.nextjs.org/docs/api-reference/segment-config#preferredregion', + isValid: (value: string) => { + try { + const parsed = JSON.parse(value) + return ( + typeof parsed === 'string' || + (Array.isArray(parsed) && !parsed.some((v) => typeof v !== 'string')) + ) + } catch (err) { + return false + } + }, + getHint: (value: any) => { + if (value === 'auto') return `Automatically chosen by Next.js.` + if (value === 'global') return `Prefer deploying globally.` + if (value === 'home') return `Prefer deploying to the Home region.` + if (Array.isArray(value)) return `Deploy to regions: ${value.join(', ')}.` + if (typeof value === 'string') return `Deploy to region: ${value}.` + }, }, revalidate: { description: @@ -375,14 +393,17 @@ const config = { ts.isStringLiteral(value) || ts.isNoSubstitutionTemplateLiteral(value) ) { - const text = removeStringQuotes(value.getText()) - const allowedValues = Object.keys(options) - .filter((v) => /^['"]/.test(v)) - .map(removeStringQuotes) + const val = '"' + removeStringQuotes(value.getText()) + '"' + const allowedValues = Object.keys(options).filter((v) => + /^['"]/.test(v) + ) - if (!allowedValues.includes(text)) { + if ( + !allowedValues.includes(val) && + !API_DOCS[name.text].isValid?.(val) + ) { isInvalid = true - displayedValue = `'${text}'` + displayedValue = val } } else if ( ts.isNumericLiteral(value) || @@ -394,7 +415,7 @@ const config = { (ts.isIdentifier(value) && value.getText() === 'Infinity') ) { const v = value.getText() - if (API_DOCS[name.text].isValid?.(v) === false) { + if (!API_DOCS[name.text].isValid?.(v)) { isInvalid = true displayedValue = v } @@ -403,14 +424,23 @@ const config = { value.kind === ts.SyntaxKind.FalseKeyword ) { const v = value.getText() - if (API_DOCS[name.text].isValid?.(v) === false) { + if (!API_DOCS[name.text].isValid?.(v)) { + isInvalid = true + displayedValue = v + } + } else if (ts.isArrayLiteralExpression(value)) { + const v = value.getText() + if ( + !API_DOCS[name.text].isValid?.( + JSON.stringify(value.elements.map((e) => e.getText())) + ) + ) { isInvalid = true displayedValue = v } } else if ( // Other literals ts.isBigIntLiteral(value) || - ts.isArrayLiteralExpression(value) || ts.isObjectLiteralExpression(value) || ts.isRegularExpressionLiteral(value) || ts.isPrefixUnaryExpression(value)