Skip to content

Commit 6639091

Browse files
authored
[0.21] Fix a bug that reuses the same ImportMap in fragments (#200)
1 parent 1dc5eab commit 6639091

File tree

4 files changed

+65
-3
lines changed

4 files changed

+65
-3
lines changed

.changeset/giant-dogs-scream.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@kinobi-so/renderers-js': patch
3+
---
4+
5+
Fix a bug that reuses the same `ImportMap` within different code fragments

packages/renderers-js/src/fragments/common.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ export class Fragment {
3131

3232
constructor(render: string, imports?: ImportMap, features?: Set<FragmentFeature>) {
3333
this.render = render;
34-
this.imports = imports ?? new ImportMap();
35-
this.features = features ?? new Set();
34+
this.imports = imports ? new ImportMap().mergeWith(imports) : new ImportMap();
35+
this.features = new Set([...(features ?? [])]);
3636
}
3737

3838
setRender(render: string): this {

packages/renderers-js/test/_setup.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ export function renderMapContains(renderMap: RenderMap, key: string, expected: (
2121
return codeContains(renderMap.get(key), expected);
2222
}
2323

24+
export function renderMapDoesNotContain(
25+
renderMap: RenderMap,
26+
key: string,
27+
expected: (RegExp | string)[] | RegExp | string,
28+
) {
29+
expect(renderMap.has(key), `RenderMap is missing key "${key}".`).toBe(true);
30+
return codeDoesNotContain(renderMap.get(key), expected);
31+
}
32+
2433
export async function codeContains(actual: string, expected: (RegExp | string)[] | RegExp | string) {
2534
const expectedArray = Array.isArray(expected) ? expected : [expected];
2635
const normalizedActual = await normalizeCode(actual);
@@ -50,6 +59,15 @@ export function renderMapContainsImports(renderMap: RenderMap, key: string, expe
5059
return codeContainsImports(renderMap.get(key), expectedImports);
5160
}
5261

62+
export function renderMapDoesNotContainImports(
63+
renderMap: RenderMap,
64+
key: string,
65+
expectedImports: Record<string, string[]>,
66+
) {
67+
expect(renderMap.has(key), `RenderMap is missing key "${key}".`).toBe(true);
68+
return codeDoesNotContainImports(renderMap.get(key), expectedImports);
69+
}
70+
5371
export async function codeContainsImports(actual: string, expectedImports: Record<string, string[]>) {
5472
const normalizedActual = await inlineCode(actual);
5573
const importPairs = Object.entries(expectedImports).flatMap(([key, value]) => {
@@ -61,6 +79,17 @@ export async function codeContainsImports(actual: string, expectedImports: Recor
6179
});
6280
}
6381

82+
export async function codeDoesNotContainImports(actual: string, expectedImports: Record<string, string[]>) {
83+
const normalizedActual = await inlineCode(actual);
84+
const importPairs = Object.entries(expectedImports).flatMap(([key, value]) => {
85+
return value.map(v => [key, v] as const);
86+
});
87+
88+
importPairs.forEach(([importFrom, importValue]) => {
89+
expect(normalizedActual).not.toMatch(new RegExp(`import{[^}]*\\b${importValue}\\b[^}]*}from'${importFrom}'`));
90+
});
91+
}
92+
6493
export function codeStringAsRegex(code: string) {
6594
const stringAsRegex = escapeRegex(code)
6695
// Transform spaces between words into required whitespace.

packages/renderers-js/test/pdasPage.test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
constantPdaSeedNodeFromString,
33
numberTypeNode,
4+
optionTypeNode,
45
pdaNode,
56
programNode,
67
publicKeyTypeNode,
@@ -10,7 +11,7 @@ import { visit } from '@kinobi-so/visitors-core';
1011
import { test } from 'vitest';
1112

1213
import { getRenderMapVisitor } from '../src';
13-
import { renderMapContains } from './_setup';
14+
import { renderMapContains, renderMapContainsImports, renderMapDoesNotContainImports } from './_setup';
1415

1516
test('it renders a PDA helper function and its input type', async () => {
1617
// Given the following PDA node.
@@ -82,3 +83,30 @@ test('it renders an empty array of seeds for seedless PDAs', async () => {
8283
'getProgramDerivedAddress({ programAddress, seeds: [] })',
8384
]);
8485
});
86+
87+
test('it does not import strict types for variable seeds', async () => {
88+
// Given the following PDA node.
89+
const node = programNode({
90+
name: 'myProgram',
91+
pdas: [
92+
pdaNode({
93+
name: 'foo',
94+
seeds: [variablePdaSeedNode('myAccount', optionTypeNode(publicKeyTypeNode()))],
95+
}),
96+
],
97+
publicKey: '1111',
98+
});
99+
100+
// When we render it.
101+
const renderMap = visit(node, getRenderMapVisitor());
102+
103+
// Then the `Option` string type should not be imported.
104+
await renderMapDoesNotContainImports(renderMap, 'pdas/foo.ts', {
105+
'@solana/web3.js': ['type Option'],
106+
});
107+
108+
// But the `OptionOrNullable` loose type should be imported.
109+
await renderMapContainsImports(renderMap, 'pdas/foo.ts', {
110+
'@solana/web3.js': ['type OptionOrNullable'],
111+
});
112+
});

0 commit comments

Comments
 (0)