UNPKG

next

Version:

The React Framework

134 lines (133 loc) 6.2 kB
import path from 'path'; import fs from 'fs'; import { ALLOWED_LAYOUT_PROPS, ALLOWED_PAGE_PROPS, NEXT_TS_ERRORS } from '../constant'; import { getTs, isPageFile, isPositionInsideNode } from '../utils'; const entry = { // Give auto completion for the component's props getCompletionsAtPosition (fileName, node, position) { var _node_parameters; const ts = getTs(); const entries = []; // Default export function might not accept parameters const paramNode = (_node_parameters = node.parameters) == null ? void 0 : _node_parameters[0]; if (paramNode && isPositionInsideNode(position, paramNode)) { const props = paramNode == null ? void 0 : paramNode.name; if (props && ts.isObjectBindingPattern(props)) { let validProps = []; let validPropsWithType = []; let type; if (isPageFile(fileName)) { // For page entries (page.js), it can only have `params` and `searchParams` // as the prop names. validProps = ALLOWED_PAGE_PROPS; validPropsWithType = ALLOWED_PAGE_PROPS; type = 'page'; } else { // For layout entires, check if it has any named slots. const currentDir = path.dirname(fileName); const items = fs.readdirSync(currentDir, { withFileTypes: true }); const slots = []; for (const item of items){ if (item.isDirectory() && item.name.startsWith('@')) { slots.push(item.name.slice(1)); } } validProps = ALLOWED_LAYOUT_PROPS.concat(slots); validPropsWithType = ALLOWED_LAYOUT_PROPS.concat(slots.map((s)=>`${s}: React.ReactNode`)); type = 'layout'; } // Auto completion for props for (const element of props.elements){ if (isPositionInsideNode(position, element)) { const nameNode = element.propertyName || element.name; if (isPositionInsideNode(position, nameNode)) { for (const name of validProps){ entries.push({ name, insertText: name, sortText: '_' + name, kind: ts.ScriptElementKind.memberVariableElement, kindModifiers: ts.ScriptElementKindModifier.none, labelDetails: { description: `Next.js ${type} prop` } }); } } break; } } // Auto completion for types if (paramNode.type && ts.isTypeLiteralNode(paramNode.type)) { for (const member of paramNode.type.members){ if (isPositionInsideNode(position, member)) { for (const name of validPropsWithType){ entries.push({ name, insertText: name, sortText: '_' + name, kind: ts.ScriptElementKind.memberVariableElement, kindModifiers: ts.ScriptElementKindModifier.none, labelDetails: { description: `Next.js ${type} prop type` } }); } break; } } } } } return entries; }, // Give error diagnostics for the component getSemanticDiagnostics (fileName, source, node) { var _node_parameters_, _node_parameters; const ts = getTs(); let validProps = []; let type; if (isPageFile(fileName)) { // For page entries (page.js), it can only have `params` and `searchParams` // as the prop names. validProps = ALLOWED_PAGE_PROPS; type = 'page'; } else { // For layout entires, check if it has any named slots. const currentDir = path.dirname(fileName); const items = fs.readdirSync(currentDir, { withFileTypes: true }); const slots = []; for (const item of items){ if (item.isDirectory() && item.name.startsWith('@')) { slots.push(item.name.slice(1)); } } validProps = ALLOWED_LAYOUT_PROPS.concat(slots); type = 'layout'; } const diagnostics = []; const props = (_node_parameters = node.parameters) == null ? void 0 : (_node_parameters_ = _node_parameters[0]) == null ? void 0 : _node_parameters_.name; if (props && ts.isObjectBindingPattern(props)) { for (const prop of props.elements){ const propName = (prop.propertyName || prop.name).getText(); if (!validProps.includes(propName)) { diagnostics.push({ file: source, category: ts.DiagnosticCategory.Error, code: NEXT_TS_ERRORS.INVALID_PAGE_PROP, messageText: `"${propName}" is not a valid ${type} prop.`, start: prop.getStart(), length: prop.getWidth() }); } } } return diagnostics; } }; export default entry; //# sourceMappingURL=entry.js.map