Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: better detection of references, handle namespace dependencies #89

Merged
merged 4 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# dts-buddy changelog

## 0.5.3

- Support `import * as X` imports ([#89](https://github.com/Rich-Harris/dts-buddy/pull/89))
- Fix deduplication logic to rename exports in fewer cases ([#89](https://github.com/Rich-Harris/dts-buddy/pull/89))
- Recognize import dependencies within namespaces ([#89](https://github.com/Rich-Harris/dts-buddy/pull/89))

## 0.5.2

- Support stripping internal types using `@internal` JSDoc tags ([#87](https://github.com/Rich-Harris/dts-buddy/pull/87))
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dts-buddy",
"version": "0.5.2",
"version": "0.5.3",
"description": "A tool for creating .d.ts bundles",
"repository": {
"type": "git",
Expand Down
21 changes: 20 additions & 1 deletion src/create-module-declaration.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,24 @@ export function create_module_declaration(id, entry, created, resolve, options)
const index = module.dts.indexOf('//# sourceMappingURL=');
if (index !== -1) result.remove(index, module.dts.length);

if (module.import_all.size > 0) {
// remove the leading `Foo.` from references to `import * as Foo` namespace imports
walk(module.ast, (node) => {
if (is_reference(node) && ts.isQualifiedName(node.parent)) {
const binding = module.import_all.get(node.getText(module.ast));
if (binding) {
result.remove(node.pos, result.original.indexOf('.', node.end) + 1);
const declaration = bundle
.get(binding.id)
?.declarations.get(node.parent.right.getText(module.ast));
if (declaration?.alias) {
result.overwrite(node.parent.right.pos, node.parent.right.end, declaration.alias);
}
}
}
});
}

ts.forEachChild(module.ast, (node) => {
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
result.remove(node.pos, node.end);
Expand Down Expand Up @@ -366,7 +384,8 @@ export function create_module_declaration(id, entry, created, resolve, options)
return false;
}

if (is_reference(node)) {
// We need to include the declarations because if references to them have changed, we need to update the declarations, too
if (is_reference(node, true)) {
const name = node.getText(module.ast);

const declaration = trace(module.file, name);
Expand Down
3 changes: 2 additions & 1 deletion src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ interface Declaration {
external: boolean;
included: boolean;
dependencies: Reference[];
/** The name we'd like to use to refer to this binding.
/**
* The name we'd like to use to refer to this binding.
* Only applies to default imports from external modules
*/
preferred_alias: string;
Expand Down
58 changes: 48 additions & 10 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** @import { Declaration, Module, Namespace } from './types' */
/** @import { Binding, Declaration, Module, Namespace } from './types' */
import fs from 'node:fs';
import path from 'node:path';
import glob from 'tiny-glob/sync.js';
Expand Down Expand Up @@ -388,7 +388,11 @@ export function get_dts(file, created, resolve, options) {

if (tsu.isNamespaceDeclaration(node)) {
const previous = current;
current = { declarations: new Map(), references: new Set(), exports: new Map() };
current = {
declarations: new Map(),
references: new Set(),
exports: new Map()
};

node.body.forEachChild(scan);

Expand All @@ -398,6 +402,18 @@ export function get_dts(file, created, resolve, options) {
}
}

for (const inner of current.declarations.values()) {
for (const inner_dep of inner.dependencies) {
if (
!declaration.dependencies.some(
(dep) => dep.name === inner_dep.name && dep.module === inner_dep.module
)
) {
declaration.dependencies.push(inner_dep);
}
}
}

current = previous;
} else {
walk(node, (node) => {
Expand All @@ -417,9 +433,15 @@ export function get_dts(file, created, resolve, options) {
module.dependencies.push(resolved);

if (node.qualifier) {
// In the case of `import('./foo').Foo.Bar`, this contains `Foo.Bar`,
// but we only want `Foo` (because we don't traverse into namespaces)
let id = node.qualifier;
while (ts.isQualifiedName(id)) {
id = id.left;
}
declaration.dependencies.push({
module: resolved ?? node.argument.literal.text,
name: node.qualifier.getText(module.ast)
name: id.getText(module.ast)
});
}
}
Expand All @@ -432,10 +454,18 @@ export function get_dts(file, created, resolve, options) {
current.references.add(name);

if (name !== declaration.name) {
declaration.dependencies.push({
module: file,
name
});
// If this references an import * as X statement, we add a dependency to Y of the X.Y access
if (module.import_all.has(name) && ts.isQualifiedName(node.parent)) {
declaration.dependencies.push({
module: /** @type {Binding} */ (module.import_all.get(name)).id,
name: node.parent.right.getText(module.ast)
});
} else {
declaration.dependencies.push({
module: file,
name
});
}
}
}
});
Expand Down Expand Up @@ -523,23 +553,25 @@ export function is_declaration(node) {

/**
* @param {ts.Node} node
* @param {boolean} [include_declarations]
* @returns {node is ts.Identifier}
*/
export function is_reference(node) {
export function is_reference(node, include_declarations = false) {
if (!ts.isIdentifier(node)) return false;

if (node.parent) {
if (is_declaration(node.parent)) {
if (ts.isVariableStatement(node.parent)) {
return node === node.parent.declarationList.declarations[0].name;
return false;
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this because the previous statement didn't make sense - the node could never be equal to the right hand side, because the node itself can only ever be VariableDeclarationList


return node === node.parent.name;
return include_declarations && node.parent.name === node;
}

if (ts.isPropertyAccessExpression(node.parent)) return node === node.parent.expression;
if (ts.isPropertyDeclaration(node.parent)) return node === node.parent.initializer;
if (ts.isPropertyAssignment(node.parent)) return node === node.parent.initializer;
if (ts.isMethodSignature(node.parent)) return node !== node.parent.name;

if (ts.isImportTypeNode(node.parent)) return false;
if (ts.isPropertySignature(node.parent)) return false;
Expand All @@ -548,6 +580,12 @@ export function is_reference(node) {
if (ts.isLabeledStatement(node.parent)) return false;
if (ts.isBreakOrContinueStatement(node.parent)) return false;
if (ts.isEnumMember(node.parent)) return false;
if (ts.isModuleDeclaration(node.parent)) return false;

// Only X in X.Y.Z is a reference we care about
if (ts.isQualifiedName(node.parent)) {
return node.parent.left === node && ts.isIdentifier(node.parent.right);
}

// `const = { x: 1 }` inexplicably becomes `namespace a { let x: number; }`
if (ts.isVariableDeclaration(node.parent)) {
Expand Down
5 changes: 5 additions & 0 deletions test/samples/import-all/input/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as X from './namespace';
import * as Types from './type';

export type Y = X.Namespace.X;
export type Z = Types.Z;
5 changes: 5 additions & 0 deletions test/samples/import-all/input/namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export namespace Namespace {
export interface X {
error(): string;
}
}
1 change: 1 addition & 0 deletions test/samples/import-all/input/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Z = true;
14 changes: 14 additions & 0 deletions test/samples/import-all/output/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
declare module 'import-all' {
export type Y =Namespace.X;
export type Z =Z_1;
namespace Namespace {
interface X {
error(): string;
}
}
type Z_1 = true;

export {};
}

//# sourceMappingURL=index.d.ts.map
20 changes: 20 additions & 0 deletions test/samples/import-all/output/index.d.ts.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": 3,
"file": "index.d.ts",
"names": [
"Y",
"Z",
"Namespace"
],
"sources": [
"../input/index.ts",
"../input/type.ts",
"../input/namespace.ts"
],
"sourcesContent": [
null,
null,
null
],
"mappings": ";aAGYA,CAACA;aCHDC,CAACA;WCAIC,SAASA"
}
3 changes: 3 additions & 0 deletions test/samples/namespace-exports/input/dependency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface Dependency {
name: string;
}
3 changes: 2 additions & 1 deletion test/samples/namespace-exports/input/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { type Namespace } from './namespace';
import { type Namespace, type NamespaceWithDeps } from './namespace';

export type X = Namespace.X;
export { NamespaceWithDeps };
8 changes: 8 additions & 0 deletions test/samples/namespace-exports/input/namespace.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Dependency } from './dependency';

export namespace Namespace {
export interface X {
x: string;
Expand All @@ -7,3 +9,9 @@ export namespace Namespace {
y: string;
}
}

export namespace NamespaceWithDeps {
export interface Z {
z: Dependency;
}
}
8 changes: 8 additions & 0 deletions test/samples/namespace-exports/output/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ declare module 'namespace-exports' {
y: string;
}
}
export namespace NamespaceWithDeps {
interface Z {
z: Dependency;
}
}
interface Dependency {
name: string;
}

export {};
}
Expand Down
12 changes: 8 additions & 4 deletions test/samples/namespace-exports/output/index.d.ts.map
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
"file": "index.d.ts",
"names": [
"X",
"Namespace"
"Namespace",
"NamespaceWithDeps",
"Dependency"
],
"sources": [
"../input/index.ts",
"../input/namespace.ts"
"../input/namespace.ts",
"../input/dependency.ts"
],
"sourcesContent": [
null,
null,
null
],
"mappings": ";aAEYA,CAACA;WCFIC,SAASA"
}
"mappings": ";aAEYA,CAACA;WCAIC,SAASA;;;;;;;;kBAUTC,iBAAiBA;;;;;WCZjBC,UAAUA"
}
4 changes: 4 additions & 0 deletions test/samples/no-renames/input/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { Y } from './type';
export { Namespace } from './namespace';
export type X = true;
export function error(): void {}
5 changes: 5 additions & 0 deletions test/samples/no-renames/input/namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export namespace Namespace {
export interface X {
error(): string;
}
}
3 changes: 3 additions & 0 deletions test/samples/no-renames/input/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Namespace } from './namespace';

export type Y = Namespace.X;
14 changes: 14 additions & 0 deletions test/samples/no-renames/output/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
declare module 'no-renames' {
export type X = true;
export function error(): void;
export type Y = Namespace.X;
export namespace Namespace {
interface X {
error(): string;
}
}

export {};
}

//# sourceMappingURL=index.d.ts.map
21 changes: 21 additions & 0 deletions test/samples/no-renames/output/index.d.ts.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": 3,
"file": "index.d.ts",
"names": [
"X",
"error",
"Y",
"Namespace"
],
"sources": [
"../input/index.ts",
"../input/type.ts",
"../input/namespace.ts"
],
"sourcesContent": [
null,
null,
null
],
"mappings": ";aAEYA,CAACA;iBACGC,KAAKA;aCDTC,CAACA;kBCFIC,SAASA"
}
3 changes: 2 additions & 1 deletion test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
"paths": {
"#lib": ["./samples/path-config/input/lib.d.ts"]
}
}
},
"exclude": ["**/actual/"]
}
Loading