diff --git a/packages/language-core/lib/codegen/codeFeatures.ts b/packages/language-core/lib/codegen/codeFeatures.ts new file mode 100644 index 0000000000..7ae98edb89 --- /dev/null +++ b/packages/language-core/lib/codegen/codeFeatures.ts @@ -0,0 +1,69 @@ +import type { VueCodeInformation } from '../types'; + +export const codeFeatures = { + all: { + verification: true, + completion: true, + semantic: true, + navigation: true, + } as VueCodeInformation, + none: {} as VueCodeInformation, + + verification: { + verification: true, + } as VueCodeInformation, + + completion: { + completion: true, + } as VueCodeInformation, + additionalCompletion: { + completion: { isAdditional: true }, + } as VueCodeInformation, + + withoutCompletion: { + verification: true, + semantic: true, + navigation: true, + } as VueCodeInformation, + + navigation: { + navigation: true, + } as VueCodeInformation, + navigationWithoutRename: { + navigation: { shouldRename: () => false }, + } as VueCodeInformation, + navigationAndCompletion: { + navigation: true, + completion: true, + } as VueCodeInformation, + navigationAndAdditionalCompletion: { + navigation: true, + completion: { isAdditional: true }, + } as VueCodeInformation, + + withoutNavigation: { + verification: true, + completion: true, + semantic: true, + } as VueCodeInformation, + + withoutHighlight: { + semantic: { shouldHighlight: () => false }, + verification: true, + navigation: true, + } as VueCodeInformation, + withoutHighlightAndCompletion: { + semantic: { shouldHighlight: () => false }, + verification: true, + navigation: true, + } as VueCodeInformation, + withoutHighlightAndNavigation: { + semantic: { shouldHighlight: () => false }, + verification: true, + completion: true, + } as VueCodeInformation, + withoutHighlightAndCompletionAndNavigation: { + semantic: { shouldHighlight: () => false }, + verification: true, + } as VueCodeInformation, +}; diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index d4552a8466..13fb67b930 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -1,8 +1,9 @@ import type { ScriptSetupRanges } from '../../parsers/scriptSetupRanges'; import type { Code, Sfc } from '../../types'; +import { codeFeatures } from '../codeFeatures'; import { endOfLine, generateSfcBlockSection, newLine } from '../utils'; import type { ScriptCodegenContext } from './context'; -import { ScriptCodegenOptions, codeFeatures } from './index'; +import type { ScriptCodegenOptions } from './index'; export function* generateComponent( options: ScriptCodegenOptions, diff --git a/packages/language-core/lib/codegen/script/componentSelf.ts b/packages/language-core/lib/codegen/script/componentSelf.ts index 331c21d823..ef17b359ec 100644 --- a/packages/language-core/lib/codegen/script/componentSelf.ts +++ b/packages/language-core/lib/codegen/script/componentSelf.ts @@ -1,10 +1,11 @@ import * as path from 'path-browserify'; import type { Code } from '../../types'; +import { codeFeatures } from '../codeFeatures'; import type { TemplateCodegenContext } from '../template/context'; import { endOfLine, generateSfcBlockSection, newLine } from '../utils'; import { generateComponentSetupReturns, generateEmitsOption, generatePropsOption } from './component'; import type { ScriptCodegenContext } from './context'; -import { codeFeatures, type ScriptCodegenOptions } from './index'; +import type { ScriptCodegenOptions } from './index'; import { getTemplateUsageVars } from './template'; export function* generateComponentSelf( diff --git a/packages/language-core/lib/codegen/script/index.ts b/packages/language-core/lib/codegen/script/index.ts index e9e8187e65..fac4679d7a 100644 --- a/packages/language-core/lib/codegen/script/index.ts +++ b/packages/language-core/lib/codegen/script/index.ts @@ -3,7 +3,8 @@ import * as path from 'path-browserify'; import type * as ts from 'typescript'; import type { ScriptRanges } from '../../parsers/scriptRanges'; import type { ScriptSetupRanges } from '../../parsers/scriptSetupRanges'; -import type { Code, Sfc, VueCodeInformation, VueCompilerOptions } from '../../types'; +import type { Code, Sfc, VueCompilerOptions } from '../../types'; +import { codeFeatures } from '../codeFeatures'; import { generateGlobalTypes, getGlobalTypesFileName } from '../globalTypes'; import type { TemplateCodegenContext } from '../template/context'; import { endOfLine, generateSfcBlockSection, newLine } from '../utils'; @@ -14,29 +15,6 @@ import { generateSrc } from './src'; import { generateStyleModulesType } from './styleModulesType'; import { generateTemplate } from './template'; -export const codeFeatures = { - all: { - verification: true, - completion: true, - semantic: true, - navigation: true, - } as VueCodeInformation, - none: {} as VueCodeInformation, - verification: { - verification: true, - } as VueCodeInformation, - navigation: { - navigation: true, - } as VueCodeInformation, - navigationWithoutRename: { - navigation: { - shouldRename() { - return false; - }, - }, - } as VueCodeInformation, -}; - export interface ScriptCodegenOptions { ts: typeof ts; compilerOptions: ts.CompilerOptions; diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 81eac14a19..2a0d8c0d11 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -1,10 +1,11 @@ import type { ScriptSetupRanges } from '../../parsers/scriptSetupRanges'; import type { Code, Sfc, TextRange } from '../../types'; +import { codeFeatures } from '../codeFeatures'; import { combineLastMapping, endOfLine, generateSfcBlockSection, newLine } from '../utils'; import { generateComponent, generateEmitsOption } from './component'; import { generateComponentSelf } from './componentSelf'; import type { ScriptCodegenContext } from './context'; -import { ScriptCodegenOptions, codeFeatures, generateScriptSectionPartiallyEnding } from './index'; +import { type ScriptCodegenOptions, generateScriptSectionPartiallyEnding } from './index'; import { generateTemplate } from './template'; export function* generateScriptSetupImports( diff --git a/packages/language-core/lib/codegen/script/src.ts b/packages/language-core/lib/codegen/script/src.ts index 350a4b44bf..a6d1989ebc 100644 --- a/packages/language-core/lib/codegen/script/src.ts +++ b/packages/language-core/lib/codegen/script/src.ts @@ -1,6 +1,6 @@ import type { Code, Sfc } from '../../types'; +import { codeFeatures } from '../codeFeatures'; import { endOfLine } from '../utils'; -import { codeFeatures } from './index'; export function* generateSrc( script: NonNullable, diff --git a/packages/language-core/lib/codegen/script/styleModulesType.ts b/packages/language-core/lib/codegen/script/styleModulesType.ts index 493ad36ccd..f501fdf490 100644 --- a/packages/language-core/lib/codegen/script/styleModulesType.ts +++ b/packages/language-core/lib/codegen/script/styleModulesType.ts @@ -1,7 +1,8 @@ import type { Code } from '../../types'; +import { codeFeatures } from '../codeFeatures'; import { endOfLine, newLine } from '../utils'; import type { ScriptCodegenContext } from './context'; -import { ScriptCodegenOptions, codeFeatures } from './index'; +import type { ScriptCodegenOptions } from './index'; import { generateCssClassProperty } from './template'; export function* generateStyleModulesType( diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 7ae52d4789..0599b3540c 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -1,11 +1,12 @@ import type { Code } from '../../types'; import { hyphenateTag } from '../../utils/shared'; +import { codeFeatures } from '../codeFeatures'; import { TemplateCodegenContext, createTemplateCodegenContext } from '../template/context'; import { generateInterpolation } from '../template/interpolation'; import { generateStyleScopedClassReferences } from '../template/styleScopedClasses'; import { endOfLine, newLine } from '../utils'; import type { ScriptCodegenContext } from './context'; -import { codeFeatures, type ScriptCodegenOptions } from './index'; +import type { ScriptCodegenOptions } from './index'; export function* generateTemplate( options: ScriptCodegenOptions, diff --git a/packages/language-core/lib/codegen/template/context.ts b/packages/language-core/lib/codegen/template/context.ts index c435b70f44..031ebb0e28 100644 --- a/packages/language-core/lib/codegen/template/context.ts +++ b/packages/language-core/lib/codegen/template/context.ts @@ -1,65 +1,10 @@ import type * as CompilerDOM from '@vue/compiler-dom'; import type { Code, VueCodeInformation } from '../../types'; +import { codeFeatures } from '../codeFeatures'; import { InlayHintInfo } from '../inlayHints'; import { endOfLine, newLine, wrapWith } from '../utils'; import type { TemplateCodegenOptions } from './index'; -const _codeFeatures = { - all: { - verification: true, - completion: true, - semantic: true, - navigation: true, - } as VueCodeInformation, - verification: { - verification: true, - } as VueCodeInformation, - completion: { - completion: true, - } as VueCodeInformation, - additionalCompletion: { - completion: { isAdditional: true }, - } as VueCodeInformation, - navigation: { - navigation: true, - } as VueCodeInformation, - navigationWithoutRename: { - navigation: { - shouldRename() { - return false; - }, - }, - } as VueCodeInformation, - navigationAndCompletion: { - navigation: true, - completion: true, - } as VueCodeInformation, - navigationAndAdditionalCompletion: { - navigation: true, - completion: { isAdditional: true }, - } as VueCodeInformation, - withoutNavigation: { - verification: true, - completion: true, - semantic: true, - } as VueCodeInformation, - withoutHighlight: { - semantic: { shouldHighlight: () => false }, - verification: true, - navigation: true, - completion: true, - } as VueCodeInformation, - withoutHighlightAndCompletion: { - semantic: { shouldHighlight: () => false }, - verification: true, - navigation: true, - } as VueCodeInformation, - withoutHighlightAndCompletionAndNavigation: { - semantic: { shouldHighlight: () => false }, - verification: true, - } as VueCodeInformation, -}; - export type TemplateCodegenContext = ReturnType; export function createTemplateCodegenContext(options: Pick) { @@ -74,34 +19,30 @@ export function createTemplateCodegenContext(options: Pick { - token.errors++; - return false; - }, - }, - }; - } - } + function resolveCodeFeatures(features: VueCodeInformation) { + if (features.verification) { + if (ignoredError) { + return { + ...features, + verification: false, + }; } - return data; - }, - }); + if (expectErrorToken) { + const token = expectErrorToken; + return { + ...features, + verification: { + shouldReport: () => { + token.errors++; + return false; + }, + }, + }; + } + } + return features; + } + const localVars = new Map(); const specialVars = new Set(); const accessExternalVariables = new Map>(); @@ -129,9 +70,15 @@ export function createTemplateCodegenContext(options: Pick(); return { + codeFeatures: new Proxy(codeFeatures, { + get(target, key: keyof typeof codeFeatures) { + const data = target[key]; + return resolveCodeFeatures(data); + }, + }), + resolveCodeFeatures, slots, dynamicSlots, - codeFeatures, specialVars, accessExternalVariables, lastGenericComment, diff --git a/packages/language-core/lib/codegen/template/element.ts b/packages/language-core/lib/codegen/template/element.ts index aed2968809..681bb0a9a5 100644 --- a/packages/language-core/lib/codegen/template/element.ts +++ b/packages/language-core/lib/codegen/template/element.ts @@ -134,10 +134,7 @@ export function* generateComponent( options, ctx, 'template', - { - ...ctx.codeFeatures.all, - completion: false, - }, + ctx.codeFeatures.withoutCompletion, dynamicTagInfo.tag, dynamicTagInfo.offsets[1], dynamicTagInfo.astHolder, @@ -227,13 +224,13 @@ export function* generateComponent( yield* wrapWith( node.loc.start.offset, node.loc.end.offset, - { + ctx.resolveCodeFeatures({ verification: { shouldReport(_source, code) { return String(code) !== '6133'; }, } - }, + }), var_componentInstance ); yield ` = ${var_functionalComponent}`; diff --git a/packages/language-core/lib/codegen/template/elementDirectives.ts b/packages/language-core/lib/codegen/template/elementDirectives.ts index 973abd9a6c..8aca0a66d7 100644 --- a/packages/language-core/lib/codegen/template/elementDirectives.ts +++ b/packages/language-core/lib/codegen/template/elementDirectives.ts @@ -2,6 +2,7 @@ import * as CompilerDOM from '@vue/compiler-dom'; import { camelize } from '@vue/shared'; import type { Code } from '../../types'; import { hyphenateAttr } from '../../utils/shared'; +import { codeFeatures } from '../codeFeatures'; import { endOfLine, wrapWith } from '../utils'; import { generateCamelized } from '../utils/camelized'; import { generateStringLiteralKey } from '../utils/stringLiteralKey'; @@ -10,6 +11,15 @@ import type { TemplateCodegenOptions } from './index'; import { generateInterpolation } from './interpolation'; import { generateObjectProperty } from './objectProperty'; +const builtInDirectives = new Set([ + 'cloak', + 'html', + 'memo', + 'once', + 'show', + 'text', +]); + export function* generateElementDirectives( options: TemplateCodegenOptions, ctx: TemplateCodegenContext, @@ -34,7 +44,7 @@ export function* generateElementDirectives( prop.loc.end.offset, ctx.codeFeatures.verification, `__VLS_asFunctionalDirective(`, - ...generateIdentifier(ctx, prop), + ...generateIdentifier(options, ctx, prop), `)(null!, { ...__VLS_directiveBindingRestFields, `, ...generateArg(options, ctx, prop), ...generateModifiers(options, ctx, prop), @@ -46,6 +56,7 @@ export function* generateElementDirectives( } function* generateIdentifier( + options: TemplateCodegenOptions, ctx: TemplateCodegenContext, prop: CompilerDOM.DirectiveNode ): Generator { @@ -58,18 +69,16 @@ function* generateIdentifier( ...generateCamelized( rawName, prop.loc.start.offset, - { - ...ctx.codeFeatures.all, - verification: false, - completion: { - // fix https://github.com/vuejs/language-tools/issues/1905 - isAdditional: true, - }, + ctx.resolveCodeFeatures({ + // fix https://github.com/vuejs/language-tools/issues/1905 + ...codeFeatures.additionalCompletion, + ...codeFeatures.withoutHighlight, + verification: options.vueCompilerOptions.checkUnknownDirectives && !builtInDirectives.has(prop.name), navigation: { resolveRenameNewName: camelize, resolveRenameEditText: getPropRenameApply(prop.name), }, - } + }) ) ); } @@ -143,7 +152,7 @@ export function* generateModifiers( ctx, mod.content, mod.loc.start.offset, - ctx.codeFeatures.withoutNavigation + ctx.codeFeatures.withoutHighlightAndNavigation ); yield `: true, `; } diff --git a/packages/language-core/lib/codegen/template/elementEvents.ts b/packages/language-core/lib/codegen/template/elementEvents.ts index e6ec49a56a..1161669d19 100644 --- a/packages/language-core/lib/codegen/template/elementEvents.ts +++ b/packages/language-core/lib/codegen/template/elementEvents.ts @@ -2,6 +2,7 @@ import * as CompilerDOM from '@vue/compiler-dom'; import { camelize, capitalize } from '@vue/shared'; import type * as ts from 'typescript'; import type { Code } from '../../types'; +import { codeFeatures } from '../codeFeatures'; import { combineLastMapping, createTsAst, endOfLine, newLine, variableNameRegex, wrapWith } from '../utils'; import { generateCamelized } from '../utils/camelized'; import type { TemplateCodegenContext } from './context'; @@ -57,10 +58,10 @@ export function* generateEventArg( start: number, directive = 'on' ): Generator { - const features = { - ...ctx.codeFeatures.withoutHighlightAndCompletion, - ...ctx.codeFeatures.navigationWithoutRename, - }; + const features = ctx.resolveCodeFeatures({ + ...codeFeatures.withoutHighlightAndCompletion, + ...codeFeatures.navigationWithoutRename, + }); if (variableNameRegex.test(camelize(name))) { yield ['', 'template', start, features]; yield directive; diff --git a/packages/language-core/lib/codegen/template/elementProps.ts b/packages/language-core/lib/codegen/template/elementProps.ts index fa536bbfc9..884a88e31c 100644 --- a/packages/language-core/lib/codegen/template/elementProps.ts +++ b/packages/language-core/lib/codegen/template/elementProps.ts @@ -4,6 +4,7 @@ import { minimatch } from 'minimatch'; import { toString } from 'muggle-string'; import type { Code, VueCodeInformation, VueCompilerOptions } from '../../types'; import { hyphenateAttr, hyphenateTag } from '../../utils/shared'; +import { codeFeatures } from '../codeFeatures'; import { createVBindShorthandInlayHintInfo } from '../inlayHints'; import { newLine, variableNameRegex, wrapWith } from '../utils'; import { generateCamelized } from '../utils/camelized'; @@ -380,28 +381,21 @@ function getPropsCodeInfo( strictPropsCheck: boolean, shouldCamelize: boolean ): VueCodeInformation { - const codeInfo = ctx.codeFeatures.withoutHighlightAndCompletion; - return { - ...codeInfo, - navigation: codeInfo.navigation - ? { - resolveRenameNewName: camelize, - resolveRenameEditText: shouldCamelize ? hyphenateAttr : undefined, - } - : false, - verification: strictPropsCheck - ? codeInfo.verification - : { - shouldReport(_source, code) { - if (String(code) === '2353' || String(code) === '2561') { - return false; - } - return typeof codeInfo.verification === 'object' - ? codeInfo.verification.shouldReport?.(_source, code) ?? true - : true; - }, - } - }; + return ctx.resolveCodeFeatures({ + ...codeFeatures.withoutHighlightAndCompletion, + navigation: { + resolveRenameNewName: camelize, + resolveRenameEditText: shouldCamelize ? hyphenateAttr : undefined, + }, + verification: strictPropsCheck || { + shouldReport(_source, code) { + if (String(code) === '2353' || String(code) === '2561') { + return false; + } + return true; + }, + } + }); } function getModelPropName(node: CompilerDOM.ElementNode, vueCompilerOptions: VueCompilerOptions) { diff --git a/packages/language-core/lib/types.ts b/packages/language-core/lib/types.ts index 0a9ea03d88..523eda9fb2 100644 --- a/packages/language-core/lib/types.ts +++ b/packages/language-core/lib/types.ts @@ -31,6 +31,7 @@ export interface VueCompilerOptions { jsxSlots: boolean; checkUnknownProps: boolean; checkUnknownEvents: boolean; + checkUnknownDirectives: boolean; checkUnknownComponents: boolean; skipTemplateCodegen: boolean; fallthroughAttributes: boolean; diff --git a/packages/language-core/lib/utils/ts.ts b/packages/language-core/lib/utils/ts.ts index d8605b0f75..d9d4877167 100644 --- a/packages/language-core/lib/utils/ts.ts +++ b/packages/language-core/lib/utils/ts.ts @@ -264,6 +264,7 @@ export function getDefaultCompilerOptions(target = 99, lib = 'vue', strictTempla jsxSlots: false, checkUnknownProps: strictTemplates, checkUnknownEvents: strictTemplates, + checkUnknownDirectives: strictTemplates, checkUnknownComponents: strictTemplates, skipTemplateCodegen: false, fallthroughAttributes: false,