microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
101.02k stars 12.49k forks source link

Greatly improve code sharing and reuse options #2327

Closed atrauzzi closed 9 years ago

atrauzzi commented 9 years ago

Creating and sharing pure typescript code has quite a few barriers that seem like the kind of complexity that TypeScript should be helping to alleviate.

I'm going through a situation right now where I want to share a pure typescript library and - unless I'm mistaken - it seems like I'm going to have to go through the pain of adding AMD to my project. Even despite the fact that it's all internal modules.

With the overall goal of making life easier for developers, I think it would be nice if:

While AMD/CommonJS are module-loading systems, they are very confusing and cluttered build systems, especially once you bring TypeScript into the mix.

It's conceivable that with these changes, you'd also reap the ability to create build profiles implicitly by defining entrypoint scripts and having typescript crawl the dependency graph. Similar to how r.js works, except at a TypeScript level.

atrauzzi commented 9 years ago

It should probably be mentioned that any solution really needs to be sensitive to the inevitability that the location of the node_modules and typescript sources that the compiler is currently targeting will not be consistent between projects.

screenshot from 2015-03-12 14 18 00

This might be one of the major pain points of coming up with a build system that knows how to integrate code from packages. Not only are people conflicted about npm vs bower, but what if what we really need are multiple node_modules directories in a project based on what's being built (front end, cli tools, server side)?

danquirk commented 9 years ago

For your first bullet, is tsconfig not a solution to the problem you're experiencing?

For the second bullet, this is something we've generally stayed out of. People have very varied build systems with a variety of linters, concatentation steps, minifiers, etc and it seems difficult for us to cover the majority of those scenarios in a satisfactory way (and convince someone to break their existing, working system to migrate to the provided TS one). Maybe the solution is to make it easier to customize pre and post build steps. We generally don't want to be in the business of creating some robust, general purpose, cross platform build system. Obviously it's important that the end to end experience be as good as possible though, so maybe we need to make changes to slot into existing workflows more easily or be better at detecting errors that might have occurred as a result of that system.

For node_modules #247 is a priority and hopefully helps alleviate some of the mismatch with using TS + npm modules now. Could you elaborate on what you mean by node_modules and TS sources not being consistent beyond that?

atrauzzi commented 9 years ago

I know I can't speak for others, but I think a build system integrated with typescript would be a welcome change to getting mired in endless build system hacking and configuration.

In an ideal world, I should be able to do this:

tsc path/to/built.js path/to/script.ts

BUT

TS does the legwork to resolve and include in the build everything that script.ts references.

What's preventing this from being a possibility?

danquirk commented 9 years ago

I think a build system integrated with typescript would be a welcome change to getting mired in endless build system hacking and configuration.

Sure, but this is really a complaint about the state of JS build systems and wishing for a great 'one build system to rule them all.' In an ideal world that would exist :) In reality peoples' needs are extremely varied which is why we have many different options (many tried to create the one true build system, now we just have a lot of different ones with different pros and cons). We don't feel like we're in any better situation to suddenly solve that problem better than all the other attempts (especially prior to ES6 having a finalized, cohesive module story). And then there's the fact that even if we actually did succeed at that, many people are not going to switch from their working build system to something new, even if the new thing is better in the abstract ('if it ain't broke...'). That said, we do want to try to slot into what exists well, and we do want to enable others to make good tools around us (like interacting with grunt/gulp/etc).

What's preventing this from being a possibility?

What do you mean by 'everything that script.ts references'? You mean a file without /// or imports? Or a file with ES6 style imports? Obviously right now the compiler does chase through your /// references and imports or will use a tsconfig related to that script. It sounds like you mostly want to avoid /// reference pain which is what tsconfig should help with.

atrauzzi commented 9 years ago

So for tsconfig, I have to manually create one for every graph of source files I want to use? Why not just have the compiler automatically infer this? r.js does this when you tell it to build a file. It will recurse through all the defines and requires and concatenate all those files together.

If I was able to statically reference other .ts files from one root-level .ts script, wouldn't that obivate the need for various mechanisms to tell the compiler about where files are? The compiler would know everything being used because it would have crawled the full import-graph (my root script, it's dependencies, their dependencies, and so on) before going through the code.

danquirk commented 9 years ago

So for tsconfig, I have to manually create one for every graph of source files I want to use?

Not for every graph necessarily, it's up to you whether you want to explicitly list everything or let reference chasing happen depending on what references are in relevant files. Think of it like a basic project file.

Why not just have the compiler automatically infer this? r.js does this when you tell it to build a file. It will recurse through all the defines and requires and concatenate all those files together.

This is exactly what the compiler currently does with .ts files based on the /// references and import/require in the file. With RequireJS you explicitly list your module's dependencies, and each dependency has its own dependency list, and the loader walks through this graph. That's the same thing tsc does when you explicitly list dependencies in the form of /// or import/require. The problem is that explicitly managing /// references in this way can be painful and sometimes things get pulled in in an unexpected order and now you have to manually try to track down how the graph was traversed and what happened. Using a tsconfig to manage the project ordering can alleviate some of this pain.

atrauzzi commented 9 years ago

So general advice for the time being is that I should be making everything external and not really try to make internal modules? That's a distinction I'm still trying to internalize, but I was under the impression that there might be a way for me to do all this without having to set up AMD....

danquirk commented 9 years ago

It's really up to you as far as how you want people to consume your code. Given that ES6 only has external modules it's certainly possible that's the right choice for you going forward though. The TypeScript compiler is only internal modules but you can find small npm packages that provide access to it through external modules. Likewise you'll see some .d.ts files written in such a way as to work with both internal and external (I believe knockout is one example like this).

atrauzzi commented 9 years ago

I'm still at a loss to really understand what the difference is between internal and external. Some of it flies over my head.

danquirk commented 9 years ago

I assume you've read https://github.com/Microsoft/TypeScript/wiki/Modules and http://www.typescriptlang.org/Handbook#modules ? Don't feel bad, it is unfortunately confusing and a situation we definitely can improve. I'm reticent to try to give an off the cuff primer since we've already spent a bunch of time trying to wordsmith the content in those two places since this confusion definitely occurs with some frequency.

The simplest distinction is that external modules are intended to be used with a module loader (either AMD or CommonJS) for all the reasons module loaders are good (plenty written on that topic for JS already). Think of internal modules simply as namespaces. Internal modules leave you with some JS (either many files or a single concatenated output files) that someone consumes by simply putting your file in scope (ex a Githubissues.

  • Githubissues is a development platform for aggregating issues.