koppajs-language-core 0.1.0

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +201 -0
  3. package/README.md +122 -0
  4. package/dist/data/htmlAttributes.d.ts +5 -0
  5. package/dist/data/htmlAttributes.js +393 -0
  6. package/dist/data/htmlAttributes.js.map +1 -0
  7. package/dist/data/htmlTags.d.ts +5 -0
  8. package/dist/data/htmlTags.js +562 -0
  9. package/dist/data/htmlTags.js.map +1 -0
  10. package/dist/data/kpaBlocks.d.ts +12 -0
  11. package/dist/data/kpaBlocks.js +55 -0
  12. package/dist/data/kpaBlocks.js.map +1 -0
  13. package/dist/diagnosticsEngine.d.ts +4 -0
  14. package/dist/diagnosticsEngine.js +11 -0
  15. package/dist/diagnosticsEngine.js.map +1 -0
  16. package/dist/index.d.ts +15 -0
  17. package/dist/index.js +47 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/language/ast.d.ts +31 -0
  20. package/dist/language/ast.js +3 -0
  21. package/dist/language/ast.js.map +1 -0
  22. package/dist/language/componentContract.d.ts +22 -0
  23. package/dist/language/componentContract.js +273 -0
  24. package/dist/language/componentContract.js.map +1 -0
  25. package/dist/language/core.d.ts +14 -0
  26. package/dist/language/core.js +46 -0
  27. package/dist/language/core.js.map +1 -0
  28. package/dist/language/diagnosticCodes.d.ts +21 -0
  29. package/dist/language/diagnosticCodes.js +14 -0
  30. package/dist/language/diagnosticCodes.js.map +1 -0
  31. package/dist/language/diagnosticsRules.d.ts +12 -0
  32. package/dist/language/diagnosticsRules.js +58 -0
  33. package/dist/language/diagnosticsRules.js.map +1 -0
  34. package/dist/language/documentModel.d.ts +3 -0
  35. package/dist/language/documentModel.js +29 -0
  36. package/dist/language/documentModel.js.map +1 -0
  37. package/dist/language/localSymbolReferences.d.ts +8 -0
  38. package/dist/language/localSymbolReferences.js +199 -0
  39. package/dist/language/localSymbolReferences.js.map +1 -0
  40. package/dist/language/parser.d.ts +2 -0
  41. package/dist/language/parser.js +94 -0
  42. package/dist/language/parser.js.map +1 -0
  43. package/dist/language/projectConfig.d.ts +14 -0
  44. package/dist/language/projectConfig.js +154 -0
  45. package/dist/language/projectConfig.js.map +1 -0
  46. package/dist/language/sourcePositions.d.ts +4 -0
  47. package/dist/language/sourcePositions.js +50 -0
  48. package/dist/language/sourcePositions.js.map +1 -0
  49. package/dist/language/symbols.d.ts +25 -0
  50. package/dist/language/symbols.js +191 -0
  51. package/dist/language/symbols.js.map +1 -0
  52. package/dist/language/templateComponents.d.ts +83 -0
  53. package/dist/language/templateComponents.js +1248 -0
  54. package/dist/language/templateComponents.js.map +1 -0
  55. package/dist/language/templateDiagnosticsRules.d.ts +3 -0
  56. package/dist/language/templateDiagnosticsRules.js +58 -0
  57. package/dist/language/templateDiagnosticsRules.js.map +1 -0
  58. package/dist/language/templateExpressions.d.ts +23 -0
  59. package/dist/language/templateExpressions.js +361 -0
  60. package/dist/language/templateExpressions.js.map +1 -0
  61. package/dist/language/templateLoopScopes.d.ts +3 -0
  62. package/dist/language/templateLoopScopes.js +347 -0
  63. package/dist/language/templateLoopScopes.js.map +1 -0
  64. package/dist/language/templateSemantics.d.ts +38 -0
  65. package/dist/language/templateSemantics.js +373 -0
  66. package/dist/language/templateSemantics.js.map +1 -0
  67. package/dist/language/workspaceIndex.d.ts +37 -0
  68. package/dist/language/workspaceIndex.js +195 -0
  69. package/dist/language/workspaceIndex.js.map +1 -0
  70. package/dist/language/workspaceRegistrations.d.ts +10 -0
  71. package/dist/language/workspaceRegistrations.js +190 -0
  72. package/dist/language/workspaceRegistrations.js.map +1 -0
  73. package/dist/language/workspaceSymbols.d.ts +11 -0
  74. package/dist/language/workspaceSymbols.js +35 -0
  75. package/dist/language/workspaceSymbols.js.map +1 -0
  76. package/dist/service.d.ts +79 -0
  77. package/dist/service.js +919 -0
  78. package/dist/service.js.map +1 -0
  79. package/dist/utils/blockUtils.d.ts +1 -0
  80. package/dist/utils/blockUtils.js +9 -0
  81. package/dist/utils/blockUtils.js.map +1 -0
  82. package/dist/utils/htmlUtils.d.ts +2 -0
  83. package/dist/utils/htmlUtils.js +65 -0
  84. package/dist/utils/htmlUtils.js.map +1 -0
  85. package/dist/utils/templateContext.d.ts +14 -0
  86. package/dist/utils/templateContext.js +41 -0
  87. package/dist/utils/templateContext.js.map +1 -0
  88. package/dist/workspaceGraph.d.ts +28 -0
  89. package/dist/workspaceGraph.js +221 -0
  90. package/dist/workspaceGraph.js.map +1 -0
  91. package/package.json +44 -0
@@ -0,0 +1,1248 @@
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.collectImportedKpaComponents = collectImportedKpaComponents;
7
+ exports.collectCanonicalTemplateTags = collectCanonicalTemplateTags;
8
+ exports.collectCanonicalTemplateComponentUsages = collectCanonicalTemplateComponentUsages;
9
+ exports.collectKnownTemplateComponents = collectKnownTemplateComponents;
10
+ exports.collectWorkspaceTemplateComponentUsagesForResolvedFile = collectWorkspaceTemplateComponentUsagesForResolvedFile;
11
+ exports.getCanonicalTemplateTagAtOffset = getCanonicalTemplateTagAtOffset;
12
+ exports.resolveCanonicalTemplateComponentAtOffset = resolveCanonicalTemplateComponentAtOffset;
13
+ exports.findImportedKpaComponentForSymbol = findImportedKpaComponentForSymbol;
14
+ exports.getImportedKpaComponentLocalSymbols = getImportedKpaComponentLocalSymbols;
15
+ exports.collectCanonicalTemplateTagsForComponent = collectCanonicalTemplateTagsForComponent;
16
+ exports.normalizeComponentRenameTarget = normalizeComponentRenameTarget;
17
+ exports.getImportedKpaComponentApi = getImportedKpaComponentApi;
18
+ exports.getResolvedTemplateComponentApi = getResolvedTemplateComponentApi;
19
+ exports.collectCanonicalTemplateComponentDiagnostics = collectCanonicalTemplateComponentDiagnostics;
20
+ exports.resolveTemplateComponentAtOffset = resolveTemplateComponentAtOffset;
21
+ exports.toKebabCase = toKebabCase;
22
+ const fs_1 = __importDefault(require("fs"));
23
+ const path_1 = __importDefault(require("path"));
24
+ const typescript_1 = __importDefault(require("typescript"));
25
+ const kpaBlocks_1 = require("../data/kpaBlocks");
26
+ const diagnosticCodes_1 = require("./diagnosticCodes");
27
+ const localSymbolReferences_1 = require("./localSymbolReferences");
28
+ const parser_1 = require("./parser");
29
+ const projectConfig_1 = require("./projectConfig");
30
+ const sourcePositions_1 = require("./sourcePositions");
31
+ const componentContract_1 = require("./componentContract");
32
+ const symbols_1 = require("./symbols");
33
+ const workspaceRegistrations_1 = require("./workspaceRegistrations");
34
+ const componentApiCache = new Map();
35
+ const allowedPassthroughComponentAttributes = new Set([
36
+ 'else',
37
+ 'else-if',
38
+ 'aria-label',
39
+ 'class',
40
+ 'id',
41
+ 'if',
42
+ 'key',
43
+ 'loop',
44
+ 'ref',
45
+ 'slot',
46
+ 'style',
47
+ ]);
48
+ function collectImportedKpaComponents(document, sourcePath) {
49
+ return document.blocks.flatMap((block) => isScriptBlock(block) ? collectComponentsFromScriptBlock(document, block, sourcePath) : []);
50
+ }
51
+ function collectCanonicalTemplateTags(document) {
52
+ return document.blocks.flatMap((block) => block.name === kpaBlocks_1.canonicalTemplateBlock
53
+ ? collectTemplateTagMatchesFromBlock(document, block).map((match) => createTemplateTag(document, block, match))
54
+ : []);
55
+ }
56
+ function collectCanonicalTemplateComponentUsages(document, sourcePath) {
57
+ const componentsByTagName = new Map();
58
+ for (const component of collectImportedKpaComponents(document, sourcePath)) {
59
+ for (const tagName of component.tagNames) {
60
+ componentsByTagName.set(tagName, component);
61
+ }
62
+ }
63
+ return document.blocks.flatMap((block) => {
64
+ if (block.name !== kpaBlocks_1.canonicalTemplateBlock) {
65
+ return [];
66
+ }
67
+ return collectTemplateTagMatchesFromBlock(document, block).flatMap((match) => {
68
+ if (match.isClosing) {
69
+ return [];
70
+ }
71
+ const component = componentsByTagName.get(match.name);
72
+ if (!component) {
73
+ return [];
74
+ }
75
+ return [
76
+ {
77
+ attributes: match.attributes.map((attribute) => createTemplateAttribute(document, block, attribute)),
78
+ component,
79
+ insertOffset: block.contentRange.start.offset + match.insertionIndex,
80
+ tag: createTemplateTag(document, block, match),
81
+ },
82
+ ];
83
+ });
84
+ });
85
+ }
86
+ function collectKnownTemplateComponents(document, sourcePath) {
87
+ const components = [];
88
+ const seenTagNames = new Set();
89
+ for (const component of collectImportedKpaComponents(document, sourcePath)) {
90
+ components.push({
91
+ importedComponent: component,
92
+ importPath: component.importPath,
93
+ kind: 'imported',
94
+ name: component.name,
95
+ resolvedFilePath: component.resolvedFilePath,
96
+ tagNames: component.tagNames,
97
+ });
98
+ for (const tagName of component.tagNames) {
99
+ seenTagNames.add(tagName);
100
+ }
101
+ }
102
+ for (const registration of (0, workspaceRegistrations_1.collectWorkspaceComponentRegistrations)(sourcePath)) {
103
+ if (seenTagNames.has(registration.tagName)) {
104
+ continue;
105
+ }
106
+ seenTagNames.add(registration.tagName);
107
+ components.push({
108
+ kind: 'workspace-registration',
109
+ name: registration.componentName,
110
+ registration,
111
+ resolvedFilePath: registration.resolvedFilePath,
112
+ tagNames: [registration.tagName],
113
+ });
114
+ }
115
+ return components;
116
+ }
117
+ function collectCanonicalTemplateComponentCallSites(document, sourcePath) {
118
+ const componentsByTagName = createKnownTemplateComponentsByTagName(document, sourcePath);
119
+ return document.blocks.flatMap((block) => {
120
+ if (block.name !== kpaBlocks_1.canonicalTemplateBlock) {
121
+ return [];
122
+ }
123
+ const matches = collectTemplateTagMatchesFromBlock(document, block);
124
+ const callSites = [];
125
+ const openTagStack = [];
126
+ for (const match of matches) {
127
+ const component = componentsByTagName.get(match.name);
128
+ if (!component) {
129
+ continue;
130
+ }
131
+ if (match.isClosing) {
132
+ const matchingOpenIndex = findMatchingOpenComponentIndex(openTagStack, component, match.name);
133
+ if (matchingOpenIndex === -1) {
134
+ continue;
135
+ }
136
+ const openTag = openTagStack.splice(matchingOpenIndex, 1)[0];
137
+ const openingUsage = createCanonicalTemplateComponentCallSite(document, block, openTag.match, component, match);
138
+ callSites.push(openingUsage);
139
+ continue;
140
+ }
141
+ if (match.isSelfClosing) {
142
+ callSites.push(createCanonicalTemplateComponentCallSite(document, block, match, component));
143
+ continue;
144
+ }
145
+ openTagStack.push({
146
+ component,
147
+ match,
148
+ });
149
+ }
150
+ for (const openTag of openTagStack) {
151
+ callSites.push(createCanonicalTemplateComponentCallSite(document, block, openTag.match, openTag.component));
152
+ }
153
+ return callSites;
154
+ });
155
+ }
156
+ function collectWorkspaceTemplateComponentUsagesForResolvedFile(document, sourcePath, resolvedFilePath) {
157
+ return collectImportedKpaComponents(document, sourcePath)
158
+ .filter((component) => component.resolvedFilePath === resolvedFilePath)
159
+ .map((component) => ({
160
+ component,
161
+ symbolRanges: deduplicateRanges((0, localSymbolReferences_1.collectLocalReferenceRangesForSymbols)(document, getImportedKpaComponentLocalSymbols(document, sourcePath, component))),
162
+ tags: collectCanonicalTemplateTagsForComponent(document, component),
163
+ }));
164
+ }
165
+ function getCanonicalTemplateTagAtOffset(document, offset) {
166
+ return collectCanonicalTemplateTags(document).find((tag) => offset >= tag.range.start.offset && offset <= tag.range.end.offset);
167
+ }
168
+ function resolveCanonicalTemplateComponentAtOffset(document, sourcePath, offset) {
169
+ const tag = getCanonicalTemplateTagAtOffset(document, offset);
170
+ if (!tag) {
171
+ return undefined;
172
+ }
173
+ const component = collectImportedKpaComponents(document, sourcePath).find((candidate) => candidate.tagNames.includes(tag.name));
174
+ if (!component) {
175
+ return undefined;
176
+ }
177
+ return {
178
+ component,
179
+ tag,
180
+ };
181
+ }
182
+ function findImportedKpaComponentForSymbol(document, sourcePath, symbol) {
183
+ return collectImportedKpaComponents(document, sourcePath).find((component) => component.name === symbol.name &&
184
+ component.importRange.start.offset === symbol.range.start.offset &&
185
+ component.importRange.end.offset === symbol.range.end.offset);
186
+ }
187
+ function getImportedKpaComponentLocalSymbols(document, sourcePath, component) {
188
+ return (0, symbols_1.collectLocalScriptSymbols)(document).all.filter((symbol) => symbol.kind === 'import' &&
189
+ findImportedKpaComponentForSymbol(document, sourcePath, symbol)?.name === component.name);
190
+ }
191
+ function collectCanonicalTemplateTagsForComponent(document, component) {
192
+ return collectCanonicalTemplateTags(document).filter((tag) => component.tagNames.includes(tag.name));
193
+ }
194
+ function normalizeComponentRenameTarget(requestedName) {
195
+ const trimmedName = requestedName.trim();
196
+ const symbolName = trimmedName.includes('-') ? toPascalCase(trimmedName) : trimmedName;
197
+ if (!/^[A-Za-z_$][\w$]*$/.test(symbolName)) {
198
+ throw new Error(`Komponentenname [${requestedName}] ist ungueltig. Erwartet wird ein gueltiger Bezeichner oder Kebab-Case.`);
199
+ }
200
+ return {
201
+ kebabTagName: toKebabCase(symbolName),
202
+ symbolName,
203
+ };
204
+ }
205
+ function getImportedKpaComponentApi(component) {
206
+ return readComponentApiFromFile(component.resolvedFilePath);
207
+ }
208
+ function getResolvedTemplateComponentApi(component) {
209
+ if (component.kind === 'imported' && component.importedComponent) {
210
+ return getImportedKpaComponentApi(component.importedComponent);
211
+ }
212
+ return readComponentApiFromFile(component.resolvedFilePath);
213
+ }
214
+ function collectCanonicalTemplateComponentDiagnostics(document, sourcePath) {
215
+ const importedComponents = collectImportedKpaComponents(document, sourcePath);
216
+ const knownComponentTags = new Set(collectKnownTemplateComponents(document, sourcePath).flatMap((component) => component.tagNames));
217
+ const diagnostics = [];
218
+ for (const component of importedComponents) {
219
+ if (!component.importPath.endsWith('.kpa') || component.resolvedFilePath) {
220
+ continue;
221
+ }
222
+ diagnostics.push({
223
+ code: diagnosticCodes_1.kpaDiagnosticCodes.unresolvedComponentImport,
224
+ message: `Komponenten-Import [${component.name}] verweist auf [${component.importPath}], konnte aber nicht im Workspace aufgeloest werden.`,
225
+ range: toDiagnosticRange(component.importRange),
226
+ });
227
+ }
228
+ for (const tag of collectCanonicalTemplateTags(document)) {
229
+ if (tag.isClosing || !looksLikeComponentTagName(tag.name) || knownComponentTags.has(tag.name)) {
230
+ continue;
231
+ }
232
+ diagnostics.push({
233
+ code: diagnosticCodes_1.kpaDiagnosticCodes.unresolvedComponentTag,
234
+ data: {
235
+ componentName: tag.name.includes('-') ? toPascalCase(tag.name) : tag.name,
236
+ tagName: tag.name,
237
+ },
238
+ message: `Komponente [${tag.name}] wurde im [template]-Block verwendet, aber nicht als .kpa-Import gefunden.`,
239
+ range: toDiagnosticRange(tag.range),
240
+ });
241
+ }
242
+ for (const callSite of collectCanonicalTemplateComponentCallSites(document, sourcePath)) {
243
+ const componentApi = getResolvedTemplateComponentApi(callSite.component);
244
+ if (!componentApi) {
245
+ continue;
246
+ }
247
+ const providedProps = new Set();
248
+ const propsByAlias = new Map();
249
+ for (const prop of componentApi.props) {
250
+ for (const alias of createComponentPropAliases(prop.name)) {
251
+ propsByAlias.set(alias, prop);
252
+ }
253
+ }
254
+ const hasSpreadAttribute = callSite.attributes.some((attribute) => attribute.kind === 'spread');
255
+ for (const attribute of callSite.attributes) {
256
+ if (attribute.kind === 'spread' || isEventBindingAttribute(attribute.name)) {
257
+ continue;
258
+ }
259
+ const prop = propsByAlias.get(getComponentPropLookupName(attribute.name));
260
+ if (!prop) {
261
+ if (shouldWarnUnknownComponentAttribute(attribute.name)) {
262
+ diagnostics.push({
263
+ code: diagnosticCodes_1.kpaDiagnosticCodes.unknownComponentProp,
264
+ message: `Attribut [${attribute.name}] ist kein bekanntes Prop von Komponente [${callSite.component.name}].`,
265
+ range: toDiagnosticRange(attribute.range),
266
+ });
267
+ }
268
+ continue;
269
+ }
270
+ providedProps.add(prop.name);
271
+ const isExpressionAssignable = prop.typeText &&
272
+ attribute.kind === 'expression' &&
273
+ isExpressionAttributeAssignableToPropType(document, sourcePath, attribute, prop.typeText);
274
+ const inferredValueType = isExpressionAssignable === undefined ? inferAttributeType(attribute) : undefined;
275
+ if (prop.typeText &&
276
+ ((isExpressionAssignable === false && attribute.kind === 'expression') ||
277
+ (inferredValueType && !isCompatiblePropType(prop.typeText, inferredValueType)))) {
278
+ diagnostics.push({
279
+ code: diagnosticCodes_1.kpaDiagnosticCodes.invalidComponentPropType,
280
+ message: `Prop [${prop.name}] an Komponente [${callSite.component.name}] erwartet [${prop.typeText}], erhalten wurde [${attribute.kind === 'expression'
281
+ ? attribute.valueText?.trim() || 'Ausdruck'
282
+ : (inferredValueType ?? 'unbekannt')}].`,
283
+ range: toDiagnosticRange(attribute.range),
284
+ });
285
+ }
286
+ }
287
+ if (componentApi.emits.length > 0) {
288
+ const knownEmitNames = new Set(componentApi.emits.map((entry) => entry.name));
289
+ for (const attribute of callSite.eventAttributes) {
290
+ const emitName = getEventNameFromAttribute(attribute.name);
291
+ if (!emitName || knownEmitNames.has(emitName)) {
292
+ continue;
293
+ }
294
+ diagnostics.push({
295
+ code: diagnosticCodes_1.kpaDiagnosticCodes.unknownComponentEmit,
296
+ message: `Event-Handler [${attribute.name}] verweist auf unbekanntes Emit [${emitName}] von Komponente [${callSite.component.name}].`,
297
+ range: toDiagnosticRange(attribute.range),
298
+ });
299
+ }
300
+ }
301
+ if (componentApi.slots.length > 0) {
302
+ const providedSlots = new Set(callSite.slotUsages.map((slotUsage) => slotUsage.name));
303
+ const knownSlotNames = new Set(componentApi.slots.map((entry) => entry.name));
304
+ if (callSite.hasDefaultSlotContent) {
305
+ providedSlots.add('default');
306
+ }
307
+ for (const slotUsage of callSite.slotUsages) {
308
+ if (knownSlotNames.has(slotUsage.name)) {
309
+ continue;
310
+ }
311
+ diagnostics.push({
312
+ code: diagnosticCodes_1.kpaDiagnosticCodes.unknownComponentSlot,
313
+ message: `Slot [${slotUsage.name}] ist kein bekannter Slot von Komponente [${callSite.component.name}].`,
314
+ range: toDiagnosticRange(slotUsage.range),
315
+ });
316
+ }
317
+ for (const requiredSlot of componentApi.slots.filter((slot) => !slot.optional)) {
318
+ if (providedSlots.has(requiredSlot.name)) {
319
+ continue;
320
+ }
321
+ diagnostics.push({
322
+ code: diagnosticCodes_1.kpaDiagnosticCodes.missingComponentSlot,
323
+ message: `Required Slot [${requiredSlot.name}] fehlt an Komponente [${callSite.component.name}].`,
324
+ range: toDiagnosticRange(callSite.tag.range),
325
+ });
326
+ }
327
+ }
328
+ if (hasSpreadAttribute) {
329
+ continue;
330
+ }
331
+ for (const requiredProp of componentApi.props.filter((prop) => !prop.optional)) {
332
+ if (providedProps.has(requiredProp.name)) {
333
+ continue;
334
+ }
335
+ diagnostics.push({
336
+ code: diagnosticCodes_1.kpaDiagnosticCodes.missingComponentProp,
337
+ data: {
338
+ componentName: callSite.component.name,
339
+ insertOffset: callSite.insertOffset,
340
+ propName: requiredProp.name,
341
+ propTypeText: requiredProp.typeText,
342
+ },
343
+ message: `Required Prop [${requiredProp.name}] fehlt an Komponente [${callSite.component.name}].`,
344
+ range: toDiagnosticRange(callSite.tag.range),
345
+ });
346
+ }
347
+ }
348
+ return diagnostics;
349
+ }
350
+ function resolveTemplateComponentAtOffset(document, sourcePath, offset) {
351
+ const tag = getCanonicalTemplateTagAtOffset(document, offset);
352
+ if (!tag) {
353
+ return undefined;
354
+ }
355
+ const component = findKnownTemplateComponentByTagName(document, sourcePath, tag.name);
356
+ if (!component) {
357
+ return undefined;
358
+ }
359
+ return {
360
+ component,
361
+ tag,
362
+ };
363
+ }
364
+ function collectComponentsFromScriptBlock(document, block, sourcePath) {
365
+ const content = document.text.slice(block.contentRange.start.offset, block.contentRange.end.offset);
366
+ const sourceFile = typescript_1.default.createSourceFile(createEmbeddedFileName(block.kind), content, typescript_1.default.ScriptTarget.Latest, true, block.kind === 'script-ts' ? typescript_1.default.ScriptKind.TS : typescript_1.default.ScriptKind.JS);
367
+ return sourceFile.statements.flatMap((statement) => {
368
+ if (!typescript_1.default.isImportDeclaration(statement) || !typescript_1.default.isStringLiteral(statement.moduleSpecifier)) {
369
+ return [];
370
+ }
371
+ const importClause = statement.importClause;
372
+ if (!importClause || importClause.isTypeOnly) {
373
+ return [];
374
+ }
375
+ const importPath = statement.moduleSpecifier.text;
376
+ const resolvedFilePath = resolveComponentImportPath(importPath, sourcePath);
377
+ const isExplicitKpaImport = importPath.endsWith('.kpa');
378
+ const isResolvedKpaImport = resolvedFilePath?.endsWith('.kpa') === true;
379
+ if (!isExplicitKpaImport && !isResolvedKpaImport) {
380
+ return [];
381
+ }
382
+ const importedBindings = [];
383
+ if (importClause.name) {
384
+ importedBindings.push(importClause.name);
385
+ }
386
+ if (importClause.namedBindings && typescript_1.default.isNamedImports(importClause.namedBindings)) {
387
+ for (const element of importClause.namedBindings.elements) {
388
+ if (!element.isTypeOnly) {
389
+ importedBindings.push(element.name);
390
+ }
391
+ }
392
+ }
393
+ return importedBindings.map((binding) => ({
394
+ importPath,
395
+ importRange: createDocumentRange(document, block, binding.getStart(sourceFile), binding.end),
396
+ name: binding.text,
397
+ resolvedFilePath,
398
+ tagNames: createComponentTagNames(binding.text),
399
+ }));
400
+ });
401
+ }
402
+ function collectTemplateTagMatchesFromBlock(document, block) {
403
+ const content = document.text.slice(block.contentRange.start.offset, block.contentRange.end.offset);
404
+ const tags = [];
405
+ for (let index = 0; index < content.length; index++) {
406
+ if (startsMustacheExpression(content, index)) {
407
+ const expressionEnd = findMustacheExpressionEnd(content, index + 2);
408
+ if (expressionEnd === undefined) {
409
+ break;
410
+ }
411
+ index = Math.max(index, expressionEnd + 1);
412
+ continue;
413
+ }
414
+ if (content[index] !== '<') {
415
+ continue;
416
+ }
417
+ const tagMatch = readTagAt(content, index);
418
+ if (!tagMatch) {
419
+ continue;
420
+ }
421
+ tags.push(tagMatch);
422
+ index = Math.max(index, tagMatch.tagEnd - 1);
423
+ }
424
+ return tags;
425
+ }
426
+ function readTagAt(content, index) {
427
+ let cursor = index + 1;
428
+ let isClosing = false;
429
+ if (content[cursor] === '/') {
430
+ isClosing = true;
431
+ cursor++;
432
+ }
433
+ while (isWhitespace(content[cursor])) {
434
+ cursor++;
435
+ }
436
+ const nameStart = cursor;
437
+ if (!isTagNameStart(content[cursor])) {
438
+ return undefined;
439
+ }
440
+ cursor++;
441
+ while (isTagNameCharacter(content[cursor])) {
442
+ cursor++;
443
+ }
444
+ const nameEnd = cursor;
445
+ const attributes = [];
446
+ while (cursor < content.length) {
447
+ while (isWhitespace(content[cursor])) {
448
+ cursor++;
449
+ }
450
+ if (content[cursor] === '>') {
451
+ return {
452
+ attributes,
453
+ insertionIndex: trimTagInsertionIndex(content, nameEnd, cursor),
454
+ isClosing,
455
+ isSelfClosing: false,
456
+ name: content.slice(nameStart, nameEnd),
457
+ nameEnd,
458
+ nameStart,
459
+ tagEnd: cursor + 1,
460
+ };
461
+ }
462
+ if (content[cursor] === '/' && content[cursor + 1] === '>') {
463
+ return {
464
+ attributes,
465
+ insertionIndex: trimTagInsertionIndex(content, nameEnd, cursor),
466
+ isClosing,
467
+ isSelfClosing: true,
468
+ name: content.slice(nameStart, nameEnd),
469
+ nameEnd,
470
+ nameStart,
471
+ tagEnd: cursor + 2,
472
+ };
473
+ }
474
+ const parsedAttribute = readAttributeAt(content, cursor);
475
+ if (!parsedAttribute) {
476
+ cursor++;
477
+ continue;
478
+ }
479
+ attributes.push(parsedAttribute.attribute);
480
+ cursor = parsedAttribute.nextIndex;
481
+ }
482
+ return {
483
+ attributes,
484
+ insertionIndex: trimTagInsertionIndex(content, nameEnd, cursor),
485
+ isClosing,
486
+ isSelfClosing: false,
487
+ name: content.slice(nameStart, nameEnd),
488
+ nameEnd,
489
+ nameStart,
490
+ tagEnd: cursor,
491
+ };
492
+ }
493
+ function readAttributeAt(content, index) {
494
+ if (content[index] === '{') {
495
+ const bracedValue = readBracedValue(content, index);
496
+ if (!bracedValue) {
497
+ return undefined;
498
+ }
499
+ const normalizedValueText = bracedValue.valueText.trim();
500
+ if (!normalizedValueText.startsWith('...')) {
501
+ return {
502
+ attribute: {
503
+ kind: 'expression',
504
+ name: normalizedValueText,
505
+ nameEnd: bracedValue.end,
506
+ nameStart: index,
507
+ valueEnd: bracedValue.end - 1,
508
+ valueStart: index + 1,
509
+ valueText: bracedValue.valueText,
510
+ },
511
+ nextIndex: bracedValue.end,
512
+ };
513
+ }
514
+ return {
515
+ attribute: {
516
+ kind: 'spread',
517
+ name: normalizedValueText,
518
+ nameEnd: bracedValue.end,
519
+ nameStart: index,
520
+ valueEnd: bracedValue.end - 1,
521
+ valueStart: index + 1,
522
+ valueText: bracedValue.valueText,
523
+ },
524
+ nextIndex: bracedValue.end,
525
+ };
526
+ }
527
+ if (!isAttributeNameStart(content[index])) {
528
+ return undefined;
529
+ }
530
+ let cursor = index + 1;
531
+ while (isAttributeNameCharacter(content[cursor])) {
532
+ cursor++;
533
+ }
534
+ const nameStart = index;
535
+ const nameEnd = cursor;
536
+ const name = content.slice(nameStart, nameEnd);
537
+ const isDynamicBinding = isDynamicBindingAttribute(name);
538
+ while (isWhitespace(content[cursor])) {
539
+ cursor++;
540
+ }
541
+ if (content[cursor] !== '=') {
542
+ return {
543
+ attribute: {
544
+ kind: 'boolean',
545
+ name,
546
+ nameEnd,
547
+ nameStart,
548
+ },
549
+ nextIndex: cursor,
550
+ };
551
+ }
552
+ cursor++;
553
+ while (isWhitespace(content[cursor])) {
554
+ cursor++;
555
+ }
556
+ if (content[cursor] === "'" || content[cursor] === '"') {
557
+ const quote = content[cursor];
558
+ const valueStart = cursor + 1;
559
+ cursor++;
560
+ while (cursor < content.length && content[cursor] !== quote) {
561
+ cursor++;
562
+ }
563
+ const valueEnd = cursor;
564
+ return {
565
+ attribute: {
566
+ kind: isDynamicBinding ? 'expression' : 'string',
567
+ name,
568
+ nameEnd,
569
+ nameStart,
570
+ valueEnd,
571
+ valueStart,
572
+ valueText: content.slice(valueStart, valueEnd),
573
+ },
574
+ nextIndex: cursor < content.length ? cursor + 1 : cursor,
575
+ };
576
+ }
577
+ if (content[cursor] === '{') {
578
+ const bracedValue = readBracedValue(content, cursor);
579
+ if (!bracedValue) {
580
+ return undefined;
581
+ }
582
+ return {
583
+ attribute: {
584
+ kind: 'expression',
585
+ name,
586
+ nameEnd,
587
+ nameStart,
588
+ valueEnd: bracedValue.end - 1,
589
+ valueStart: cursor + 1,
590
+ valueText: bracedValue.valueText,
591
+ },
592
+ nextIndex: bracedValue.end,
593
+ };
594
+ }
595
+ const valueStart = cursor;
596
+ while (cursor < content.length &&
597
+ !isWhitespace(content[cursor]) &&
598
+ content[cursor] !== '>' &&
599
+ !(content[cursor] === '/' && content[cursor + 1] === '>')) {
600
+ cursor++;
601
+ }
602
+ return {
603
+ attribute: {
604
+ kind: isDynamicBinding ? 'expression' : 'unquoted',
605
+ name,
606
+ nameEnd,
607
+ nameStart,
608
+ valueEnd: cursor,
609
+ valueStart,
610
+ valueText: content.slice(valueStart, cursor),
611
+ },
612
+ nextIndex: cursor,
613
+ };
614
+ }
615
+ function readBracedValue(content, startIndex) {
616
+ let cursor = startIndex + 1;
617
+ let depth = 1;
618
+ let quote;
619
+ let escaped = false;
620
+ while (cursor < content.length) {
621
+ const character = content[cursor];
622
+ if (quote) {
623
+ if (escaped) {
624
+ escaped = false;
625
+ cursor++;
626
+ continue;
627
+ }
628
+ if (character === '\\') {
629
+ escaped = true;
630
+ cursor++;
631
+ continue;
632
+ }
633
+ if (character === quote) {
634
+ quote = undefined;
635
+ }
636
+ cursor++;
637
+ continue;
638
+ }
639
+ if (character === "'" || character === '"' || character === '`') {
640
+ quote = character;
641
+ cursor++;
642
+ continue;
643
+ }
644
+ if (character === '{') {
645
+ depth++;
646
+ cursor++;
647
+ continue;
648
+ }
649
+ if (character === '}') {
650
+ depth--;
651
+ if (depth === 0) {
652
+ return {
653
+ end: cursor + 1,
654
+ valueText: content.slice(startIndex + 1, cursor),
655
+ };
656
+ }
657
+ }
658
+ cursor++;
659
+ }
660
+ return undefined;
661
+ }
662
+ function startsMustacheExpression(content, index) {
663
+ return content[index] === '{' && content[index + 1] === '{';
664
+ }
665
+ function findMustacheExpressionEnd(content, startIndex) {
666
+ let cursor = startIndex;
667
+ let nestedBraceDepth = 0;
668
+ let quote;
669
+ let escaped = false;
670
+ while (cursor < content.length) {
671
+ const character = content[cursor];
672
+ if (quote) {
673
+ if (escaped) {
674
+ escaped = false;
675
+ cursor++;
676
+ continue;
677
+ }
678
+ if (character === '\\') {
679
+ escaped = true;
680
+ cursor++;
681
+ continue;
682
+ }
683
+ if (character === quote) {
684
+ quote = undefined;
685
+ }
686
+ cursor++;
687
+ continue;
688
+ }
689
+ if (character === "'" || character === '"' || character === '`') {
690
+ quote = character;
691
+ cursor++;
692
+ continue;
693
+ }
694
+ if (character === '{') {
695
+ nestedBraceDepth++;
696
+ cursor++;
697
+ continue;
698
+ }
699
+ if (character === '}') {
700
+ if (nestedBraceDepth > 0) {
701
+ nestedBraceDepth--;
702
+ cursor++;
703
+ continue;
704
+ }
705
+ if (content[cursor + 1] === '}') {
706
+ return cursor;
707
+ }
708
+ }
709
+ cursor++;
710
+ }
711
+ return undefined;
712
+ }
713
+ function trimTagInsertionIndex(content, minimumIndex, closingIndex) {
714
+ let insertionIndex = closingIndex;
715
+ while (insertionIndex > minimumIndex && isWhitespace(content[insertionIndex - 1])) {
716
+ insertionIndex--;
717
+ }
718
+ return insertionIndex;
719
+ }
720
+ function createTemplateTag(document, block, match) {
721
+ return {
722
+ block,
723
+ isClosing: match.isClosing,
724
+ name: match.name,
725
+ range: (0, sourcePositions_1.createLocatedRange)(document.lineStarts, block.contentRange.start.offset + match.nameStart, block.contentRange.start.offset + match.nameEnd),
726
+ };
727
+ }
728
+ function createTemplateAttribute(document, block, attribute) {
729
+ return {
730
+ kind: attribute.kind,
731
+ name: attribute.name,
732
+ range: (0, sourcePositions_1.createLocatedRange)(document.lineStarts, block.contentRange.start.offset + attribute.nameStart, block.contentRange.start.offset + attribute.nameEnd),
733
+ valueRange: attribute.valueStart !== undefined && attribute.valueEnd !== undefined
734
+ ? (0, sourcePositions_1.createLocatedRange)(document.lineStarts, block.contentRange.start.offset + attribute.valueStart, block.contentRange.start.offset + attribute.valueEnd)
735
+ : undefined,
736
+ valueText: attribute.valueText,
737
+ };
738
+ }
739
+ function createCanonicalTemplateComponentCallSite(document, block, openingMatch, component, closingMatch) {
740
+ const openingTag = createTemplateTag(document, block, openingMatch);
741
+ const bodyStartOffset = block.contentRange.start.offset + openingMatch.tagEnd;
742
+ const bodyEndOffset = closingMatch
743
+ ? block.contentRange.start.offset + closingMatch.nameStart - 1
744
+ : bodyStartOffset;
745
+ const slotUsages = closingMatch && bodyEndOffset >= bodyStartOffset
746
+ ? collectSlotUsagesFromContent(document, block, bodyStartOffset, bodyEndOffset)
747
+ : [];
748
+ const eventAttributes = openingMatch.attributes
749
+ .map((attribute) => createTemplateAttribute(document, block, attribute))
750
+ .filter((attribute) => isEventBindingAttribute(attribute.name));
751
+ return {
752
+ attributes: openingMatch.attributes.map((attribute) => createTemplateAttribute(document, block, attribute)),
753
+ component,
754
+ eventAttributes,
755
+ hasDefaultSlotContent: closingMatch !== undefined &&
756
+ hasDefaultSlotBodyContent(document, bodyStartOffset, bodyEndOffset),
757
+ insertOffset: block.contentRange.start.offset + openingMatch.insertionIndex,
758
+ slotUsages,
759
+ tag: openingTag,
760
+ };
761
+ }
762
+ function findMatchingOpenComponentIndex(openTagStack, component, closingTagName) {
763
+ for (let index = openTagStack.length - 1; index >= 0; index--) {
764
+ const candidate = openTagStack[index];
765
+ if (candidate.component.name === component.name &&
766
+ candidate.component.tagNames.includes(closingTagName)) {
767
+ return index;
768
+ }
769
+ }
770
+ return -1;
771
+ }
772
+ function createKnownTemplateComponentsByTagName(document, sourcePath) {
773
+ const componentsByTagName = new Map();
774
+ for (const component of collectKnownTemplateComponents(document, sourcePath)) {
775
+ for (const tagName of component.tagNames) {
776
+ if (!componentsByTagName.has(tagName)) {
777
+ componentsByTagName.set(tagName, component);
778
+ }
779
+ }
780
+ }
781
+ return componentsByTagName;
782
+ }
783
+ function findKnownTemplateComponentByTagName(document, sourcePath, tagName) {
784
+ return createKnownTemplateComponentsByTagName(document, sourcePath).get(tagName);
785
+ }
786
+ function collectSlotUsagesFromContent(document, block, bodyStartOffset, bodyEndOffset) {
787
+ const relativeStart = bodyStartOffset - block.contentRange.start.offset;
788
+ const content = document.text.slice(bodyStartOffset, bodyEndOffset);
789
+ const slotUsages = [];
790
+ const slotPattern = /\bslot\s*=\s*(?:"([^"]+)"|'([^']+)'|([A-Za-z_$][\w$-]*))/g;
791
+ let match;
792
+ while ((match = slotPattern.exec(content)) !== null) {
793
+ const slotName = match[1] ?? match[2] ?? match[3];
794
+ if (!slotName) {
795
+ continue;
796
+ }
797
+ const slotNamePrefix = match[0].slice(0, match[0].indexOf(slotName));
798
+ const slotNameStart = relativeStart + match.index + slotNamePrefix.length;
799
+ const slotNameEnd = slotNameStart + slotName.length;
800
+ slotUsages.push({
801
+ name: slotName,
802
+ range: (0, sourcePositions_1.createLocatedRange)(document.lineStarts, block.contentRange.start.offset + slotNameStart, block.contentRange.start.offset + slotNameEnd),
803
+ });
804
+ }
805
+ return slotUsages;
806
+ }
807
+ function hasDefaultSlotBodyContent(document, bodyStartOffset, bodyEndOffset) {
808
+ if (bodyEndOffset <= bodyStartOffset) {
809
+ return false;
810
+ }
811
+ const bodyContent = document.text.slice(bodyStartOffset, bodyEndOffset);
812
+ if (!/\S/.test(bodyContent)) {
813
+ return false;
814
+ }
815
+ const strippedNamedSlotContent = bodyContent.replace(/<([A-Za-z][A-Za-z0-9:_-]*)([^>]*)\bslot\s*=\s*(?:"[^"]+"|'[^']+'|[A-Za-z_$][\w$-]*)([^>]*)>[\s\S]*?<\/\1\s*>/g, '');
816
+ return /\S/.test(strippedNamedSlotContent);
817
+ }
818
+ function isEventBindingAttribute(attributeName) {
819
+ return (attributeName.startsWith('@') ||
820
+ attributeName.startsWith('on:') ||
821
+ /^on[A-Z]/.test(attributeName));
822
+ }
823
+ function getEventNameFromAttribute(attributeName) {
824
+ if (attributeName.startsWith('@')) {
825
+ return attributeName.slice(1);
826
+ }
827
+ if (attributeName.startsWith('on:')) {
828
+ return attributeName.slice(3);
829
+ }
830
+ if (/^on[A-Z]/.test(attributeName)) {
831
+ return attributeName[2].toLowerCase() + attributeName.slice(3);
832
+ }
833
+ return undefined;
834
+ }
835
+ function createComponentTagNames(name) {
836
+ const tagNames = [name];
837
+ const kebabAlias = toKebabCase(name);
838
+ if (kebabAlias !== name && kebabAlias.includes('-')) {
839
+ tagNames.push(kebabAlias);
840
+ }
841
+ return tagNames;
842
+ }
843
+ function createComponentPropAliases(name) {
844
+ const aliases = [name];
845
+ const kebabAlias = toKebabCase(name);
846
+ if (kebabAlias !== name) {
847
+ aliases.push(kebabAlias);
848
+ }
849
+ return aliases;
850
+ }
851
+ function extractComponentApiFromDocument(document) {
852
+ const props = new Map();
853
+ const emits = new Map();
854
+ const slots = new Map();
855
+ for (const block of document.blocks) {
856
+ if (block.name === kpaBlocks_1.canonicalTemplateBlock) {
857
+ for (const slotEntry of collectSlotEntriesFromTemplateBlock(document, block)) {
858
+ if (!slots.has(slotEntry.name)) {
859
+ slots.set(slotEntry.name, slotEntry);
860
+ }
861
+ }
862
+ continue;
863
+ }
864
+ if (!isScriptBlock(block)) {
865
+ continue;
866
+ }
867
+ const content = document.text.slice(block.contentRange.start.offset, block.contentRange.end.offset);
868
+ const sourceFile = typescript_1.default.createSourceFile(createEmbeddedFileName(block.kind), content, typescript_1.default.ScriptTarget.Latest, true, block.kind === 'script-ts' ? typescript_1.default.ScriptKind.TS : typescript_1.default.ScriptKind.JS);
869
+ for (const statement of sourceFile.statements) {
870
+ if (typescript_1.default.isInterfaceDeclaration(statement) && isPropsInterfaceName(statement.name.text)) {
871
+ for (const entry of collectInterfaceEntries(statement, sourceFile)) {
872
+ props.set(entry.name, entry);
873
+ }
874
+ continue;
875
+ }
876
+ if (typescript_1.default.isInterfaceDeclaration(statement) && isSlotsInterfaceName(statement.name.text)) {
877
+ for (const entry of collectInterfaceEntries(statement, sourceFile)) {
878
+ slots.set(entry.name, entry);
879
+ }
880
+ continue;
881
+ }
882
+ if (typescript_1.default.isTypeAliasDeclaration(statement) && isEmitsTypeName(statement.name.text)) {
883
+ for (const entry of collectTypeLiteralEntries(statement.type, sourceFile)) {
884
+ emits.set(entry.name, entry);
885
+ }
886
+ }
887
+ }
888
+ for (const runtimeProp of (0, componentContract_1.collectRuntimeComponentProps)(sourceFile)) {
889
+ props.set(runtimeProp.name, {
890
+ name: runtimeProp.name,
891
+ optional: runtimeProp.optional,
892
+ typeText: runtimeProp.typeText,
893
+ });
894
+ }
895
+ }
896
+ return {
897
+ emits: [...emits.values()],
898
+ props: [...props.values()],
899
+ slots: [...slots.values()],
900
+ };
901
+ }
902
+ function collectSlotEntriesFromTemplateBlock(document, block) {
903
+ const content = document.text.slice(block.contentRange.start.offset, block.contentRange.end.offset);
904
+ const entries = [];
905
+ const seenNames = new Set();
906
+ const slotPattern = /<slot\b([^>]*)>/g;
907
+ let match;
908
+ while ((match = slotPattern.exec(content)) !== null) {
909
+ const attributeText = match[1] ?? '';
910
+ const slotNameMatch = /\bname\s*=\s*(?:"([^"]+)"|'([^']+)'|([A-Za-z_$][\w$-]*))/.exec(attributeText);
911
+ const slotName = slotNameMatch?.[1] ?? slotNameMatch?.[2] ?? slotNameMatch?.[3] ?? 'default';
912
+ if (seenNames.has(slotName)) {
913
+ continue;
914
+ }
915
+ seenNames.add(slotName);
916
+ entries.push({
917
+ name: slotName,
918
+ optional: true,
919
+ });
920
+ }
921
+ return entries;
922
+ }
923
+ function collectInterfaceEntries(declaration, sourceFile) {
924
+ const entries = [];
925
+ for (const member of declaration.members) {
926
+ if ((typescript_1.default.isPropertySignature(member) || typescript_1.default.isMethodSignature(member)) &&
927
+ getComponentMemberName(member.name)) {
928
+ const name = getComponentMemberName(member.name);
929
+ if (!name) {
930
+ continue;
931
+ }
932
+ entries.push({
933
+ name,
934
+ optional: Boolean(member.questionToken),
935
+ typeText: member.type ? member.type.getText(sourceFile).trim() : undefined,
936
+ });
937
+ }
938
+ }
939
+ return entries;
940
+ }
941
+ function collectTypeLiteralEntries(node, sourceFile) {
942
+ if (!typescript_1.default.isTypeLiteralNode(node)) {
943
+ return [];
944
+ }
945
+ const entries = [];
946
+ for (const member of node.members) {
947
+ if ((typescript_1.default.isPropertySignature(member) || typescript_1.default.isMethodSignature(member)) &&
948
+ getComponentMemberName(member.name)) {
949
+ const name = getComponentMemberName(member.name);
950
+ if (!name) {
951
+ continue;
952
+ }
953
+ entries.push({
954
+ name,
955
+ optional: Boolean(member.questionToken),
956
+ typeText: member.type ? member.type.getText(sourceFile).trim() : undefined,
957
+ });
958
+ }
959
+ }
960
+ return entries;
961
+ }
962
+ function getComponentMemberName(name) {
963
+ if (!name) {
964
+ return undefined;
965
+ }
966
+ if (typescript_1.default.isIdentifier(name) || typescript_1.default.isStringLiteral(name) || typescript_1.default.isNumericLiteral(name)) {
967
+ return name.text;
968
+ }
969
+ return undefined;
970
+ }
971
+ function isPropsInterfaceName(name) {
972
+ return name === 'Props' || name.endsWith('Props');
973
+ }
974
+ function isSlotsInterfaceName(name) {
975
+ return name === 'Slots' || name.endsWith('Slots');
976
+ }
977
+ function isEmitsTypeName(name) {
978
+ return name === 'Emits' || name.endsWith('Emits');
979
+ }
980
+ function isExpressionAttributeAssignableToPropType(document, sourcePath, attribute, propTypeText) {
981
+ const valueText = attribute.valueText?.trim();
982
+ if (!valueText) {
983
+ return undefined;
984
+ }
985
+ const virtualFileName = createComponentTypecheckVirtualFileName(sourcePath);
986
+ const compilerOptions = loadTypecheckCompilerOptions(virtualFileName);
987
+ const virtualSourceText = createComponentTypecheckSource(document, valueText, propTypeText);
988
+ const languageServiceHost = {
989
+ getCompilationSettings: () => compilerOptions,
990
+ getCurrentDirectory: () => path_1.default.dirname(virtualFileName),
991
+ getDefaultLibFileName: (options) => typescript_1.default.getDefaultLibFilePath(options),
992
+ getScriptFileNames: () => [virtualFileName],
993
+ getScriptVersion: () => '0',
994
+ getScriptSnapshot: (fileName) => {
995
+ if (fileName === virtualFileName) {
996
+ return typescript_1.default.ScriptSnapshot.fromString(virtualSourceText);
997
+ }
998
+ const sourceText = typescript_1.default.sys.readFile(fileName);
999
+ return sourceText !== undefined ? typescript_1.default.ScriptSnapshot.fromString(sourceText) : undefined;
1000
+ },
1001
+ fileExists: (fileName) => fileName === virtualFileName || typescript_1.default.sys.fileExists(fileName),
1002
+ readFile: (fileName) => fileName === virtualFileName ? virtualSourceText : typescript_1.default.sys.readFile(fileName),
1003
+ readDirectory: typescript_1.default.sys.readDirectory,
1004
+ directoryExists: (directoryName) => directoryName === path_1.default.dirname(virtualFileName) || typescript_1.default.sys.directoryExists(directoryName),
1005
+ getDirectories: typescript_1.default.sys.getDirectories,
1006
+ useCaseSensitiveFileNames: () => typescript_1.default.sys.useCaseSensitiveFileNames,
1007
+ getNewLine: () => typescript_1.default.sys.newLine,
1008
+ };
1009
+ const languageService = typescript_1.default.createLanguageService(languageServiceHost);
1010
+ const diagnostics = languageService.getSemanticDiagnostics(virtualFileName);
1011
+ if (diagnostics.some((diagnostic) => diagnostic.code === 2304 || diagnostic.code === 2552 || diagnostic.code === 2693)) {
1012
+ return undefined;
1013
+ }
1014
+ return diagnostics.length === 0;
1015
+ }
1016
+ function createComponentTypecheckVirtualFileName(sourcePath) {
1017
+ if (sourcePath) {
1018
+ return `${sourcePath}.component-check.ts`;
1019
+ }
1020
+ return path_1.default.join(process.cwd(), '__kpa_virtual__', 'untitled.kpa.component-check.ts');
1021
+ }
1022
+ function createComponentTypecheckSource(document, valueText, propTypeText) {
1023
+ const parts = [];
1024
+ const scriptBlock = document.blocks.find(isScriptBlock);
1025
+ if (scriptBlock) {
1026
+ const content = document.text.slice(scriptBlock.contentRange.start.offset, scriptBlock.contentRange.end.offset);
1027
+ parts.push(content);
1028
+ parts.push('\n');
1029
+ for (const symbol of (0, symbols_1.collectTemplateContextSymbols)(document)) {
1030
+ appendComponentTypecheckBindingDeclaration(parts, symbol);
1031
+ }
1032
+ }
1033
+ parts.push(`type __KpaTarget = ${propTypeText};\n`);
1034
+ parts.push(`const __kpa_value__ = (${valueText});\n`);
1035
+ parts.push('const __kpa_assign__: __KpaTarget = __kpa_value__;\n');
1036
+ return parts.join('');
1037
+ }
1038
+ function appendComponentTypecheckBindingDeclaration(parts, symbol) {
1039
+ parts.push(`const ${symbol.name} = `);
1040
+ if (symbol.valueText) {
1041
+ parts.push(`${symbol.valueText};\n`);
1042
+ return;
1043
+ }
1044
+ parts.push(`undefined as unknown as ${getComponentTypecheckPropValueType(symbol)};\n`);
1045
+ }
1046
+ function getComponentTypecheckPropValueType(symbol) {
1047
+ const baseType = symbol.typeText?.trim() || 'unknown';
1048
+ return symbol.optional ? `${baseType} | undefined` : baseType;
1049
+ }
1050
+ function loadTypecheckCompilerOptions(virtualFileName) {
1051
+ const configFile = typescript_1.default.findConfigFile(path_1.default.dirname(virtualFileName), typescript_1.default.sys.fileExists);
1052
+ if (configFile) {
1053
+ const configFileResult = typescript_1.default.readConfigFile(configFile, typescript_1.default.sys.readFile);
1054
+ if (!configFileResult.error) {
1055
+ const parsedConfig = typescript_1.default.parseJsonConfigFileContent(configFileResult.config, typescript_1.default.sys, path_1.default.dirname(configFile));
1056
+ return {
1057
+ ...parsedConfig.options,
1058
+ allowJs: true,
1059
+ checkJs: true,
1060
+ };
1061
+ }
1062
+ }
1063
+ return {
1064
+ allowJs: true,
1065
+ checkJs: true,
1066
+ module: typescript_1.default.ModuleKind.Node16,
1067
+ moduleResolution: typescript_1.default.ModuleResolutionKind.Node16,
1068
+ strict: true,
1069
+ target: typescript_1.default.ScriptTarget.ES2022,
1070
+ };
1071
+ }
1072
+ function inferAttributeType(attribute) {
1073
+ switch (attribute.kind) {
1074
+ case 'boolean':
1075
+ return 'boolean';
1076
+ case 'string':
1077
+ case 'unquoted':
1078
+ return 'string';
1079
+ case 'expression': {
1080
+ const valueText = attribute.valueText?.trim();
1081
+ if (!valueText) {
1082
+ return undefined;
1083
+ }
1084
+ if (/^(true|false)$/.test(valueText)) {
1085
+ return 'boolean';
1086
+ }
1087
+ if (/^-?\d+(?:\.\d+)?$/.test(valueText)) {
1088
+ return 'number';
1089
+ }
1090
+ if (/^(["'`]).*\1$/s.test(valueText)) {
1091
+ return 'string';
1092
+ }
1093
+ if (valueText === 'null') {
1094
+ return 'null';
1095
+ }
1096
+ if (/^\[.*\]$/s.test(valueText)) {
1097
+ return 'array';
1098
+ }
1099
+ if (/^\{.*\}$/s.test(valueText)) {
1100
+ return 'object';
1101
+ }
1102
+ return undefined;
1103
+ }
1104
+ case 'spread':
1105
+ return undefined;
1106
+ }
1107
+ }
1108
+ function isCompatiblePropType(typeText, inferredType) {
1109
+ const normalizedTypeText = typeText.replace(/\s+/g, '');
1110
+ if (normalizedTypeText.includes('any') ||
1111
+ normalizedTypeText.includes('unknown') ||
1112
+ normalizedTypeText.includes('never')) {
1113
+ return true;
1114
+ }
1115
+ switch (inferredType) {
1116
+ case 'string':
1117
+ return (normalizedTypeText.includes('string') ||
1118
+ /'[^']*'|"[^"]*"/.test(typeText) ||
1119
+ normalizedTypeText.includes('String'));
1120
+ case 'number':
1121
+ return normalizedTypeText.includes('number');
1122
+ case 'boolean':
1123
+ return (normalizedTypeText.includes('boolean') ||
1124
+ normalizedTypeText.includes('true') ||
1125
+ normalizedTypeText.includes('false'));
1126
+ case 'null':
1127
+ return normalizedTypeText.includes('null');
1128
+ case 'array':
1129
+ return normalizedTypeText.includes('[]') || normalizedTypeText.includes('Array<');
1130
+ case 'object':
1131
+ return (normalizedTypeText.includes('{') ||
1132
+ normalizedTypeText.includes('object') ||
1133
+ normalizedTypeText.includes('Record<'));
1134
+ }
1135
+ }
1136
+ function shouldWarnUnknownComponentAttribute(name) {
1137
+ const normalizedName = getComponentPropLookupName(name);
1138
+ if (allowedPassthroughComponentAttributes.has(normalizedName)) {
1139
+ return false;
1140
+ }
1141
+ return !(normalizedName.startsWith('aria-') ||
1142
+ name.startsWith('bind:') ||
1143
+ name.startsWith('class:') ||
1144
+ normalizedName.startsWith('data-') ||
1145
+ name.startsWith('on:') ||
1146
+ name.startsWith('style:') ||
1147
+ name.startsWith('use:'));
1148
+ }
1149
+ function isDynamicBindingAttribute(name) {
1150
+ return name.startsWith(':') || name.startsWith('bind:');
1151
+ }
1152
+ function getComponentPropLookupName(name) {
1153
+ if (name.startsWith(':')) {
1154
+ return name.slice(1);
1155
+ }
1156
+ if (name.startsWith('bind:')) {
1157
+ return name.slice(5);
1158
+ }
1159
+ return name;
1160
+ }
1161
+ function toKebabCase(name) {
1162
+ return name
1163
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
1164
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
1165
+ .replace(/[_\s]+/g, '-')
1166
+ .toLowerCase();
1167
+ }
1168
+ function toPascalCase(name) {
1169
+ return name
1170
+ .split(/[-_\s]+/)
1171
+ .filter((part) => part.length > 0)
1172
+ .map((part) => part[0].toUpperCase() + part.slice(1))
1173
+ .join('');
1174
+ }
1175
+ function resolveComponentImportPath(importPath, sourcePath) {
1176
+ return (0, projectConfig_1.resolveWorkspaceImportPath)(importPath, sourcePath, ['.kpa']);
1177
+ }
1178
+ function readComponentApiFromFile(resolvedFilePath) {
1179
+ if (!resolvedFilePath) {
1180
+ return undefined;
1181
+ }
1182
+ try {
1183
+ const fileStats = fs_1.default.statSync(resolvedFilePath);
1184
+ const cachedEntry = componentApiCache.get(resolvedFilePath);
1185
+ if (cachedEntry && cachedEntry.mtimeMs === fileStats.mtimeMs) {
1186
+ return cachedEntry.api;
1187
+ }
1188
+ const text = fs_1.default.readFileSync(resolvedFilePath, 'utf8');
1189
+ const document = (0, parser_1.parseKpaDocument)(text);
1190
+ const api = extractComponentApiFromDocument(document);
1191
+ componentApiCache.set(resolvedFilePath, {
1192
+ api,
1193
+ mtimeMs: fileStats.mtimeMs,
1194
+ });
1195
+ return api;
1196
+ }
1197
+ catch {
1198
+ return undefined;
1199
+ }
1200
+ }
1201
+ function createDocumentRange(document, block, startOffsetInBlock, endOffsetInBlock) {
1202
+ const startOffset = block.contentRange.start.offset + startOffsetInBlock;
1203
+ const endOffset = block.contentRange.start.offset + endOffsetInBlock;
1204
+ return (0, sourcePositions_1.createLocatedRange)(document.lineStarts, startOffset, endOffset);
1205
+ }
1206
+ function isScriptBlock(block) {
1207
+ return block.kind === 'script-ts' || block.kind === 'script-js';
1208
+ }
1209
+ function createEmbeddedFileName(kind) {
1210
+ return kind === 'script-ts' ? 'embedded.kpa.ts' : 'embedded.kpa.js';
1211
+ }
1212
+ function looksLikeComponentTagName(name) {
1213
+ return /^[A-Z]/.test(name) || /^[a-z][a-z0-9]*-[a-z0-9-]+$/.test(name);
1214
+ }
1215
+ function isTagNameStart(character) {
1216
+ return character !== undefined && /[A-Za-z]/.test(character);
1217
+ }
1218
+ function isTagNameCharacter(character) {
1219
+ return character !== undefined && /[A-Za-z0-9:_-]/.test(character);
1220
+ }
1221
+ function isAttributeNameStart(character) {
1222
+ return character !== undefined && /[A-Za-z_:@]/.test(character);
1223
+ }
1224
+ function isAttributeNameCharacter(character) {
1225
+ return character !== undefined && /[A-Za-z0-9_:@.-]/.test(character);
1226
+ }
1227
+ function isWhitespace(character) {
1228
+ return character === ' ' || character === '\t' || character === '\n' || character === '\r';
1229
+ }
1230
+ function toDiagnosticRange(range) {
1231
+ return {
1232
+ endChar: range.end.character,
1233
+ line: range.start.line,
1234
+ startChar: range.start.character,
1235
+ };
1236
+ }
1237
+ function deduplicateRanges(ranges) {
1238
+ const seenRanges = new Set();
1239
+ return ranges.filter((range) => {
1240
+ const key = `${range.start.offset}:${range.end.offset}`;
1241
+ if (seenRanges.has(key)) {
1242
+ return false;
1243
+ }
1244
+ seenRanges.add(key);
1245
+ return true;
1246
+ });
1247
+ }
1248
+ //# sourceMappingURL=templateComponents.js.map