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

Generate "declare module" definition file #66

Open
heruan opened this issue Dec 21, 2015 · 28 comments
Open

Generate "declare module" definition file #66

heruan opened this issue Dec 21, 2015 · 28 comments

Comments

@heruan
Copy link

heruan commented Dec 21, 2015

Is tsproject able to bundle TS definitions in a single .d.ts file in the following form?

declare module 'bundle-name' {
  // project definititions
}

It would be useful to bundle plugins and provide a single module-definition file.

@ToddThomson
Copy link
Owner

TsProject produces a single definition file for bundles. Definition files are built using the Typescript compiler API from the generated bundle source code, so are really dependent on the bundle source files.
I tend to construct bundles using sets of ES6 classes and so the definition files look like:

export declare class TodoItem {
    title: string;
    completed: boolean;
    constructor(title: string, completed: boolean);
}
... additional exports

Please let me know if this answers your question or not.

@ToddThomson
Copy link
Owner

No response from @heruan so I am closing this issue.

@ToddThomson
Copy link
Owner

@heruan I am reopening this issue. I now understand what you are asking for. I will try to get this into the 1.2.x NPM release.

@ToddThomson
Copy link
Owner

See issue #71

@heruan
Copy link
Author

heruan commented Jan 18, 2016

Thank you @ToddThomson! I was busy and couldn't give feedback, but that's what I meant 👍
I'll gladly test when the feature will be available on master.

@ToddThomson
Copy link
Owner

My requirements for TsProject bundles are this:

  1. Nodejs single file application/module which exports a minimal API. Usually this is just a single API method.
  2. Browser client-side single file component/application module
  3. Nodejs or Browser single file library module.

For all 3 of these uses I want to have a clean bundle definition file with only the exports that define the public API. I also want to be able to minify ALL internal identifiers.

I believe this can be achieved by convention and a bundle configuration property. For a commonjs node module I can make use of a Typescript namespace:

export namespace TsProject {
    export function src( configFilePath: string, settings?: any ) {
    }
}

// Nodejs module exports
module.exports = TsProject;

With the bundle configuration specifying the exportNamespace as TsProject the d.ts definition file will be constructed properly and all other exports will be considered internal giving the TsProject minifier a context to further minimize internal identifiers.

@ToddThomson
Copy link
Owner

If the bundle package is set to "library", TsProject will now wrap the the non-external module references in:

export namespace "bundle name" {
}

this will result in a d.ts file like

// import statements to external referenced modules 
export declare namespace "bundle name" {
    // exported types
}

The "bundle name" is configured with "packageNamespace" in the bundle configuration.

@ToddThomson
Copy link
Owner

If the bundle package is set to "component", TsProject will only export the API in the namespace that matches the bundleNamespace configuration option. For TsProject itself ( a node application/component ), bundleNamespace is set to "TsProject" and package is set to "component". This results in the d.ts definition file:

import * as stream from "stream";
export declare namespace TsProject {
    function src(configFilePath: string, settings?: any): stream.Readable;
}

@ToddThomson
Copy link
Owner

@heruan This feature is now provided in the NPM release 1.2.0-rc.5.

@heruan
Copy link
Author

heruan commented Feb 5, 2016

Thank you very much @ToddThomson! I'll try it out ASAP.

@ToddThomson
Copy link
Owner

@heruan You're welcome. Please use the latest rc.7 for testing.

ToddThomson added a commit that referenced this issue Feb 5, 2016
Addresses issues #71, #66, #65
@ToddThomson
Copy link
Owner

Feature(s) provided in TsProject 1.2.

@heruan
Copy link
Author

heruan commented Apr 5, 2016

That's great, thank you! Is it also possibile to have TsProject generate just the bundles .d.ts files, without transpiling to JavaScript? I.e. having just the .d.ts files (one for each bundle) in the outputStream.

@heruan
Copy link
Author

heruan commented Apr 5, 2016

Also, I cannot get declare module in the bundle definition, only declare namespace. Can I configure TsProject to output declare module instead?

@ToddThomson
Copy link
Owner

@heruan When I added this feature I originally provided "declare module". However, when I reviewed the feature with how Typescript 1.8.x defines a "module" it was apparent that "declare namespace" was the correct usage.

@ToddThomson
Copy link
Owner

@heruan I have complete control over the output stream and what file streams are output. To have only the bundle .d.ts definition file output I would need to add a bundle configuration option. Perhaps you could add an issue for this and I will add it to the 1.3 milestone.

@heruan
Copy link
Author

heruan commented Apr 7, 2016

@ToddThomson I didn't know about declare namespace, but to work with ES6 modules a declare module is needed (also, many modules have names with dash chars). Maybe you could add a new option in tsconfig.json to output module declarations like this?

{
    "bundles": {
        "my-module": {
            "files": [ "..." ],
            "config": {
                "package": "module",
                "declaration": true
            }
        }
    }
}

and use the bundles key as module name:

declare module "my-module" {
// ...
}

@ToddThomson
Copy link
Owner

@heruan I'm going to reopen this issue for tracking purposes.

I will review the use of declare "namespace" vs "module" again. However, I am almost certain that declare module in Typescript is now only used for pre Typescript 1.5.

The namespace "name" comes from ts code, so you should be able to use whatever you like.

@ToddThomson ToddThomson reopened this Apr 7, 2016
@ToddThomson
Copy link
Owner

@heruan OK, for ES6 modules, declare module should be used. Namespaces still must be supported. I will support both based on the code context.

@ToddThomson
Copy link
Owner

The TsProject packageNamespace bundle configuration option will be renamed to packageModuleName. TsProject will determine if the module name refers to a Typescript namespace ( "Internal" module ) or an ES6 module and output accordingly.

@heruan
Copy link
Author

heruan commented Apr 8, 2016

That would be great! Why not use the keys of the bundles object in tsconfig.json as module names?

@ToddThomson
Copy link
Owner

@heruan I'll have some time early next week to resolve this issue. Whatever is simplest and works with all package types will be what I go with. Thanks again for your input.

@ToddThomson ToddThomson modified the milestones: TsProject Release 1.2.2, TsProject Release 1.2 Apr 13, 2016
@ToddThomson
Copy link
Owner

ToddThomson commented Apr 15, 2016

@heruan I've researched this issue and it is not straightforward in that there are a few TsProject concepts/features that are related. I want to find the simplest solution.
It terms of this issue, using TypeScript 1.8.x:

  1. TypeScript can generate an ambientDeclaration of the form declare module "mod-name" { }. Albeit with a bit of trickery.
  2. TypeScript does support the the generation of declare namespace NamespaceIdentifier { }
    2b) Typescript allows declare module NamespaceIdentifier {} but it is only for backwards compatibility with older TypeScript versions. TsProject will use the namespaceKeyword.

So, what I need to get from you is code ( a complete minimal TypeScript Project) that I can see that shows your need to have TsProject add an ambientDeclaration to the bundle d.ts file.

My thoughts right now are that I can produce a bundle d.ts file directly with TypeScript 1.8 using:

MyApp.ts - doesn't have to exist as a physical file for TsProject. TsProject could inject the simple code SourceFile.

export {} // A module named "MyApp" ( or whatever the physical filename is. if injected then use bundle name )

in App.ts

namespace MyApp {
  // bundle API goes here...
  export function src(): string {
      return "src";
  }
}

declare module "MyApp" {
    export = MyApp;
}

MyAppConsumer.ts

/// <reference path="app.d.ts" />

var bundle: string = MyApp.src();

The MyApp.d.ts produced is:

declare namespace MyApp {
    function src(): string;
}
declare module "MyApp" {
    export = MyApp;
}

@ToddThomson
Copy link
Owner

@heruan I'd like to release v1.2.2 this week if I can get this issue closed. Do you have some time early this week?

@heruan
Copy link
Author

heruan commented Apr 18, 2016

Thank you @ToddThomson for your intereset. Here's a sample repository: https://github.com/heruan/tsproject-bundles
gulp build will produce this dist/src/bundles/foobar.d.ts:

export declare namespace foobar {
    class Foo {
    }
    class Bar {
    }
}

where I would expect this:

declare module "foobar" {
    class Foo {
    }
    class Bar {
    }
}

@ToddThomson
Copy link
Owner

ToddThomson commented Apr 18, 2016

@heruan Thank-you. Can you update your test project to include a "consumer" for your bundled library? I would like to see how the consumer would use the foobar.d.ts in the way that you have requested.

@ToddThomson
Copy link
Owner

@heruan There is so much conflicting information on how to structure a Typescript Declaration Module properly! From what I've read through concerning using Ambient Module Declarations ( what you are asking for - declare module "moduleName" {} ) within a d.ts is not the "current" / "proper" approach.
What I really need to see from you is how you want to consume a bundle created with TsProject and why this consumer requires an ambient module declaration within the bundle d.ts file.

@heruan
Copy link
Author

heruan commented Apr 19, 2016

Thank you @ToddThomson! The output you show here, i.e.

declare namespace MyApp {
    function src(): string;
}
declare module "MyApp" {
    export = MyApp;
}

could work for me, how can I get that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants