From f0ef13fdda2d133ff15402e5d67ae33a1f215552 Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Wed, 10 Jan 2024 17:25:58 +0000 Subject: [PATCH] Fix js-experimental ImportMap by not rendering empty statements --- .changeset/tiny-ducks-love.md | 5 ++ src/renderers/js-experimental/ImportMap.ts | 12 ++- .../js-experimental/ImportMap.test.ts | 77 +++++++++++++++++++ 3 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 .changeset/tiny-ducks-love.md create mode 100644 test/renderers/js-experimental/ImportMap.test.ts diff --git a/.changeset/tiny-ducks-love.md b/.changeset/tiny-ducks-love.md new file mode 100644 index 000000000..77d09909b --- /dev/null +++ b/.changeset/tiny-ducks-love.md @@ -0,0 +1,5 @@ +--- +'@metaplex-foundation/kinobi': patch +--- + +Fix js-experimental ImportMap by not rendering empty statements diff --git a/src/renderers/js-experimental/ImportMap.ts b/src/renderers/js-experimental/ImportMap.ts index 8b9c88213..a7d920934 100644 --- a/src/renderers/js-experimental/ImportMap.ts +++ b/src/renderers/js-experimental/ImportMap.ts @@ -36,8 +36,11 @@ export class ImportMap { new Map(); add(module: ImportFrom, imports: string | string[] | Set): ImportMap { + const newImports = new Set( + typeof imports === 'string' ? [imports] : imports + ); + if (newImports.size === 0) return this; const currentImports = this._imports.get(module) ?? new Set(); - const newImports = typeof imports === 'string' ? [imports] : imports; newImports.forEach((i) => currentImports.add(i)); this._imports.set(module, currentImports); return this; @@ -47,8 +50,11 @@ export class ImportMap { module: ImportFrom, imports: string | string[] | Set ): ImportMap { + const importsToRemove = new Set( + typeof imports === 'string' ? [imports] : imports + ); + if (importsToRemove.size === 0) return this; const currentImports = this._imports.get(module) ?? new Set(); - const importsToRemove = typeof imports === 'string' ? [imports] : imports; importsToRemove.forEach((i) => currentImports.delete(i)); if (currentImports.size === 0) { this._imports.delete(module); @@ -93,7 +99,7 @@ export class ImportMap { return this._imports.size === 0; } - toString(dependencies: Record): string { + toString(dependencies: Record = {}): string { const dependencyMap = { ...DEFAULT_MODULE_MAP, ...dependencies }; const importStatements = [...this._imports.entries()] .map(([module, imports]) => { diff --git a/test/renderers/js-experimental/ImportMap.test.ts b/test/renderers/js-experimental/ImportMap.test.ts new file mode 100644 index 000000000..9fd8d95a0 --- /dev/null +++ b/test/renderers/js-experimental/ImportMap.test.ts @@ -0,0 +1,77 @@ +import test from 'ava'; +import { ImportMap } from '../../../src/renderers/js-experimental/ImportMap'; + +test('it renders JavaScript import statements', (t) => { + // Given an import map with 3 imports from 2 sources. + const importMap = new ImportMap() + .add('@solana/addresses', ['getAddressEncoder', 'Address']) + .add('@solana/transactions', 'Transaction'); + + // When we render it. + const importStatements = importMap.toString(); + + // Then we expect the following import statements. + t.is( + importStatements, + "import { Address, getAddressEncoder } from '@solana/addresses';\n" + + "import { Transaction } from '@solana/transactions';" + ); +}); + +test('it renders JavaScript import aliases', (t) => { + // Given an import map with an import alias. + const importMap = new ImportMap() + .add('@solana/addresses', 'Address') + .addAlias('@solana/addresses', 'Address', 'SolanaAddress'); + + // When we render it. + const importStatements = importMap.toString(); + + // Then we expect the following import statement. + t.is( + importStatements, + "import { Address as SolanaAddress } from '@solana/addresses';" + ); +}); + +test('it offers some default dependency mappings', (t) => { + // Given an import map with some recognized dependency keys. + const importMap = new ImportMap() + .add('solanaAddresses', 'Address') + .add('solanaCodecsCore', 'Codec') + .add('generatedTypes', 'MyType') + .add('shared', 'myHelper') + .add('hooked', 'MyCustomType'); + + // When we render it. + const importStatements = importMap.toString(); + + // Then we expect the following import statements. + t.is( + importStatements, + "import { Address } from '@solana/addresses';\n" + + "import { Codec } from '@solana/codecs-core';\n" + + "import { MyCustomType } from '../../hooked';\n" + + "import { myHelper } from '../shared';\n" + + "import { MyType } from '../types';" + ); +}); + +test('it supports custom dependency mappings', (t) => { + // Given an import map with some custom dependency keys. + const importMap = new ImportMap().add('myDependency', 'MyType'); + + // When we render it whilst providing custom dependency mappings. + const importStatements = importMap.toString({ + myDependency: 'my/custom/path', + }); + + // Then we expect the following import statement. + t.is(importStatements, "import { MyType } from 'my/custom/path';"); +}); + +test('it does not render empty import statements', (t) => { + t.is(new ImportMap().toString(), ''); + t.is(new ImportMap().add('shared', []).toString(), ''); + t.is(new ImportMap().addAlias('shared', 'Foo', 'Bar').toString(), ''); +});