clava 0.1.18 → 0.1.19

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # clava
2
2
 
3
+ ## 0.1.19
4
+
5
+ ### Patch Changes
6
+
7
+ - b349c39: Fixed TypeScript Go to Definition and Rename Symbol for variant props on Clava components.
8
+
9
+ The exported component prop types now preserve the original variant property symbols while still reading their allowed values from the fully merged variant definitions. This keeps editor navigation pointing back to the local `variants` object without regressing inherited variant merging or `null`-based disabling.
10
+
3
11
  ## 0.1.18
4
12
 
5
13
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -73,7 +73,7 @@ type MergeExtendedComputedVariants<T> = T extends readonly [infer First, ...infe
73
73
  type ExtractVariants<T> = T extends CVComponent<infer V, any, infer E, any> ? V & MergeExtendedVariants<E> : {};
74
74
  type ExtractComputedVariants<T> = T extends CVComponent<any, infer CV, infer E, any> ? CV & Omit<MergeExtendedComputedVariants<E>, keyof CV> : {};
75
75
  type MergeVariantDefinition<Child, Parent> = Child extends Record<string, any> ? Parent extends Record<string, any> ? Omit<Parent, keyof Child> & Child : Child : Child;
76
- type MergeVariantMaps<Child, Parent> = { [K in keyof Child | keyof Parent]: K extends keyof Child ? K extends keyof Parent ? MergeVariantDefinition<Child[K], Parent[K]> : Child[K] : K extends keyof Parent ? Parent[K] : never };
76
+ type MergeVariantMaps<Child, Parent> = Omit<Parent, keyof Child> & Child & { [K in keyof Child & keyof Parent]: MergeVariantDefinition<Child[K], Parent[K]> };
77
77
  type MergeExtendedAllVariants<E extends AnyComponent[]> = MergeExtendedVariants<E> & MergeExtendedComputedVariants<E>;
78
78
  type MergeBaseVariants<V, E extends AnyComponent[]> = MergeVariantMaps<NoInfer<V>, MergeExtendedAllVariants<E>>;
79
79
  type MergeVariants<V, CV, E extends AnyComponent[]> = NoInfer<CV> & Omit<MergeBaseVariants<V, E>, keyof CV>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clava",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Clava library",
5
5
  "keywords": [
6
6
  "class variance",
@@ -0,0 +1,177 @@
1
+ import {
2
+ mkdirSync,
3
+ mkdtempSync,
4
+ readFileSync,
5
+ rmSync,
6
+ writeFileSync,
7
+ } from "node:fs";
8
+ import { dirname, join, resolve } from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+ import ts from "typescript";
11
+ import { afterEach, describe, expect, test } from "vitest";
12
+
13
+ const sourceDir = dirname(fileURLToPath(import.meta.url));
14
+ const packageDir = resolve(sourceDir, "..");
15
+ const workspaceDir = resolve(packageDir, "../..");
16
+ const tempDirs: string[] = [];
17
+
18
+ interface LanguageServiceFixture {
19
+ consumerFile: string;
20
+ consumerSource: string;
21
+ service: ts.LanguageService;
22
+ }
23
+
24
+ function createCompilerOptions(): ts.CompilerOptions {
25
+ return {
26
+ target: ts.ScriptTarget.ES2020,
27
+ module: ts.ModuleKind.NodeNext,
28
+ moduleResolution: ts.ModuleResolutionKind.NodeNext,
29
+ allowImportingTsExtensions: true,
30
+ strict: true,
31
+ skipLibCheck: true,
32
+ esModuleInterop: true,
33
+ resolveJsonModule: true,
34
+ lib: ["ES2020", "DOM", "DOM.Iterable"],
35
+ customConditions: ["source"],
36
+ };
37
+ }
38
+
39
+ function getFixturePosition(source: string, pattern: string): number {
40
+ const position = source.indexOf(pattern);
41
+ if (position === -1) {
42
+ throw new Error(`Pattern not found in fixture source: ${pattern}`);
43
+ }
44
+ return position;
45
+ }
46
+
47
+ function createLanguageServiceHost(
48
+ scriptFileNames: string[],
49
+ currentDirectory: string,
50
+ options: ts.CompilerOptions,
51
+ ): ts.LanguageServiceHost {
52
+ return {
53
+ getCompilationSettings: () => options,
54
+ getScriptFileNames: () => scriptFileNames,
55
+ getScriptVersion: () => "0",
56
+ getScriptSnapshot: (fileName) => {
57
+ if (!ts.sys.fileExists(fileName)) return;
58
+ return ts.ScriptSnapshot.fromString(readFileSync(fileName, "utf8"));
59
+ },
60
+ getCurrentDirectory: () => currentDirectory,
61
+ getDefaultLibFileName: ts.getDefaultLibFilePath,
62
+ fileExists: (fileName) => ts.sys.fileExists(fileName),
63
+ readFile: (fileName) => ts.sys.readFile(fileName),
64
+ readDirectory: (...args) => ts.sys.readDirectory(...args),
65
+ directoryExists: (directoryName) => ts.sys.directoryExists(directoryName),
66
+ getDirectories: (directoryName) => ts.sys.getDirectories(directoryName),
67
+ resolveModuleNames: (moduleNames, containingFile) => {
68
+ return moduleNames.map((moduleName) => {
69
+ return ts.resolveModuleName(moduleName, containingFile, options, ts.sys)
70
+ .resolvedModule;
71
+ });
72
+ },
73
+ };
74
+ }
75
+
76
+ function createLanguageServiceFixture(
77
+ consumerSource: string,
78
+ ): LanguageServiceFixture {
79
+ const tempDir = mkdtempSync(join(workspaceDir, ".tmp-language-service-"));
80
+ tempDirs.push(tempDir);
81
+
82
+ const fixtureSourceDir = join(tempDir, "src");
83
+ mkdirSync(fixtureSourceDir, { recursive: true });
84
+
85
+ for (const fileName of ["index.ts", "types.ts", "utils.ts"]) {
86
+ writeFileSync(
87
+ join(fixtureSourceDir, fileName),
88
+ readFileSync(join(sourceDir, fileName), "utf8"),
89
+ );
90
+ }
91
+
92
+ const consumerFile = join(tempDir, "consumer.ts");
93
+ writeFileSync(consumerFile, consumerSource);
94
+
95
+ const options = createCompilerOptions();
96
+ const scriptFileNames = [
97
+ consumerFile,
98
+ join(fixtureSourceDir, "index.ts"),
99
+ join(fixtureSourceDir, "types.ts"),
100
+ join(fixtureSourceDir, "utils.ts"),
101
+ ];
102
+
103
+ const host = createLanguageServiceHost(scriptFileNames, tempDir, options);
104
+ const service = ts.createLanguageService(host);
105
+
106
+ return { consumerFile, consumerSource, service };
107
+ }
108
+
109
+ function getVariantFixtureSource() {
110
+ return `import { cv } from "./src/index.ts";
111
+
112
+ const button = cv({
113
+ variants: {
114
+ size: { sm: "sm", lg: "lg" },
115
+ },
116
+ });
117
+
118
+ button({
119
+ size: "sm",
120
+ });
121
+ `;
122
+ }
123
+
124
+ afterEach(() => {
125
+ while (tempDirs.length) {
126
+ const tempDir = tempDirs.pop();
127
+ if (!tempDir) continue;
128
+ rmSync(tempDir, { recursive: true, force: true });
129
+ }
130
+ });
131
+
132
+ describe("TypeScript language service", () => {
133
+ test("goes to the local variant definition from a variant prop usage", () => {
134
+ const fixture = createLanguageServiceFixture(getVariantFixtureSource());
135
+ const definitionStart = getFixturePosition(
136
+ fixture.consumerSource,
137
+ "size: { sm",
138
+ );
139
+ const usageStart = getFixturePosition(fixture.consumerSource, 'size: "sm"');
140
+ const definitions =
141
+ fixture.service.getDefinitionAtPosition(
142
+ fixture.consumerFile,
143
+ usageStart + 1,
144
+ ) ?? [];
145
+
146
+ expect(definitions).toHaveLength(1);
147
+ expect(definitions[0]).toMatchObject({
148
+ fileName: fixture.consumerFile,
149
+ textSpan: { start: definitionStart, length: 4 },
150
+ });
151
+ });
152
+
153
+ test("renames variant prop usages when renaming the variant definition", () => {
154
+ const fixture = createLanguageServiceFixture(getVariantFixtureSource());
155
+ const definitionStart = getFixturePosition(
156
+ fixture.consumerSource,
157
+ "size: { sm",
158
+ );
159
+ const usageStart = getFixturePosition(fixture.consumerSource, 'size: "sm"');
160
+ const renameLocations =
161
+ fixture.service.findRenameLocations(
162
+ fixture.consumerFile,
163
+ definitionStart + 1,
164
+ false,
165
+ false,
166
+ { providePrefixAndSuffixTextForRename: true },
167
+ ) ?? [];
168
+
169
+ const renameStarts = renameLocations
170
+ .filter((location) => location.fileName === fixture.consumerFile)
171
+ .map((location) => location.textSpan.start);
172
+
173
+ expect(renameStarts).toHaveLength(2);
174
+ expect(renameStarts).toContain(definitionStart);
175
+ expect(renameStarts).toContain(usageStart);
176
+ });
177
+ });
package/src/types.ts CHANGED
@@ -208,15 +208,13 @@ type MergeVariantDefinition<Child, Parent> =
208
208
  : Child
209
209
  : Child;
210
210
 
211
- type MergeVariantMaps<Child, Parent> = {
212
- [K in keyof Child | keyof Parent]: K extends keyof Child
213
- ? K extends keyof Parent
214
- ? MergeVariantDefinition<Child[K], Parent[K]>
215
- : Child[K]
216
- : K extends keyof Parent
217
- ? Parent[K]
218
- : never;
219
- };
211
+ type MergeVariantMaps<Child, Parent> = Omit<Parent, keyof Child> &
212
+ Child & {
213
+ [K in keyof Child & keyof Parent]: MergeVariantDefinition<
214
+ Child[K],
215
+ Parent[K]
216
+ >;
217
+ };
220
218
 
221
219
  type MergeExtendedAllVariants<E extends AnyComponent[]> =
222
220
  MergeExtendedVariants<E> & MergeExtendedComputedVariants<E>;