Description
Proposal
To better support the development and consumption of node.js modules in TypeScript, I propose that we add the ability to emit a single declaration file for a node.js package. To that end, I suggest we add the following features:
- Add a
packageName
compiler option, used to specify the name of the node.js package. - Add a
packageMain
compiler option, used to specify the path to the main typescript module for the package. - Add a
packageDeclaration
compiler option, used to specify the output path of the declaration file for the package. - When the above three options are supplied, along with
declaration
and eithermodule
or atarget
ofES6
, we should emit a single declaration file for the program inputs.
The output for a packageDeclaration
would have the following form:
- Exports for the main module will be defined inside of an ambient external module that has the name provided via
packageName
. - Exports for relative modules will be defined inside of an ambient external module that has a name derived from the package name, and the path of the module relative to the nearest
package.json
file, common directory path, or the current directory.
Example
Given the following sources:
a.ts
import { C } from "./lib/b";
export function func(): C {
return new C();
}
lib\b.ts
export class C {
}
index.ts
export * from "./a";
And the following command line:
tsc index.ts a.ts lib/b.ts --module commonjs --declaration --packageName sample --packageMain index.ts --packageDeclaration sample.d.ts
You would get the following package declaration output file:
sample.d.ts
declare module "sample" {
export * from "sample/a";
}
declare module "sample/a" {
import { C } from "sample/lib/b";
function func(): C;
}
declare module "sample/lib/b" {
class C {
}
}
Out of scope
It would be nice to also be able to emit only the exports visible from the main module, and reduce the overall output side of the declaration file, but not all node.js packages are designed to work that way. Instead, in the future we could investigate an option to choose whether to emit a single ambient module declaration. The proposed approach can be used whether you intend to have a single output module or the ability to reach nested submodules, while the more restrictive approach would alienate some package authors.
It is also out of scope to infer package information from the package.json
file, although that may be considered in the future. That might look something like having a package
compiler option that can read the package.json
file to infer the packageName
from the "name"
field of the package, and the packageDeclaration
from the "typings"
(proposed) field of the package. However, its more complicated to derive packageMain
as the "main"
field of the package will be pointing to the output file and not the original typescript file. We might have to propose a "devMain"
, or some
other similarly named field, to satisfy this requirement.
As proposed, using packageDeclaration
will circumvent regular declaration file generation. We could opt to support both, by writing individual declaration files for each non-declaration input as well as the package-level declaration, though I am not certain whether that would be necessary.
A sample implementation for this proposal can be found in the packageDeclaration2 branch.