@vue/language-service 1.8.5 → 1.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,7 +16,7 @@ async function convertTagName(ts, context, uri, casing, vueCompilerOptions) {
16
16
  const template = desc.template;
17
17
  const document = context.documents.getDocumentByFileName(rootFile.snapshot, rootFile.fileName);
18
18
  const edits = [];
19
- const components = (0, helpers_1.checkComponentNames)(ts, languageService, rootFile, vueCompilerOptions);
19
+ const components = (0, helpers_1.getComponentNames)(ts, languageService, rootFile, vueCompilerOptions);
20
20
  const tags = (0, helpers_1.getTemplateTagsAndAttrs)(rootFile);
21
21
  for (const [tagName, { offsets }] of tags) {
22
22
  const componentName = components.find(component => component === tagName || (0, shared_1.hyphenate)(component) === tagName);
@@ -48,12 +48,12 @@ async function convertAttrName(ts, context, uri, casing, vueCompilerOptions) {
48
48
  const template = desc.template;
49
49
  const document = context.documents.getDocumentByFileName(rootFile.snapshot, rootFile.fileName);
50
50
  const edits = [];
51
- const components = (0, helpers_1.checkComponentNames)(ts, languageService, rootFile, vueCompilerOptions);
51
+ const components = (0, helpers_1.getComponentNames)(ts, languageService, rootFile, vueCompilerOptions);
52
52
  const tags = (0, helpers_1.getTemplateTagsAndAttrs)(rootFile);
53
53
  for (const [tagName, { attrs }] of tags) {
54
54
  const componentName = components.find(component => component === tagName || (0, shared_1.hyphenate)(component) === tagName);
55
55
  if (componentName) {
56
- const props = (0, helpers_1.checkPropsOfTag)(ts, languageService, rootFile, componentName, vueCompilerOptions);
56
+ const props = (0, helpers_1.getPropsByTag)(ts, languageService, rootFile, componentName, vueCompilerOptions);
57
57
  for (const [attrName, { offsets }] of attrs) {
58
58
  const propName = props.find(prop => prop === attrName || (0, shared_1.hyphenate)(prop) === attrName);
59
59
  if (propName) {
@@ -124,7 +124,7 @@ function detect(ts, context, uri, vueCompilerOptions) {
124
124
  return result;
125
125
  }
126
126
  function getTagNameCase(file) {
127
- const components = (0, helpers_1.checkComponentNames)(ts, languageService, file, vueCompilerOptions);
127
+ const components = (0, helpers_1.getComponentNames)(ts, languageService, file, vueCompilerOptions);
128
128
  const tagNames = (0, helpers_1.getTemplateTagsAndAttrs)(file);
129
129
  const result = [];
130
130
  let anyComponentUsed = false;
@@ -21,6 +21,8 @@ const vue_template_1 = require("./plugins/vue-template");
21
21
  const vue_twoslash_queries_1 = require("./plugins/vue-twoslash-queries");
22
22
  const vue_visualize_hidden_callback_param_1 = require("./plugins/vue-visualize-hidden-callback-param");
23
23
  const vue_directive_comments_1 = require("./plugins/vue-directive-comments");
24
+ const vue_extract_file_1 = require("./plugins/vue-extract-file");
25
+ const vue_toggle_v_bind_codeaction_1 = require("./plugins/vue-toggle-v-bind-codeaction");
24
26
  const types_1 = require("./types");
25
27
  function resolveConfig(config, compilerOptions = {}, vueCompilerOptions = {}, ts = require('typescript'), codegenStack = false) {
26
28
  const resolvedVueCompilerOptions = vue.resolveVueCompilerOptions(vueCompilerOptions);
@@ -30,23 +32,16 @@ function resolveConfig(config, compilerOptions = {}, vueCompilerOptions = {}, ts
30
32
  return config;
31
33
  }
32
34
  exports.resolveConfig = resolveConfig;
33
- const unicodeReg = /\\u/g;
34
35
  function resolvePlugins(services, vueCompilerOptions) {
35
36
  const originalTsPlugin = services?.typescript ?? (0, volar_service_typescript_1.default)();
36
37
  services ??= {};
37
- services.typescript = (_context, modules) => {
38
- const base = typeof originalTsPlugin === 'function' ? originalTsPlugin(_context, modules) : originalTsPlugin;
39
- if (!_context || !modules?.typescript)
38
+ services.typescript = (ctx, modules) => {
39
+ const base = typeof originalTsPlugin === 'function' ? originalTsPlugin(ctx, modules) : originalTsPlugin;
40
+ if (!ctx || !modules?.typescript)
40
41
  return base;
41
42
  const ts = modules.typescript;
42
- const transformedItem = new WeakSet();
43
43
  return {
44
44
  ...base,
45
- transformCompletionItem(item) {
46
- if (transformedItem.has(item)) {
47
- return item;
48
- }
49
- },
50
45
  async provideCompletionItems(document, position, context, item) {
51
46
  const result = await base.provideCompletionItems?.(document, position, context, item);
52
47
  if (result) {
@@ -55,18 +50,18 @@ function resolvePlugins(services, vueCompilerOptions) {
55
50
  && (!item.labelDetails?.description || item.labelDetails.description.indexOf('__VLS_') === -1));
56
51
  // handle component auto-import patch
57
52
  let casing;
58
- for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) {
59
- const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
53
+ for (const [_, map] of ctx.documents.getMapsByVirtualFileUri(document.uri)) {
54
+ const virtualFile = ctx.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
60
55
  if (virtualFile instanceof vue.VueFile) {
61
56
  const isAutoImport = !!map.toSourcePosition(position, data => typeof data.completion === 'object' && !!data.completion.autoImportOnly);
62
57
  if (isAutoImport) {
63
- const source = _context.documents.getVirtualFileByUri(document.uri)[1];
58
+ const source = ctx.documents.getVirtualFileByUri(document.uri)[1];
64
59
  for (const item of result.items) {
65
60
  item.data.__isComponentAutoImport = true;
66
61
  }
67
62
  // fix #2458
68
63
  if (source) {
69
- casing ??= await (0, nameCasing_1.getNameCasing)(ts, _context, _context.env.fileNameToUri(source.fileName), vueCompilerOptions);
64
+ casing ??= await (0, nameCasing_1.getNameCasing)(ts, ctx, ctx.env.fileNameToUri(source.fileName), vueCompilerOptions);
70
65
  if (casing.tag === types_1.TagNameCasing.Kebab) {
71
66
  for (const item of result.items) {
72
67
  item.filterText = (0, shared_1.hyphenate)(item.filterText ?? item.label);
@@ -91,7 +86,7 @@ function resolvePlugins(services, vueCompilerOptions) {
91
86
  if (itemData?.uri
92
87
  && item.textEdit?.newText.endsWith(suffix)
93
88
  && item.additionalTextEdits?.length === 1 && item.additionalTextEdits[0].newText.indexOf('import ' + item.textEdit.newText + ' from ') >= 0
94
- && (await _context.env.getConfiguration?.('vue.complete.normalizeComponentImportName') ?? true)) {
89
+ && (await ctx.env.getConfiguration?.('vue.complete.normalizeComponentImportName') ?? true)) {
95
90
  newName = item.textEdit.newText.slice(0, -suffix.length);
96
91
  newName = newName[0].toUpperCase() + newName.substring(1);
97
92
  if (newName === 'Index') {
@@ -106,9 +101,9 @@ function resolvePlugins(services, vueCompilerOptions) {
106
101
  }
107
102
  item.additionalTextEdits[0].newText = item.additionalTextEdits[0].newText.replace('import ' + item.textEdit.newText + ' from ', 'import ' + newName + ' from ');
108
103
  item.textEdit.newText = newName;
109
- const source = _context.documents.getVirtualFileByUri(itemData.uri)[1];
104
+ const source = ctx.documents.getVirtualFileByUri(itemData.uri)[1];
110
105
  if (source) {
111
- const casing = await (0, nameCasing_1.getNameCasing)(ts, _context, _context.env.fileNameToUri(source.fileName), vueCompilerOptions);
106
+ const casing = await (0, nameCasing_1.getNameCasing)(ts, ctx, ctx.env.fileNameToUri(source.fileName), vueCompilerOptions);
112
107
  if (casing.tag === types_1.TagNameCasing.Kebab) {
113
108
  item.textEdit.newText = (0, shared_1.hyphenate)(item.textEdit.newText);
114
109
  }
@@ -120,65 +115,26 @@ function resolvePlugins(services, vueCompilerOptions) {
120
115
  }
121
116
  }
122
117
  const data = item.data;
123
- if (item.data?.__isComponentAutoImport && data && item.additionalTextEdits?.length && item.textEdit) {
124
- let transformed = false;
125
- for (const [_, map] of _context.documents.getMapsByVirtualFileUri(data.uri)) {
126
- const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
127
- if (virtualFile instanceof vue.VueFile) {
128
- const sfc = virtualFile.sfc;
129
- const componentName = newName ?? item.textEdit.newText;
130
- const textDoc = _context.documents.getDocumentByFileName(virtualFile.snapshot, virtualFile.fileName);
131
- if (sfc.scriptAst && sfc.script) {
132
- const _scriptRanges = vue.scriptRanges.parseScriptRanges(ts, sfc.scriptAst, !!sfc.scriptSetup, true);
133
- const exportDefault = _scriptRanges.exportDefault;
134
- if (exportDefault) {
135
- // https://github.com/microsoft/TypeScript/issues/36174
136
- const printer = ts.createPrinter();
137
- if (exportDefault.componentsOption && exportDefault.componentsOptionNode) {
138
- const newNode = {
139
- ...exportDefault.componentsOptionNode,
140
- properties: [
141
- ...exportDefault.componentsOptionNode.properties,
142
- ts.factory.createShorthandPropertyAssignment(componentName),
143
- ],
144
- };
145
- const printText = printer.printNode(ts.EmitHint.Expression, newNode, sfc.scriptAst);
146
- const editRange = {
147
- start: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.componentsOption.start),
148
- end: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.componentsOption.end),
149
- };
150
- transformed = true;
151
- item.additionalTextEdits.push({
152
- range: editRange,
153
- newText: unescape(printText.replace(unicodeReg, '%u')),
154
- });
155
- }
156
- else if (exportDefault.args && exportDefault.argsNode) {
157
- const newNode = {
158
- ...exportDefault.argsNode,
159
- properties: [
160
- ...exportDefault.argsNode.properties,
161
- ts.factory.createShorthandPropertyAssignment(`components: { ${componentName} }`),
162
- ],
163
- };
164
- const printText = printer.printNode(ts.EmitHint.Expression, newNode, sfc.scriptAst);
165
- const editRange = {
166
- start: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.args.start),
167
- end: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.args.end),
168
- };
169
- transformed = true;
170
- item.additionalTextEdits.push({
171
- range: editRange,
172
- newText: unescape(printText.replace(unicodeReg, '%u')),
173
- });
174
- }
175
- }
176
- }
118
+ if (item.data?.__isComponentAutoImport && data && item.additionalTextEdits?.length && item.textEdit && itemData?.uri) {
119
+ const fileName = ctx.env.uriToFileName(itemData.uri);
120
+ const langaugeService = ctx.inject('typescript/languageService');
121
+ const [virtualFile] = ctx.virtualFiles.getVirtualFile(fileName);
122
+ const ast = langaugeService.getProgram()?.getSourceFile(fileName);
123
+ const exportDefault = ast ? vue.scriptRanges.parseScriptRanges(ts, ast, false, true).exportDefault : undefined;
124
+ if (virtualFile && ast && exportDefault) {
125
+ const componentName = newName ?? item.textEdit.newText;
126
+ const optionEdit = (0, vue_extract_file_1.createAddComponentToOptionEdit)(ts, ast, componentName);
127
+ if (optionEdit) {
128
+ const textDoc = ctx.documents.getDocumentByFileName(virtualFile.snapshot, virtualFile.fileName);
129
+ item.additionalTextEdits.push({
130
+ range: {
131
+ start: textDoc.positionAt(optionEdit.range.start),
132
+ end: textDoc.positionAt(optionEdit.range.end),
133
+ },
134
+ newText: optionEdit.newText,
135
+ });
177
136
  }
178
137
  }
179
- if (transformed) {
180
- transformedItem.add(item);
181
- }
182
138
  }
183
139
  return item;
184
140
  },
@@ -244,6 +200,8 @@ function resolvePlugins(services, vueCompilerOptions) {
244
200
  services['vue/autoInsertSpaces'] ??= (0, vue_autoinsert_space_1.default)();
245
201
  services['vue/visualizeHiddenCallbackParam'] ??= (0, vue_visualize_hidden_callback_param_1.default)();
246
202
  services['vue/directiveComments'] ??= (0, vue_directive_comments_1.default)();
203
+ services['vue/extractComponent'] ??= (0, vue_extract_file_1.default)();
204
+ services['vue/toggleVBind'] ??= (0, vue_toggle_v_bind_codeaction_1.default)();
247
205
  services.emmet ??= (0, volar_service_emmet_1.default)();
248
206
  return services;
249
207
  }
@@ -0,0 +1,7 @@
1
+ import { Service } from '@volar/language-service';
2
+ import type * as ts from 'typescript/lib/tsserverlibrary';
3
+ export default function (): Service;
4
+ export declare function createAddComponentToOptionEdit(ts: typeof import('typescript/lib/tsserverlibrary'), ast: ts.SourceFile, componentName: string): {
5
+ range: import("@vue/language-core").TextRange;
6
+ newText: string;
7
+ } | undefined;
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAddComponentToOptionEdit = void 0;
4
+ const language_core_1 = require("@vue/language-core");
5
+ const unicodeReg = /\\u/g;
6
+ function default_1() {
7
+ return (ctx, modules) => {
8
+ if (!modules?.typescript)
9
+ return {};
10
+ const ts = modules.typescript;
11
+ return {
12
+ async provideCodeActions(document, range, _context) {
13
+ const startOffset = document.offsetAt(range.start);
14
+ const endOffset = document.offsetAt(range.end);
15
+ if (startOffset === endOffset) {
16
+ return;
17
+ }
18
+ const [vueFile] = ctx.documents.getVirtualFileByUri(document.uri);
19
+ if (!vueFile || !(vueFile instanceof language_core_1.VueFile))
20
+ return;
21
+ const { sfc } = vueFile;
22
+ const script = sfc.scriptSetup ?? sfc.script;
23
+ const scriptAst = sfc.scriptSetupAst ?? sfc.scriptAst;
24
+ if (!sfc.template || !sfc.templateAst || !script || !scriptAst)
25
+ return;
26
+ const templateCodeRange = selectTemplateCode(startOffset, endOffset, sfc.template, sfc.templateAst);
27
+ if (!templateCodeRange)
28
+ return;
29
+ return [
30
+ {
31
+ title: 'Extract into new dumb component',
32
+ kind: 'refactor.move.newFile.dumb',
33
+ data: {
34
+ uri: document.uri,
35
+ range: [startOffset, endOffset],
36
+ newName: 'NewComponent',
37
+ },
38
+ },
39
+ ];
40
+ },
41
+ async resolveCodeAction(codeAction) {
42
+ const { uri, range, newName } = codeAction.data;
43
+ const document = ctx.getTextDocument(uri);
44
+ const [startOffset, endOffset] = range;
45
+ const [vueFile] = ctx.documents.getVirtualFileByUri(document.uri);
46
+ const { sfc } = vueFile;
47
+ const script = sfc.scriptSetup ?? sfc.script;
48
+ const scriptAst = sfc.scriptSetupAst ?? sfc.scriptAst;
49
+ if (!sfc.template || !sfc.templateAst || !script || !scriptAst)
50
+ return codeAction;
51
+ const templateCodeRange = selectTemplateCode(startOffset, endOffset, sfc.template, sfc.templateAst);
52
+ if (!templateCodeRange)
53
+ return codeAction;
54
+ const languageService = ctx.inject('typescript/languageService');
55
+ const languageServiceHost = ctx.inject('typescript/languageServiceHost');
56
+ const sourceFile = languageService.getProgram().getSourceFile(vueFile.mainScriptName);
57
+ const sourceFileKind = languageServiceHost.getScriptKind?.(vueFile.mainScriptName);
58
+ const toExtract = collectExtractProps();
59
+ const initialIndentSetting = await ctx.env.getConfiguration('volar.format.initialIndent');
60
+ const newUri = document.uri.substring(0, document.uri.lastIndexOf('/') + 1) + `${newName}.vue`;
61
+ const lastImportNode = getLastImportNode(scriptAst);
62
+ let newFileTags = [];
63
+ newFileTags.push(constructTag('template', [], initialIndentSetting.html, sfc.template.content.substring(templateCodeRange[0], templateCodeRange[1])));
64
+ if (toExtract.length) {
65
+ newFileTags.push(constructTag('script', ['setup', 'lang="ts"'], isInitialIndentNeeded(ts, sourceFileKind, initialIndentSetting), generateNewScriptContents()));
66
+ }
67
+ if (sfc.template.startTagEnd > script.startTagEnd) {
68
+ newFileTags = newFileTags.reverse();
69
+ }
70
+ const currentFileEdits = [
71
+ {
72
+ range: {
73
+ start: document.positionAt(sfc.template.startTagEnd + templateCodeRange[0]),
74
+ end: document.positionAt(sfc.template.startTagEnd + templateCodeRange[1]),
75
+ },
76
+ newText: generateReplaceTemplate(),
77
+ },
78
+ {
79
+ range: lastImportNode ? {
80
+ start: document.positionAt(script.startTagEnd + lastImportNode.end),
81
+ end: document.positionAt(script.startTagEnd + lastImportNode.end),
82
+ } : {
83
+ start: document.positionAt(script.startTagEnd),
84
+ end: document.positionAt(script.startTagEnd),
85
+ },
86
+ newText: `\nimport ${newName} from './${newName}.vue'`,
87
+ },
88
+ ];
89
+ if (sfc.script && sfc.scriptAst) {
90
+ const edit = createAddComponentToOptionEdit(ts, sfc.scriptAst, newName);
91
+ if (edit) {
92
+ currentFileEdits.push({
93
+ range: {
94
+ start: document.positionAt(sfc.script.startTagEnd + edit.range.start),
95
+ end: document.positionAt(sfc.script.startTagEnd + edit.range.end),
96
+ },
97
+ newText: edit.newText,
98
+ });
99
+ }
100
+ }
101
+ return {
102
+ ...codeAction,
103
+ edit: {
104
+ documentChanges: [
105
+ // editing current file
106
+ {
107
+ textDocument: {
108
+ uri: document.uri,
109
+ version: null,
110
+ },
111
+ edits: currentFileEdits,
112
+ },
113
+ // creating new file with content
114
+ {
115
+ uri: newUri,
116
+ kind: 'create',
117
+ },
118
+ {
119
+ textDocument: {
120
+ uri: newUri,
121
+ version: null,
122
+ },
123
+ edits: [
124
+ {
125
+ range: {
126
+ start: { line: 0, character: 0 },
127
+ end: { line: 0, character: 0 },
128
+ },
129
+ newText: newFileTags.join('\n'),
130
+ },
131
+ ],
132
+ },
133
+ ],
134
+ },
135
+ };
136
+ function getLastImportNode(sourceFile) {
137
+ let lastImportNode;
138
+ for (const statement of sourceFile.statements) {
139
+ if (ts.isImportDeclaration(statement)) {
140
+ lastImportNode = statement;
141
+ }
142
+ else {
143
+ break;
144
+ }
145
+ }
146
+ return lastImportNode;
147
+ }
148
+ function collectExtractProps() {
149
+ const result = new Map();
150
+ const checker = languageService.getProgram().getTypeChecker();
151
+ const maps = [...ctx.documents.getMapsByVirtualFileName(vueFile.mainScriptName)];
152
+ sourceFile.forEachChild(function visit(node) {
153
+ if (ts.isPropertyAccessExpression(node)
154
+ && ts.isIdentifier(node.expression)
155
+ && node.expression.text === '__VLS_ctx'
156
+ && ts.isIdentifier(node.name)) {
157
+ const { name } = node;
158
+ for (const [_, map] of maps) {
159
+ const source = map.map.toSourceOffset(name.getEnd());
160
+ if (source && source[0] >= sfc.template.startTagEnd + templateCodeRange[0] && source[0] <= sfc.template.startTagEnd + templateCodeRange[1] && source[1].data.semanticTokens) {
161
+ if (!result.has(name.text)) {
162
+ const type = checker.getTypeAtLocation(node);
163
+ const typeString = checker.typeToString(type, node, ts.TypeFormatFlags.NoTruncation);
164
+ result.set(name.text, {
165
+ name: name.text,
166
+ type: typeString.includes('__VLS_') ? 'any' : typeString,
167
+ model: false,
168
+ });
169
+ }
170
+ const isModel = ts.isPostfixUnaryExpression(node.parent) || ts.isBinaryExpression(node.parent);
171
+ if (isModel) {
172
+ result.get(name.text).model = true;
173
+ }
174
+ break;
175
+ }
176
+ }
177
+ }
178
+ node.forEachChild(visit);
179
+ });
180
+ return [...result.values()];
181
+ }
182
+ function generateNewScriptContents() {
183
+ const lines = [];
184
+ const props = [...toExtract.values()].filter(p => !p.model);
185
+ const models = [...toExtract.values()].filter(p => p.model);
186
+ if (props.length) {
187
+ lines.push(`defineProps<{ \n\t${props.map(p => `${p.name}: ${p.type};`).join('\n\t')}\n}>()`);
188
+ }
189
+ for (const model of models) {
190
+ lines.push(`const ${model.name} = defineModel<${model.type}>('${model.name}', { required: true })`);
191
+ }
192
+ return lines.join('\n');
193
+ }
194
+ function generateReplaceTemplate() {
195
+ const props = [...toExtract.values()].filter(p => !p.model);
196
+ const models = [...toExtract.values()].filter(p => p.model);
197
+ return [
198
+ `<${newName}`,
199
+ ...props.map(p => `:${p.name}="${p.name}"`),
200
+ ...models.map(p => `v-model:${p.name}="${p.name}"`),
201
+ `/>`,
202
+ ].join(' ');
203
+ }
204
+ },
205
+ transformCodeAction(item) {
206
+ return item; // ignore mapping
207
+ },
208
+ };
209
+ };
210
+ }
211
+ exports.default = default_1;
212
+ function selectTemplateCode(startOffset, endOffset, templateBlock, templateAst) {
213
+ if (startOffset < templateBlock.startTagEnd || endOffset > templateBlock.endTagStart)
214
+ return;
215
+ const insideNodes = [];
216
+ templateAst.children.forEach(function visit(node) {
217
+ if (node.loc.start.offset + templateBlock.startTagEnd >= startOffset
218
+ && node.loc.end.offset + templateBlock.startTagEnd <= endOffset) {
219
+ insideNodes.push(node);
220
+ }
221
+ if ('children' in node) {
222
+ node.children.forEach(node => {
223
+ if (typeof node === 'object') {
224
+ visit(node);
225
+ }
226
+ });
227
+ }
228
+ else if ('branches' in node) {
229
+ node.branches.forEach(visit);
230
+ }
231
+ else if ('content' in node) {
232
+ if (typeof node.content === 'object') {
233
+ visit(node.content);
234
+ }
235
+ }
236
+ });
237
+ if (insideNodes.length) {
238
+ const first = insideNodes.sort((a, b) => a.loc.start.offset - b.loc.start.offset)[0];
239
+ const last = insideNodes.sort((a, b) => b.loc.end.offset - a.loc.end.offset)[0];
240
+ return [first.loc.start.offset, last.loc.end.offset];
241
+ }
242
+ }
243
+ function constructTag(name, attributes, initialIndent, content) {
244
+ if (initialIndent)
245
+ content = content.split('\n').map(line => `\t${line}`).join('\n');
246
+ const attributesString = attributes.length ? ` ${attributes.join(' ')}` : '';
247
+ return `<${name}${attributesString}>\n${content}\n</${name}>\n`;
248
+ }
249
+ function isInitialIndentNeeded(ts, languageKind, initialIndentSetting) {
250
+ const languageKindIdMap = {
251
+ [ts.ScriptKind.JS]: 'javascript',
252
+ [ts.ScriptKind.TS]: 'typescript',
253
+ [ts.ScriptKind.JSX]: 'javascriptreact',
254
+ [ts.ScriptKind.TSX]: 'typescriptreact',
255
+ };
256
+ return initialIndentSetting[languageKindIdMap[languageKind]] ?? false;
257
+ }
258
+ function createAddComponentToOptionEdit(ts, ast, componentName) {
259
+ const exportDefault = language_core_1.scriptRanges.parseScriptRanges(ts, ast, false, true).exportDefault;
260
+ if (!exportDefault)
261
+ return;
262
+ // https://github.com/microsoft/TypeScript/issues/36174
263
+ const printer = ts.createPrinter();
264
+ if (exportDefault.componentsOption && exportDefault.componentsOptionNode) {
265
+ const newNode = {
266
+ ...exportDefault.componentsOptionNode,
267
+ properties: [
268
+ ...exportDefault.componentsOptionNode.properties,
269
+ ts.factory.createShorthandPropertyAssignment(componentName),
270
+ ],
271
+ };
272
+ const printText = printer.printNode(ts.EmitHint.Expression, newNode, ast);
273
+ return {
274
+ range: exportDefault.componentsOption,
275
+ newText: unescape(printText.replace(unicodeReg, '%u')),
276
+ };
277
+ }
278
+ else if (exportDefault.args && exportDefault.argsNode) {
279
+ const newNode = {
280
+ ...exportDefault.argsNode,
281
+ properties: [
282
+ ...exportDefault.argsNode.properties,
283
+ ts.factory.createShorthandPropertyAssignment(`components: { ${componentName} }`),
284
+ ],
285
+ };
286
+ const printText = printer.printNode(ts.EmitHint.Expression, newNode, ast);
287
+ return {
288
+ range: exportDefault.args,
289
+ newText: unescape(printText.replace(unicodeReg, '%u')),
290
+ };
291
+ }
292
+ }
293
+ exports.createAddComponentToOptionEdit = createAddComponentToOptionEdit;
294
+ //# sourceMappingURL=vue-extract-file.js.map
@@ -85,7 +85,7 @@ exports.default = (options) => (_context, modules) => {
85
85
  if (virtualFile && virtualFile instanceof vue.VueFile && scanner) {
86
86
  // visualize missing required props
87
87
  const casing = await (0, nameCasing_1.getNameCasing)(ts, _context, map.sourceFileDocument.uri, options.vueCompilerOptions);
88
- const components = (0, helpers_1.checkComponentNames)(ts, languageService, virtualFile, options.vueCompilerOptions);
88
+ const components = (0, helpers_1.getComponentNames)(ts, languageService, virtualFile, options.vueCompilerOptions);
89
89
  const componentProps = {};
90
90
  let token;
91
91
  let current;
@@ -97,7 +97,7 @@ exports.default = (options) => (_context, modules) => {
97
97
  : components.find(component => component === tagName || (0, shared_1.hyphenate)(component) === tagName);
98
98
  const checkTag = tagName.indexOf('.') >= 0 ? tagName : component;
99
99
  if (checkTag) {
100
- componentProps[checkTag] ??= (0, helpers_1.checkPropsOfTag)(ts, languageService, virtualFile, checkTag, options.vueCompilerOptions, true);
100
+ componentProps[checkTag] ??= (0, helpers_1.getPropsByTag)(ts, languageService, virtualFile, checkTag, options.vueCompilerOptions, true);
101
101
  current = {
102
102
  unburnedRequiredProps: [...componentProps[checkTag]],
103
103
  labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
@@ -229,7 +229,7 @@ exports.default = (options) => (_context, modules) => {
229
229
  const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
230
230
  if (!virtualFile || !(virtualFile instanceof vue.VueFile))
231
231
  continue;
232
- const templateScriptData = (0, helpers_1.checkComponentNames)(ts, languageService, virtualFile, options.vueCompilerOptions);
232
+ const templateScriptData = (0, helpers_1.getComponentNames)(ts, languageService, virtualFile, options.vueCompilerOptions);
233
233
  const components = new Set([
234
234
  ...templateScriptData,
235
235
  ...templateScriptData.map(shared_1.hyphenate),
@@ -290,7 +290,7 @@ exports.default = (options) => (_context, modules) => {
290
290
  getId: () => 'vue-template',
291
291
  isApplicable: () => true,
292
292
  provideTags: () => {
293
- const components = (0, helpers_1.checkComponentNames)(ts, languageService, vueSourceFile, options.vueCompilerOptions)
293
+ const components = (0, helpers_1.getComponentNames)(ts, languageService, vueSourceFile, options.vueCompilerOptions)
294
294
  .filter(name => name !== 'Transition'
295
295
  && name !== 'TransitionGroup'
296
296
  && name !== 'KeepAlive'
@@ -326,9 +326,24 @@ exports.default = (options) => (_context, modules) => {
326
326
  },
327
327
  provideAttributes: (tag) => {
328
328
  const attrs = (0, helpers_1.getElementAttrs)(ts, languageService, languageServiceHost, tag);
329
- const props = new Set((0, helpers_1.checkPropsOfTag)(ts, languageService, vueSourceFile, tag, options.vueCompilerOptions));
330
- const events = (0, helpers_1.checkEventsOfTag)(ts, languageService, vueSourceFile, tag, options.vueCompilerOptions);
329
+ const props = new Set((0, helpers_1.getPropsByTag)(ts, languageService, vueSourceFile, tag, options.vueCompilerOptions));
330
+ const events = (0, helpers_1.getEventsOfTag)(ts, languageService, vueSourceFile, tag, options.vueCompilerOptions);
331
331
  const attributes = [];
332
+ const tsCodegen = vue.tsCodegen.get(vueSourceFile.sfc);
333
+ if (tsCodegen) {
334
+ let ctxVars = [
335
+ ...tsCodegen.scriptRanges.value?.bindings.map(binding => vueSourceFile.sfc.script.content.substring(binding.start, binding.end)) ?? [],
336
+ ...tsCodegen.scriptSetupRanges.value?.bindings.map(binding => vueSourceFile.sfc.scriptSetup.content.substring(binding.start, binding.end)) ?? [],
337
+ ...(0, helpers_1.getTemplateCtx)(ts, languageService, vueSourceFile) ?? [],
338
+ ];
339
+ ctxVars = [...new Set(ctxVars)];
340
+ const dirs = ctxVars.map(shared_1.hyphenate).filter(v => v.startsWith('v-'));
341
+ for (const dir of dirs) {
342
+ attributes.push({
343
+ name: dir,
344
+ });
345
+ }
346
+ }
332
347
  for (const prop of [...props, ...attrs]) {
333
348
  const isGlobal = !props.has(prop);
334
349
  const name = casing.attr === types_1.AttrNameCasing.Camel ? prop : (0, shared_1.hyphenate)(prop);
@@ -407,7 +422,7 @@ exports.default = (options) => (_context, modules) => {
407
422
  function afterHtmlCompletion(completionList, map, vueSourceFile) {
408
423
  const languageService = _context.inject('typescript/languageService');
409
424
  const replacement = getReplacement(completionList, map.sourceFileDocument);
410
- const componentNames = new Set((0, helpers_1.checkComponentNames)(ts, languageService, vueSourceFile, options.vueCompilerOptions).map(shared_1.hyphenate));
425
+ const componentNames = new Set((0, helpers_1.getComponentNames)(ts, languageService, vueSourceFile, options.vueCompilerOptions).map(shared_1.hyphenate));
411
426
  if (replacement) {
412
427
  const isEvent = replacement.text.startsWith('v-on:') || replacement.text.startsWith('@');
413
428
  const isProp = replacement.text.startsWith('v-bind:') || replacement.text.startsWith(':');
@@ -469,39 +484,37 @@ exports.default = (options) => (_context, modules) => {
469
484
  if (itemId) {
470
485
  item.documentation = undefined;
471
486
  }
472
- if (itemIdKey && itemId) {
473
- if (itemId.type === 'componentProp' || itemId.type === 'componentEvent') {
474
- const [componentName] = itemId.args;
487
+ if (item.kind === 10 && componentNames.has((0, shared_1.hyphenate)(item.label))) {
488
+ item.kind = 6;
489
+ item.sortText = '\u0000' + (item.sortText ?? item.label);
490
+ }
491
+ else if (itemId && (itemId.type === 'componentProp' || itemId.type === 'componentEvent')) {
492
+ const [componentName] = itemId.args;
493
+ if (componentName !== '*') {
494
+ item.sortText = '\u0000' + (item.sortText ?? item.label);
495
+ }
496
+ if (itemId.type === 'componentProp') {
475
497
  if (componentName !== '*') {
476
- item.sortText = '\u0000' + (item.sortText ?? item.label);
498
+ item.kind = 5;
477
499
  }
478
- if (itemId.type === 'componentProp') {
479
- if (componentName !== '*') {
480
- item.kind = 5;
481
- }
482
- }
483
- else {
484
- item.kind = componentName !== '*' ? 3 : 23;
485
- }
486
- }
487
- else if (item.label === 'v-if'
488
- || item.label === 'v-else-if'
489
- || item.label === 'v-else'
490
- || item.label === 'v-for') {
491
- item.kind = 2;
492
- item.sortText = '\u0003' + (item.sortText ?? item.label);
493
- }
494
- else if (item.label.startsWith('v-')) {
495
- item.kind = 3;
496
- item.sortText = '\u0002' + (item.sortText ?? item.label);
497
500
  }
498
501
  else {
499
- item.sortText = '\u0001' + (item.sortText ?? item.label);
502
+ item.kind = componentName !== '*' ? 3 : 23;
500
503
  }
501
504
  }
502
- else if (item.kind === 10 && componentNames.has((0, shared_1.hyphenate)(item.label))) {
503
- item.kind = 6;
504
- item.sortText = '\u0000' + (item.sortText ?? item.label);
505
+ else if (item.label === 'v-if'
506
+ || item.label === 'v-else-if'
507
+ || item.label === 'v-else'
508
+ || item.label === 'v-for') {
509
+ item.kind = 14;
510
+ item.sortText = '\u0003' + (item.sortText ?? item.label);
511
+ }
512
+ else if (item.label.startsWith('v-')) {
513
+ item.kind = 3;
514
+ item.sortText = '\u0002' + (item.sortText ?? item.label);
515
+ }
516
+ else {
517
+ item.sortText = '\u0001' + (item.sortText ?? item.label);
505
518
  }
506
519
  }
507
520
  options.updateCustomData(htmlOrPugService, []);
@@ -0,0 +1,2 @@
1
+ import { Service } from '@volar/language-service';
2
+ export default function (): Service;