-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Suggestion: multi-file external modules #17
Comments
👍 |
👍 |
I'm highly interested in this issue getting closed. I don't like an idea of concatenating What do you mean by "module boundaries"? |
Given:
export class A{}
export class B{} What will the This declare module 'foo'{
class A{};
class B{};
} or declare module 'foo/a'{
class A{};
}
declare module 'foo/b'{
class B{};
} My vote : |
Why should it create a module for each class at all, if we're explicitly making one module file? My vote goes for the first option. |
@RyanCavanaugh can you append the following as well
If you want I can create a separate issue for that. |
To add my 2 cents, I agree I would like to see 1, but I know that can be a challenge if for example 2 different files contain the same class or variable name. But either 1 or 2 I would like to see this implemented someway because my application uses requirejs extensively to deliver SPA functionality with durandal. |
I'd like to see 1) too since our current desired use case is to use one file per class/interface/enum and one folder per module. While I think it shouldn't be too hard too implement inside the compiler, the tooling is harder, notably in VS: how do you determine which files compose a module? Personally, what I would really like to see to solve this problem is a mix between internal and external module syntax at the language level. Something along the lines of:
Both files would be compiled into one module file library.js. Each file can only declare at most one external module, and cannot have any other top-level declarations if there's an external module declared. Another benefit of this approach is that it would no longer be magic that a file becomes an external module (with its contents no longer in global scope) as soon as there's an import or export since there's the possibility of explicitly defining that a module is external. That's what most people I've seen working with TS and external modules are struggling with when introduced with the concept. (Still, the old way would still work, for compatibility reasons and implicit single-file external module). |
My vote is for option 1 as well. In terms of determining which files are part of a module, maybe we could use reference paths... something like this:
If you compile this with |
I say my vote would be for If you're working in a large org or team and want to develop reusable packages as part of a large system (i.e. any large enterprise app) then you'll hit this problem. You'd be be using external modules as you'll be relying on an external loader (unlike internal modules that assume it's all loaded already). Each module will defer to the module loader to find the correct script not by an assumption it's already loaded (this just referring to it's module object) . The separate modules need a single .d.ts so it can be consumed by dependencies at compile time. Currently having (potentially hundreds) of single .d.ts for an external module package is useless. We've got our system running using dts-bundle which scrapes all the single files into a single .d.ts (same approach as outlined in #17), but there are other teams here stumped by the complexity required to get this running, there off building monolithic internal module applications that can't be split out into separate packages. related issue #1236 |
Everyone in this thread is voting for If my definition for class
export class A{}
export class A{} Would the output be: declare module 'foo'{
class A{};
class A{};
} Additionally, if declare module 'foo/a'{
class A{};
}
declare module 'foo/b'{
class A{};
} How would the compiler know where the base path was? When you import this project into other projects the module names should be from and inclusive of the project root correct? So in another project the modules would really need to be defined as: declare module 'project/foo/b'{
class A{};
} I imagine this is going to boil down to the addition of a |
@xealot Are you suggesting that the folder structure defines modules? I'd rather module definitions be more explicit, that's why I was suggesting having a main |
Well currently typescript is using folder structure to import modules, at least for AMD modules it does. @xealot and @MrJul makes a good point that merging definitions like 1 would make it hard on tooling (intelisense, etc) I agree with @MrJul 's solution by adding a new 'external module' keyword would make the most sense to me and be nice on tooling. |
@KevinB7 I'm not suggesting that folder structure defines modules so much as pointing out that's already how things work. With the single exception of Typescript's internal module system, external TS modules, CommonJS, AMD and the upcoming ES6 standard base modules at files and their locations in the path if I'm not mistaken. |
Time for me to go re-read the spec. 😓 |
I've been working with a large project for a while now, we are attempting to break down our project into multiple smaller libraries now and have run into problems around this topic. We have come up with two possible paths this could go. First, make our smaller libraries into internal modules, then wrap them manually into an external module. Second, keep our existing external module setup, one file for each class/interface/function, each with a single default export ('export = itemName'). The issue with this path ends up being the same as the internal module path. We still need to create a 'library' file that imports the public types and exports them. In both of these cases, one simple problem blocks implementation. The compiler does not let you export a type declared in a different external module as part of your module. To that end I would suggest a third option and or extension to option 1 As per the es6 exports specification We would then do export default interface A{} //current form export = class A{}
import default as A from 'a'
export default class B implements A{}
export {default as A} from 'a'
export {default as B} from 'b' this should then compile a
And should throw a compilation error if you don't export A from your module since B implements A This probably means #1215 has to be finished first. I disagree with making the use of folder structure, and automatic exports, since most of classes, functions, and interfaces used internally to a library should not be exposed unless the library explicitly needs them to be shared. |
Like the suggestion of @park9140. Similarly, I've been using the following pattern: Add a comment on internal modules: module MyLib {
export class A {}
}
// export default MyLib.A b.ts module MyLib {
export class B {}
}
// export default MyLib.B lib.ts module "mylib" {
// import default as A from "./a";
// import default as B from "./b";
} With some hackery in node, internal modules are evaluated in an 'internal sandbox' and we return the default export in the sandbox: vm.runInContext(code, sandBoxInternal, "a.ts")
return sandBoxInternal[sandBoxInternal.export.default] || null 'external' modules still use the regular require(). At build time (tsc lib.ts), you can concatenate the 'internal' modules (a.ts, b.ts) inside your external module. |
I hate being in a position where I am feeling pain but I don't have a suggestion for how to fix it - I just want it to go away. When I use TypeScript external modules for something non-trivial, I'm in pain. Some smart people on this thread have proposed fixes - if @park9140 's aligns with ES6, that sounds awesome to me. |
One library I created I wanted to have things in separate files and then export each modules from a common namespace... I ended up doing this in lib.ts:
|
My suggestion is that this scenario is available if you build your solution in single file:
So as u see - we got valid single JS module that can be used with AMD with shim:{MODULENAME:{exports:"MODULENAME"}} My suggestion to allow TS generate fully complicated AMD/UMD modules:
So we can provide any module-level initialization module on real phisical module binding. For now i do it manually and then minimize. |
Why not consider tooling? Group .ts files into modules using directory structure or comments or whatever code-indifferent mechanism, and have the tool rewrite the imports and the exports into a new .ts file to keep everything going. In case of conflicts, emit errors. |
Proposal suggestion(Based on #5085, inspired by the I would like to propose a mechanism for creating partial files, which are not compiled by default, but embedded into files which reference them. It's just like bundling, but with less hassle and in a more unified approach. As a brief example, suppose that we have 4 files:
The
The content of partial files should be copied and instantly evaluated in their container file. For instance, whether we would also like to use the
Although this may seem like a regular import, it's basically an include function which copies the partial file's full content at the place desired. That means, partial files should not be reused in multiple containers, but should be used for code organisation purposes. When both Last but not least, in order to keep backwards compatibility, there should be a tsconfig setting ( |
@kripod https://github.com/toddthomson/tsproject already does this. It adds bundling to the tsconfig.json file and will build a bundle from modules using ES6 syntax. TsProject is a gulp adapter that can be installed with npm. |
@ToddThomson I have seen that project before proposing my specification, but bundling from a single file seems to be overcomplicated for such a little effect. |
@kripod No that is not how it works. Your Typescript ES6 exported modules are defined in separate files. When you specify a bundle it works out all the dependencies and build a single bundled ts, js and d.ts. Take a look at the sample on the github tsproject repository. |
I am in the same situation as @park9140 and I am little tried. But I dream about something similar to .net assembly which could have multiple files and I could load it. Thant means would be good have that java script external module be like assembly. That meas we could indemnify 4 issues: Generally I would like to focus on developing business logic, provide solution than |
@mironx have you looked at browserify or webpack? |
@mhegazy Thanks your encouragement I the ice has broken up :-) and What I would like to achieve?But first what I would describe what I need to achieve. I would like to create my suite of component which would exists in java script file.
apps
It would be like: software suite, framework or components.
My adventure with webpackI have tested webpack with grunt. I create GruntFile.js which could be use by each project. Problems
SummarizationIt this moment the biggest problem for me is point "a". Main requriment New version of typescript compilatorI looked on roadmap of typescript compilator and I noticed interesting issues:
Conclusion
Maybe crazy idea for typescript compilatorWhat do you think to put to to output java script file declaration? Architecture for enterprise, long term solutionIn new year I and my team I will have make some architect decision - I really appreciate you for each comments and criticism in this topic. I think thant others have similar problems with typescript projects - how to organize it Thank you and sorry that I extended this issue of details |
ES6 modules provide the ability to build modules from smaller ones using A follow up feature for the TS compiler is #4433, to enable publishing a single .d.ts file that has the "shape" of the "entry point" module, and thus completely hide the internal modules from consumers. |
This is my solution uses TypeScript 1.8, The solution build multi-Class in a module. |
@mhegazy can you elaborate on this approach more of using modules for implementation, but smashing into 1 file with 1 entry point? |
there is no "smashing" happening. Each file is emitted as a module, but you have one entry point. e.g.: // a.ts
export var a = 0; // b.ts
export var b = 0; // index.ts
export * from "./a";
export * from "./b"; compiling with define("a", ["require", "exports"], function (require, exports) {
"use strict";
exports.a = 0;
});
define("b", ["require", "exports"], function (require, exports) {
"use strict";
exports.b = 0;
});
define("index", ["require", "exports", "a", "b"], function (require, exports, a_1, b_1) {
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
__export(a_1);
__export(b_1);
}); your users would import |
Doesn't this force me to use amd module loading? |
if you are using Node, then there is no need to bundle i would say. the issue with UMD is that it needs to work the same way on node and amd. since the way you can require an internal module is different from amd to node, we can not grantee that the transformation would work. |
I think rollup.js looks very promising as well. I haven't tried it yet though. |
Those work well for applications, but I am looking to build a library that works with amd and vanilla js. Isn't the whole purpose of UMD to be agnostic between commonjs and amd? I imagine that UMD would work like |
Support compiling multiple input .ts files into one external module.
Need to determine exactly how the module boundaries are defined when doing this.
The text was updated successfully, but these errors were encountered: