From 70d0d19c900353da508304d086afb77c32272d05 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 17 Dec 2022 01:31:37 +0800 Subject: [PATCH] feat: add Angular example (#2215) --- .vscode/launch.json | 17 + examples/angular-language-core/LICENSE | 21 + examples/angular-language-core/package.json | 24 + examples/angular-language-core/src/index.ts | 3 + .../angular-language-core/src/modules/html.ts | 488 ++++++++++++++++++ .../angular-language-core/src/modules/ts.ts | 200 +++++++ .../angular-language-core/tsconfig.build.json | 20 + examples/angular-language-server/LICENSE | 21 + .../bin/angular-language-server.js | 8 + examples/angular-language-server/package.json | 24 + examples/angular-language-server/src/index.ts | 64 +++ .../tsconfig.build.json | 26 + examples/svelte-language-core/src/index.ts | 11 +- examples/vscode-angular/.vscodeignore | 5 + examples/vscode-angular/LICENSE | 21 + examples/vscode-angular/README.md | 44 ++ examples/vscode-angular/client.js | 3 + ...gular-template-language-configuration.json | 8 + examples/vscode-angular/package.json | 97 ++++ examples/vscode-angular/scripts/build.js | 31 ++ examples/vscode-angular/server.js | 3 + examples/vscode-angular/src/client.ts | 83 +++ .../syntaxes/angular-directives.json | 123 +++++ .../syntaxes/angular-interpolations.json | 44 ++ examples/vscode-angular/tsconfig.build.json | 24 + examples/vscode-svelte/package.json | 4 +- examples/vscode-svelte/src/client.ts | 3 - .../src/common.ts | 11 +- .../language-core/src/documentRegistry.ts | 16 +- packages/language-core/src/languageContext.ts | 6 +- packages/language-core/src/types.ts | 6 +- .../src/features/customFeatures.ts | 2 +- .../language-server/src/registerFeatures.ts | 11 +- packages/language-service/src/documents.ts | 6 +- .../src/languageFeatures/fileRename.ts | 2 +- packages/language-service/src/types.ts | 2 + .../src/utils/definePlugin.ts | 14 +- .../src/utils/featureWorkers.ts | 6 +- packages/typescript/src/index.ts | 2 +- .../src/features/tsVersion.ts | 14 +- pnpm-lock.yaml | 278 ++++++---- tsconfig.build.json | 6 + .../src/generators/template.ts | 2 +- .../vue-language-core/src/sourceFile.ts | 31 ++ .../vue-language-core/src/utils/transform.ts | 4 +- .../vue-language-service/src/helpers.ts | 2 +- .../src/plugins/vue-autoinsert-parentheses.ts | 2 +- .../src/plugins/vue-twoslash-queries.ts | 2 +- 48 files changed, 1674 insertions(+), 171 deletions(-) create mode 100644 examples/angular-language-core/LICENSE create mode 100644 examples/angular-language-core/package.json create mode 100644 examples/angular-language-core/src/index.ts create mode 100644 examples/angular-language-core/src/modules/html.ts create mode 100644 examples/angular-language-core/src/modules/ts.ts create mode 100644 examples/angular-language-core/tsconfig.build.json create mode 100644 examples/angular-language-server/LICENSE create mode 100755 examples/angular-language-server/bin/angular-language-server.js create mode 100644 examples/angular-language-server/package.json create mode 100644 examples/angular-language-server/src/index.ts create mode 100644 examples/angular-language-server/tsconfig.build.json create mode 100644 examples/vscode-angular/.vscodeignore create mode 100644 examples/vscode-angular/LICENSE create mode 100644 examples/vscode-angular/README.md create mode 100644 examples/vscode-angular/client.js create mode 100644 examples/vscode-angular/languages/angular-template-language-configuration.json create mode 100644 examples/vscode-angular/package.json create mode 100644 examples/vscode-angular/scripts/build.js create mode 100644 examples/vscode-angular/server.js create mode 100644 examples/vscode-angular/src/client.ts create mode 100644 examples/vscode-angular/syntaxes/angular-directives.json create mode 100644 examples/vscode-angular/syntaxes/angular-interpolations.json create mode 100644 examples/vscode-angular/tsconfig.build.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 86fc0f720..cf487d4b6 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -36,6 +36,23 @@ "script": "watch" } }, + { + "name": "Launch Angular Example", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--disable-extensions", + "--extensionDevelopmentPath=${workspaceRoot}/examples/vscode-angular" + ], + "outFiles": [ + "${workspaceRoot}/*/*/out/**/*.js" + ], + "preLaunchTask": { + "type": "npm", + "script": "watch" + } + }, { "type": "extensionHost", "request": "launch", diff --git a/examples/angular-language-core/LICENSE b/examples/angular-language-core/LICENSE new file mode 100644 index 000000000..b55e47a7e --- /dev/null +++ b/examples/angular-language-core/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present Johnson Chu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/angular-language-core/package.json b/examples/angular-language-core/package.json new file mode 100644 index 000000000..45dc12068 --- /dev/null +++ b/examples/angular-language-core/package.json @@ -0,0 +1,24 @@ +{ + "name": "@volar-examples/angular-language-core", + "version": "1.0.13", + "main": "out/index.js", + "license": "MIT", + "files": [ + "out/**/*.js", + "out/**/*.d.ts" + ], + "repository": { + "type": "git", + "url": "https://github.com/johnsoncodehk/volar.git", + "directory": "examples/angular-language-core" + }, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "^15.1.0", + "@volar/language-core": "1.0.13", + "vscode-languageserver-textdocument": "^1.0.7", + "vscode-uri": "^3.0.6" + }, + "devDependencies": { + "@angular/compiler": "^15.0.4" + } +} diff --git a/examples/angular-language-core/src/index.ts b/examples/angular-language-core/src/index.ts new file mode 100644 index 000000000..cfa180365 --- /dev/null +++ b/examples/angular-language-core/src/index.ts @@ -0,0 +1,3 @@ +export * from '@volar/language-core'; +export * from './modules/ts'; +export * from './modules/html'; diff --git a/examples/angular-language-core/src/modules/html.ts b/examples/angular-language-core/src/modules/html.ts new file mode 100644 index 000000000..a6e707070 --- /dev/null +++ b/examples/angular-language-core/src/modules/html.ts @@ -0,0 +1,488 @@ +import { DocumentCapabilities, EmbeddedFileKind, LanguageModule, SourceFile } from '@volar/language-core'; +import type { TmplAstNode, TmplAstTemplate, ParsedTemplate, ParseSourceSpan } from '@angular/compiler'; +import { Codegen } from './ts'; +import type * as ts from 'typescript/lib/tsserverlibrary'; + +const { parseTemplate }: typeof import('@angular/compiler') = require('@angular-eslint/bundled-angular-compiler'); + +export class HTMLTemplateFile implements SourceFile { + + public text: string; + public capabilities: DocumentCapabilities = { + diagnostic: true, + }; + public kind = EmbeddedFileKind.TextFile; + public mappings: SourceFile['mappings'] = []; + public embeddeds: SourceFile['embeddeds'] = []; + public parsed: ParsedTemplate; + + constructor( + private ts: typeof import('typescript/lib/tsserverlibrary'), + public fileName: string, + public snapshot: ts.IScriptSnapshot, + ) { + + const generated = generate(ts, fileName, snapshot.getText(0, snapshot.getLength())); + + this.text = snapshot.getText(0, snapshot.getLength()); // TODO: use empty string with snapshot + this.mappings = [ + { + data: { + diagnostic: true, + }, + generatedRange: [0, this.text.length], + sourceRange: [0, this.text.length], + }, + ]; + this.embeddeds = [ + { + fileName: fileName + '.__template.ts', + text: generated.codegen.text, + capabilities: { + diagnostic: true, + foldingRange: false, + documentFormatting: false, + documentSymbol: false, + codeAction: false, + inlayHint: true, + }, + kind: EmbeddedFileKind.TypeScriptHostFile, + mappings: generated.codegen.mappings, + embeddeds: [], + }, + ]; + this.parsed = generated.parsed; + } + + update(snapshot: ts.IScriptSnapshot) { + const generated = generate(this.ts, this.fileName, snapshot.getText(0, snapshot.getLength())); + this.text = snapshot.getText(0, snapshot.getLength()); + this.mappings = [ + { + data: { + diagnostic: true, + }, + generatedRange: [0, this.text.length], + sourceRange: [0, this.text.length], + }, + ]; + this.embeddeds[0].text = generated.codegen.text; + this.embeddeds[0].mappings = generated.codegen.mappings; + this.parsed = generated.parsed; + } +} + +export function createHtmlLanguageModule(ts: typeof import('typescript/lib/tsserverlibrary')): LanguageModule { + return { + createSourceFile(fileName, snapshot) { + if (fileName.endsWith('.html')) { + return new HTMLTemplateFile(ts, fileName, snapshot); + } + }, + updateSourceFile(sourceFile, snapshot) { + sourceFile.update(snapshot); + }, + }; +} + +function generate( + ts: typeof import('typescript/lib/tsserverlibrary'), + fileName: string, + fileText: string, +) { + + const parsed = parseTemplate(fileText, fileName, { + preserveWhitespaces: true, + }); + const codegen = new Codegen(fileText); + const localVars: Record = {}; + const templateBlocksConditions: Record = {}; + const conditions: string[] = []; + const ngTemplates: TmplAstTemplate[] = []; + + let elementIndex = 0; + + codegen.text += 'export { };\n'; + codegen.text += `declare const __ctx: __Templates2Components['${fileName}'];\n`; + codegen.text += `declare const __components: __Selectors2Components & HTMLElementTagNameMap;\n`; + + const visitor: Parameters[0] = { + visit(node) { + node.visit(visitor); + }, + visitElement(element) { + codegen.text += `{\n`; + // const isComponent = element.name.indexOf('-') >= 0; + const index = elementIndex++; + codegen.text += `const __element_${index} = __components`; + codegen.addPropertyAccess( + element.startSourceSpan.start.offset + '<'.length, + element.startSourceSpan.start.offset + '<'.length + element.name.length, + ); + codegen.text += `;\n`; + for (const input of element.inputs) { + codegen.text += `__element_${index}`; + codegen.addPropertyAccess(input.keySpan.start.offset, input.keySpan.end.offset); + codegen.text += ' = ('; + if (input.valueSpan) { + addInterpolationFragment(input.valueSpan.start.offset, input.valueSpan.end.offset); + } + codegen.text += `);\n`; + } + for (const attr of element.attributes) { + if (attr.keySpan) { + codegen.text += `__element_${index}`; + codegen.addPropertyAccess(attr.keySpan.start.offset, attr.keySpan.end.offset); + codegen.text += ' = "'; + if (attr.valueSpan) { + codegen.addSourceText(attr.valueSpan.start.offset, attr.valueSpan.end.offset); + } + codegen.text += `";\n`; + } + } + for (const child of element.children) { + child.visit(visitor); + } + for (const output of element.outputs) { + codegen.text += `__element_${index}`; + codegen.addPropertyAccess(output.keySpan.start.offset, output.keySpan.end.offset); + codegen.text += `.subscribe($event => { `; + localVars.$event ??= 0; + localVars.$event++; + addInterpolationFragment(output.handlerSpan.start.offset, output.handlerSpan.end.offset); + localVars.$event--; + codegen.text += ' });\n'; + } + codegen.text += `}\n`; + }, + visitTemplate(template) { + + if (template.tagName === 'ng-template') { + ngTemplates.push(template); + } + + let conditionText = 'true'; + let forOfSource: ParseSourceSpan | undefined; + let forOfBinding: ParseSourceSpan | undefined; + + for (const attr of template.templateAttrs) { + if (attr.name === 'ngIf' && attr.valueSpan) { + codegen.text += `if (`; + conditionText = addInterpolationFragment(attr.valueSpan.start.offset, attr.valueSpan.end.offset); + codegen.text += `) {\n`; + conditions.push(conditionText); + for (const child of template.children) { + child.visit(visitor); + } + conditions.pop(); + codegen.text += `}\n`; + } + if (attr.name === 'ngIfElse' && attr.valueSpan) { + codegen.text += '(__templates)'; + codegen.addPropertyAccess(attr.valueSpan.start.offset, attr.valueSpan.end.offset); + codegen.text += ';\n'; + const templateBlock = fileText.substring(attr.valueSpan.start.offset, attr.valueSpan.end.offset); + templateBlocksConditions[templateBlock] ??= []; + templateBlocksConditions[templateBlock].push([ + ...conditions, + `!(${conditionText})`, + ]); + } + if (attr.name === 'ngForOf' && attr.valueSpan) { + forOfSource = attr.valueSpan; + } + } + + for (const v of template.variables) { + if (v.value === '$implicit') { + forOfBinding = v.keySpan; + } + } + + if (forOfSource && forOfBinding) { + codegen.text += `for (const `; + const binding = codegen.addSourceText(forOfBinding.start.offset, forOfBinding.end.offset); + codegen.text += ` of `; + addInterpolationFragment(forOfSource.start.offset, forOfSource.end.offset); + codegen.text += `) {\n`; + localVars[binding] ??= 0; + localVars[binding]++; + for (const child of template.children) { + child.visit(visitor); + } + localVars[binding]--; + codegen.text += `}\n`; + } + }, + visitContent(content) { + content.visit(visitor); + }, + visitVariable(variable) { + variable.visit(visitor); + }, + visitReference(reference) { + reference.visit(visitor); + }, + visitTextAttribute(attribute) { + attribute.visit(visitor); + }, + visitBoundAttribute(attribute) { + attribute.visit(visitor); + }, + visitBoundEvent(event) { + event.visit(visitor); + }, + visitText(_text) { + // text.visit(visitor); + }, + visitBoundText(text) { + const content = fileText.substring(text.value.sourceSpan.start, text.value.sourceSpan.end); + const interpolations = content.matchAll(/{{[\s\S]*?}}/g); + for (const interpolation of interpolations) { + const start = text.value.sourceSpan.start + interpolation.index! + '{{'.length; + const length = interpolation[0].length - '{{'.length - '}}'.length; + addInterpolationFragment(start, start + length); + codegen.text += ';\n'; + } + }, + visitIcu(icu) { + icu.visit(visitor); + }, + }; + + for (const node of parsed.nodes) { + node.visit(visitor); + } + + codegen.text += 'var __templates = {\n'; + for (const template of ngTemplates) { + for (const reference of template.references) { + codegen.addObjectKey(reference.keySpan.start.offset, reference.keySpan.end.offset); + codegen.text += ': (() => {\n'; + let ifBlockOpen = false; + const templateBlock = fileText.substring(reference.keySpan.start.offset, reference.keySpan.end.offset); + if (templateBlocksConditions[templateBlock]) { + ifBlockOpen = true; + codegen.text += `if (`; + codegen.text += templateBlocksConditions[templateBlock].map(conditions => conditions.join(' && ')).join(' || '); + codegen.text += `) {\n`; + } + for (const child of template.children) { + child.visit(visitor); + } + if (ifBlockOpen) { + ifBlockOpen = false; + codegen.text += `}\n`; + } + codegen.text += `}) as unknown as typeof import('@angular/core').TemplateRef,\n`; + } + } + codegen.text += '};\n'; + + return { + codegen, + parsed, + }; + + function addInterpolationFragment(start: number, end: number) { + const code = fileText.substring(start, end); + const ast = ts.createSourceFile(fileName + '.ts', code, ts.ScriptTarget.Latest); + let full = ''; + walkInterpolationFragment(ts, code, ast, (fragment, offset, isJustForErrorMapping) => { + full += fragment; + if (offset !== undefined) { + codegen.addSourceText( + start + offset, + start + offset + fragment.length, + isJustForErrorMapping ? { diagnostic: true } : undefined, + ); + } + else { + codegen.text += fragment; + } + }, localVars,); + return full; + } +} + +function walkInterpolationFragment( + ts: typeof import('typescript/lib/tsserverlibrary'), + code: string, + ast: ts.SourceFile, + cb: (fragment: string, offset: number | undefined, isJustForErrorMapping?: boolean) => void, + localVars: Record, +) { + + let ctxVars: { + text: string, + isShorthand: boolean, + offset: number, + }[] = []; + + const varCb = (id: ts.Identifier, isShorthand: boolean) => { + if ( + !!localVars[id.text] || + id.text.startsWith('__') + ) { + return; + } + ctxVars.push({ + text: id.text, + isShorthand: isShorthand, + offset: id.getStart(ast), + }); + }; + ast.forEachChild(node => walkIdentifiers(ts, node, varCb, localVars)); + + ctxVars = ctxVars.sort((a, b) => a.offset - b.offset); + + if (ctxVars.length) { + + if (ctxVars[0].isShorthand) { + cb(code.substring(0, ctxVars[0].offset + ctxVars[0].text.length), 0); + cb(': ', undefined); + } + else { + cb(code.substring(0, ctxVars[0].offset), 0); + } + + for (let i = 0; i < ctxVars.length - 1; i++) { + + cb('(__ctx).', undefined); + if (ctxVars[i + 1].isShorthand) { + cb(code.substring(ctxVars[i].offset, ctxVars[i + 1].offset + ctxVars[i + 1].text.length), ctxVars[i].offset); + cb(': ', undefined); + } + else { + cb(code.substring(ctxVars[i].offset, ctxVars[i + 1].offset), ctxVars[i].offset); + } + } + + cb('', ctxVars[ctxVars.length - 1].offset, true); + cb('(__ctx).', undefined); + cb(code.substring(ctxVars[ctxVars.length - 1].offset), ctxVars[ctxVars.length - 1].offset); + } + else { + cb(code, 0); + } + + return ctxVars; +} + +function walkIdentifiers( + ts: typeof import('typescript/lib/tsserverlibrary'), + node: ts.Node, + cb: (varNode: ts.Identifier, isShorthand: boolean) => void, + localVars: Record, +) { + + const blockVars: string[] = []; + + if (ts.isIdentifier(node)) { + cb(node, false); + } + else if (ts.isShorthandPropertyAssignment(node)) { + cb(node.name, true); + } + else if (ts.isPropertyAccessExpression(node)) { + walkIdentifiers(ts, node.expression, cb, localVars); + } + else if (ts.isVariableDeclaration(node)) { + + colletVars(ts, node.name, blockVars); + + for (const varName of blockVars) + localVars[varName] = (localVars[varName] ?? 0) + 1; + + if (node.initializer) + walkIdentifiers(ts, node.initializer, cb, localVars); + } + else if (ts.isArrowFunction(node)) { + + const functionArgs: string[] = []; + + for (const param of node.parameters) { + colletVars(ts, param.name, functionArgs); + if (param.type) { + walkIdentifiers(ts, param.type, cb, localVars); + } + } + + for (const varName of functionArgs) + localVars[varName] = (localVars[varName] ?? 0) + 1; + + walkIdentifiers(ts, node.body, cb, localVars); + + for (const varName of functionArgs) + localVars[varName]--; + } + else if (ts.isObjectLiteralExpression(node)) { + for (const prop of node.properties) { + if (ts.isPropertyAssignment(prop)) { + // fix https://github.com/johnsoncodehk/volar/issues/1176 + if (ts.isComputedPropertyName(prop.name)) { + walkIdentifiers(ts, prop.name.expression, cb, localVars); + } + walkIdentifiers(ts, prop.initializer, cb, localVars); + } + // fix https://github.com/johnsoncodehk/volar/issues/1156 + else if (ts.isShorthandPropertyAssignment(prop)) { + walkIdentifiers(ts, prop, cb, localVars); + } + // fix https://github.com/johnsoncodehk/volar/issues/1148#issuecomment-1094378126 + else if (ts.isSpreadAssignment(prop)) { + // TODO: cannot report "Spread types may only be created from object types.ts(2698)" + walkIdentifiers(ts, prop.expression, cb, localVars); + } + } + } + else if (ts.isTypeReferenceNode(node)) { + // fix https://github.com/johnsoncodehk/volar/issues/1422 + node.forEachChild(node => walkIdentifiersInTypeReference(ts, node, cb)); + } + else { + node.forEachChild(node => walkIdentifiers(ts, node, cb, localVars)); + } + + for (const varName of blockVars) { + localVars[varName]--; + } +} + +function walkIdentifiersInTypeReference( + ts: typeof import('typescript/lib/tsserverlibrary'), + node: ts.Node, + cb: (varNode: ts.Identifier, isShorthand: boolean) => void, +) { + if (ts.isTypeQueryNode(node) && ts.isIdentifier(node.exprName)) { + cb(node.exprName, false); + } + else { + node.forEachChild(node => walkIdentifiersInTypeReference(ts, node, cb)); + } +} + +function colletVars( + ts: typeof import('typescript/lib/tsserverlibrary'), + node: ts.Node, + result: string[], +) { + if (ts.isIdentifier(node)) { + result.push(node.text); + } + else if (ts.isObjectBindingPattern(node)) { + for (const el of node.elements) { + colletVars(ts, el.name, result); + } + } + else if (ts.isArrayBindingPattern(node)) { + for (const el of node.elements) { + if (ts.isBindingElement(el)) { + colletVars(ts, el.name, result); + } + } + } + else { + node.forEachChild(node => colletVars(ts, node, result)); + } +} diff --git a/examples/angular-language-core/src/modules/ts.ts b/examples/angular-language-core/src/modules/ts.ts new file mode 100644 index 000000000..444ed9bd2 --- /dev/null +++ b/examples/angular-language-core/src/modules/ts.ts @@ -0,0 +1,200 @@ +import { LanguageModule, SourceFile, EmbeddedFileKind, PositionCapabilities } from '@volar/language-core'; +import type * as ts from 'typescript/lib/tsserverlibrary'; +import * as path from 'path'; +import type { Mapping } from '@volar/source-map'; + +export function createTsLanguageModule( + ts: typeof import('typescript/lib/tsserverlibrary'), +) { + + const languageModule: LanguageModule = { + createSourceFile(fileName, snapshot) { + if (fileName.endsWith('.ts')) { + const text = snapshot.getText(0, snapshot.getLength()); + const ast = ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest); + const virtualFile = createVirtualFile(ast); + return { + ast, + snapshot, + fileName, + text: virtualFile.text, + capabilities: { + diagnostic: true, + foldingRange: true, + documentFormatting: true, + documentSymbol: true, + codeAction: true, + inlayHint: true, + }, + kind: EmbeddedFileKind.TypeScriptHostFile, + mappings: virtualFile.mappings, + embeddeds: [], + }; + } + }, + updateSourceFile(sourceFile, snapshot) { + const text = snapshot.getText(0, snapshot.getLength()); + const change = snapshot.getChangeRange(sourceFile.snapshot); + + // incremental update for better performance + sourceFile.ast = change + ? sourceFile.ast.update(text, change) + : ts.createSourceFile(sourceFile.fileName, text, ts.ScriptTarget.Latest); + sourceFile.snapshot = snapshot; + + const gen = createVirtualFile(sourceFile.ast); + sourceFile.text = gen.text; + sourceFile.mappings = gen.mappings; + }, + }; + return languageModule; + + function createVirtualFile(ast: ts.SourceFile) { + + const classComponents: { + templateUrl?: string, + selectorNode?: ts.StringLiteral, + urlNodes: ts.Node[], + decoratorName: string, + className: string, + }[] = []; + + ast.forEachChild(node => { + if (ts.isClassDeclaration(node)) { + if (node.modifiers?.find(mod => mod.kind === ts.SyntaxKind.ExportKeyword)) { + const decorator = node.modifiers.find((mod) => ts.isDecorator(mod)) as ts.Decorator | undefined; + if ( + decorator + && ts.isCallExpression(decorator.expression) + && decorator.expression.arguments.length + && ts.isObjectLiteralExpression(decorator.expression.arguments[0]) + ) { + const decoratorName = decorator.expression.expression.getText(ast); + const className = node.name?.getText(ast) || ''; + const classComponent: typeof classComponents[number] = { + className, + decoratorName, + urlNodes: [], + }; + const selectorProp = decorator.expression.arguments[0].properties.find((prop) => prop.name?.getText(ast) === 'selector'); + if (selectorProp && ts.isPropertyAssignment(selectorProp) && ts.isStringLiteral(selectorProp.initializer)) { + classComponent.selectorNode = selectorProp.initializer; + } + const templateUrlProp = decorator.expression.arguments[0].properties.find((prop) => prop.name?.getText(ast) === 'templateUrl'); + if (templateUrlProp && ts.isPropertyAssignment(templateUrlProp) && ts.isStringLiteral(templateUrlProp.initializer)) { + const templateUrl = path.resolve(path.dirname(ast.fileName), templateUrlProp.initializer.text); + classComponent.templateUrl = templateUrl; + classComponent.urlNodes.push(templateUrlProp.initializer); + } + const styleUrlsProp = decorator.expression.arguments[0].properties.find((prop) => prop.name?.getText(ast) === 'styleUrls'); + if (styleUrlsProp && ts.isPropertyAssignment(styleUrlsProp) && ts.isArrayLiteralExpression(styleUrlsProp.initializer)) { + for (const url of styleUrlsProp.initializer.elements) { + if (ts.isStringLiteral(url)) { + classComponent.urlNodes.push(url); + } + } + } + classComponents.push(classComponent); + } + } + } + }); + + const codegen = new Codegen(ast.getText()); + + codegen.addSourceText(0, ast.end); + + if (classComponents.length) { + codegen.text += `\n/* Volar: Virtual Code */\n`; + for (const classComponent of classComponents) { + for (const urlNode of classComponent.urlNodes) { + codegen.text += `import `; + codegen.addSourceText(urlNode.getStart(ast), urlNode.getEnd()); + codegen.text += `;\n`; + } + } + const classComponentsWithTemplateUrl = classComponents.filter(component => !!component.templateUrl); + codegen.text += `declare global {\n`; + if (classComponentsWithTemplateUrl.length) { + codegen.text += `type __WithComponent

= C1 extends import('@angular/core').Component ? { [k in P]: C2 } : {};\n`; + codegen.text += `interface __Templates2Components extends\n`; + codegen.text += classComponentsWithTemplateUrl.map((component) => { + return `__WithComponent<'${component.templateUrl}', ${component.decoratorName}, ${component.className}>`; + }).join(',\n'); + codegen.text += `\n{ }\n`; + } + const classComponentsWithSelector = classComponents.filter(component => !!component.selectorNode); + if (classComponentsWithSelector.length) { + codegen.text += `type __WithComponent2

= C1 extends import('@angular/core').Component ? P : {};\n`; + for (const classComponentWithSelector of classComponentsWithSelector) { + codegen.text += `interface __Selectors2Components extends __WithComponent2<{ `; + codegen.addSourceText(classComponentWithSelector.selectorNode!.getStart(ast), classComponentWithSelector.selectorNode!.getEnd()); + codegen.text += `: ${classComponentWithSelector.className} }, ${classComponentWithSelector.decoratorName}> { }\n`; + } + } + codegen.text += `}\n`; + } + + return codegen; + } +} + +const fullCap: PositionCapabilities = { + hover: true, + references: true, + definition: true, + rename: true, + completion: true, + diagnostic: true, + semanticTokens: true, +}; + +export class Codegen { + + static validTsVar = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/; + + constructor(public sourceCode: string) { } + + public text = ''; + public mappings: Mapping[] = []; + + public addSourceText(start: number, end: number, data: PositionCapabilities = fullCap) { + this.mappings.push({ + sourceRange: [start, end], + generatedRange: [this.text.length, this.text.length + end - start], + data, + }); + const addText = this.sourceCode.substring(start, end); + this.text += addText; + return addText; + } + + public addPropertyAccess(start: number, end: number, data: PositionCapabilities = fullCap) { + if (Codegen.validTsVar.test(this.sourceCode.substring(start, end))) { + this.text += `.`; + this.addSourceText(start, end, data); + } + else { + this.text += `[`; + this.addSourceTextWithQuotes(start, end, data); + this.text += `]`; + } + } + + public addObjectKey(start: number, end: number, data: PositionCapabilities = fullCap) { + if (Codegen.validTsVar.test(this.sourceCode.substring(start, end))) { + this.addSourceText(start, end, data); + } + else { + this.addSourceTextWithQuotes(start, end, data); + } + } + + public addSourceTextWithQuotes(start: number, end: number, data: PositionCapabilities = fullCap) { + this.addSourceText(start, start, data); + this.text += `'`; + this.addSourceText(start, end, data); + this.text += `'`; + this.addSourceText(end, end, data); + } +} diff --git a/examples/angular-language-core/tsconfig.build.json b/examples/angular-language-core/tsconfig.build.json new file mode 100644 index 000000000..9d1438807 --- /dev/null +++ b/examples/angular-language-core/tsconfig.build.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "out", + "rootDir": "src", + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + ".vscode-test" + ], + "references": [ + { + "path": "../../packages/language-core/tsconfig.build.json" + } + ] +} \ No newline at end of file diff --git a/examples/angular-language-server/LICENSE b/examples/angular-language-server/LICENSE new file mode 100644 index 000000000..b55e47a7e --- /dev/null +++ b/examples/angular-language-server/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present Johnson Chu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/angular-language-server/bin/angular-language-server.js b/examples/angular-language-server/bin/angular-language-server.js new file mode 100755 index 000000000..d18b06411 --- /dev/null +++ b/examples/angular-language-server/bin/angular-language-server.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node +if (process.argv.includes("--version")) { + const pkgJSON = require("../package.json"); + console.log(`${pkgJSON["version"]}`); +} +else { + require("../out/index.js"); +} diff --git a/examples/angular-language-server/package.json b/examples/angular-language-server/package.json new file mode 100644 index 000000000..10e3e84a6 --- /dev/null +++ b/examples/angular-language-server/package.json @@ -0,0 +1,24 @@ +{ + "name": "@volar-examples/angular-language-server", + "version": "1.0.13", + "main": "out/index.js", + "license": "MIT", + "files": [ + "out/**/*.js", + "out/**/*.d.ts" + ], + "bin": { + "angular-language-server": "./bin/angular-language-server.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/johnsoncodehk/volar.git", + "directory": "examples/angular-language-server" + }, + "dependencies": { + "@volar-examples/angular-language-core": "1.0.13", + "@volar-plugins/typescript": "1.0.13", + "@volar/language-server": "1.0.13", + "@volar/shared": "1.0.13" + } +} diff --git a/examples/angular-language-server/src/index.ts b/examples/angular-language-server/src/index.ts new file mode 100644 index 000000000..7fc9b633a --- /dev/null +++ b/examples/angular-language-server/src/index.ts @@ -0,0 +1,64 @@ +import { createTsLanguageModule, createHtmlLanguageModule, HTMLTemplateFile } from '@volar-examples/angular-language-core'; +import createTsPlugin from '@volar-plugins/typescript'; +import { createLanguageServer, LanguageServerPlugin } from '@volar/language-server/node'; +import type { LanguageServicePlugin, SourceFileDocuments, Diagnostic } from '@volar/language-service'; + +const plugin: LanguageServerPlugin = () => ({ + extraFileExtensions: [{ extension: 'html', isMixedContent: true, scriptKind: 7 }], + semanticService: { + getLanguageModules(host) { + return [ + createTsLanguageModule(host.getTypeScriptModule()), + createHtmlLanguageModule(host.getTypeScriptModule()), + ]; + }, + getServicePlugins(_host, service) { + return [ + createTsPlugin(), + createNgTemplateLsPlugin(service.context.documents), + ]; + }, + }, + syntacticService: { + getLanguageModules(ts) { + return [ + createTsLanguageModule(ts), + createHtmlLanguageModule(ts), + ]; + }, + getServicePlugins() { + return [ + createTsPlugin(), + ]; + } + }, +}); + +function createNgTemplateLsPlugin(docs: SourceFileDocuments): LanguageServicePlugin { + + return { + + validation: { + + onSyntactic(document) { + + const file = docs.get(document.uri)?.file; + + if (file instanceof HTMLTemplateFile) { + return (file.parsed.errors ?? []).map(error => ({ + range: { + start: { line: error.span.start.line, character: error.span.start.col }, + end: { line: error.span.end.line, character: error.span.end.col }, + }, + severity: error.level === 1 ? 1 : 2, + source: 'ng-template', + message: error.msg, + })); + } + }, + } + }; +} + +createLanguageServer([plugin]); + diff --git a/examples/angular-language-server/tsconfig.build.json b/examples/angular-language-server/tsconfig.build.json new file mode 100644 index 000000000..f47b88dde --- /dev/null +++ b/examples/angular-language-server/tsconfig.build.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "out", + "rootDir": "src", + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + ".vscode-test" + ], + "references": [ + { + "path": "../../packages/language-server/tsconfig.build.json" + }, + { + "path": "../../plugins/typescript/tsconfig.build.json" + }, + { + "path": "../angular-language-core/tsconfig.build.json" + } + ] +} \ No newline at end of file diff --git a/examples/svelte-language-core/src/index.ts b/examples/svelte-language-core/src/index.ts index 55584b942..2b1b3b5ea 100644 --- a/examples/svelte-language-core/src/index.ts +++ b/examples/svelte-language-core/src/index.ts @@ -11,10 +11,19 @@ export const languageModule: LanguageModule = { if (fileName.endsWith('.svelte')) { const text = snapshot.getText(0, snapshot.getLength()); return { - snapshot, fileName, text, + kind: EmbeddedFileKind.TextFile, embeddeds: getEmbeddeds(fileName, text), + capabilities: { + diagnostic: true, + foldingRange: true, + documentFormatting: true, + documentSymbol: true, + codeAction: true, + inlayHint: true, + }, + mappings: [], }; } }, diff --git a/examples/vscode-angular/.vscodeignore b/examples/vscode-angular/.vscodeignore new file mode 100644 index 000000000..dc83992c1 --- /dev/null +++ b/examples/vscode-angular/.vscodeignore @@ -0,0 +1,5 @@ +out +scripts +src +tsconfig.build.json +tsconfig.build.tsbuildinfo diff --git a/examples/vscode-angular/LICENSE b/examples/vscode-angular/LICENSE new file mode 100644 index 000000000..b55e47a7e --- /dev/null +++ b/examples/vscode-angular/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present Johnson Chu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/vscode-angular/README.md b/examples/vscode-angular/README.md new file mode 100644 index 000000000..afe4f7d25 --- /dev/null +++ b/examples/vscode-angular/README.md @@ -0,0 +1,44 @@ +# Volar language server example for Angular + +⚠️⚠️⚠️⚠️⚠️ + +Don't use it in the projects you work on. + +It's not a production-ready tool, this is a quick implementation for demonstration purposes only, functionality reliability and correctness are not guaranteed, and there are no plan to maintenance this plugin. + +⚠️⚠️⚠️⚠️⚠️ + +## Features + +- Template AST error diagnosis +- Directives, Interpolations Syntax Highlighting +- Intellisense Support for Template Directives and Interpolations +- Component props and event types Support +- Type Narrowing Support for `*ngIf` if else block +- Only 500 KB and Fast + +## Usage + +We have only one stubborn way to setup (that has the best performance) because this is for demonstration purposes only. Please make sure you follow each step. + +1. Disable "TypeScript and JavaScript Language Features" to [takeover language support for .ts](https://vuejs.org/guide/typescript/overview.html#volar-takeover-mode) + + 1. Search `@builtin typescript-language-features` in Extensions sidebar + + 2. Right click and select "Disable (Workspace)" + +2. Disable "Angular Language Service" to avoid conflict + + 1. Search `Angular.ng-template` in Extensions sidebar + + 2. Right click and select "Disable (Workspace)" + +3. Create / Open `.vscode/settings.json` in workspace and put following setting to avoid "Vue Language Features (Volar)" auto active with takeover mode + + ```json + { + "volar.takeOverMode.enabled": false + } + ``` + +4. Reload VSCode diff --git a/examples/vscode-angular/client.js b/examples/vscode-angular/client.js new file mode 100644 index 000000000..a83481c85 --- /dev/null +++ b/examples/vscode-angular/client.js @@ -0,0 +1,3 @@ +let modulePath = './dist/node/client'; +try { modulePath = require.resolve('./out/client'); } catch { } +module.exports = require(modulePath); diff --git a/examples/vscode-angular/languages/angular-template-language-configuration.json b/examples/vscode-angular/languages/angular-template-language-configuration.json new file mode 100644 index 000000000..70fc48500 --- /dev/null +++ b/examples/vscode-angular/languages/angular-template-language-configuration.json @@ -0,0 +1,8 @@ +{ + "colorizedBracketPairs": [ + [ + "{{", + "}}" + ], + ] +} \ No newline at end of file diff --git a/examples/vscode-angular/package.json b/examples/vscode-angular/package.json new file mode 100644 index 000000000..bc302a05e --- /dev/null +++ b/examples/vscode-angular/package.json @@ -0,0 +1,97 @@ +{ + "private": true, + "name": "vscode-angular", + "version": "1.0.13", + "repository": { + "type": "git", + "url": "https://github.com/johnsoncodehk/volar.git", + "directory": "examples/vscode-angular" + }, + "displayName": "Angular (Volar)", + "description": "Angular (Volar)", + "author": "johnsoncodehk", + "publisher": "johnsoncodehk", + "engines": { + "vscode": "^1.67.0" + }, + "activationEvents": [ + "onLanguage:html", + "onLanguage:typescript" + ], + "main": "client", + "contributes": { + "languages": [ + { + "id": "angular-directives" + }, + { + "id": "angular-interpolations" + }, + { + "id": "html", + "configuration": "./languages/angular-template-language-configuration.json" + } + ], + "grammars": [ + { + "language": "angular-directives", + "scopeName": "text.html.angular.directives", + "path": "./syntaxes/angular-directives.json", + "injectTo": [ + "text.html.derivative" + ], + "embeddedLanguages": { + "source.ts": "typescript" + } + }, + { + "language": "angular-interpolations", + "scopeName": "angular.interpolations", + "path": "./syntaxes/angular-interpolations.json", + "injectTo": [ + "text.html.derivative" + ] + } + ], + "commands": [ + { + "command": "volar-angular.action.showVirtualFiles", + "title": "Show Virtual Files", + "category": "Volar-Angular (Debug)" + } + ], + "configuration": { + "type": "object", + "title": "Volar-Angular", + "properties": { + "volar-angular-language-server.trace.server": { + "scope": "window", + "type": "string", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "off", + "description": "Traces the communication between VS Code and the language server." + } + } + } + }, + "scripts": { + "prebuild": "cd ../.. && npm run build", + "build": "node scripts/build -- --minify", + "pack": "npm run build && vsce package --pre-release", + "release": "npm run build && vsce publish --pre-release" + }, + "devDependencies": { + "@types/vscode": "1.67.0", + "@volar-examples/angular-language-server": "1.0.13", + "@volar/language-server": "1.0.13", + "@volar/vscode-language-client": "1.0.13", + "esbuild": "latest", + "typesafe-path": "^0.2.2", + "vsce": "latest", + "vscode-languageclient": "^8.0.2" + } +} diff --git a/examples/vscode-angular/scripts/build.js b/examples/vscode-angular/scripts/build.js new file mode 100644 index 000000000..f37b53b00 --- /dev/null +++ b/examples/vscode-angular/scripts/build.js @@ -0,0 +1,31 @@ +require('esbuild').build({ + entryPoints: { + client: './out/client.js', + server: './node_modules/@volar-examples/angular-language-server/bin/angular-language-server.js', + }, + bundle: true, + metafile: process.argv.includes('--metafile'), + outdir: './dist/node', + external: [ + 'vscode', + ], + format: 'cjs', + platform: 'node', + tsconfig: '../../tsconfig.build.json', + define: { 'process.env.NODE_ENV': '"production"' }, + minify: process.argv.includes('--minify'), + watch: process.argv.includes('--watch'), + plugins: [ + { + name: 'umd2esm', + setup(build) { + build.onResolve({ filter: /^(vscode-.*|estree-walker|jsonc-parser)/ }, args => { + const pathUmdMay = require.resolve(args.path, { paths: [args.resolveDir] }) + // Call twice the replace is to solve the problem of the path in Windows + const pathEsm = pathUmdMay.replace('/umd/', '/esm/').replace('\\umd\\', '\\esm\\') + return { path: pathEsm } + }) + }, + }, + ], +}).catch(() => process.exit(1)) diff --git a/examples/vscode-angular/server.js b/examples/vscode-angular/server.js new file mode 100644 index 000000000..81afbfc21 --- /dev/null +++ b/examples/vscode-angular/server.js @@ -0,0 +1,3 @@ +let modulePath = './dist/node/server'; +try { modulePath = require.resolve('@volar/angular-language-server/bin/angular-language-server'); } catch { } +module.exports = require(modulePath); diff --git a/examples/vscode-angular/src/client.ts b/examples/vscode-angular/src/client.ts new file mode 100644 index 000000000..3430f6b04 --- /dev/null +++ b/examples/vscode-angular/src/client.ts @@ -0,0 +1,83 @@ +import type { LanguageServerInitializationOptions } from '@volar/language-server'; +import * as path from 'typesafe-path'; +import * as vscode from 'vscode'; +import * as lsp from 'vscode-languageclient/node'; +import { + registerShowVirtualFiles, + registerTsConfig, + registerTsVersion, +} from '@volar/vscode-language-client'; +import * as os from 'os'; +import * as fs from 'fs'; + +let client: lsp.BaseLanguageClient; + +export async function activate(context: vscode.ExtensionContext) { + + const cancellationPipeName = path.join(os.tmpdir() as path.OsPath, `vscode-${context.extension.id}-cancellation-pipe.tmp` as path.PosixPath); + const isSupportDoc = (document: vscode.TextDocument) => documentSelector.some(selector => selector.language === document.languageId); + let cancellationPipeUpdateKey: string | undefined; + + vscode.workspace.onDidChangeTextDocument((e) => { + let key = e.document.uri.toString() + '|' + e.document.version; + if (cancellationPipeUpdateKey === undefined) { + cancellationPipeUpdateKey = key; + return; + } + if (isSupportDoc(e.document) && cancellationPipeUpdateKey !== key) { + cancellationPipeUpdateKey = key; + fs.writeFileSync(cancellationPipeName, ''); + } + }); + + const documentSelector: lsp.DocumentFilter[] = [ + { language: 'html' }, + { language: 'typescript' }, + ]; + const initializationOptions: LanguageServerInitializationOptions = { + typescript: { + tsdk: path.join( + vscode.env.appRoot as path.OsPath, + 'extensions/node_modules/typescript/lib' as path.PosixPath, + ), + }, + cancellationPipeName, + noProjectReferences: true, + // @ts-expect-error + __noPluginCommands: true, + }; + const serverModule = vscode.Uri.joinPath(context.extensionUri, 'server.js'); + const runOptions = { execArgv: [] }; + const debugOptions = { execArgv: ['--nolazy', '--inspect=' + 6009] }; + const serverOptions: lsp.ServerOptions = { + run: { + module: serverModule.fsPath, + transport: lsp.TransportKind.ipc, + options: runOptions + }, + debug: { + module: serverModule.fsPath, + transport: lsp.TransportKind.ipc, + options: debugOptions + }, + }; + const clientOptions: lsp.LanguageClientOptions = { + documentSelector, + initializationOptions, + }; + client = new lsp.LanguageClient( + 'volar-angular-language-server', + 'Angular (Volar Example)', + serverOptions, + clientOptions, + ); + await client.start(); + + registerShowVirtualFiles('volar-angular.action.showVirtualFiles', context, client); + registerTsConfig('volar-angular.action.showTsConfig', context, client, isSupportDoc); + registerTsVersion('volar-angular.action.showTsVersion', context, client, isSupportDoc, text => text + ' (angular)'); +} + +export function deactivate(): Thenable | undefined { + return client?.stop(); +} diff --git a/examples/vscode-angular/syntaxes/angular-directives.json b/examples/vscode-angular/syntaxes/angular-directives.json new file mode 100644 index 000000000..9286ddf43 --- /dev/null +++ b/examples/vscode-angular/syntaxes/angular-directives.json @@ -0,0 +1,123 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "fileTypes": [ ], + "injectionSelector": "L:meta.tag -meta.attribute -source.tsx -source.js.jsx, L:meta.element -meta.attribute", + "patterns": [ + { + "include": "#directives" + } + ], + "repository": { + "directives": { + "begin": "((\\*?)(\\[)((ngIf)|(ngFor)|([^\\]]*))(\\]))|((\\*)((ngIf)|(ngFor)|([\\w-]*)))|((\\()(([^\\)]*))(\\)))", + "beginCaptures": { + "2": { + "name": "punctuation.separator.key-value.html.angular" + }, + "3": { + "name": "punctuation.separator.key-value.html.angular" + }, + "5": { + "name": "keyword.control.conditional.angular" + }, + "6": { + "name": "keyword.control.loop.angular" + }, + "7": { + "name": "entity.other.attribute-name.html.angular" + }, + "8": { + "name": "punctuation.separator.key-value.html.angular" + }, + "10": { + "name": "punctuation.separator.key-value.html.angular" + }, + "12": { + "name": "keyword.control.conditional.angular" + }, + "13": { + "name": "keyword.control.loop.angular" + }, + "14": { + "name": "entity.other.attribute-name.html.angular" + }, + "16": { + "name": "punctuation.separator.key-value.html.angular" + }, + "17": { + "name": "entity.other.attribute-name.html.angular" + }, + "18": { + "name": "punctuation.separator.key-value.html.angular" + } + }, + "end": "(?=\\s*+[^=\\s])", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.html.angular" + } + }, + "name": "meta.attribute.directive.angular", + "patterns": [ + { + "include": "#directives-expression" + } + ] + }, + "directives-expression": { + "patterns": [ + { + "begin": "(=)\\s*('|\"|`)", + "beginCaptures": { + "1": { + "name": "punctuation.separator.key-value.html.angular" + }, + "2": { + "name": "punctuation.definition.string.begin.html.angular" + } + }, + "end": "(\\2)", + "endCaptures": { + "1": { + "name": "punctuation.definition.string.end.html.angular" + } + }, + "patterns": [ + { + "begin": "(?<=('|\"|`))", + "end": "(?=\\1)", + "name": "source.ts.embedded.html.angular", + "patterns": [ + { + "include": "source.ts" + } + ] + } + ] + }, + { + "begin": "(=)\\s*(?=[^'\"`])", + "beginCaptures": { + "1": { + "name": "punctuation.separator.key-value.html.angular" + } + }, + "end": "(?=(\\s|>|\\/>))", + "patterns": [ + { + "begin": "(?=[^'\"`])", + "end": "(?=(\\s|>|\\/>))", + "name": "source.ts.embedded.html.angular", + "patterns": [ + { + "include": "source.ts" + } + ] + } + ] + } + ] + } + }, + "scopeName": "text.html.angular.directives" +} \ No newline at end of file diff --git a/examples/vscode-angular/syntaxes/angular-interpolations.json b/examples/vscode-angular/syntaxes/angular-interpolations.json new file mode 100644 index 000000000..44be88b68 --- /dev/null +++ b/examples/vscode-angular/syntaxes/angular-interpolations.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "fileTypes": [], + "injectionSelector": "L:text.pug -comment -string.comment, L:text.html.derivative -comment.block, L:text.html.markdown -comment.block", + "patterns": [ + { + "include": "#angular-interpolations" + } + ], + "repository": { + "angular-interpolations": { + "patterns": [ + { + "begin": "\\{\\{", + "beginCaptures": [ + { + "name": "punctuation.definition.tag.begin.html.angular" + } + ], + "end": "\\}\\}", + "endCaptures": [ + { + "name": "punctuation.definition.tag.end.html.angular" + } + ], + "name": "expression.embbeded.angular", + "patterns": [ + { + "begin": "\\G", + "end": "(?=\\}\\})", + "name": "source.ts.embedded.html.angular", + "patterns": [ + { + "include": "source.ts" + } + ] + } + ] + } + ] + } + }, + "scopeName": "angular.interpolations" +} \ No newline at end of file diff --git a/examples/vscode-angular/tsconfig.build.json b/examples/vscode-angular/tsconfig.build.json new file mode 100644 index 000000000..3f29db9bf --- /dev/null +++ b/examples/vscode-angular/tsconfig.build.json @@ -0,0 +1,24 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "out", + "rootDir": "src", + }, + "include": [ + "src", + "src/**/*.json" + ], + "exclude": [ + "node_modules", + ".vscode-test" + ], + "references": [ + { + "path": "../../packages/vscode-language-client/tsconfig.build.json" + }, + { + "path": "../angular-language-server/tsconfig.build.json" + } + ] +} \ No newline at end of file diff --git a/examples/vscode-svelte/package.json b/examples/vscode-svelte/package.json index 0c1ae1291..6bd3535ea 100644 --- a/examples/vscode-svelte/package.json +++ b/examples/vscode-svelte/package.json @@ -1,11 +1,11 @@ { "private": true, - "name": "example-vscode-svelte", + "name": "vscode-svelte", "version": "1.0.13", "repository": { "type": "git", "url": "https://github.com/johnsoncodehk/volar.git", - "directory": "examples/svelte" + "directory": "examples/vscode-svelte" }, "displayName": "Svelte Language Server (Example)", "description": "Svelte Language Server Example", diff --git a/examples/vscode-svelte/src/client.ts b/examples/vscode-svelte/src/client.ts index c8b9b8952..fa0fe2c0a 100644 --- a/examples/vscode-svelte/src/client.ts +++ b/examples/vscode-svelte/src/client.ts @@ -35,9 +35,6 @@ export async function activate(context: vscode.ExtensionContext) { const clientOptions: lsp.LanguageClientOptions = { documentSelector, initializationOptions, - synchronize: { - fileEvents: vscode.workspace.createFileSystemWatcher('{**/*.svelte,**/*.js,**/*.jsx,**/*.ts,**/*.tsx,**/*.json}') - }, middleware: { workspace: { configuration(params, token, next) { diff --git a/extensions/vscode-vue-language-features/src/common.ts b/extensions/vscode-vue-language-features/src/common.ts index 9db8cc15e..f32cd454c 100644 --- a/extensions/vscode-vue-language-features/src/common.ts +++ b/extensions/vscode-vue-language-features/src/common.ts @@ -132,8 +132,15 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang && ['javascript', 'typescript', 'javascriptreact', 'typescriptreact'].includes(document.languageId) ); }, - () => takeOverModeEnabled(), - () => noProjectReferences(), + text => { + if (takeOverModeEnabled()) { + text += ' (vue)'; + } + if (noProjectReferences()) { + text += ' (noProjectReferences)'; + } + return text; + }, ); for (const client of clients) { diff --git a/packages/language-core/src/documentRegistry.ts b/packages/language-core/src/documentRegistry.ts index 40b6b6293..d167f53e6 100644 --- a/packages/language-core/src/documentRegistry.ts +++ b/packages/language-core/src/documentRegistry.ts @@ -3,12 +3,10 @@ import { computed, shallowReactive } from '@vue/reactivity'; import { Teleport } from './sourceMaps'; import type { EmbeddedFile, LanguageModule, SourceFile } from './types'; -export function forEachEmbeddeds(input: EmbeddedFile[], cb: (embedded: EmbeddedFile) => void) { - for (const child of input) { - if (child) { - cb(child); - } - forEachEmbeddeds(child.embeddeds, cb); +export function forEachEmbeddeds(file: EmbeddedFile, cb: (embedded: EmbeddedFile) => void) { + cb(file); + for (const child of file.embeddeds) { + forEachEmbeddeds(child, cb); } } @@ -22,7 +20,7 @@ export function createDocumentRegistry() { const embeddedDocumentsMap = computed(() => { const map = new WeakMap(); for (const [sourceFile] of all.value) { - forEachEmbeddeds(sourceFile.embeddeds, embedded => { + forEachEmbeddeds(sourceFile, embedded => { map.set(embedded, sourceFile); }); } @@ -31,7 +29,7 @@ export function createDocumentRegistry() { const sourceMapsByFileName = computed(() => { const map = new Map(); for (const [sourceFile] of all.value) { - forEachEmbeddeds(sourceFile.embeddeds, embedded => { + forEachEmbeddeds(sourceFile, embedded => { map.set(normalizePath(embedded.fileName), { sourceFile, embedded }); }); } @@ -41,7 +39,7 @@ export function createDocumentRegistry() { const map = new Map(); for (const key in files) { const [sourceFile] = files[key]!; - forEachEmbeddeds(sourceFile.embeddeds, embedded => { + forEachEmbeddeds(sourceFile, embedded => { if (embedded.teleportMappings) { map.set(normalizePath(embedded.fileName), getTeleport(sourceFile, embedded.teleportMappings)); } diff --git a/packages/language-core/src/languageContext.ts b/packages/language-core/src/languageContext.ts index f21c727b2..49b82f12f 100644 --- a/packages/language-core/src/languageContext.ts +++ b/packages/language-core/src/languageContext.ts @@ -199,7 +199,7 @@ export function createEmbeddedLanguageServiceHost( for (const [sourceFile, languageModule, snapshot] of sourceFilesToUpdate) { - forEachEmbeddeds(sourceFile.embeddeds, embedded => { + forEachEmbeddeds(sourceFile, embedded => { fileVersions.delete(embedded.fileName); }); @@ -207,7 +207,7 @@ export function createEmbeddedLanguageServiceHost( const newScripts: Record = {}; if (!tsFileUpdated) { - forEachEmbeddeds(sourceFile.embeddeds, embedded => { + forEachEmbeddeds(sourceFile, embedded => { if (embedded.kind) { oldScripts[embedded.fileName] = embedded.text; } @@ -218,7 +218,7 @@ export function createEmbeddedLanguageServiceHost( documentRegistry.onSourceFileUpdated(sourceFile); if (!tsFileUpdated) { - forEachEmbeddeds(sourceFile.embeddeds, embedded => { + forEachEmbeddeds(sourceFile, embedded => { if (embedded.kind) { newScripts[embedded.fileName] = embedded.text; } diff --git a/packages/language-core/src/types.ts b/packages/language-core/src/types.ts index 124642e0a..d9aae3531 100644 --- a/packages/language-core/src/types.ts +++ b/packages/language-core/src/types.ts @@ -48,10 +48,8 @@ export interface TextRange { end: number, } -export interface SourceFile { - fileName: string, - text: string, - embeddeds: EmbeddedFile[], +export interface SourceFile extends EmbeddedFile { + // TODO: snapshot } export enum EmbeddedFileKind { diff --git a/packages/language-server/src/features/customFeatures.ts b/packages/language-server/src/features/customFeatures.ts index 6737e7e82..413d1a887 100644 --- a/packages/language-server/src/features/customFeatures.ts +++ b/packages/language-server/src/features/customFeatures.ts @@ -77,7 +77,7 @@ export function register( if (project) { const sourceFile = project.project?.getLanguageService().context.core.mapper.get(shared.getPathOfUri(document.uri))?.[0]; if (sourceFile) { - forEachEmbeddeds(sourceFile.embeddeds, e => { + forEachEmbeddeds(sourceFile, e => { if (e.text && e.kind === 1) { fileNames.push(e.fileName); } diff --git a/packages/language-server/src/registerFeatures.ts b/packages/language-server/src/registerFeatures.ts index 3fbe888a4..e1cc1df4d 100644 --- a/packages/language-server/src/registerFeatures.ts +++ b/packages/language-server/src/registerFeatures.ts @@ -170,12 +170,11 @@ export function setupSemanticCapabilities( server.codeLensProvider = { resolveProvider: true, }; - server.executeCommandProvider = { - commands: [ - ...server.executeCommandProvider?.commands ?? [], - embedded.executePluginCommand, - ] - }; + server.executeCommandProvider = { commands: [...server.executeCommandProvider?.commands ?? []] }; + // @ts-expect-error + if (!initOptions.__noPluginCommands) { + server.executeCommandProvider.commands.push(embedded.executePluginCommand); + } } if (!initOptions.respectClientCapabilities || params.textDocument?.semanticTokens) { server.semanticTokensProvider = { diff --git a/packages/language-service/src/documents.ts b/packages/language-service/src/documents.ts index 87ca00540..299a20efb 100644 --- a/packages/language-service/src/documents.ts +++ b/packages/language-service/src/documents.ts @@ -252,20 +252,20 @@ export function parseSourceFileDocument(sourceFile: SourceFile) { // computed const document = computed(() => TextDocument.create( shared.getUriByPath(sourceFile.fileName), - sourceFile.fileName.endsWith('.md') ? 'markdown' : 'vue', + shared.syntaxToLanguageId(sourceFile.fileName.slice(sourceFile.fileName.lastIndexOf('.') + 1)), documentVersion++, sourceFile.text, )); const allSourceMaps = computed(() => { const result: EmbeddedDocumentSourceMap[] = []; - forEachEmbeddeds(sourceFile.embeddeds, embedded => { + forEachEmbeddeds(sourceFile, embedded => { result.push(getSourceMap(embedded)); }); return result; }); const teleports = computed(() => { const result: TeleportSourceMap[] = []; - forEachEmbeddeds(sourceFile.embeddeds, embedded => { + forEachEmbeddeds(sourceFile, embedded => { if (embedded.teleportMappings) { const embeddedDocument = getEmbeddedDocument(embedded)!; const sourceMap = new TeleportSourceMap( diff --git a/packages/language-service/src/languageFeatures/fileRename.ts b/packages/language-service/src/languageFeatures/fileRename.ts index 4163f8361..b139a22af 100644 --- a/packages/language-service/src/languageFeatures/fileRename.ts +++ b/packages/language-service/src/languageFeatures/fileRename.ts @@ -14,7 +14,7 @@ export function register(context: LanguageServiceRuntimeContext) { let tsExt: string | undefined; - forEachEmbeddeds(vueDocument.file.embeddeds, embedded => { + forEachEmbeddeds(vueDocument.file, embedded => { if (embedded.kind && embedded.fileName.replace(vueDocument.file.fileName, '').match(/^\.(js|ts)x?$/)) { tsExt = embedded.fileName.substring(embedded.fileName.lastIndexOf('.')); } diff --git a/packages/language-service/src/types.ts b/packages/language-service/src/types.ts index 151190941..37fcff76f 100644 --- a/packages/language-service/src/types.ts +++ b/packages/language-service/src/types.ts @@ -7,6 +7,8 @@ import type { SchemaRequestService } from 'vscode-json-languageservice'; import { URI } from 'vscode-uri'; import type * as vscode from 'vscode-languageserver-protocol'; +export * from 'vscode-languageserver-protocol'; + export interface DocumentServiceRuntimeContext { typescript: typeof import('typescript/lib/tsserverlibrary'); plugins: LanguageServicePlugin[]; diff --git a/packages/language-service/src/utils/definePlugin.ts b/packages/language-service/src/utils/definePlugin.ts index b81e75fcd..df51ca712 100644 --- a/packages/language-service/src/utils/definePlugin.ts +++ b/packages/language-service/src/utils/definePlugin.ts @@ -3,20 +3,20 @@ import { EmbeddedDocumentSourceMap, SourceFileDocument } from '../documents'; export async function visitEmbedded( vueDocument: SourceFileDocument, cb: (sourceMap: EmbeddedDocumentSourceMap) => Promise, - embeddeds = vueDocument.file.embeddeds, + current = vueDocument.file, ) { - for (const embedded of embeddeds) { + for (const embedded of current.embeddeds) { - if (!await visitEmbedded(vueDocument, cb, embedded.embeddeds)) { + if (!await visitEmbedded(vueDocument, cb, embedded)) { return false; } + } - const sourceMap = vueDocument.getSourceMap(embedded); + const sourceMap = vueDocument.getSourceMap(current); - if (!await cb(sourceMap)) { - return false; - } + if (!await cb(sourceMap)) { + return false; } return true; diff --git a/packages/language-service/src/utils/featureWorkers.ts b/packages/language-service/src/utils/featureWorkers.ts index f3b3a8711..62c34f7d5 100644 --- a/packages/language-service/src/utils/featureWorkers.ts +++ b/packages/language-service/src/utils/featureWorkers.ts @@ -72,8 +72,7 @@ export async function documentArgFeatureWorker( return true; }); } - - if (results.length === 0 || !!combineResult) { + else if (results.length === 0 || !!combineResult) { context.prepareLanguageServices(document); @@ -149,8 +148,7 @@ export async function languageFeatureWorker( return true; }); } - - if (document && (results.length === 0 || !!combineResult)) { + else if (document && (results.length === 0 || !!combineResult)) { for (const plugin of context.plugins) { diff --git a/packages/typescript/src/index.ts b/packages/typescript/src/index.ts index 691ab3238..6c827b0c6 100644 --- a/packages/typescript/src/index.ts +++ b/packages/typescript/src/index.ts @@ -49,7 +49,7 @@ export function createLanguageService(host: embedded.LanguageServiceHost, mods: const file = core.mapper.get(args.fileName); let edits: readonly ts.FileTextChanges[] = []; if (file) { - embedded.forEachEmbeddeds(file[0].embeddeds, embedded => { + embedded.forEachEmbeddeds(file[0], embedded => { if (embedded.kind && embedded.capabilities.codeAction) { edits = edits.concat(ls.organizeImports({ ...args, diff --git a/packages/vscode-language-client/src/features/tsVersion.ts b/packages/vscode-language-client/src/features/tsVersion.ts index 73f59bea3..fe850502c 100644 --- a/packages/vscode-language-client/src/features/tsVersion.ts +++ b/packages/vscode-language-client/src/features/tsVersion.ts @@ -12,8 +12,7 @@ export async function register( context: vscode.ExtensionContext, client: BaseLanguageClient, shouldStatusBarShow: (document: vscode.TextDocument) => boolean, - takeOverModeEnabled: () => boolean, - noProjectReferences: () => boolean, + resolveStatusText: (text: string) => string, ) { const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right); @@ -52,9 +51,9 @@ export async function register( } : undefined, }, { - takeover: takeOverModeEnabled() ? { + takeover: { label: 'What is Takeover Mode?', - } : undefined, + }, } ]); @@ -92,12 +91,7 @@ export async function register( else { const tsVersion = await getTsVersion(getTsdk(context).uri); statusBar.text = tsVersion ?? 'x.x.x'; - if (takeOverModeEnabled()) { - statusBar.text += ' (takeover)'; - } - if (noProjectReferences()) { - statusBar.text += ' (noProjectReferences)'; - } + statusBar.text = resolveStatusText(statusBar.text); statusBar.show(); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c2aaff57..f65ac48bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,13 +15,40 @@ importers: optionalDependencies: '@lerna-lite/cli': 1.13.0 '@vscode/test-web': 0.0.33 - vitepress: 1.0.0-alpha.31_@types+node@18.11.13 + vitepress: 1.0.0-alpha.32_@types+node@18.11.15 vue: 3.2.45 devDependencies: - '@types/node': 18.11.13 + '@types/node': 18.11.15 typescript: 4.9.4 - vite: 4.0.0_@types+node@18.11.13 - vitest: 0.25.7 + vite: 4.0.1_@types+node@18.11.15 + vitest: 0.25.8 + + examples/angular-language-core: + specifiers: + '@angular-eslint/bundled-angular-compiler': ^15.1.0 + '@angular/compiler': ^15.0.4 + '@volar/language-core': 1.0.13 + vscode-languageserver-textdocument: ^1.0.7 + vscode-uri: ^3.0.6 + dependencies: + '@angular-eslint/bundled-angular-compiler': 15.1.0 + '@volar/language-core': link:../../packages/language-core + vscode-languageserver-textdocument: 1.0.7 + vscode-uri: 3.0.6 + devDependencies: + '@angular/compiler': 15.0.4 + + examples/angular-language-server: + specifiers: + '@volar-examples/angular-language-core': 1.0.13 + '@volar-plugins/typescript': 1.0.13 + '@volar/language-server': 1.0.13 + '@volar/shared': 1.0.13 + dependencies: + '@volar-examples/angular-language-core': link:../angular-language-core + '@volar-plugins/typescript': link:../../plugins/typescript + '@volar/language-server': link:../../packages/language-server + '@volar/shared': link:../../packages/shared examples/svelte-language-core: specifiers: @@ -65,6 +92,26 @@ importers: '@volar-examples/svelte-language-core': link:../svelte-language-core '@volar/typescript': link:../../packages/typescript + examples/vscode-angular: + specifiers: + '@types/vscode': 1.67.0 + '@volar-examples/angular-language-server': 1.0.13 + '@volar/language-server': 1.0.13 + '@volar/vscode-language-client': 1.0.13 + esbuild: latest + typesafe-path: ^0.2.2 + vsce: latest + vscode-languageclient: ^8.0.2 + devDependencies: + '@types/vscode': 1.67.0 + '@volar-examples/angular-language-server': link:../angular-language-server + '@volar/language-server': link:../../packages/language-server + '@volar/vscode-language-client': link:../../packages/vscode-language-client + esbuild: 0.16.7 + typesafe-path: 0.2.2 + vsce: 2.15.0 + vscode-languageclient: 8.0.2 + examples/vscode-svelte: specifiers: '@types/vscode': 1.67.0 @@ -105,7 +152,7 @@ importers: dependencies: typescript-vue-plugin-forward: file:extensions/vscode-typescript-vue-plugin/typescript-vue-plugin-forward devDependencies: - esbuild: 0.16.4 + esbuild: 0.16.7 typescript-vue-plugin: link:../../vue-language-tools/typescript-vue-plugin vsce: 2.15.0 @@ -138,8 +185,8 @@ importers: '@volar/vscode-language-client': link:../../packages/vscode-language-client '@volar/vue-language-core': link:../../vue-language-tools/vue-language-core '@volar/vue-language-server': link:../../vue-language-tools/vue-language-server - esbuild: 0.16.4 - esbuild-plugin-copy: 2.0.1_esbuild@0.16.4 + esbuild: 0.16.7 + esbuild-plugin-copy: 2.0.1_esbuild@0.16.7 esbuild-visualizer: 0.4.0 path-browserify: 1.0.1 punycode: 2.1.1 @@ -644,6 +691,22 @@ packages: dev: false optional: true + /@angular-eslint/bundled-angular-compiler/15.1.0: + resolution: {integrity: sha512-zcOx+PnYuVDIG3wd/JVzCYdEUarKGtgIcN4iU9ZF+BVk5e8i9cbD3U8U3EDJKbrrokbFl9GBBJMCOa6XYTGJwQ==} + dev: false + + /@angular/compiler/15.0.4: + resolution: {integrity: sha512-KtxgRJUGZamOXpIILFG2FTUW+bbc2phi/o6955/Q4LR1HOICQrYEy8PrT1Gp+lVXFKgDG+6cb01lH14LoBQvyw==} + engines: {node: ^14.20.0 || ^16.13.0 || >=18.10.0} + peerDependencies: + '@angular/core': 15.0.4 + peerDependenciesMeta: + '@angular/core': + optional: true + dependencies: + tslib: 2.4.0 + dev: true + /@babel/code-frame/7.18.6: resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} engines: {node: '>=6.9.0'} @@ -742,176 +805,176 @@ packages: resolution: {integrity: sha512-8HqW8EVqjnCmWXVpqAOZf+EGESdkR27odcMMMGefgKXtar00SoYNSryGv//TELI4T3QFsECo78p+0lmalk/CFA==} dev: false - /@esbuild/android-arm/0.16.4: - resolution: {integrity: sha512-rZzb7r22m20S1S7ufIc6DC6W659yxoOrl7sKP1nCYhuvUlnCFHVSbATG4keGUtV8rDz11sRRDbWkvQZpzPaHiw==} + /@esbuild/android-arm/0.16.7: + resolution: {integrity: sha512-yhzDbiVcmq6T1/XEvdcJIVcXHdLjDJ5cQ0Dp9R9p9ERMBTeO1dR5tc8YYv8zwDeBw1xZm+Eo3MRo8cwclhBS0g==} engines: {node: '>=12'} cpu: [arm] os: [android] requiresBuild: true optional: true - /@esbuild/android-arm64/0.16.4: - resolution: {integrity: sha512-VPuTzXFm/m2fcGfN6CiwZTlLzxrKsWbPkG7ArRFpuxyaHUm/XFHQPD4xNwZT6uUmpIHhnSjcaCmcla8COzmZ5Q==} + /@esbuild/android-arm64/0.16.7: + resolution: {integrity: sha512-tYFw0lBJSEvLoGzzYh1kXuzoX1iPkbOk3O29VqzQb0HbOy7t/yw1hGkvwoJhXHwzQUPsShyYcTgRf6bDBcfnTw==} engines: {node: '>=12'} cpu: [arm64] os: [android] requiresBuild: true optional: true - /@esbuild/android-x64/0.16.4: - resolution: {integrity: sha512-MW+B2O++BkcOfMWmuHXB15/l1i7wXhJFqbJhp82IBOais8RBEQv2vQz/jHrDEHaY2X0QY7Wfw86SBL2PbVOr0g==} + /@esbuild/android-x64/0.16.7: + resolution: {integrity: sha512-3P2OuTxwAtM3k/yEWTNUJRjMPG1ce8rXs51GTtvEC5z1j8fC1plHeVVczdeHECU7aM2/Buc0MwZ6ciM/zysnWg==} engines: {node: '>=12'} cpu: [x64] os: [android] requiresBuild: true optional: true - /@esbuild/darwin-arm64/0.16.4: - resolution: {integrity: sha512-a28X1O//aOfxwJVZVs7ZfM8Tyih2Za4nKJrBwW5Wm4yKsnwBy9aiS/xwpxiiTRttw3EaTg4Srerhcm6z0bu9Wg==} + /@esbuild/darwin-arm64/0.16.7: + resolution: {integrity: sha512-VUb9GK23z8jkosHU9yJNUgQpsfJn+7ZyBm6adi2Ec5/U241eR1tAn82QicnUzaFDaffeixiHwikjmnec/YXEZg==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] requiresBuild: true optional: true - /@esbuild/darwin-x64/0.16.4: - resolution: {integrity: sha512-e3doCr6Ecfwd7VzlaQqEPrnbvvPjE9uoTpxG5pyLzr2rI2NMjDHmvY1E5EO81O/e9TUOLLkXA5m6T8lfjK9yAA==} + /@esbuild/darwin-x64/0.16.7: + resolution: {integrity: sha512-duterlv3tit3HI9vhzMWnSVaB1B6YsXpFq1Ntd6Fou82BB1l4tucYy3FI9dHv3tvtDuS0NiGf/k6XsdBqPZ01w==} engines: {node: '>=12'} cpu: [x64] os: [darwin] requiresBuild: true optional: true - /@esbuild/freebsd-arm64/0.16.4: - resolution: {integrity: sha512-Oup3G/QxBgvvqnXWrBed7xxkFNwAwJVHZcklWyQt7YCAL5bfUkaa6FVWnR78rNQiM8MqqLiT6ZTZSdUFuVIg1w==} + /@esbuild/freebsd-arm64/0.16.7: + resolution: {integrity: sha512-9kkycpBFes/vhi7B7o0cf+q2WdJi+EpVzpVTqtWFNiutARWDFFLcB93J8PR1cG228sucsl3B+7Ts27izE6qiaQ==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] requiresBuild: true optional: true - /@esbuild/freebsd-x64/0.16.4: - resolution: {integrity: sha512-vAP+eYOxlN/Bpo/TZmzEQapNS8W1njECrqkTpNgvXskkkJC2AwOXwZWai/Kc2vEFZUXQttx6UJbj9grqjD/+9Q==} + /@esbuild/freebsd-x64/0.16.7: + resolution: {integrity: sha512-5Ahf6jzWXJ4J2uh9dpy5DKOO+PeRUE/9DMys6VuYfwgQzd6n5+pVFm58L2Z2gRe611RX6SdydnNaiIKM3svY7g==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] requiresBuild: true optional: true - /@esbuild/linux-arm/0.16.4: - resolution: {integrity: sha512-A47ZmtpIPyERxkSvIv+zLd6kNIOtJH03XA0Hy7jaceRDdQaQVGSDt4mZqpWqJYgDk9rg96aglbF6kCRvPGDSUA==} + /@esbuild/linux-arm/0.16.7: + resolution: {integrity: sha512-QqJnyCfu5OF78Olt7JJSZ7OSv/B4Hf+ZJWp4kkq9xwMsgu7yWq3crIic8gGOpDYTqVKKMDAVDgRXy5Wd/nWZyQ==} engines: {node: '>=12'} cpu: [arm] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-arm64/0.16.4: - resolution: {integrity: sha512-2zXoBhv4r5pZiyjBKrOdFP4CXOChxXiYD50LRUU+65DkdS5niPFHbboKZd/c81l0ezpw7AQnHeoCy5hFrzzs4g==} + /@esbuild/linux-arm64/0.16.7: + resolution: {integrity: sha512-2wv0xYDskk2+MzIm/AEprDip39a23Chptc4mL7hsHg26P0gD8RUhzmDu0KCH2vMThUI1sChXXoK9uH0KYQKaDg==} engines: {node: '>=12'} cpu: [arm64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-ia32/0.16.4: - resolution: {integrity: sha512-uxdSrpe9wFhz4yBwt2kl2TxS/NWEINYBUFIxQtaEVtglm1eECvsj1vEKI0KX2k2wCe17zDdQ3v+jVxfwVfvvjw==} + /@esbuild/linux-ia32/0.16.7: + resolution: {integrity: sha512-APVYbEilKbD5ptmKdnIcXej2/+GdV65TfTjxR2Uk8t1EsOk49t6HapZW6DS/Bwlvh5hDwtLapdSumIVNGxgqLg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-loong64/0.16.4: - resolution: {integrity: sha512-peDrrUuxbZ9Jw+DwLCh/9xmZAk0p0K1iY5d2IcwmnN+B87xw7kujOkig6ZRcZqgrXgeRGurRHn0ENMAjjD5DEg==} + /@esbuild/linux-loong64/0.16.7: + resolution: {integrity: sha512-5wPUAGclplQrAW7EFr3F84Y/d++7G0KykohaF4p54+iNWhUnMVU8Bh2sxiEOXUy4zKIdpHByMgJ5/Ko6QhtTUw==} engines: {node: '>=12'} cpu: [loong64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-mips64el/0.16.4: - resolution: {integrity: sha512-sD9EEUoGtVhFjjsauWjflZklTNr57KdQ6xfloO4yH1u7vNQlOfAlhEzbyBKfgbJlW7rwXYBdl5/NcZ+Mg2XhQA==} + /@esbuild/linux-mips64el/0.16.7: + resolution: {integrity: sha512-hxzlXtWF6yWfkE/SMTscNiVqLOAn7fOuIF3q/kiZaXxftz1DhZW/HpnTmTTWrzrS7zJWQxHHT4QSxyAj33COmA==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-ppc64/0.16.4: - resolution: {integrity: sha512-X1HSqHUX9D+d0l6/nIh4ZZJ94eQky8d8z6yxAptpZE3FxCWYWvTDd9X9ST84MGZEJx04VYUD/AGgciddwO0b8g==} + /@esbuild/linux-ppc64/0.16.7: + resolution: {integrity: sha512-WM83Dac0LdXty5xPhlOuCD5Egfk1xLND/oRLYeB7Jb/tY4kzFSDgLlq91wYbHua/s03tQGA9iXvyjgymMw62Vw==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-riscv64/0.16.4: - resolution: {integrity: sha512-97ANpzyNp0GTXCt6SRdIx1ngwncpkV/z453ZuxbnBROCJ5p/55UjhbaG23UdHj88fGWLKPFtMoU4CBacz4j9FA==} + /@esbuild/linux-riscv64/0.16.7: + resolution: {integrity: sha512-3nkNnNg4Ax6MS/l8O8Ynq2lGEVJYyJ2EoY3PHjNJ4PuZ80EYLMrFTFZ4L/Hc16AxgtXKwmNP9TM0YKNiBzBiJQ==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-s390x/0.16.4: - resolution: {integrity: sha512-pUvPQLPmbEeJRPjP0DYTC1vjHyhrnCklQmCGYbipkep+oyfTn7GTBJXoPodR7ZS5upmEyc8lzAkn2o29wD786A==} + /@esbuild/linux-s390x/0.16.7: + resolution: {integrity: sha512-3SA/2VJuv0o1uD7zuqxEP+RrAyRxnkGddq0bwHQ98v1KNlzXD/JvxwTO3T6GM5RH6JUd29RTVQTOJfyzMkkppA==} engines: {node: '>=12'} cpu: [s390x] os: [linux] requiresBuild: true optional: true - /@esbuild/linux-x64/0.16.4: - resolution: {integrity: sha512-N55Q0mJs3Sl8+utPRPBrL6NLYZKBCLLx0bme/+RbjvMforTGGzFvsRl4xLTZMUBFC1poDzBEPTEu5nxizQ9Nlw==} + /@esbuild/linux-x64/0.16.7: + resolution: {integrity: sha512-xi/tbqCqvPIzU+zJVyrpz12xqciTAPMi2fXEWGnapZymoGhuL2GIWIRXg4O2v5BXaYA5TSaiKYE14L0QhUTuQg==} engines: {node: '>=12'} cpu: [x64] os: [linux] requiresBuild: true optional: true - /@esbuild/netbsd-x64/0.16.4: - resolution: {integrity: sha512-LHSJLit8jCObEQNYkgsDYBh2JrJT53oJO2HVdkSYLa6+zuLJh0lAr06brXIkljrlI+N7NNW1IAXGn/6IZPi3YQ==} + /@esbuild/netbsd-x64/0.16.7: + resolution: {integrity: sha512-NUsYbq3B+JdNKn8SXkItFvdes9qTwEoS3aLALtiWciW/ystiCKM20Fgv9XQBOXfhUHyh5CLEeZDXzLOrwBXuCQ==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] requiresBuild: true optional: true - /@esbuild/openbsd-x64/0.16.4: - resolution: {integrity: sha512-nLgdc6tWEhcCFg/WVFaUxHcPK3AP/bh+KEwKtl69Ay5IBqUwKDaq/6Xk0E+fh/FGjnLwqFSsarsbPHeKM8t8Sw==} + /@esbuild/openbsd-x64/0.16.7: + resolution: {integrity: sha512-qjwzsgeve9I8Tbsko2FEkdSk2iiezuNGFgipQxY/736NePXDaDZRodIejYGWOlbYXugdxb0nif5yvypH6lKBmA==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] requiresBuild: true optional: true - /@esbuild/sunos-x64/0.16.4: - resolution: {integrity: sha512-08SluG24GjPO3tXKk95/85n9kpyZtXCVwURR2i4myhrOfi3jspClV0xQQ0W0PYWHioJj+LejFMt41q+PG3mlAQ==} + /@esbuild/sunos-x64/0.16.7: + resolution: {integrity: sha512-mFWDz4RoBTzPphTCkM7Kc7Qpa0o/Z01acajR+Ai7LdfKgcP/C6jYOaKwv7nKzD0+MjOT20j7You9g4ozYy1dKQ==} engines: {node: '>=12'} cpu: [x64] os: [sunos] requiresBuild: true optional: true - /@esbuild/win32-arm64/0.16.4: - resolution: {integrity: sha512-yYiRDQcqLYQSvNQcBKN7XogbrSvBE45FEQdH8fuXPl7cngzkCvpsG2H9Uey39IjQ6gqqc+Q4VXYHsQcKW0OMjQ==} + /@esbuild/win32-arm64/0.16.7: + resolution: {integrity: sha512-m39UmX19RvEIuC8sYZ0M+eQtdXw4IePDSZ78ZQmYyFaXY9krq4YzQCK2XWIJomNLtg4q+W5aXr8bW3AbqWNoVg==} engines: {node: '>=12'} cpu: [arm64] os: [win32] requiresBuild: true optional: true - /@esbuild/win32-ia32/0.16.4: - resolution: {integrity: sha512-5rabnGIqexekYkh9zXG5waotq8mrdlRoBqAktjx2W3kb0zsI83mdCwrcAeKYirnUaTGztR5TxXcXmQrEzny83w==} + /@esbuild/win32-ia32/0.16.7: + resolution: {integrity: sha512-1cbzSEZA1fANwmT6rjJ4G1qQXHxCxGIcNYFYR9ctI82/prT38lnwSRZ0i5p/MVXksw9eMlHlet6pGu2/qkXFCg==} engines: {node: '>=12'} cpu: [ia32] os: [win32] requiresBuild: true optional: true - /@esbuild/win32-x64/0.16.4: - resolution: {integrity: sha512-sN/I8FMPtmtT2Yw+Dly8Ur5vQ5a/RmC8hW7jO9PtPSQUPkowxWpcUZnqOggU7VwyT3Xkj6vcXWd3V/qTXwultQ==} + /@esbuild/win32-x64/0.16.7: + resolution: {integrity: sha512-QaQ8IH0JLacfGf5cf0HCCPnQuCTd/dAI257vXBgb/cccKGbH/6pVtI1gwhdAQ0Y48QSpTIFrh9etVyNdZY+zzw==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -1485,11 +1548,11 @@ packages: /@types/chai-subset/1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: - '@types/chai': 4.3.3 + '@types/chai': 4.3.4 dev: true - /@types/chai/4.3.3: - resolution: {integrity: sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==} + /@types/chai/4.3.4: + resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} dev: true /@types/minimatch/5.1.2: @@ -1501,8 +1564,8 @@ packages: dev: false optional: true - /@types/node/18.11.13: - resolution: {integrity: sha512-IASpMGVcWpUsx5xBOrxMj7Bl8lqfuTY7FKAnPmu5cHkfQVWF8GulWS1jbRqA934qZL35xh5xN/+Xe/i26Bod4w==} + /@types/node/18.11.15: + resolution: {integrity: sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw==} dev: true /@types/node/18.8.0: @@ -1532,14 +1595,14 @@ packages: '@types/node': 18.8.0 dev: true - /@vitejs/plugin-vue/4.0.0_vite@4.0.0+vue@3.2.45: + /@vitejs/plugin-vue/4.0.0_vite@4.0.1+vue@3.2.45: resolution: {integrity: sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.0.0_@types+node@18.11.13 + vite: 4.0.1_@types+node@18.11.15 vue: 3.2.45 dev: false optional: true @@ -2097,13 +2160,13 @@ packages: dev: false optional: true - /chai/4.3.6: - resolution: {integrity: sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==} + /chai/4.3.7: + resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} engines: {node: '>=4'} dependencies: assertion-error: 1.1.0 check-error: 1.0.2 - deep-eql: 3.0.1 + deep-eql: 4.1.3 get-func-name: 2.0.0 loupe: 2.3.4 pathval: 1.1.1 @@ -2634,9 +2697,9 @@ packages: dev: false optional: true - /deep-eql/3.0.1: - resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==} - engines: {node: '>=0.12'} + /deep-eql/4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} dependencies: type-detect: 4.0.8 dev: true @@ -2863,13 +2926,13 @@ packages: dev: false optional: true - /esbuild-plugin-copy/2.0.1_esbuild@0.16.4: + /esbuild-plugin-copy/2.0.1_esbuild@0.16.7: resolution: {integrity: sha512-/mvriqGv2QAyrkui3REZaLEjwqESBKWZQQJtOZEausI8C4QMChREXGASNzmWpTlHo/v+ipLW73QCiNemBKggMw==} peerDependencies: esbuild: '>= 0.14.0' dependencies: chalk: 4.1.2 - esbuild: 0.16.4 + esbuild: 0.16.7 fs-extra: 10.1.0 globby: 11.1.0 dev: true @@ -2883,34 +2946,34 @@ packages: yargs: 17.6.2 dev: true - /esbuild/0.16.4: - resolution: {integrity: sha512-qQrPMQpPTWf8jHugLWHoGqZjApyx3OEm76dlTXobHwh/EBbavbRdjXdYi/GWr43GyN0sfpap14GPkb05NH3ROA==} + /esbuild/0.16.7: + resolution: {integrity: sha512-P6OBFYFSQOGzfApqCeYKqfKRRbCIRsdppTXFo4aAvtiW3o8TTyiIplBvHJI171saPAiy3WlawJHCveJVIOIx1A==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.16.4 - '@esbuild/android-arm64': 0.16.4 - '@esbuild/android-x64': 0.16.4 - '@esbuild/darwin-arm64': 0.16.4 - '@esbuild/darwin-x64': 0.16.4 - '@esbuild/freebsd-arm64': 0.16.4 - '@esbuild/freebsd-x64': 0.16.4 - '@esbuild/linux-arm': 0.16.4 - '@esbuild/linux-arm64': 0.16.4 - '@esbuild/linux-ia32': 0.16.4 - '@esbuild/linux-loong64': 0.16.4 - '@esbuild/linux-mips64el': 0.16.4 - '@esbuild/linux-ppc64': 0.16.4 - '@esbuild/linux-riscv64': 0.16.4 - '@esbuild/linux-s390x': 0.16.4 - '@esbuild/linux-x64': 0.16.4 - '@esbuild/netbsd-x64': 0.16.4 - '@esbuild/openbsd-x64': 0.16.4 - '@esbuild/sunos-x64': 0.16.4 - '@esbuild/win32-arm64': 0.16.4 - '@esbuild/win32-ia32': 0.16.4 - '@esbuild/win32-x64': 0.16.4 + '@esbuild/android-arm': 0.16.7 + '@esbuild/android-arm64': 0.16.7 + '@esbuild/android-x64': 0.16.7 + '@esbuild/darwin-arm64': 0.16.7 + '@esbuild/darwin-x64': 0.16.7 + '@esbuild/freebsd-arm64': 0.16.7 + '@esbuild/freebsd-x64': 0.16.7 + '@esbuild/linux-arm': 0.16.7 + '@esbuild/linux-arm64': 0.16.7 + '@esbuild/linux-ia32': 0.16.7 + '@esbuild/linux-loong64': 0.16.7 + '@esbuild/linux-mips64el': 0.16.7 + '@esbuild/linux-ppc64': 0.16.7 + '@esbuild/linux-riscv64': 0.16.7 + '@esbuild/linux-s390x': 0.16.7 + '@esbuild/linux-x64': 0.16.7 + '@esbuild/netbsd-x64': 0.16.7 + '@esbuild/openbsd-x64': 0.16.7 + '@esbuild/sunos-x64': 0.16.7 + '@esbuild/win32-arm64': 0.16.7 + '@esbuild/win32-ia32': 0.16.7 + '@esbuild/win32-x64': 0.16.7 /escalade/3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} @@ -5051,8 +5114,8 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 - /postcss/8.4.19: - resolution: {integrity: sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==} + /postcss/8.4.20: + resolution: {integrity: sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.4 @@ -5988,7 +6051,6 @@ packages: /tslib/2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} - dev: false /tsscmp/1.0.6: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} @@ -6187,8 +6249,8 @@ packages: dev: false optional: true - /vite/4.0.0_@types+node@18.11.13: - resolution: {integrity: sha512-ynad+4kYs8Jcnn8J7SacS9vAbk7eMy0xWg6E7bAhS1s79TK+D7tVFGXVZ55S7RNLRROU1rxoKlvZ/qjaB41DGA==} + /vite/4.0.1_@types+node@18.11.15: + resolution: {integrity: sha512-kZQPzbDau35iWOhy3CpkrRC7It+HIHtulAzBhMqzGHKRf/4+vmh8rPDDdv98SWQrFWo6//3ozwsRmwQIPZsK9g==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -6212,27 +6274,27 @@ packages: terser: optional: true dependencies: - '@types/node': 18.11.13 - esbuild: 0.16.4 - postcss: 8.4.19 + '@types/node': 18.11.15 + esbuild: 0.16.7 + postcss: 8.4.20 resolve: 1.22.1 rollup: 3.7.1 optionalDependencies: fsevents: 2.3.2 - /vitepress/1.0.0-alpha.31_@types+node@18.11.13: - resolution: {integrity: sha512-FWFXLs7WLbFbemxjBWo2S2+qUZCIoeLLyAKfVUpIu3LUB8oQ8cyIANRGO6f6zsM51u2bvJU9Sm+V6Z0WjOWS2Q==} + /vitepress/1.0.0-alpha.32_@types+node@18.11.15: + resolution: {integrity: sha512-Q45N1cLdRr8MAu/+wAKKow7CNQD4sNBlSNsW1UxYfkvSgwPN/dlEmpEkQl/uOpE3C1kv3jvvEJVY0RAIaJFWYQ==} hasBin: true requiresBuild: true dependencies: '@docsearch/css': 3.3.0 '@docsearch/js': 3.3.0 - '@vitejs/plugin-vue': 4.0.0_vite@4.0.0+vue@3.2.45 + '@vitejs/plugin-vue': 4.0.0_vite@4.0.1+vue@3.2.45 '@vue/devtools-api': 6.4.5 '@vueuse/core': 9.6.0_vue@3.2.45 body-scroll-lock: 4.0.0-beta.0 shiki: 0.11.1 - vite: 4.0.0_@types+node@18.11.13 + vite: 4.0.1_@types+node@18.11.15 vue: 3.2.45 transitivePeerDependencies: - '@algolia/client-search' @@ -6249,8 +6311,8 @@ packages: dev: false optional: true - /vitest/0.25.7: - resolution: {integrity: sha512-lJ+Ue+v8kHl2JzjaKHJ9u5Yo/loU7zrWK2/Whn8OKQjtq5G7nkeWfXuq3elZaC8xKdkdIuWiiIicaNBG1F5yzg==} + /vitest/0.25.8: + resolution: {integrity: sha512-X75TApG2wZTJn299E/TIYevr4E9/nBo1sUtZzn0Ci5oK8qnpZAZyhwg0qCeMSakGIWtc6oRwcQFyFfW14aOFWg==} engines: {node: '>=v14.16.0'} hasBin: true peerDependencies: @@ -6271,12 +6333,12 @@ packages: jsdom: optional: true dependencies: - '@types/chai': 4.3.3 + '@types/chai': 4.3.4 '@types/chai-subset': 1.3.3 - '@types/node': 18.11.13 + '@types/node': 18.11.15 acorn: 8.8.1 acorn-walk: 8.2.0 - chai: 4.3.6 + chai: 4.3.7 debug: 4.3.4 local-pkg: 0.4.2 source-map: 0.6.1 @@ -6284,7 +6346,7 @@ packages: tinybench: 2.3.1 tinypool: 0.3.0 tinyspy: 1.0.2 - vite: 4.0.0_@types+node@18.11.13 + vite: 4.0.1_@types+node@18.11.15 transitivePeerDependencies: - less - sass diff --git a/tsconfig.build.json b/tsconfig.build.json index c85438142..1163d9c64 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -29,6 +29,12 @@ { "path": "./examples/svelte-tsc/tsconfig.build.json" }, + { + "path": "./examples/vscode-svelte/tsconfig.build.json" + }, + { + "path": "./examples/vscode-angular/tsconfig.build.json" + }, // IDE / tests { "path": "./tsconfig.json" diff --git a/vue-language-tools/vue-language-core/src/generators/template.ts b/vue-language-tools/vue-language-core/src/generators/template.ts index fb686f96b..659a8806a 100644 --- a/vue-language-tools/vue-language-core/src/generators/template.ts +++ b/vue-language-tools/vue-language-core/src/generators/template.ts @@ -1394,7 +1394,7 @@ export function generate( prop.loc.start.offset, capabilitiesSet.diagnosticOnly, ]); - codeGen.push(`(await import('./__VLS_types.js')).directiveFunction(__VLS_ctx.`); + codeGen.push(`(await import('./__VLS_types.js')).directiveFunction((__VLS_ctx).`); codeGen.push([ camelize('v-' + prop.name), 'template', diff --git a/vue-language-tools/vue-language-core/src/sourceFile.ts b/vue-language-tools/vue-language-core/src/sourceFile.ts index a018e53a1..cfc3bcedd 100644 --- a/vue-language-tools/vue-language-core/src/sourceFile.ts +++ b/vue-language-tools/vue-language-core/src/sourceFile.ts @@ -349,6 +349,37 @@ export class VueSourceFile implements SourceFile { return blocks; }); + get kind(): EmbeddedFileKind { + return EmbeddedFileKind.TextFile; + } + + get capabilities(): DocumentCapabilities { + return { + diagnostic: true, + foldingRange: true, + documentFormatting: true, + documentSymbol: true, + codeAction: true, + inlayHint: true, + }; + } + + get mappings(): Mapping[] { + return [{ + sourceRange: [0, this._snapshot.value.getLength()], + generatedRange: [0, this._snapshot.value.getLength()], + data: { + hover: true, + references: true, + definition: true, + rename: true, + completion: true, + diagnostic: true, + semanticTokens: true, + }, + }]; + } + get text() { return this._snapshot.value.getText(0, this._snapshot.value.getLength()); } diff --git a/vue-language-tools/vue-language-core/src/utils/transform.ts b/vue-language-tools/vue-language-core/src/utils/transform.ts index 9b293d396..dc555dd02 100644 --- a/vue-language-tools/vue-language-core/src/utils/transform.ts +++ b/vue-language-tools/vue-language-core/src/utils/transform.ts @@ -75,7 +75,7 @@ export function walkInterpolationFragment( } } else { - cb('__VLS_ctx.', undefined); + cb('(__VLS_ctx).', undefined); if (ctxVars[i + 1].isShorthand) { cb(code.substring(ctxVars[i].offset, ctxVars[i + 1].offset + ctxVars[i + 1].text.length), ctxVars[i].offset); cb(': ', undefined); @@ -100,7 +100,7 @@ export function walkInterpolationFragment( } else { cb('', ctxVars[ctxVars.length - 1].offset, true); - cb('__VLS_ctx.', undefined); + cb('(__VLS_ctx).', undefined); cb(code.substring(ctxVars[ctxVars.length - 1].offset), ctxVars[ctxVars.length - 1].offset); } } diff --git a/vue-language-tools/vue-language-service/src/helpers.ts b/vue-language-tools/vue-language-service/src/helpers.ts index 38db8c24c..f3771e96a 100644 --- a/vue-language-tools/vue-language-service/src/helpers.ts +++ b/vue-language-tools/vue-language-service/src/helpers.ts @@ -186,7 +186,7 @@ function getComponentsType( let file: embedded.SourceFile | undefined; let tsSourceFile: ts.SourceFile | undefined; - embedded.forEachEmbeddeds(sourceFile.embeddeds, embedded => { + embedded.forEachEmbeddeds(sourceFile, embedded => { if (embedded.fileName === sourceFile.tsFileName) { file = embedded; } diff --git a/vue-language-tools/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts b/vue-language-tools/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts index 516e50604..eb2b93ce1 100644 --- a/vue-language-tools/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts +++ b/vue-language-tools/vue-language-service/src/plugins/vue-autoinsert-parentheses.ts @@ -33,7 +33,7 @@ export default function (options: { let templateFormatScript: EmbeddedFile | undefined; - embedded.forEachEmbeddeds(vueDocument.file.embeddeds, embedded => { + embedded.forEachEmbeddeds(vueDocument.file, embedded => { if (embedded.fileName.endsWith('.__VLS_template_format.ts')) { templateFormatScript = embedded; } diff --git a/vue-language-tools/vue-language-service/src/plugins/vue-twoslash-queries.ts b/vue-language-tools/vue-language-service/src/plugins/vue-twoslash-queries.ts index 78d927224..fef323dc2 100644 --- a/vue-language-tools/vue-language-service/src/plugins/vue-twoslash-queries.ts +++ b/vue-language-tools/vue-language-service/src/plugins/vue-twoslash-queries.ts @@ -33,7 +33,7 @@ export default function (options: { })]); } - forEachEmbeddeds(vueFile.embeddeds, (embedded) => { + forEachEmbeddeds(vueFile, (embedded) => { if (embedded.kind === EmbeddedFileKind.TypeScriptHostFile) { const sourceMap = vueDocument.getSourceMap(embedded); for (const [pointerPosition, hoverOffset] of hoverOffsets) {