@ktjs/ts-plugin 0.1.3 → 0.1.5

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.
package/README.md CHANGED
@@ -6,12 +6,12 @@ TypeScript language service plugin for KT.js `k-for` scope variables in TSX.
6
6
 
7
7
  - Editor-only enhancement (tsserver), no runtime transform.
8
8
  - Suppresses TS2304 (`Cannot find name ...`) for aliases declared by `k-for`.
9
+ - Infers alias types from iterable/indexed sources (for example `k-for="item in users"` makes `item` resolve to `users[number]`).
10
+ - Provides hover type info and member completions for inferred aliases.
9
11
  - Supports Vue-like syntax:
10
12
  - `k-for="item in list"`
11
13
  - `k-for="(item, i) in list"`
12
14
  - `k-for="(value, key, i) in mapLike"`
13
- - Keeps legacy fallback mode:
14
- - `k-for={list}` with `k-for-item` / `k-for-index` (or configured defaults).
15
15
 
16
16
  ## Install
17
17
 
@@ -19,6 +19,8 @@ TypeScript language service plugin for KT.js `k-for` scope variables in TSX.
19
19
  pnpm add -D @ktjs/ts-plugin typescript
20
20
  ```
21
21
 
22
+ Then open a `.tsx` or `.ts` file in your editor, press `Ctrl+Shift+P` and select "TypeScript: Select TypeScript Version", then choose "Use workspace version" to make sure your editor uses the version of TypeScript where the plugin is installed.
23
+
22
24
  ## Where to install it
23
25
 
24
26
  Install it in the same project/workspace that owns the `tsconfig.json` where you enable the plugin.
package/dist/ast.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type tsModule from 'typescript/lib/tsserverlibrary';
2
+ export declare function findIdentifierAtPosition(sourceFile: tsModule.SourceFile, position: number, ts: typeof tsModule): tsModule.Identifier | undefined;
3
+ export declare function normalizePosition(position: number, sourceFile: tsModule.SourceFile): number;
4
+ export declare function findInnermostNode(node: tsModule.Node, position: number, ts: typeof tsModule): tsModule.Node | undefined;
package/dist/ast.js ADDED
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findIdentifierAtPosition = findIdentifierAtPosition;
4
+ exports.normalizePosition = normalizePosition;
5
+ exports.findInnermostNode = findInnermostNode;
6
+ function findIdentifierAtPosition(sourceFile, position, ts) {
7
+ const exact = findInnermostNode(sourceFile, normalizePosition(position, sourceFile), ts);
8
+ if (exact && ts.isIdentifier(exact)) {
9
+ return exact;
10
+ }
11
+ if (position > 0) {
12
+ const previous = findInnermostNode(sourceFile, normalizePosition(position - 1, sourceFile), ts);
13
+ if (previous && ts.isIdentifier(previous)) {
14
+ return previous;
15
+ }
16
+ }
17
+ return undefined;
18
+ }
19
+ function normalizePosition(position, sourceFile) {
20
+ const max = Math.max(sourceFile.text.length - 1, 0);
21
+ if (position < 0) {
22
+ return 0;
23
+ }
24
+ if (position > max) {
25
+ return max;
26
+ }
27
+ return position;
28
+ }
29
+ function findInnermostNode(node, position, ts) {
30
+ if (position < node.getFullStart() || position >= node.end) {
31
+ return undefined;
32
+ }
33
+ let match;
34
+ ts.forEachChild(node, (child) => {
35
+ const childMatch = findInnermostNode(child, position, ts);
36
+ if (childMatch) {
37
+ match = childMatch;
38
+ }
39
+ });
40
+ return match || node;
41
+ }
@@ -0,0 +1,7 @@
1
+ import type tsModule from 'typescript/lib/tsserverlibrary';
2
+ import type { KForBinding, MemberCompletionContext } from './types';
3
+ export declare function createMemberCompletionEntries(types: tsModule.Type[], prefix: string, checker: tsModule.TypeChecker, scopeNode: tsModule.Node, ts: typeof tsModule): tsModule.CompletionEntry[];
4
+ export declare function createAliasCompletionEntries(bindings: Map<string, KForBinding>, text: string, position: number, ts: typeof tsModule): tsModule.CompletionEntry[];
5
+ export declare function mergeCompletionInfo(completions: tsModule.CompletionInfo | undefined, extraEntries: tsModule.CompletionEntry[], forceMemberCompletion: boolean): tsModule.CompletionInfo;
6
+ export declare function createBindingTypeMap(bindings: Map<string, KForBinding>): Map<string, readonly tsModule.Type[]>;
7
+ export declare function getMemberCompletionContext(text: string, position: number): MemberCompletionContext | undefined;
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMemberCompletionEntries = createMemberCompletionEntries;
4
+ exports.createAliasCompletionEntries = createAliasCompletionEntries;
5
+ exports.mergeCompletionInfo = mergeCompletionInfo;
6
+ exports.createBindingTypeMap = createBindingTypeMap;
7
+ exports.getMemberCompletionContext = getMemberCompletionContext;
8
+ const constants_1 = require("./constants");
9
+ const identifiers_1 = require("./identifiers");
10
+ const type_resolution_1 = require("./type-resolution");
11
+ function createMemberCompletionEntries(types, prefix, checker, scopeNode, ts) {
12
+ if (types.length === 0) {
13
+ return [];
14
+ }
15
+ const entries = new Map();
16
+ const candidates = (0, type_resolution_1.uniqueTypes)(types, checker, scopeNode, ts);
17
+ for (let i = 0; i < candidates.length; i++) {
18
+ const properties = checker.getPropertiesOfType(checker.getApparentType(candidates[i]));
19
+ for (let j = 0; j < properties.length; j++) {
20
+ const property = properties[j];
21
+ const name = property.getName();
22
+ if (!(0, identifiers_1.isValidIdentifier)(name) || name.startsWith('__@')) {
23
+ continue;
24
+ }
25
+ if (prefix && !name.startsWith(prefix)) {
26
+ continue;
27
+ }
28
+ if (entries.has(name)) {
29
+ continue;
30
+ }
31
+ entries.set(name, {
32
+ name,
33
+ kind: getCompletionKind(property, ts),
34
+ kindModifiers: '',
35
+ sortText: '0',
36
+ });
37
+ }
38
+ }
39
+ return Array.from(entries.values()).sort((left, right) => left.name.localeCompare(right.name));
40
+ }
41
+ function createAliasCompletionEntries(bindings, text, position, ts) {
42
+ const prefix = getIdentifierPrefix(text, position);
43
+ if (prefix.isMemberAccess) {
44
+ return [];
45
+ }
46
+ const entries = [];
47
+ for (const binding of bindings.values()) {
48
+ if (prefix.text && !binding.name.startsWith(prefix.text)) {
49
+ continue;
50
+ }
51
+ entries.push({
52
+ name: binding.name,
53
+ kind: ts.ScriptElementKind.localVariableElement,
54
+ kindModifiers: '',
55
+ sortText: '0',
56
+ });
57
+ }
58
+ return entries;
59
+ }
60
+ function mergeCompletionInfo(completions, extraEntries, forceMemberCompletion) {
61
+ if (!completions) {
62
+ return {
63
+ isGlobalCompletion: !forceMemberCompletion,
64
+ isMemberCompletion: forceMemberCompletion,
65
+ isNewIdentifierLocation: !forceMemberCompletion,
66
+ entries: extraEntries,
67
+ };
68
+ }
69
+ const seen = new Set();
70
+ for (let i = 0; i < completions.entries.length; i++) {
71
+ seen.add(completions.entries[i].name);
72
+ }
73
+ const merged = completions.entries.slice();
74
+ for (let i = 0; i < extraEntries.length; i++) {
75
+ const entry = extraEntries[i];
76
+ if (seen.has(entry.name)) {
77
+ continue;
78
+ }
79
+ seen.add(entry.name);
80
+ merged.push(entry);
81
+ }
82
+ return {
83
+ ...completions,
84
+ isMemberCompletion: completions.isMemberCompletion || forceMemberCompletion,
85
+ entries: merged,
86
+ };
87
+ }
88
+ function createBindingTypeMap(bindings) {
89
+ const map = new Map();
90
+ for (const binding of bindings.values()) {
91
+ map.set(binding.name, binding.types);
92
+ }
93
+ return map;
94
+ }
95
+ function getMemberCompletionContext(text, position) {
96
+ const sliceStart = Math.max(0, position - 200);
97
+ const snippet = text.slice(sliceStart, position);
98
+ const match = constants_1.MEMBER_COMPLETION_PATTERN.exec(snippet);
99
+ if (!match) {
100
+ return undefined;
101
+ }
102
+ return {
103
+ receiver: match[1],
104
+ prefix: match[2] || '',
105
+ };
106
+ }
107
+ function getCompletionKind(symbol, ts) {
108
+ const flags = symbol.flags;
109
+ if (flags & ts.SymbolFlags.Method) {
110
+ return ts.ScriptElementKind.memberFunctionElement;
111
+ }
112
+ if (flags & ts.SymbolFlags.Function) {
113
+ return ts.ScriptElementKind.functionElement;
114
+ }
115
+ if (flags & (ts.SymbolFlags.GetAccessor | ts.SymbolFlags.SetAccessor | ts.SymbolFlags.Property)) {
116
+ return ts.ScriptElementKind.memberVariableElement;
117
+ }
118
+ return ts.ScriptElementKind.memberVariableElement;
119
+ }
120
+ function getIdentifierPrefix(text, position) {
121
+ let start = position;
122
+ while (start > 0 && (0, identifiers_1.isIdentifierPart)(text.charCodeAt(start - 1))) {
123
+ start--;
124
+ }
125
+ const prefix = text.slice(start, position);
126
+ const isMemberAccess = start > 0 && text.charCodeAt(start - 1) === 46;
127
+ return { text: prefix, isMemberAccess };
128
+ }
@@ -0,0 +1,3 @@
1
+ import type { KForPluginConfig, ResolvedConfig } from './types';
2
+ export declare function resolveConfig(config?: KForPluginConfig): ResolvedConfig;
3
+ export declare function isJsxLikeFile(fileName: string): boolean;
package/dist/config.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveConfig = resolveConfig;
7
+ exports.isJsxLikeFile = isJsxLikeFile;
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ function resolveConfig(config) {
10
+ return {
11
+ forAttr: config?.forAttr || 'k-for',
12
+ itemAttr: config?.itemAttr || 'k-for-item',
13
+ indexAttr: config?.indexAttr || 'k-for-index',
14
+ itemName: config?.itemName || 'item',
15
+ indexName: config?.indexName || 'index',
16
+ allowOfKeyword: config?.allowOfKeyword !== false,
17
+ };
18
+ }
19
+ function isJsxLikeFile(fileName) {
20
+ const ext = node_path_1.default.extname(fileName).toLowerCase();
21
+ return ext === '.tsx' || ext === '.jsx';
22
+ }
@@ -0,0 +1,5 @@
1
+ export declare const DIAGNOSTIC_CANNOT_FIND_NAME = 2304;
2
+ export declare const IDENTIFIER_RE: RegExp;
3
+ export declare const KFOR_SINGLE_PATTERN: RegExp;
4
+ export declare const KFOR_TUPLE_PATTERN: RegExp;
5
+ export declare const MEMBER_COMPLETION_PATTERN: RegExp;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MEMBER_COMPLETION_PATTERN = exports.KFOR_TUPLE_PATTERN = exports.KFOR_SINGLE_PATTERN = exports.IDENTIFIER_RE = exports.DIAGNOSTIC_CANNOT_FIND_NAME = void 0;
4
+ exports.DIAGNOSTIC_CANNOT_FIND_NAME = 2304;
5
+ exports.IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
6
+ exports.KFOR_SINGLE_PATTERN = /^([A-Za-z_$][A-Za-z0-9_$]*)\s+(in|of)\s+([\s\S]+)$/;
7
+ exports.KFOR_TUPLE_PATTERN = /^\(\s*([A-Za-z_$][A-Za-z0-9_$]*)(?:\s*,\s*([A-Za-z_$][A-Za-z0-9_$]*))?(?:\s*,\s*([A-Za-z_$][A-Za-z0-9_$]*))?\s*\)\s+(in|of)\s+([\s\S]+)$/;
8
+ exports.MEMBER_COMPLETION_PATTERN = /([A-Za-z_$][A-Za-z0-9_$]*(?:\s*(?:\.\s*[A-Za-z_$][A-Za-z0-9_$]*|\[\s*(?:'[^'\\]*(?:\\.[^'\\]*)*'|"[^"\\]*(?:\\.[^"\\]*)*"|\d+)\s*\]))*)\s*\.\s*([A-Za-z_$][A-Za-z0-9_$]*)?$/;
@@ -0,0 +1,3 @@
1
+ export declare function isValidIdentifier(name: string): boolean;
2
+ export declare function uniqueIdentifiers(names: string[]): string[];
3
+ export declare function isIdentifierPart(code: number): boolean;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isValidIdentifier = isValidIdentifier;
4
+ exports.uniqueIdentifiers = uniqueIdentifiers;
5
+ exports.isIdentifierPart = isIdentifierPart;
6
+ const constants_1 = require("./constants");
7
+ function isValidIdentifier(name) {
8
+ return constants_1.IDENTIFIER_RE.test(name);
9
+ }
10
+ function uniqueIdentifiers(names) {
11
+ const seen = new Set();
12
+ const result = [];
13
+ for (let i = 0; i < names.length; i++) {
14
+ const name = names[i];
15
+ if (!isValidIdentifier(name) || seen.has(name)) {
16
+ continue;
17
+ }
18
+ seen.add(name);
19
+ result.push(name);
20
+ }
21
+ return result;
22
+ }
23
+ function isIdentifierPart(code) {
24
+ return ((code >= 65 && code <= 90) ||
25
+ (code >= 97 && code <= 122) ||
26
+ (code >= 48 && code <= 57) ||
27
+ code === 95 ||
28
+ code === 36);
29
+ }
package/dist/index.js CHANGED
@@ -1,17 +1,16 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- const node_path_1 = __importDefault(require("node:path"));
6
- const DIAGNOSTIC_CANNOT_FIND_NAME = 2304;
7
- const IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
8
- const KFOR_SINGLE_PATTERN = /^([A-Za-z_$][A-Za-z0-9_$]*)\s+(in|of)\s+([\s\S]+)$/;
9
- const KFOR_TUPLE_PATTERN = /^\(\s*([A-Za-z_$][A-Za-z0-9_$]*)(?:\s*,\s*([A-Za-z_$][A-Za-z0-9_$]*))?(?:\s*,\s*([A-Za-z_$][A-Za-z0-9_$]*))?\s*\)\s+(in|of)\s+([\s\S]+)$/;
2
+ const ast_1 = require("./ast");
3
+ const completion_1 = require("./completion");
4
+ const constants_1 = require("./constants");
5
+ const config_1 = require("./config");
6
+ const identifiers_1 = require("./identifiers");
7
+ const scope_analysis_1 = require("./scope-analysis");
8
+ const type_resolution_1 = require("./type-resolution");
10
9
  function init(modules) {
11
10
  const ts = modules.typescript;
12
11
  function create(info) {
13
12
  const languageService = info.languageService;
14
- const config = resolveConfig(info.config);
13
+ const config = (0, config_1.resolveConfig)(info.config);
15
14
  const proxy = Object.create(null);
16
15
  for (const key of Object.keys(languageService)) {
17
16
  const member = languageService[key];
@@ -19,172 +18,92 @@ function init(modules) {
19
18
  }
20
19
  proxy.getSemanticDiagnostics = (fileName) => {
21
20
  const diagnostics = languageService.getSemanticDiagnostics(fileName);
22
- if (!isJsxLikeFile(fileName)) {
21
+ if (!(0, config_1.isJsxLikeFile)(fileName)) {
23
22
  return diagnostics;
24
23
  }
25
- const sourceFile = languageService.getProgram()?.getSourceFile(fileName);
26
- if (!sourceFile) {
24
+ const analysis = (0, scope_analysis_1.getFileAnalysis)(fileName, languageService, ts, config);
25
+ if (!analysis) {
27
26
  return diagnostics;
28
27
  }
29
- const scopes = collectKForScopes(sourceFile, ts, config);
30
- if (scopes.length === 0) {
31
- return diagnostics;
32
- }
33
- return diagnostics.filter((diag) => {
34
- if (diag.code !== DIAGNOSTIC_CANNOT_FIND_NAME || diag.start == null || diag.length == null) {
28
+ return diagnostics.filter((diagnostic) => {
29
+ if (diagnostic.code !== constants_1.DIAGNOSTIC_CANNOT_FIND_NAME ||
30
+ diagnostic.start == null ||
31
+ diagnostic.length == null) {
35
32
  return true;
36
33
  }
37
- const name = sourceFile.text.slice(diag.start, diag.start + diag.length).trim();
38
- if (!isValidIdentifier(name)) {
34
+ const name = analysis.sourceFile.text.slice(diagnostic.start, diagnostic.start + diagnostic.length).trim();
35
+ if (!(0, identifiers_1.isValidIdentifier)(name)) {
39
36
  return true;
40
37
  }
41
- return !isSuppressed(diag.start, name, scopes);
38
+ return !(0, scope_analysis_1.isSuppressed)(diagnostic.start, name, analysis.scopes);
42
39
  });
43
40
  };
44
- return proxy;
45
- }
46
- return { create };
47
- }
48
- function resolveConfig(config) {
49
- return {
50
- forAttr: config?.forAttr || 'k-for',
51
- itemAttr: config?.itemAttr || 'k-for-item',
52
- indexAttr: config?.indexAttr || 'k-for-index',
53
- itemName: config?.itemName || 'item',
54
- indexName: config?.indexName || 'index',
55
- allowOfKeyword: config?.allowOfKeyword !== false,
56
- };
57
- }
58
- function isJsxLikeFile(fileName) {
59
- const ext = node_path_1.default.extname(fileName).toLowerCase();
60
- return ext === '.tsx' || ext === '.jsx';
61
- }
62
- function collectKForScopes(sourceFile, ts, config) {
63
- const scopes = [];
64
- const visit = (node) => {
65
- if (ts.isJsxElement(node)) {
66
- const forAttr = getJsxAttribute(node.openingElement, config.forAttr, ts);
67
- if (forAttr) {
68
- const names = resolveScopeNames(node.openingElement, forAttr, config, ts);
69
- const start = node.openingElement.end;
70
- const end = node.closingElement.getStart(sourceFile);
71
- if (start < end && names.length > 0) {
72
- scopes.push({ start, end, names });
41
+ proxy.getQuickInfoAtPosition = (fileName, position) => {
42
+ const quickInfo = languageService.getQuickInfoAtPosition(fileName, position);
43
+ if (!(0, config_1.isJsxLikeFile)(fileName)) {
44
+ return quickInfo;
45
+ }
46
+ const analysis = (0, scope_analysis_1.getFileAnalysis)(fileName, languageService, ts, config);
47
+ if (!analysis) {
48
+ return quickInfo;
49
+ }
50
+ const identifier = (0, ast_1.findIdentifierAtPosition)(analysis.sourceFile, position, ts);
51
+ if (!identifier) {
52
+ return quickInfo;
53
+ }
54
+ const bindings = (0, scope_analysis_1.collectBindingsAtPosition)(position, analysis.scopes);
55
+ const binding = bindings.get(identifier.text);
56
+ if (!binding) {
57
+ return quickInfo;
58
+ }
59
+ const typeText = (0, type_resolution_1.formatTypeList)(binding.types, analysis.checker, identifier, ts);
60
+ return {
61
+ kind: ts.ScriptElementKind.localVariableElement,
62
+ kindModifiers: '',
63
+ textSpan: {
64
+ start: identifier.getStart(analysis.sourceFile),
65
+ length: identifier.getWidth(analysis.sourceFile),
66
+ },
67
+ displayParts: [{ text: `(k-for) ${binding.name}: ${typeText}`, kind: 'text' }],
68
+ };
69
+ };
70
+ proxy.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
71
+ const completions = languageService.getCompletionsAtPosition(fileName, position, options, formattingSettings);
72
+ if (!(0, config_1.isJsxLikeFile)(fileName)) {
73
+ return completions;
74
+ }
75
+ const analysis = (0, scope_analysis_1.getFileAnalysis)(fileName, languageService, ts, config);
76
+ if (!analysis) {
77
+ return completions;
78
+ }
79
+ const bindings = (0, scope_analysis_1.collectBindingsAtPosition)(position, analysis.scopes);
80
+ if (bindings.size === 0) {
81
+ return completions;
82
+ }
83
+ const contextNode = (0, ast_1.findInnermostNode)(analysis.sourceFile, (0, ast_1.normalizePosition)(position, analysis.sourceFile), ts) ||
84
+ analysis.sourceFile;
85
+ const localBindings = (0, completion_1.createBindingTypeMap)(bindings);
86
+ const memberContext = (0, completion_1.getMemberCompletionContext)(analysis.sourceFile.text, position);
87
+ if (memberContext) {
88
+ const receiverTypes = (0, type_resolution_1.resolveExpressionTypesFromText)(memberContext.receiver, {
89
+ checker: analysis.checker,
90
+ ts,
91
+ scopeNode: contextNode,
92
+ localBindings,
93
+ });
94
+ const memberEntries = (0, completion_1.createMemberCompletionEntries)(receiverTypes, memberContext.prefix, analysis.checker, contextNode, ts);
95
+ if (memberEntries.length > 0) {
96
+ return (0, completion_1.mergeCompletionInfo)(completions, memberEntries, true);
73
97
  }
74
98
  }
75
- }
76
- ts.forEachChild(node, visit);
77
- };
78
- visit(sourceFile);
79
- return scopes;
80
- }
81
- function resolveScopeNames(opening, forAttr, config, ts) {
82
- const forExpression = getAttributeText(forAttr, ts);
83
- if (forExpression !== undefined) {
84
- const parsedNames = parseKForAliases(forExpression, config.allowOfKeyword);
85
- return parsedNames || [];
86
- }
87
- const itemName = getScopeName(opening, config.itemAttr, config.itemName, ts);
88
- const indexName = getScopeName(opening, config.indexAttr, config.indexName, ts);
89
- return uniqueIdentifiers([itemName, indexName]);
90
- }
91
- function getScopeName(opening, attrName, fallback, ts) {
92
- const attr = getJsxAttribute(opening, attrName, ts);
93
- const raw = getAttributeText(attr, ts, true);
94
- if (raw && isValidIdentifier(raw)) {
95
- return raw;
96
- }
97
- return fallback;
98
- }
99
- function getJsxAttribute(opening, attrName, ts) {
100
- const attrs = opening.attributes.properties;
101
- for (let i = 0; i < attrs.length; i++) {
102
- const attr = attrs[i];
103
- if (!ts.isJsxAttribute(attr)) {
104
- continue;
105
- }
106
- if (getAttributeName(attr.name) === attrName) {
107
- return attr;
108
- }
109
- }
110
- return undefined;
111
- }
112
- function getAttributeText(attr, ts, allowIdentifier = false) {
113
- if (!attr || !attr.initializer) {
114
- return undefined;
115
- }
116
- if (ts.isStringLiteral(attr.initializer)) {
117
- return attr.initializer.text;
118
- }
119
- if (!ts.isJsxExpression(attr.initializer) || !attr.initializer.expression) {
120
- return undefined;
121
- }
122
- const expr = attr.initializer.expression;
123
- if (ts.isStringLiteralLike(expr)) {
124
- return expr.text;
125
- }
126
- if (allowIdentifier && ts.isIdentifier(expr)) {
127
- return expr.text;
128
- }
129
- return undefined;
130
- }
131
- function getAttributeName(name) {
132
- if ('text' in name) {
133
- return String(name.text);
134
- }
135
- return name.getText();
136
- }
137
- function isSuppressed(position, diagnosticName, scopes) {
138
- for (let i = scopes.length - 1; i >= 0; i--) {
139
- const scope = scopes[i];
140
- if (position < scope.start || position >= scope.end) {
141
- continue;
142
- }
143
- if (scope.names.includes(diagnosticName)) {
144
- return true;
145
- }
146
- }
147
- return false;
148
- }
149
- function parseKForAliases(raw, allowOfKeyword) {
150
- const value = raw.trim();
151
- if (!value) {
152
- return null;
153
- }
154
- const tupleMatch = KFOR_TUPLE_PATTERN.exec(value);
155
- if (tupleMatch) {
156
- const keyword = tupleMatch[4];
157
- const source = tupleMatch[5]?.trim();
158
- if ((!allowOfKeyword && keyword === 'of') || !source) {
159
- return null;
160
- }
161
- return uniqueIdentifiers([tupleMatch[1], tupleMatch[2], tupleMatch[3]].filter(Boolean));
162
- }
163
- const singleMatch = KFOR_SINGLE_PATTERN.exec(value);
164
- if (singleMatch) {
165
- const keyword = singleMatch[2];
166
- const source = singleMatch[3]?.trim();
167
- if ((!allowOfKeyword && keyword === 'of') || !source) {
168
- return null;
169
- }
170
- return uniqueIdentifiers([singleMatch[1]]);
171
- }
172
- return null;
173
- }
174
- function isValidIdentifier(name) {
175
- return IDENTIFIER_RE.test(name);
176
- }
177
- function uniqueIdentifiers(names) {
178
- const seen = new Set();
179
- const result = [];
180
- for (let i = 0; i < names.length; i++) {
181
- const name = names[i];
182
- if (!isValidIdentifier(name) || seen.has(name)) {
183
- continue;
184
- }
185
- seen.add(name);
186
- result.push(name);
99
+ const aliasEntries = (0, completion_1.createAliasCompletionEntries)(bindings, analysis.sourceFile.text, position, ts);
100
+ if (aliasEntries.length > 0) {
101
+ return (0, completion_1.mergeCompletionInfo)(completions, aliasEntries, false);
102
+ }
103
+ return completions;
104
+ };
105
+ return proxy;
187
106
  }
188
- return result;
107
+ return { create };
189
108
  }
190
109
  module.exports = init;
@@ -0,0 +1,6 @@
1
+ import type tsModule from 'typescript/lib/tsserverlibrary';
2
+ import type { JsxOpeningLikeElement } from './types';
3
+ export declare function getScopeName(opening: JsxOpeningLikeElement, attrName: string, fallback: string, ts: typeof tsModule): string;
4
+ export declare function getJsxAttribute(opening: tsModule.JsxOpeningElement | tsModule.JsxSelfClosingElement, attrName: string, ts: typeof tsModule): tsModule.JsxAttribute | undefined;
5
+ export declare function getAttributeText(attr: tsModule.JsxAttribute | undefined, ts: typeof tsModule, allowIdentifier?: boolean): string | undefined;
6
+ export declare function getAttributeExpression(attr: tsModule.JsxAttribute | undefined, ts: typeof tsModule): tsModule.Expression | undefined;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getScopeName = getScopeName;
4
+ exports.getJsxAttribute = getJsxAttribute;
5
+ exports.getAttributeText = getAttributeText;
6
+ exports.getAttributeExpression = getAttributeExpression;
7
+ const identifiers_1 = require("./identifiers");
8
+ function getScopeName(opening, attrName, fallback, ts) {
9
+ const attr = getJsxAttribute(opening, attrName, ts);
10
+ const raw = getAttributeText(attr, ts, true);
11
+ if (raw && (0, identifiers_1.isValidIdentifier)(raw)) {
12
+ return raw;
13
+ }
14
+ return fallback;
15
+ }
16
+ function getJsxAttribute(opening, attrName, ts) {
17
+ const attrs = opening.attributes.properties;
18
+ for (let i = 0; i < attrs.length; i++) {
19
+ const attr = attrs[i];
20
+ if (!ts.isJsxAttribute(attr)) {
21
+ continue;
22
+ }
23
+ if (getAttributeName(attr.name) === attrName) {
24
+ return attr;
25
+ }
26
+ }
27
+ return undefined;
28
+ }
29
+ function getAttributeText(attr, ts, allowIdentifier = false) {
30
+ if (!attr || !attr.initializer) {
31
+ return undefined;
32
+ }
33
+ if (ts.isStringLiteral(attr.initializer)) {
34
+ return attr.initializer.text;
35
+ }
36
+ if (!ts.isJsxExpression(attr.initializer) || !attr.initializer.expression) {
37
+ return undefined;
38
+ }
39
+ const expr = attr.initializer.expression;
40
+ if (ts.isStringLiteralLike(expr)) {
41
+ return expr.text;
42
+ }
43
+ if (allowIdentifier && ts.isIdentifier(expr)) {
44
+ return expr.text;
45
+ }
46
+ return undefined;
47
+ }
48
+ function getAttributeExpression(attr, ts) {
49
+ if (!attr?.initializer || !ts.isJsxExpression(attr.initializer) || !attr.initializer.expression) {
50
+ return undefined;
51
+ }
52
+ return attr.initializer.expression;
53
+ }
54
+ function getAttributeName(name) {
55
+ if ('text' in name) {
56
+ return String(name.text);
57
+ }
58
+ return name.getText();
59
+ }
@@ -0,0 +1,2 @@
1
+ import type { ParsedKForExpression } from './types';
2
+ export declare function parseKForExpression(raw: string, allowOfKeyword: boolean): ParsedKForExpression | null;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseKForExpression = parseKForExpression;
4
+ const constants_1 = require("./constants");
5
+ const identifiers_1 = require("./identifiers");
6
+ function parseKForExpression(raw, allowOfKeyword) {
7
+ const value = raw.trim();
8
+ if (!value) {
9
+ return null;
10
+ }
11
+ const tupleMatch = constants_1.KFOR_TUPLE_PATTERN.exec(value);
12
+ if (tupleMatch) {
13
+ const keyword = tupleMatch[4];
14
+ const source = tupleMatch[5]?.trim();
15
+ if ((!allowOfKeyword && keyword === 'of') || !source) {
16
+ return null;
17
+ }
18
+ return {
19
+ aliases: (0, identifiers_1.uniqueIdentifiers)([tupleMatch[1], tupleMatch[2], tupleMatch[3]].filter(Boolean)),
20
+ source,
21
+ };
22
+ }
23
+ const singleMatch = constants_1.KFOR_SINGLE_PATTERN.exec(value);
24
+ if (singleMatch) {
25
+ const keyword = singleMatch[2];
26
+ const source = singleMatch[3]?.trim();
27
+ if ((!allowOfKeyword && keyword === 'of') || !source) {
28
+ return null;
29
+ }
30
+ return {
31
+ aliases: (0, identifiers_1.uniqueIdentifiers)([singleMatch[1]]),
32
+ source,
33
+ };
34
+ }
35
+ return null;
36
+ }
@@ -0,0 +1,5 @@
1
+ import type tsModule from 'typescript/lib/tsserverlibrary';
2
+ import type { FileAnalysis, KForBinding, KForScope, ResolvedConfig } from './types';
3
+ export declare function getFileAnalysis(fileName: string, languageService: tsModule.LanguageService, ts: typeof tsModule, config: ResolvedConfig): FileAnalysis | undefined;
4
+ export declare function isSuppressed(position: number, diagnosticName: string, scopes: KForScope[]): boolean;
5
+ export declare function collectBindingsAtPosition(position: number, scopes: KForScope[]): Map<string, KForBinding>;
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFileAnalysis = getFileAnalysis;
4
+ exports.isSuppressed = isSuppressed;
5
+ exports.collectBindingsAtPosition = collectBindingsAtPosition;
6
+ const jsx_attributes_1 = require("./jsx-attributes");
7
+ const kfor_parser_1 = require("./kfor-parser");
8
+ const identifiers_1 = require("./identifiers");
9
+ const type_resolution_1 = require("./type-resolution");
10
+ function getFileAnalysis(fileName, languageService, ts, config) {
11
+ const program = languageService.getProgram();
12
+ if (!program) {
13
+ return undefined;
14
+ }
15
+ const sourceFile = program.getSourceFile(fileName);
16
+ if (!sourceFile) {
17
+ return undefined;
18
+ }
19
+ const checker = program.getTypeChecker();
20
+ const scopes = collectKForScopes(sourceFile, checker, ts, config);
21
+ if (scopes.length === 0) {
22
+ return undefined;
23
+ }
24
+ return { sourceFile, checker, scopes };
25
+ }
26
+ function isSuppressed(position, diagnosticName, scopes) {
27
+ for (let i = scopes.length - 1; i >= 0; i--) {
28
+ const scope = scopes[i];
29
+ if (position < scope.start || position >= scope.end) {
30
+ continue;
31
+ }
32
+ for (let j = 0; j < scope.bindings.length; j++) {
33
+ if (scope.bindings[j].name === diagnosticName) {
34
+ return true;
35
+ }
36
+ }
37
+ }
38
+ return false;
39
+ }
40
+ function collectBindingsAtPosition(position, scopes) {
41
+ const bindings = new Map();
42
+ for (let i = scopes.length - 1; i >= 0; i--) {
43
+ const scope = scopes[i];
44
+ if (position < scope.start || position >= scope.end) {
45
+ continue;
46
+ }
47
+ for (let j = 0; j < scope.bindings.length; j++) {
48
+ const binding = scope.bindings[j];
49
+ if (!bindings.has(binding.name)) {
50
+ bindings.set(binding.name, binding);
51
+ }
52
+ }
53
+ }
54
+ return bindings;
55
+ }
56
+ function collectKForScopes(sourceFile, checker, ts, config) {
57
+ const scopes = [];
58
+ const visit = (node) => {
59
+ if (ts.isJsxElement(node)) {
60
+ const forAttr = (0, jsx_attributes_1.getJsxAttribute)(node.openingElement, config.forAttr, ts);
61
+ if (forAttr) {
62
+ const bindings = resolveScopeBindings(node.openingElement, forAttr, checker, config, ts);
63
+ const start = node.openingElement.end;
64
+ const end = node.closingElement.getStart(sourceFile);
65
+ if (start < end && bindings.length > 0) {
66
+ scopes.push({ start, end, bindings });
67
+ }
68
+ }
69
+ }
70
+ ts.forEachChild(node, visit);
71
+ };
72
+ visit(sourceFile);
73
+ return scopes;
74
+ }
75
+ function resolveScopeBindings(opening, forAttr, checker, config, ts) {
76
+ const forExpression = (0, jsx_attributes_1.getAttributeText)(forAttr, ts);
77
+ if (forExpression !== undefined) {
78
+ const parsed = (0, kfor_parser_1.parseKForExpression)(forExpression, config.allowOfKeyword);
79
+ if (parsed) {
80
+ const sourceTypes = (0, type_resolution_1.resolveExpressionTypesFromText)(parsed.source, {
81
+ checker,
82
+ ts,
83
+ scopeNode: opening,
84
+ });
85
+ return createBindings(parsed.aliases, sourceTypes, checker, opening, ts);
86
+ }
87
+ }
88
+ const itemName = (0, jsx_attributes_1.getScopeName)(opening, config.itemAttr, config.itemName, ts);
89
+ const indexName = (0, jsx_attributes_1.getScopeName)(opening, config.indexAttr, config.indexName, ts);
90
+ const aliases = (0, identifiers_1.uniqueIdentifiers)([itemName, indexName]);
91
+ const sourceTypes = getLegacyForSourceTypes(forAttr, checker, ts);
92
+ return createBindings(aliases, sourceTypes, checker, opening, ts);
93
+ }
94
+ function createBindings(names, sourceTypes, checker, scopeNode, ts) {
95
+ if (names.length === 0) {
96
+ return [];
97
+ }
98
+ const inferred = inferBindingTypes(sourceTypes, names.length, checker, scopeNode, ts);
99
+ const bindings = [];
100
+ for (let i = 0; i < names.length; i++) {
101
+ bindings.push({
102
+ name: names[i],
103
+ types: inferred[i] || [],
104
+ });
105
+ }
106
+ return bindings;
107
+ }
108
+ function inferBindingTypes(sourceTypes, bindingCount, checker, scopeNode, ts) {
109
+ const slots = Array.from({ length: bindingCount }, () => []);
110
+ const candidates = expandUnionTypes(sourceTypes, ts);
111
+ for (let i = 0; i < candidates.length; i++) {
112
+ const sourceType = checker.getApparentType(candidates[i]);
113
+ const elementType = checker.getIndexTypeOfType(sourceType, ts.IndexKind.Number);
114
+ const stringValueType = elementType ? undefined : checker.getIndexTypeOfType(sourceType, ts.IndexKind.String);
115
+ const valueTypes = elementType ? [elementType] : stringValueType ? [stringValueType] : [];
116
+ if (valueTypes.length === 0) {
117
+ continue;
118
+ }
119
+ slots[0].push(...valueTypes);
120
+ if (bindingCount > 1) {
121
+ slots[1].push(elementType ? checker.getNumberType() : checker.getStringType());
122
+ }
123
+ if (bindingCount > 2) {
124
+ slots[2].push(checker.getNumberType());
125
+ }
126
+ }
127
+ for (let i = 0; i < slots.length; i++) {
128
+ slots[i] = (0, type_resolution_1.uniqueTypes)(slots[i], checker, scopeNode, ts);
129
+ }
130
+ return slots;
131
+ }
132
+ function expandUnionTypes(types, ts) {
133
+ const result = [];
134
+ for (let i = 0; i < types.length; i++) {
135
+ const type = types[i];
136
+ if (type.flags & ts.TypeFlags.Union) {
137
+ const union = type;
138
+ result.push(...union.types);
139
+ continue;
140
+ }
141
+ result.push(type);
142
+ }
143
+ return result;
144
+ }
145
+ function getLegacyForSourceTypes(forAttr, checker, ts) {
146
+ const expression = (0, jsx_attributes_1.getAttributeExpression)(forAttr, ts);
147
+ if (!expression || ts.isStringLiteralLike(expression)) {
148
+ return [];
149
+ }
150
+ return [checker.getTypeAtLocation(expression)];
151
+ }
@@ -0,0 +1,5 @@
1
+ import type tsModule from 'typescript/lib/tsserverlibrary';
2
+ import type { TypeResolutionContext } from './types';
3
+ export declare function resolveExpressionTypesFromText(raw: string, context: TypeResolutionContext): tsModule.Type[];
4
+ export declare function formatTypeList(types: tsModule.Type[], checker: tsModule.TypeChecker, scopeNode: tsModule.Node, ts: typeof tsModule): string;
5
+ export declare function uniqueTypes(types: tsModule.Type[], checker: tsModule.TypeChecker, scopeNode: tsModule.Node, ts: typeof tsModule): tsModule.Type[];
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveExpressionTypesFromText = resolveExpressionTypesFromText;
4
+ exports.formatTypeList = formatTypeList;
5
+ exports.uniqueTypes = uniqueTypes;
6
+ function resolveExpressionTypesFromText(raw, context) {
7
+ const value = raw.trim();
8
+ if (!value) {
9
+ return [];
10
+ }
11
+ const sourceFile = context.ts.createSourceFile('__k_for_expression.ts', `(${value});`, context.ts.ScriptTarget.Latest, true, context.ts.ScriptKind.TS);
12
+ if (sourceFile.statements.length === 0) {
13
+ return [];
14
+ }
15
+ const statement = sourceFile.statements[0];
16
+ if (!context.ts.isExpressionStatement(statement)) {
17
+ return [];
18
+ }
19
+ const types = resolveExpressionTypes(statement.expression, context);
20
+ return uniqueTypes(types, context.checker, context.scopeNode, context.ts);
21
+ }
22
+ function formatTypeList(types, checker, scopeNode, ts) {
23
+ if (types.length === 0) {
24
+ return 'any';
25
+ }
26
+ const texts = new Set();
27
+ for (let i = 0; i < types.length; i++) {
28
+ texts.add(checker.typeToString(types[i], scopeNode, ts.TypeFormatFlags.NoTruncation));
29
+ }
30
+ return Array.from(texts).join(' | ');
31
+ }
32
+ function uniqueTypes(types, checker, scopeNode, ts) {
33
+ const seen = new Set();
34
+ const result = [];
35
+ for (let i = 0; i < types.length; i++) {
36
+ const type = types[i];
37
+ const text = checker.typeToString(type, scopeNode, ts.TypeFormatFlags.NoTruncation);
38
+ if (seen.has(text)) {
39
+ continue;
40
+ }
41
+ seen.add(text);
42
+ result.push(type);
43
+ }
44
+ return result;
45
+ }
46
+ function resolveExpressionTypes(expr, context) {
47
+ const ts = context.ts;
48
+ const target = unwrapExpression(expr, ts);
49
+ if (ts.isIdentifier(target)) {
50
+ return resolveIdentifierTypes(target.text, context);
51
+ }
52
+ if (ts.isPropertyAccessExpression(target)) {
53
+ const objectTypes = resolveExpressionTypes(target.expression, context);
54
+ return resolvePropertyTypes(objectTypes, target.name.text, context, false);
55
+ }
56
+ if (ts.isElementAccessExpression(target)) {
57
+ const objectTypes = resolveExpressionTypes(target.expression, context);
58
+ const argument = target.argumentExpression;
59
+ if (!argument) {
60
+ return [];
61
+ }
62
+ if (ts.isStringLiteralLike(argument)) {
63
+ return resolvePropertyTypes(objectTypes, argument.text, context, true);
64
+ }
65
+ if (ts.isNumericLiteral(argument)) {
66
+ return resolveNumericElementTypes(objectTypes, Number(argument.text), context);
67
+ }
68
+ return resolveIndexedTypes(objectTypes, context);
69
+ }
70
+ if (ts.isCallExpression(target)) {
71
+ const calleeTypes = resolveExpressionTypes(target.expression, context);
72
+ const result = [];
73
+ for (let i = 0; i < calleeTypes.length; i++) {
74
+ const signatures = context.checker.getSignaturesOfType(calleeTypes[i], ts.SignatureKind.Call);
75
+ for (let j = 0; j < signatures.length; j++) {
76
+ result.push(context.checker.getReturnTypeOfSignature(signatures[j]));
77
+ }
78
+ }
79
+ return uniqueTypes(result, context.checker, context.scopeNode, ts);
80
+ }
81
+ if (ts.isConditionalExpression(target)) {
82
+ const whenTrue = resolveExpressionTypes(target.whenTrue, context);
83
+ const whenFalse = resolveExpressionTypes(target.whenFalse, context);
84
+ return uniqueTypes([...whenTrue, ...whenFalse], context.checker, context.scopeNode, ts);
85
+ }
86
+ if (ts.isBinaryExpression(target) &&
87
+ (target.operatorToken.kind === ts.SyntaxKind.BarBarToken ||
88
+ target.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
89
+ target.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken)) {
90
+ const left = resolveExpressionTypes(target.left, context);
91
+ const right = resolveExpressionTypes(target.right, context);
92
+ return uniqueTypes([...left, ...right], context.checker, context.scopeNode, ts);
93
+ }
94
+ return [];
95
+ }
96
+ function unwrapExpression(expr, ts) {
97
+ let current = expr;
98
+ while (true) {
99
+ if (ts.isParenthesizedExpression(current)) {
100
+ current = current.expression;
101
+ continue;
102
+ }
103
+ if (ts.isAsExpression(current) || ts.isTypeAssertionExpression(current) || ts.isSatisfiesExpression(current)) {
104
+ current = current.expression;
105
+ continue;
106
+ }
107
+ if (ts.isNonNullExpression(current)) {
108
+ current = current.expression;
109
+ continue;
110
+ }
111
+ return current;
112
+ }
113
+ }
114
+ function resolveIdentifierTypes(name, context) {
115
+ const localTypes = context.localBindings?.get(name);
116
+ if (localTypes && localTypes.length > 0) {
117
+ return [...localTypes];
118
+ }
119
+ const symbol = resolveSymbolInScope(name, context);
120
+ if (!symbol) {
121
+ return [];
122
+ }
123
+ return [context.checker.getTypeOfSymbolAtLocation(symbol, context.scopeNode)];
124
+ }
125
+ function resolveSymbolInScope(name, context) {
126
+ const symbols = context.checker.getSymbolsInScope(context.scopeNode, context.ts.SymbolFlags.Value | context.ts.SymbolFlags.Alias);
127
+ for (let i = 0; i < symbols.length; i++) {
128
+ const symbol = symbols[i];
129
+ if (symbol.getName() !== name) {
130
+ continue;
131
+ }
132
+ if (symbol.flags & context.ts.SymbolFlags.Alias) {
133
+ const aliased = context.checker.getAliasedSymbol(symbol);
134
+ if (aliased.flags & context.ts.SymbolFlags.Value) {
135
+ return aliased;
136
+ }
137
+ continue;
138
+ }
139
+ if (symbol.flags & context.ts.SymbolFlags.Value) {
140
+ return symbol;
141
+ }
142
+ }
143
+ return undefined;
144
+ }
145
+ function resolvePropertyTypes(objectTypes, propertyName, context, allowStringIndexFallback) {
146
+ const result = [];
147
+ for (let i = 0; i < objectTypes.length; i++) {
148
+ const targetType = context.checker.getApparentType(objectTypes[i]);
149
+ const property = context.checker.getPropertyOfType(targetType, propertyName);
150
+ if (property) {
151
+ result.push(context.checker.getTypeOfSymbolAtLocation(property, context.scopeNode));
152
+ continue;
153
+ }
154
+ if (allowStringIndexFallback) {
155
+ const stringIndexType = context.checker.getIndexTypeOfType(targetType, context.ts.IndexKind.String);
156
+ if (stringIndexType) {
157
+ result.push(stringIndexType);
158
+ }
159
+ }
160
+ }
161
+ return uniqueTypes(result, context.checker, context.scopeNode, context.ts);
162
+ }
163
+ function resolveNumericElementTypes(objectTypes, index, context) {
164
+ const result = [];
165
+ const indexName = String(index);
166
+ for (let i = 0; i < objectTypes.length; i++) {
167
+ const targetType = context.checker.getApparentType(objectTypes[i]);
168
+ const property = context.checker.getPropertyOfType(targetType, indexName);
169
+ if (property) {
170
+ result.push(context.checker.getTypeOfSymbolAtLocation(property, context.scopeNode));
171
+ }
172
+ const numericIndexType = context.checker.getIndexTypeOfType(targetType, context.ts.IndexKind.Number);
173
+ if (numericIndexType) {
174
+ result.push(numericIndexType);
175
+ }
176
+ }
177
+ return uniqueTypes(result, context.checker, context.scopeNode, context.ts);
178
+ }
179
+ function resolveIndexedTypes(objectTypes, context) {
180
+ const result = [];
181
+ for (let i = 0; i < objectTypes.length; i++) {
182
+ const targetType = context.checker.getApparentType(objectTypes[i]);
183
+ const stringIndexType = context.checker.getIndexTypeOfType(targetType, context.ts.IndexKind.String);
184
+ if (stringIndexType) {
185
+ result.push(stringIndexType);
186
+ }
187
+ const numericIndexType = context.checker.getIndexTypeOfType(targetType, context.ts.IndexKind.Number);
188
+ if (numericIndexType) {
189
+ result.push(numericIndexType);
190
+ }
191
+ }
192
+ return uniqueTypes(result, context.checker, context.scopeNode, context.ts);
193
+ }
@@ -0,0 +1,46 @@
1
+ import type tsModule from 'typescript/lib/tsserverlibrary';
2
+ export interface KForPluginConfig {
3
+ forAttr?: string;
4
+ itemAttr?: string;
5
+ indexAttr?: string;
6
+ itemName?: string;
7
+ indexName?: string;
8
+ allowOfKeyword?: boolean;
9
+ }
10
+ export interface ResolvedConfig {
11
+ forAttr: string;
12
+ itemAttr: string;
13
+ indexAttr: string;
14
+ itemName: string;
15
+ indexName: string;
16
+ allowOfKeyword: boolean;
17
+ }
18
+ export interface KForBinding {
19
+ name: string;
20
+ types: tsModule.Type[];
21
+ }
22
+ export interface KForScope {
23
+ start: number;
24
+ end: number;
25
+ bindings: KForBinding[];
26
+ }
27
+ export interface ParsedKForExpression {
28
+ aliases: string[];
29
+ source: string;
30
+ }
31
+ export interface TypeResolutionContext {
32
+ checker: tsModule.TypeChecker;
33
+ ts: typeof tsModule;
34
+ scopeNode: tsModule.Node;
35
+ localBindings?: ReadonlyMap<string, readonly tsModule.Type[]>;
36
+ }
37
+ export interface MemberCompletionContext {
38
+ receiver: string;
39
+ prefix: string;
40
+ }
41
+ export interface FileAnalysis {
42
+ sourceFile: tsModule.SourceFile;
43
+ checker: tsModule.TypeChecker;
44
+ scopes: KForScope[];
45
+ }
46
+ export type JsxOpeningLikeElement = tsModule.JsxOpeningElement | tsModule.JsxSelfClosingElement;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@ktjs/ts-plugin",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "TypeScript language service plugin for kt.js k-for scope names in TSX.",
5
5
  "type": "commonjs",
6
- "main": "./dist/index.cjs",
7
- "types": "./dist/index.d.cts",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./dist/index.d.cts",
11
- "require": "./dist/index.cjs",
12
- "default": "./dist/index.cjs"
10
+ "types": "./dist/index.d.ts",
11
+ "require": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
13
  }
14
14
  },
15
15
  "files": [