diff --git a/CHANGELOG.md b/CHANGELOG.md index f34c1ca467..9644756046 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,64 @@ # Changelog -## 3.0.0 (2025-06-25) +## 3.0.3 (2025-07-18) + +### Bug Fixes + +- fix(language-core): find `node_modules` based on file's directory (#5525) - Thanks to @KazariEX! +- fix(language-core): do not spread exposed object (#5526) - Thanks to @KazariEX! +- fix(vscode): prompt manual reload in remote envs (#5530) + +### Other Changes + +- refactor(tsc): return the result of runTsc (#5517) - Thanks to @escaton! + +## 3.0.2 (2025-07-18) + +### Features + +- feat(language-core): introduce `globalTypesPath` option for non-npm like environment (#5505) - Thanks to @KazariEX! +- feat: forward tsserver's semantic tokens via language server (#5512) - Thanks to @KazariEX! + +### Bug Fixes + +- fix(vscode): correct syntax highlight of control directives ending with `/` or `)` - Thanks to @KazariEX! +- fix(language-core): infer parameter type of union slots to be union instead of intersection (#5475) - Thanks to @KazariEX! +- fix(vscode): remove `colorizedBracketPairs` config for plaintext +- fix(language-core): avoid early access to local types to skip unnecessary type generation - Thanks to @KazariEX! +- fix(language-core): treat `` without `is` prop as normal component - Thanks to @KazariEX! +- fix(vscode): make sure tsserver loads `@vue/typescript-plugin` last (#5483) +- fix(language-core): only keep navigation code feature on static `name` value of `` - Thanks to @KazariEX! +- fix(language-server): add `allowJs` to reactivity analyze host - Thanks to @KazariEX! +- fix(language-core): do not set template lang to `md` for markdown (#5497) - Thanks to @KazariEX! +- fix(typescript-plugin): exclude items of kind `module` from template completion - Thanks to @KazariEX! +- fix(language-core): walk identifiers correctly within type nodes in interpolation (#5501) - Thanks to @KazariEX! +- fix(language-service): correct position calculation of twoslash queries (#5503) - Thanks to @KazariEX! +- fix(language-core): avoid redundant increment of block variable depth (#5511) - Thanks to @KazariEX! +- fix(language-service): re-implement twoslash queries in script - Thanks to @KazariEX! + +### Other Changes + +- refactor(vscode): make welcome page code public - Thanks to @KazariEX! +- refactor(vscode): add premium feature settings +- chore: migrate from `minimatch` to `picomatch` (#5499) - Thanks to @KazariEX! +- chore: update volar to 2.4.19 + - fix(typescript): skip source file search when `.d.${ext}.ts` file exists (volarjs/volar.js#277) +- revert: type support of slot children (#5137) (#5514) - Thanks to @KazariEX! + +## 3.0.1 (2025-07-02) + +### Bug Fixes + +- fix(language-core): remove calculation logic of element inner loc (#5460) - Thanks to @KazariEX! +- fix(vscode): correct syntax highlight of `v-else` (#5470) - Thanks to @KazariEX! + +### Other Changes + +- docs(vscode): update Russian translation for VS Code extension (#5461) - Thanks to @AndreyYolkin! +- chore: update volar to 2.4.17 + - typescript: correctly use `getModeForUsageLocation` to calculate the resolution mode + +## 3.0.0 (2025-07-01) ### Features diff --git a/dprint.json b/dprint.json index c49deea9a1..18e1b0a159 100644 --- a/dprint.json +++ b/dprint.json @@ -2,7 +2,8 @@ "useTabs": true, "typescript": { "quoteStyle": "preferSingle", - "arrowFunction.useParentheses": "preferNone" + "arrowFunction.useParentheses": "preferNone", + "nextControlFlowPosition": "nextLine" }, "json": { }, diff --git a/extensions/vscode/README.md b/extensions/vscode/README.md index 00546dc1f9..08dd856bd9 100644 --- a/extensions/vscode/README.md +++ b/extensions/vscode/README.md @@ -1,4 +1,4 @@ -# Vue - Official +# Vue (Official) ## Quick Start diff --git a/extensions/vscode/index.ts b/extensions/vscode/index.ts index a42ecadc28..98feb55257 100644 --- a/extensions/vscode/index.ts +++ b/extensions/vscode/index.ts @@ -16,6 +16,28 @@ import { } from 'reactive-vscode'; import * as vscode from 'vscode'; import { config } from './lib/config'; +import { activate as activateWelcome } from './lib/welcome'; + +let needRestart = false; + +const incompatibleExtensionIds = [ + 'johnsoncodehk.vscode-typescript-vue-plugin', + 'Vue.vscode-typescript-vue-plugin', +]; + +for (const extensionId of incompatibleExtensionIds) { + const extension = vscode.extensions.getExtension(extensionId); + if (extension) { + vscode.window.showErrorMessage( + `The "${extensionId}" extension is incompatible with the Vue extension. Please uninstall it.`, + 'Show Extension', + ).then(action => { + if (action === 'Show Extension') { + vscode.commands.executeCommand('workbench.extensions.search', '@id:' + extensionId); + } + }); + } +} let client: lsp.BaseLanguageClient | undefined; @@ -44,6 +66,27 @@ export const { activate, deactivate } = defineExtension(async () => { nextTick(() => stop()); + if (needRestart) { + if (vscode.env.remoteName) { + vscode.window.showInformationMessage( + 'Please restart the extension host to activate Vue support in remote environments.', + 'Restart Extension Host', + 'Reload Window', + ).then(action => { + if (action === 'Restart Extension Host') { + vscode.commands.executeCommand('workbench.action.restartExtensionHost'); + } + else if (action === 'Reload Window') { + vscode.commands.executeCommand('workbench.action.reloadWindow'); + } + }); + } + else { + vscode.commands.executeCommand('workbench.action.restartExtensionHost'); + } + return; + } + watch(() => config.server.includeLanguages, async () => { const reload = await vscode.window.showInformationMessage( 'Please restart extension host to apply the new language settings.', @@ -70,6 +113,7 @@ export const { activate, deactivate } = defineExtension(async () => { activateAutoInsertion(selectors, client); activateDocumentDropEdit(selectors, client); + activateWelcome(); }, { immediate: true }); useCommand('vue.action.restartServer', async () => { @@ -178,6 +222,13 @@ try { s => s + '.concat("vue")', ); + // sort plugins for johnsoncodehk.tsslint, zardoy.ts-essential-plugins + const vuePluginName = require('./package.json').contributes.typescriptServerPlugins[0].name; + text = text.replace( + '"--globalPlugins",i.plugins', + s => s + `.sort((a,b)=>(b.name==="${vuePluginName}"?-1:0)-(a.name==="${vuePluginName}"?-1:0))`, + ); + return text; } return readFileSync(...args); @@ -191,6 +242,7 @@ try { } if (tsExtension.isActive) { - vscode.commands.executeCommand('workbench.action.restartExtensionHost'); + needRestart = true; } -} catch {} +} +catch {} diff --git a/extensions/vscode/lib/generated-meta.ts b/extensions/vscode/lib/generated-meta.ts index 1c0af10ec0..3d42137fdf 100644 --- a/extensions/vscode/lib/generated-meta.ts +++ b/extensions/vscode/lib/generated-meta.ts @@ -4,7 +4,7 @@ // Meta info export const publisher = 'Vue'; export const name = 'volar'; -export const version = '3.0.0-beta.4'; +export const version = '3.0.1'; export const displayName = 'Vue (Official)'; export const description = 'Language Support for Vue'; export const extensionId = `${publisher}.${name}`; @@ -12,12 +12,19 @@ export const extensionId = `${publisher}.${name}`; /** * Type union of all commands */ -export type CommandKey = 'vue.action.restartServer'; +export type CommandKey = + | 'vue.welcome' + | 'vue.action.restartServer'; /** * Commands map registed by `Vue.volar` */ export const commands = { + /** + * %command.welcome% + * @value `vue.welcome` + */ + welcome: 'vue.welcome', /** * %command.action.restartServer% * @value `vue.action.restartServer` @@ -30,6 +37,9 @@ export const commands = { */ export type ConfigKey = | 'vue.trace.server' + | 'vue.editor.focusMode' + | 'vue.editor.reactivityVisualization' + | 'vue.editor.templateInterpolationDecorators' | 'vue.server.includeLanguages' | 'vue.codeActions.askNewComponentName' | 'vue.suggest.componentNameCasing' @@ -49,6 +59,9 @@ export type ConfigKey = export interface ConfigKeyTypeMap { 'vue.trace.server': 'off' | 'messages' | 'verbose'; + 'vue.editor.focusMode': boolean; + 'vue.editor.reactivityVisualization': boolean; + 'vue.editor.templateInterpolationDecorators': boolean; 'vue.server.includeLanguages': string[]; 'vue.codeActions.askNewComponentName': boolean; 'vue.suggest.componentNameCasing': 'preferKebabCase' | 'preferPascalCase' | 'alwaysKebabCase' | 'alwaysPascalCase'; @@ -76,6 +89,9 @@ export interface ConfigKeyTypeMap { export interface ConfigShorthandMap { traceServer: 'vue.trace.server'; + editorFocusMode: 'vue.editor.focusMode'; + editorReactivityVisualization: 'vue.editor.reactivityVisualization'; + editorTemplateInterpolationDecorators: 'vue.editor.templateInterpolationDecorators'; serverIncludeLanguages: 'vue.server.includeLanguages'; codeActionsAskNewComponentName: 'vue.codeActions.askNewComponentName'; suggestComponentNameCasing: 'vue.suggest.componentNameCasing'; @@ -96,6 +112,9 @@ export interface ConfigShorthandMap { export interface ConfigShorthandTypeMap { traceServer: 'off' | 'messages' | 'verbose'; + editorFocusMode: boolean; + editorReactivityVisualization: boolean; + editorTemplateInterpolationDecorators: boolean; serverIncludeLanguages: string[]; codeActionsAskNewComponentName: boolean; suggestComponentNameCasing: 'preferKebabCase' | 'preferPascalCase' | 'alwaysKebabCase' | 'alwaysPascalCase'; @@ -139,6 +158,33 @@ export const configs = { key: 'vue.trace.server', default: 'off', } as ConfigItem<'vue.trace.server'>, + /** + * @key `vue.editor.focusMode` + * @default `true` + * @type `boolean` + */ + editorFocusMode: { + key: 'vue.editor.focusMode', + default: true, + } as ConfigItem<'vue.editor.focusMode'>, + /** + * @key `vue.editor.reactivityVisualization` + * @default `true` + * @type `boolean` + */ + editorReactivityVisualization: { + key: 'vue.editor.reactivityVisualization', + default: true, + } as ConfigItem<'vue.editor.reactivityVisualization'>, + /** + * @key `vue.editor.templateInterpolationDecorators` + * @default `true` + * @type `boolean` + */ + editorTemplateInterpolationDecorators: { + key: 'vue.editor.templateInterpolationDecorators', + default: true, + } as ConfigItem<'vue.editor.templateInterpolationDecorators'>, /** * @key `vue.server.includeLanguages` * @default `["vue"]` @@ -287,6 +333,9 @@ export const configs = { export interface ScopedConfigKeyTypeMap { 'trace.server': 'off' | 'messages' | 'verbose'; + 'editor.focusMode': boolean; + 'editor.reactivityVisualization': boolean; + 'editor.templateInterpolationDecorators': boolean; 'server.includeLanguages': string[]; 'codeActions.askNewComponentName': boolean; 'suggest.componentNameCasing': 'preferKebabCase' | 'preferPascalCase' | 'alwaysKebabCase' | 'alwaysPascalCase'; @@ -316,6 +365,9 @@ export const scopedConfigs = { scope: 'vue', defaults: { 'trace.server': 'off', + 'editor.focusMode': true, + 'editor.reactivityVisualization': true, + 'editor.templateInterpolationDecorators': true, 'server.includeLanguages': ['vue'], 'codeActions.askNewComponentName': true, 'suggest.componentNameCasing': 'preferPascalCase', @@ -340,6 +392,11 @@ export interface NestedConfigs { 'trace': { 'server': 'off' | 'messages' | 'verbose'; }; + 'editor': { + 'focusMode': boolean; + 'reactivityVisualization': boolean; + 'templateInterpolationDecorators': boolean; + }; 'server': { 'includeLanguages': string[]; }; @@ -388,6 +445,11 @@ export interface NestedScopedConfigs { 'trace': { 'server': 'off' | 'messages' | 'verbose'; }; + 'editor': { + 'focusMode': boolean; + 'reactivityVisualization': boolean; + 'templateInterpolationDecorators': boolean; + }; 'server': { 'includeLanguages': string[]; }; diff --git a/extensions/vscode/lib/welcome.ts b/extensions/vscode/lib/welcome.ts new file mode 100644 index 0000000000..895e49fd4f --- /dev/null +++ b/extensions/vscode/lib/welcome.ts @@ -0,0 +1,531 @@ +import { extensionContext, useCommand } from 'reactive-vscode'; +import * as vscode from 'vscode'; + +let panel: vscode.WebviewPanel | undefined; + +export function activate() { + useCommand('vue.welcome', () => { + if (panel) { + panel.reveal(vscode.ViewColumn.One); + return; + } + + panel = vscode.window.createWebviewPanel( + 'vue.welcome', + 'Welcome to Vue', + vscode.ViewColumn.One, + { enableScripts: true }, + ); + + panel.webview.html = getWelcomeHtml(); + panel.webview.onDidReceiveMessage(message => { + switch (message.command) { + case 'verifySponsor': + vscode.commands.executeCommand('vue.action.verify'); + break; + } + }); + + panel.onDidDispose(() => { + panel = undefined; + }); + }); +} + +function getWelcomeHtml() { + const version = extensionContext.value?.extension.packageJSON.version; + + return /* HTML */ ` + + + + + + + Codestin Search App + + + + + +
+ + + + + + + + + + +
+

Vue (Official) ${version}

+ +
+
+
+ +

📣 What's New

+
+

3.0.2

+
    +
  • 🚀 Improve memory usage in extreme cases
  • +
  • 🐛 15+ bug fixes
  • +
+
+ + + + + Full Release Notes + +
+ Released: July 2025 + + v3.0.2 +
+
+
+

3.0.0

+
    +
  • 🚀 Significantly improved Hybrid Mode stability
  • +
  • ✨ Introduced several new DX enhancement features
  • +
  • 🌍 Expanded support for additional localizations
  • +
  • 🎨 UI tweaks: removed all Vue-related status bar items
  • +
  • 🐛 Squashed numerous bugs throughout the extension
  • +
+
+ ⚠️ Deprecation Notice: Dropping Vue 2 Support in v3.1 + (Discussion #5395) +
+ +
+ + + + + Full Release Notes + +
+ Released: July 2025 + + v3.0.0 +
+
+
+ +

🎥 Learn More Features

+

Discover advanced capabilities of the extension:

+
+ + + +
+

+ ⚠️ Unable to load the video? Watch on YouTube +

+ + +

✨ Core Features

+
+
+
🧩
+

Template Intelligence

+

Smart completions for directives, components and props in Vue templates with type inference

+
+
+
🔍
+

Type Checking

+

Full TypeScript support with type inference across SFCs and reactive type checking

+
+
+
🎨
+

Syntax Highlighting

+

Comprehensive syntax highlighting for Single File Components and template expressions

+
+
+ +

💎 Premium Features

+
+
+
🧩
+

Interpolation Highlight 🌟

+

Enhanced highlighting for template interpolations and expressions

+
+
+
🧩
+

Focus Mode 🌟🌟

+

Isolate and focus on specific SFC blocks during development

+
+
+
🧩
+

Reactivity Visualization 🌟🌟🌟🌟

+

Visualize Vue's reactivity system in component scripts

+
+
+
🚧
+

More Features Coming Soon

+
+
+ +
+

Support the development and unlock these features:

+ + + + + GitHub Sponsors + + + + + + 爱发电 (afdian) + + +
+ +

📚 Resources

+ + +

🔧 Troubleshooting

+
+ Why are some features not working? +
+

Make sure you have:

+
    +
  • The latest version of the extension installed
  • +
  • Vue 3.x in your project dependencies
  • +
  • TSDK 5.3 or later
  • +
  • Try disabling other extensions to rule out conflicts
  • +
+
+
+
+ Where to report issues? +
+

Please report any problems on our GitHub Issues page with:

+
    +
  • Detailed reproduction steps
  • +
  • Screenshots or screencasts if applicable
  • +
  • Your project setup information
  • +
+
+
+ +

❤️ Thanks to Our Sponsors

+
+

This project is made possible thanks to our generous sponsors:

+
+ +

+ Become a sponsor to support Vue + tooling development +

+
+ + +`; +} diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 027823a8b5..7de1a28ba6 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "volar", - "version": "3.0.0", + "version": "3.0.3", "repository": { "type": "git", "url": "https://github.com/vuejs/language-tools.git", @@ -67,10 +67,6 @@ { "id": "jade", "configuration": "./languages/sfc-template-language-configuration.json" - }, - { - "id": "plaintext", - "configuration": "./languages/sfc-template-language-configuration.json" } ], "typescriptServerPlugins": [ @@ -221,6 +217,32 @@ ] } ], + "semanticTokenScopes": [ + { + "language": "vue", + "scopes": { + "component": [ + "support.class.component.vue" + ] + } + }, + { + "language": "markdown", + "scopes": { + "component": [ + "support.class.component.vue" + ] + } + }, + { + "language": "html", + "scopes": { + "component": [ + "support.class.component.vue" + ] + } + } + ], "breakpoints": [ { "language": "vue" @@ -241,6 +263,21 @@ "default": "off", "markdownDescription": "%configuration.trace.server%" }, + "vue.editor.focusMode": { + "type": "boolean", + "default": true, + "markdownDescription": "Sponsor this extension to unlock premium features. [Learn more](https://youtu.be/RcPcO4_Ct_U)" + }, + "vue.editor.reactivityVisualization": { + "type": "boolean", + "default": true, + "markdownDescription": "Sponsor this extension to unlock premium features. [Learn more](https://youtu.be/RcPcO4_Ct_U)" + }, + "vue.editor.templateInterpolationDecorators": { + "type": "boolean", + "default": true, + "markdownDescription": "Sponsor this extension to unlock premium features. [Learn more](https://youtu.be/RcPcO4_Ct_U)" + }, "vue.server.includeLanguages": { "type": "array", "items": { @@ -362,6 +399,11 @@ } }, "commands": [ + { + "command": "vue.welcome", + "title": "%command.welcome%", + "category": "Vue" + }, { "command": "vue.action.restartServer", "title": "%command.action.restartServer%", @@ -421,11 +463,11 @@ "@types/node": "^22.10.4", "@types/semver": "^7.5.3", "@types/vscode": "1.88.0", - "@volar/vscode": "2.4.16", + "@volar/vscode": "2.4.20", "@vscode/vsce": "^3.2.1", "@vue/compiler-sfc": "^3.5.0", - "@vue/language-server": "3.0.0", - "@vue/typescript-plugin": "3.0.0", + "@vue/language-server": "3.0.3", + "@vue/typescript-plugin": "3.0.3", "reactive-vscode": "^0.2.9", "rolldown": "1.0.0-beta.8", "semver": "^7.5.4", diff --git a/extensions/vscode/package.nls.ja.json b/extensions/vscode/package.nls.ja.json index c7037b1a35..aee8afda88 100644 --- a/extensions/vscode/package.nls.ja.json +++ b/extensions/vscode/package.nls.ja.json @@ -1,9 +1,6 @@ { "configuration.trace.server": "VS Code と Vue 言語サーバー間の通信をトレースします。", "configuration.server.includeLanguages": "拡張機能を有効にする言語を指定します。", - "configuration.splitEditors.icon": "エディターのタイトル領域にエディター分割ボタンを表示します。", - "configuration.splitEditors.layout.left": "左側の分割されたエディターに含めるブロック。", - "configuration.splitEditors.layout.right": "右側の分割されたエディターに含めるブロック。", "configuration.codeActions.askNewComponentName": "コンポーネントを抽出する時に新しいコンポーネント名を尋ねます。", "configuration.suggest.componentNameCasing": "コンポーネント名の命名規則。", "configuration.suggest.propNameCasing": "属性名の命名規則。", @@ -20,5 +17,5 @@ "configuration.format.style.initialIndent": "`