[bug] Circular references break .d.ts building
- Dominant language
- TypeScript
- Stars
- 11.3k
- Forks
- 275
- PR merge metrics
- No merged PRs in 30d
Description
In 2 files, I have each other referencing each other directly through the import statement.
The files look a little something like this:
LocalBucket.ts
```ts
/**
* Bucket used for saving ratelimits
* @protected
*/
class LocalBucket {
/**
* ratelimiter used for ratelimiting requests
*/
public ratelimiter: import("./Ratelimiter");
/**
* Key used internally to routify requests
*/
public routeKey: string;
/**
* Create a new bucket
* @param ratelimiter ratelimiter used for ratelimiting requests
* @param routeKey Key used internally to routify requests. Assigned by ratelimiter
*/
public constructor(ratelimiter: import("./Ratelimiter"), routeKey: string) {
this.ratelimiter = ratelimiter;
this.routeKey = routeKey;
}
}
export = LocalBucket;
```
Ratelimiter.ts
```ts
import LocalBucket = require("./LocalBucket");
const routeRegex = /\/([a-z-]+)\/(?:\d+)/g;
const reactionsRegex = /\/reactions\/[^/]+/g;
const reactionsUserRegex = /\/reactions\/:id\/[^/]+/g;
const webhooksRegex = /^\/webhooks\/(\d+)\/[A-Za-z0-9-_]+/;
const isMessageEndpointRegex = /\/messages\/:id$/;
/**
* Ratelimiter used for handling the ratelimits imposed by the rest api
* @protected
*/
class Ratelimiter {
/**
* An object of Buckets that store rate limit info
*/
public buckets: { [routeKey: string]: LocalBucket; } = {};
/**
* Queue a rest call to be executed
* @param fn function to call once the ratelimit is ready
* @param url Endpoint of the request
* @param method Http method used by the request
*/
public queue(fn: (bucket: import("./LocalBucket")) => any, url: string, method: string) {
const routeKey = this.routify(url, method);
if (!this.buckets[routeKey]) this.buckets[routeKey] = new LocalBucket(this, routeKey);
this.buckets[routeKey].queue(fn);
}
}
```
Whenever tsup gets to building the .d.ts, it tries to generate this:
```js
var __Ratelimiter = /*#__PURE__*/Object.freeze({
__proto__: null,
get default () { return Ratelimiter; }
});
```
Although this causes an "Expected a property assignment" error which comes from the rollup dist file.
Here's the full stack:
```
Error: Expected a property assignment
6 | var __Ratelimiter = /*#__PURE__*/Object.freeze({
7 | __proto__: null,
> 8 | get default () { return Ratelimiter; }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
9 | });
10 |
11 | declare const Constants: {
at NamespaceFixer.findNamespaces (A:\Windows\Documents\GitHub\SnowTransfer\node_modules\tsup\dist\rollup.js:6121:21)
at NamespaceFixer.fix (A:\Windows\Documents\GitHub\SnowTransfer\node_modules\tsup\dist\rollup.js:6142:48)
at Object.renderChunk (A:\Windows\Documents\GitHub\SnowTransfer\node_modules\tsup\dist\rollup.js:7199:25)
at A:\Windows\Documents\GitHub\SnowTransfer\node_modules\rollup\dist\shared\rollup.js:23807:40
Error: error occured in dts build
at Worker. (A:\Windows\Documents\GitHub\SnowTransfer\node_modules\tsup\dist\index.js:2226:26)
at Worker.emit (node:events:513:28)
at MessagePort. (node:internal/worker:233:53)
at [nodejs.internal.kHybridDispatch] (node:internal/event_target:645:20)
at exports.emitMessage (node:internal/per_context/messageport:23:28)
DTS Build error
```
After making a type in LocalBucket which represented the RateLimiter partially as I needed it, the error went away. Although, I'm a little confused as generating a getter has no benefit and the plain names can be referenced anywhere in the file regardless of where they appear.
Contributor guide
Research direction
Reproduce the failure with the circular imports shown in LocalBucket.ts and Ratelimiter.ts, then start at tsup's d.ts build path and the NamespaceFixer stack in tsup/dist/rollup.js. The work is done when the circular-reference example completes d.ts generation without the "Expected a property assignment" error and the behavior is covered by a regression check.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- build-system, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100