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

//# sourceTypingsURL=... #4846

Closed
g162h3 opened this issue Sep 18, 2015 · 16 comments
Closed

//# sourceTypingsURL=... #4846

g162h3 opened this issue Sep 18, 2015 · 16 comments
Labels
Duplicate An existing issue was already created Suggestion An idea for TypeScript

Comments

@g162h3
Copy link

g162h3 commented Sep 18, 2015

Let's say you develop a library in ts. The library is then included into js and ts projects. Would it make sense for the latter to allow including just the compiled js file, but still have the typings?

lib.js:

...
//# sourceTypingsURL=lib.d.ts
//# sourceMappingURL=lib.js.map

foo.ts:

/// <reference path="lib.js" />
...

This would also allow to embed other js files without invalidating the source map.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 18, 2015

@g162h3 can you elaborate on your scenario? by "project" your mean a VS project? and by "js" you mean the generated js files from the ts sources?

@d180cf
Copy link

d180cf commented Sep 20, 2015

I meant the project in the most general sense. Let's consider two projects: bluebird and angular. Let's also assume that they are written in ts and that angular wants to use bluebird as the Promise/A+ polyfill. How does angular include bluebird?

As far as I know, there is currently one way to do this: bluebird gets compiled into a js file and a d.ts file, then angular adds a reference to bluebird.d.ts and also somehow (depends on the build system they are using) includes bluebird.js.

I'm suggesting to simplify this by allowing to reference js files from ts files:

/// <reference path="libs/bluebird.js" />
namespace angular {
  // ...
}

angular and bluebird are just examples - I'm a developer of neither of these.

@DanielRosenwasser DanielRosenwasser added the Suggestion An idea for TypeScript label Sep 21, 2015
@mhegazy
Copy link
Contributor

mhegazy commented Sep 30, 2015

The js file is a runtime dependency; the /// reference and the // #sourcetypingsurl are design time dependencies; so having one does not remove the need for the other. Can you elaborate on how this would be used and how referencing the is file would simplify the workflow?

@d180cf
Copy link

d180cf commented Sep 30, 2015

It can be used just like I've described: a .ts files will reference to a .js file which will make tsc include the .js file into the output and take typings from the file referred to by sourceTypingsURL.

@DanielRosenwasser
Copy link
Member

I think I understand the scenario, but you could also just pass the typings through tsc and integrate the js file with a custom concatenation step in your build process.

@d180cf
Copy link

d180cf commented Sep 30, 2015

It's possible to concatenate .js files with an extra build step, just like it's possible to not use the tsc's out parameter and concatenate files with your own script, but it's not convenient. Btw, to keep the source map valid, that extra build step needs to be quite sophisticated.

Another reason to make this work is async/await you're working on. Since Promise isn't available everywhere yet, people will have to use some polyfill, like bluebird. That polyfill needs to be added to the top of the .js file, breaking the source map. But if tsc allowed to reference .js files, this wouldn't be a problem.

@kitsonk
Copy link
Contributor

kitsonk commented Sep 30, 2015

Btw, to keep the source map valid, that extra build step needs to be quite sophisticated.

Most tooling that bundles/concatenate files rewrite the source maps. It is expected in fact.

Since Promise isn't available everywhere yet, people will have to use some polyfill, like bluebird.

Realistically you wouldn't concatenate bluebird to every file. That is what modules are for. If you need polyfills, you tend to load those once, either through a script import in your application or through your module loader, or as a final bundle/layer that is then loaded by your module loader. Adding them to the top of every file that is dependent on them is not a good design pattern.

Ultimately, my opinion is these workflows and options are too complex for TypeScript to do well. There is discussion though around module bundling and I am sure they will emit valid source maps for whatever path they choose to follow.

I don't see how adding //# sourceTypingsURL=lib.d.ts wouldn't invalidate a source map. I am not sure of any tool out there that is looking for that string and even if it were detected, what would a tool do with it?

@d180cf
Copy link

d180cf commented Oct 5, 2015

Another example of where this would be useful.

Let's say there is a library and a couple satellite projects that were created to test the library: the unit tests and the testbench. The folder structure looks as following:

/src/*.ts - the lib files
/tests/*.ts - unit tests that include /src
/testbench/*.ts - a UI testbench that also includes /src

All this is written with /// reference <path = "..." />` because the library needs to be shipped as a single js file.

To build all this, tsc needs to be invoked 3 times: to build the library itself, to build the unit tests and to build the testbench. Every time tsc will be recompiling /src/*.ts files: it will essentially compile the library 3 times.

With what I'm proposing this 3x recompilation could be avoided by first compiling /src into lib.js and lib.d.ts and then including the compiled lib.js into the othe two projects:

/// <reference path = "lib.js" />

test("some test", () => {
  const x = lib.add(2, 3);
  expect(x).toEqual(5);
});

In the world of C this corresponds to how obj files work: a shared library consists of .c and .h files which are compiled first to lib.obj (the .h files aren't merged into obe big lib.h file, though), then the main program (the one that uses the library) refers to the library's interface via #include "lib.h"and is compiled to main.obj and then all these obj files are linked together into one executable file.

Still not convincing?

@mhegazy
Copy link
Contributor

mhegazy commented Oct 5, 2015

With what I'm proposing this 3x recompilation could be avoided by first compiling /src into lib.js and lib.d.ts and then including the compiled lib.js into the other two projects:

you can do this today with no additional references. just call the compiler with --declarations and then pass the mylib.d.ts to the compiler the next time around. you just need to concat the JS code your self.

@mhegazy
Copy link
Contributor

mhegazy commented Oct 5, 2015

I am still not sure i understand what is the workflow that this proposal is solving, and can not be done today..

@d180cf
Copy link

d180cf commented Oct 5, 2015

This proposal is all about making tsc concatenate js files: you're saying that I can do this myself, while I'm saying that this is a responsibility of tsc.

We can imagine the following discussion if tsc didn't have the --out option:

A: When I add a <reference path="foo.ts" /> into bar.ts I want tsc to concatenate foo.js with bar.js
B: It's not tsc's responsibility to merge files. You can merge ts files yourself and then compile that big file.

This sounds not very different from what we are debating about now.

@d180cf
Copy link

d180cf commented Oct 5, 2015

B: Or in the opposite order: compile ts files separately and then merge all the produced js files.

Possible? Definitely. But would be extremely inconvenient as I couldn't just type tsc in the command line and get lib.js - I would've needed to come up with my own compile script that would invoke tsc, invoke some tool that merges files, cleanup all those temp files, worry about source maps and so on.

@mhegazy
Copy link
Contributor

mhegazy commented Oct 5, 2015

tsc can not just concat the files. it has to parse them, transpile them, and emit them again. it has to respect your --removeComments your --target and your --module, if we do that also, we need to support reading the original sourceMaps and concatenating them, something that is not supported today. so the perf win would not be that significant in the example listed above, given that we are still having to locate, parse and type check the additional .d.ts as well.

if what you care about is perf, the separate concat tool would be the best along with passing the .d.ts from your intermediate build target.

I think really what you need is a build system. tsc can not handle all build needs, nor was it designed to do that; once you have more than a 1 build target, a build system is a good thing to have. for our code base, we use Jake, all other popular build systems like grunt, gulp, broccoli, etc.. have a concat task that respects sourcemaps.

@d180cf
Copy link

d180cf commented Oct 5, 2015

I'm actually suggesting to include the js file blindly, without parsing it. The idea is to look at such js+d.ts pairs as opaque binary obj files: when obj files are linked together, the linker sees the overall structure of the module (function names, variables and so on), but doesn't attempt to parse the assembly code in those functions.

Whether it needs to respect --removeComments is debatable: this option tells to remove comments from the main program's code, but I don't see why it should apply to 3rd party libs that are included this way.

The argument about --module doesn't seem to apply: if a js file is being included via <reference ... /> then the output will be a js bundle (aka "an internal module").

The --target problem is more interesting, it doesn't seem to be a tsc's problem. It's the library developer's responsibility to choose an set of es3..7 features that will be appropriate for the target audience of the library. If this matters (i.e. using native es6 generators makes the lib 5x faster), then the author will probably ship two versions of that lib.

Readinf the lib's source map is critically important, though. Without that debugging will be a pain.

@DanielRosenwasser
Copy link
Member

I'm actually suggesting to include the js file blindly, without parsing it.

The problem is that there are people who have different expectations of that the compiler should do with a .js file (see #2302). I think easing into TypeScript by using it as a downlevel compiler is the scenario that other tools cannot perform as easily, whereas in this scenario, concatenating has many other existing solutions.

@mhegazy
Copy link
Contributor

mhegazy commented Dec 9, 2015

JS files are now supported in compilation using --allowJS (#4792). please give that a try reopen if there are issues.

@mhegazy mhegazy closed this as completed Dec 9, 2015
@mhegazy mhegazy added the Duplicate An existing issue was already created label Dec 9, 2015
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

5 participants