@ts-bridge/cli
Advanced tools
Comparing version 0.1.1 to 0.1.2
@@ -10,2 +10,15 @@ # Changelog | ||
## [0.1.2] | ||
### Changed | ||
- Bump minimum TypeScript version to 4.8 ([#12](https://github.com/ts-bridge/ts-bridge/pull/12)) | ||
- The tool was already incompatible with TypeScript versions older than 4.8, | ||
but this change makes the minimum version explicit. | ||
### Fixed | ||
- Add transformer to remove type imports and exports ([#10](https://github.com/ts-bridge/ts-bridge/pull/10)) | ||
- This fixes compatibility with TypeScript 4.8 and later in certain cases. | ||
## [0.1.1] | ||
@@ -29,4 +42,5 @@ | ||
[Unreleased]: https://github.com/ts-bridge/ts-bridge/compare/@ts-bridge/cli@0.1.1...HEAD | ||
[Unreleased]: https://github.com/ts-bridge/ts-bridge/compare/@ts-bridge/cli@0.1.2...HEAD | ||
[0.1.2]: https://github.com/ts-bridge/ts-bridge/compare/@ts-bridge/cli@0.1.1...@ts-bridge/cli@0.1.2 | ||
[0.1.1]: https://github.com/ts-bridge/ts-bridge/compare/@ts-bridge/cli@0.1.0...@ts-bridge/cli@0.1.1 | ||
[0.1.0]: https://github.com/ts-bridge/ts-bridge/releases/tag/@ts-bridge/cli@0.1.0 |
@@ -11,3 +11,3 @@ import { dirname, join } from 'path'; | ||
import { executeSteps } from './steps.js'; | ||
import { getExportExtensionTransformer, getImportExtensionTransformer, getRequireExtensionTransformer, } from './transformers.js'; | ||
import { getTypeImportExportTransformer, getExportExtensionTransformer, getImportExtensionTransformer, getRequireExtensionTransformer, } from './transformers.js'; | ||
const { createProgram, getPreEmitDiagnostics, ModuleResolutionKind } = typescript; | ||
@@ -234,2 +234,3 @@ /** | ||
getExportExtensionTransformer(extension, options), | ||
getTypeImportExportTransformer(options), | ||
...getTransformers(type, options), | ||
@@ -236,0 +237,0 @@ ], |
@@ -695,3 +695,3 @@ import { evaluate, getMockNodeModule, getMockPackageJson, getMockTsConfig, getVirtualEnvironment, noOp, } from '@ts-bridge/test-utils'; | ||
module: 'ES2022', | ||
moduleResolution: 'Node10', | ||
moduleResolution: 'Node', | ||
}, | ||
@@ -719,3 +719,3 @@ }), | ||
module: 'ES2022', | ||
moduleResolution: 'Node10', | ||
moduleResolution: 'Node', | ||
}, | ||
@@ -722,0 +722,0 @@ }), |
@@ -1,2 +0,2 @@ | ||
import type { CompilerOptions, TypeChecker, Node, SourceFile, ImportDeclaration, Statement, System } from 'typescript'; | ||
import type { CompilerOptions, TypeChecker, Node, SourceFile, ImportDeclaration, Statement, System, ExportDeclaration } from 'typescript'; | ||
import typescript from 'typescript'; | ||
@@ -85,2 +85,18 @@ /** | ||
/** | ||
* Get the import declaration without type-only imports. | ||
* | ||
* @param node - The import declaration node. | ||
* @returns The import declaration without type-only imports. If there are no | ||
* type-only imports, the node is returned as is. | ||
*/ | ||
export declare function getNonTypeImports(node: ImportDeclaration): ImportDeclaration | undefined; | ||
/** | ||
* Get the export declaration without type-only exports. | ||
* | ||
* @param node - The export declaration node. | ||
* @returns The export declaration without type-only exports. If there are no | ||
* type-only exports, the node is returned as is. | ||
*/ | ||
export declare function getNonTypeExports(node: ExportDeclaration): ExportDeclaration | undefined; | ||
/** | ||
* Create a namespace import, e.g.: | ||
@@ -87,0 +103,0 @@ * |
@@ -8,3 +8,3 @@ import chalk from 'chalk'; | ||
import { getIdentifierName } from './utils.js'; | ||
const { factory, isNamespaceImport, isStringLiteral, NodeFlags, resolveModuleName, SymbolFlags, SyntaxKind, } = typescript; | ||
const { factory, isNamedExports, isNamedImports, isNamespaceImport, isStringLiteral, NodeFlags, resolveModuleName, SymbolFlags, SyntaxKind, } = typescript; | ||
/** | ||
@@ -175,2 +175,37 @@ * Get the new import path for the given file and import path. This function | ||
/** | ||
* Get the import declaration without type-only imports. | ||
* | ||
* @param node - The import declaration node. | ||
* @returns The import declaration without type-only imports. If there are no | ||
* type-only imports, the node is returned as is. | ||
*/ | ||
export function getNonTypeImports(node) { | ||
if (!node.importClause?.namedBindings || | ||
!isNamedImports(node.importClause.namedBindings)) { | ||
return node; | ||
} | ||
const elements = node.importClause.namedBindings.elements.filter((element) => !element.isTypeOnly); | ||
if (elements.length === 0) { | ||
return undefined; | ||
} | ||
return factory.updateImportDeclaration(node, node.modifiers, factory.updateImportClause(node.importClause, false, node.importClause.name, factory.updateNamedImports(node.importClause.namedBindings, elements)), node.moduleSpecifier, node.attributes); | ||
} | ||
/** | ||
* Get the export declaration without type-only exports. | ||
* | ||
* @param node - The export declaration node. | ||
* @returns The export declaration without type-only exports. If there are no | ||
* type-only exports, the node is returned as is. | ||
*/ | ||
export function getNonTypeExports(node) { | ||
if (!node.exportClause || !isNamedExports(node.exportClause)) { | ||
return node; | ||
} | ||
const elements = node.exportClause.elements.filter((element) => !element.isTypeOnly); | ||
if (elements.length === 0) { | ||
return undefined; | ||
} | ||
return factory.updateExportDeclaration(node, node.modifiers, false, factory.updateNamedExports(node.exportClause, elements), node.moduleSpecifier, node.attributes); | ||
} | ||
/** | ||
* Create a namespace import, e.g.: | ||
@@ -177,0 +212,0 @@ * |
import { getMockNodeModule, getMockPackageJson, getVirtualEnvironment, noOp, } from '@ts-bridge/test-utils'; | ||
import typescript from 'typescript'; | ||
import { describe, expect, it, vi } from 'vitest'; | ||
import { getImportMetaUrl, getImportPath, getNamedImportNodes, getNamespaceImport, getUniqueIdentifier, } from './generator.js'; | ||
import { getImportMetaUrl, getImportPath, getNamedImportNodes, getNamespaceImport, getNonTypeExports, getNonTypeImports, getUniqueIdentifier, } from './generator.js'; | ||
const { factory } = typescript; | ||
@@ -334,2 +334,70 @@ /** | ||
}); | ||
describe('getNonTypeImports', () => { | ||
it('removes type imports from an import declaration', () => { | ||
const importDeclaration = factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([ | ||
factory.createImportSpecifier(true, undefined, factory.createIdentifier('foo')), | ||
factory.createImportSpecifier(false, undefined, factory.createIdentifier('bar')), | ||
])), factory.createStringLiteral('foo'), undefined); | ||
const result = getNonTypeImports(importDeclaration); | ||
expect(result).not.toBeUndefined(); | ||
expect(result).not.toStrictEqual(importDeclaration); | ||
expect(compile(result)).toMatchInlineSnapshot(` | ||
""use strict"; | ||
import { bar } from "foo"; | ||
" | ||
`); | ||
}); | ||
it('returns the same node if the import declaration is not a named import', () => { | ||
const importDeclaration = factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamespaceImport(factory.createIdentifier('foo'))), factory.createStringLiteral('bar'), undefined); | ||
const result = getNonTypeImports(importDeclaration); | ||
expect(result).toBe(importDeclaration); | ||
}); | ||
it('returns the same node if there is no import clause', () => { | ||
const importDeclaration = factory.createImportDeclaration(undefined, undefined, factory.createStringLiteral('foo'), undefined); | ||
const result = getNonTypeImports(importDeclaration); | ||
expect(result).toBe(importDeclaration); | ||
}); | ||
it('returns `undefined` if there are no non-type named imports', () => { | ||
const importDeclaration = factory.createImportDeclaration(undefined, factory.createImportClause(false, undefined, factory.createNamedImports([ | ||
factory.createImportSpecifier(true, undefined, factory.createIdentifier('foo')), | ||
factory.createImportSpecifier(true, undefined, factory.createIdentifier('bar')), | ||
])), factory.createStringLiteral('bar'), undefined); | ||
const result = getNonTypeImports(importDeclaration); | ||
expect(result).toBeUndefined(); | ||
}); | ||
}); | ||
describe('getNonTypeExports', () => { | ||
it('removes type exports from an export declaration', () => { | ||
const exportDeclaration = factory.createExportDeclaration(undefined, false, factory.createNamedExports([ | ||
factory.createExportSpecifier(true, undefined, factory.createIdentifier('foo')), | ||
factory.createExportSpecifier(false, undefined, factory.createIdentifier('bar')), | ||
])); | ||
const result = getNonTypeExports(exportDeclaration); | ||
expect(result).not.toBeUndefined(); | ||
expect(result).not.toStrictEqual(exportDeclaration); | ||
expect(compile(result)).toMatchInlineSnapshot(` | ||
""use strict"; | ||
export { bar }; | ||
" | ||
`); | ||
}); | ||
it('returns the same node if the export declaration is not a named export', () => { | ||
const exportDeclaration = factory.createExportDeclaration(undefined, false, factory.createNamespaceExport(factory.createIdentifier('foo'))); | ||
const result = getNonTypeExports(exportDeclaration); | ||
expect(result).toBe(exportDeclaration); | ||
}); | ||
it('returns the same node if there is no export clause', () => { | ||
const exportDeclaration = factory.createExportDeclaration(undefined, false, undefined); | ||
const result = getNonTypeExports(exportDeclaration); | ||
expect(result).toBe(exportDeclaration); | ||
}); | ||
it('returns `undefined` if there are no non-type named exports', () => { | ||
const exportDeclaration = factory.createExportDeclaration(undefined, false, factory.createNamedExports([ | ||
factory.createExportSpecifier(true, undefined, factory.createIdentifier('foo')), | ||
factory.createExportSpecifier(true, undefined, factory.createIdentifier('bar')), | ||
])); | ||
const result = getNonTypeExports(exportDeclaration); | ||
expect(result).toBeUndefined(); | ||
}); | ||
}); | ||
describe('getNamespaceImport', () => { | ||
@@ -336,0 +404,0 @@ it('returns a namespace import', () => { |
@@ -174,2 +174,19 @@ import type { CustomTransformer, ResolutionMode, SourceFile, System, TransformationContext, Transformer, TypeChecker } from 'typescript'; | ||
/** | ||
* Get a transformer that removes type-only imports and exports. This is the | ||
* standard behaviour for TypeScript 5.x, but this transformer is needed for | ||
* TypeScript 4.x. This may be a bug in TypeScript 4.x's compiler API. | ||
* | ||
* For example, the following type-only imports and exports: | ||
* ```ts | ||
* import type { Foo } from 'module'; | ||
* export type { Foo }; | ||
* ``` | ||
* | ||
* will be removed. | ||
* | ||
* @param _context - The transformer options. This is not used. | ||
* @returns The transformer function. | ||
*/ | ||
export declare function getTypeImportExportTransformer(_context: TransformerOptions): (context: TransformationContext) => Transformer<SourceFile>; | ||
/** | ||
* Get a custom transformer that sets the target module kind for the source | ||
@@ -176,0 +193,0 @@ * file. |
import typescript from 'typescript'; | ||
import { getImportMetaUrl, getImportPath, getNamedImportNodes, getNamespaceImport, getUniqueIdentifier, isGlobal, } from './generator.js'; | ||
import { getImportMetaUrl, getImportPath, getNamedImportNodes, getNamespaceImport, getNonTypeExports, getNonTypeImports, getUniqueIdentifier, isGlobal, } from './generator.js'; | ||
import { CJS_SHIMS_PACKAGE, ESM_REQUIRE_SHIMS_PACKAGE, ESM_SHIMS_PACKAGE, } from './shims.js'; | ||
@@ -355,2 +355,40 @@ const { factory, isBindingElement, isCallExpression, isExportDeclaration, isFunctionDeclaration, isIdentifier, isImportDeclaration, isMetaProperty, isPropertyAccessExpression, isStringLiteral, isVariableDeclaration, visitEachChild, visitNode, SyntaxKind, } = typescript; | ||
/** | ||
* Get a transformer that removes type-only imports and exports. This is the | ||
* standard behaviour for TypeScript 5.x, but this transformer is needed for | ||
* TypeScript 4.x. This may be a bug in TypeScript 4.x's compiler API. | ||
* | ||
* For example, the following type-only imports and exports: | ||
* ```ts | ||
* import type { Foo } from 'module'; | ||
* export type { Foo }; | ||
* ``` | ||
* | ||
* will be removed. | ||
* | ||
* @param _context - The transformer options. This is not used. | ||
* @returns The transformer function. | ||
*/ | ||
export function getTypeImportExportTransformer(_context) { | ||
return (context) => { | ||
return (sourceFile) => { | ||
const visitor = (node) => { | ||
if (isImportDeclaration(node)) { | ||
if (node.importClause?.isTypeOnly) { | ||
return undefined; | ||
} | ||
return getNonTypeImports(node); | ||
} | ||
if (isExportDeclaration(node)) { | ||
if (node.isTypeOnly) { | ||
return undefined; | ||
} | ||
return getNonTypeExports(node); | ||
} | ||
return visitEachChild(node, visitor, context); | ||
}; | ||
return visitNode(sourceFile, visitor); | ||
}; | ||
}; | ||
} | ||
/** | ||
* Get a custom transformer that sets the target module kind for the source | ||
@@ -357,0 +395,0 @@ * file. |
import { getMockNodeModule, getVirtualEnvironment, } from '@ts-bridge/test-utils'; | ||
import { describe, expect, it } from 'vitest'; | ||
import { getBuildTypeOptions } from './build-type.js'; | ||
import { getExportExtensionTransformer, getGlobalsTransformer, getImportExtensionTransformer, getImportMetaTransformer, getNamedImportTransformer, getRequireExtensionTransformer, getRequireTransformer, getTargetTransformer, } from './transformers.js'; | ||
import { getExportExtensionTransformer, getGlobalsTransformer, getImportExtensionTransformer, getImportMetaTransformer, getNamedImportTransformer, getRequireExtensionTransformer, getRequireTransformer, getTargetTransformer, getTypeImportExportTransformer, } from './transformers.js'; | ||
/** | ||
@@ -1031,1 +1031,50 @@ * Compile a string of (TypeScript) code to JavaScript, and return the compiled | ||
}); | ||
describe('getTypeImportExportTransformer', () => { | ||
it('removes type imports and exports', async () => { | ||
const environment = getVirtualEnvironment({ | ||
files: { | ||
'/index.ts': ` | ||
import type { foo } from './foo'; | ||
export type { foo } from './foo'; | ||
`, | ||
'/foo.ts': 'export type foo = "bar";', | ||
}, | ||
}); | ||
expect(await compile({ | ||
format: 'module', | ||
environment, | ||
transformer: getTypeImportExportTransformer({ | ||
typeChecker: environment.typeChecker, | ||
system: environment.system, | ||
baseDirectory: '/', | ||
}), | ||
})).toMatchInlineSnapshot(` | ||
"export {}; | ||
" | ||
`); | ||
}); | ||
it('removes named type imports and exports', async () => { | ||
const environment = getVirtualEnvironment({ | ||
files: { | ||
'/index.ts': ` | ||
import { type foo } from './foo'; | ||
import type { bar } from './foo'; | ||
export { type foo, bar } from './foo'; | ||
`, | ||
'/foo.ts': 'export type foo = "bar"; export const bar = "baz";', | ||
}, | ||
}); | ||
expect(await compile({ | ||
format: 'module', | ||
environment, | ||
transformer: getTypeImportExportTransformer({ | ||
typeChecker: environment.typeChecker, | ||
system: environment.system, | ||
baseDirectory: '/', | ||
}), | ||
})).toMatchInlineSnapshot(` | ||
"export { bar } from './foo'; | ||
" | ||
`); | ||
}); | ||
}); |
{ | ||
"name": "@ts-bridge/cli", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"description": "Bridge the gap between ES modules and CommonJS modules with an easy-to-use alternative to `tsc`.", | ||
@@ -64,3 +64,3 @@ "keywords": [ | ||
"@ts-bridge/shims": "^0.1.1", | ||
"typescript": ">=4.0.0" | ||
"typescript": ">=4.8.0" | ||
}, | ||
@@ -67,0 +67,0 @@ "peerDependenciesMeta": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
228007
5584