-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Guidance on migrating away from global namespaces to file modules #12473
Comments
The only module systems that actually can mix local/global declarations are SystemJS and AMD (Require.js). I would utilize the existing namespaces as globals from your files written as modules, and progressively turn each namespace into a module. Once you've moved all your namespaces into modules, you can use whatever module loader you want (e.g. CommonJS). Does that sort of help? What questions have you encountered if not? |
I think you're describing a pattern where, in the files that you are starting to convert "to" file moduling, you could import the globals if you were using AMD or SystemJS. I think that does sort of help -- it lets me take time making the change to my codebase in isolation. I'm starting to look into exactly how that works syntactically. It does only "sort of" help, though, because what I really want to be able to do is keep our global namespaces in place (but deprecated) until both our conversion and dependent libraries' conversions are complete. Is there any avenue for doing something like that? |
Also, I would be curious to know what strategy you're suggesting could be used to import global namespaces using either of the loaders you mentioned (I'm in webpack, so I think AMD). I'm not seeing much documented about how that is accomplished (or I'm searching for the wrong thing :) ). |
You can simply keep the namespace versions around while also making the module versions available. They can coexist, but I'll just mention that in some places where you mean to use the module version, you may forget to use an import and accidentally use the global namespace. In practice, this won't cause any problems. Using AMD/System.js as an intermediate step has the advantage that it can be used with However, if I were determined to use Webpack throughout this process, I'd use the I don't know if Webpack has an easy way of concatenating global files with your output's bundle. @TheLarkInn would know better than I do. 😄 |
How does this work tactically? From what I can tell, TS has a file level "Switch" that gets flipped when using imports statements that make it really difficult to do this without physically copying code between files (e.g. if I want File1 to be available in a global namespace as well as a file module, I need to have two different files). Am I missing something? I'm mostly focused on the TS-specific parts of the problem in this issue, as I think it's very specific TS level behavior that has this "file-level switch" whenever imports or exports make it into the equation. |
If you have a global declaration (i.e. your namespaces), then those will be visible to all your modules anyway. So you can have export function abc() {
// This reference to 'foo' will be visible here.
foo.abc();
} or // This reference to 'foo' will be visible here.
export = foo; |
As i noted in #8004 (comment), most of the work is in figuring what are your "modules" going to be. you can then pick one of these, preferably at the leaf, and switch it to a module. a module can use a global, but not the opposite. then the user of this module (e.g. html page with a script tag today) will have to reference it using a module loader. then keep iterating until you change your core to modules as well.. |
I always take the break-it-till-you-make-it-(work) approach with webpack and globals. Ideally you want to bundle as much lib source as possible. So I start out by just ripping off the bandaid at once: remove all script tags from your html page, then try and download locally as many npm deps to replace those scripts as possible so they can be bundled together the right way. This would involve additional shimming for "broken modules" like jquery, as well as downloading any typings for libs that you are using in your src. Externals is an okay choice but the more you bundle src locally the more powerful/optimized your builds will be. I know this doesn't cover the entire migration process but this is usually a great start to freeing yourselves from the global namespaces. |
The biggest obstacle to moving from global namespaces is that I already have a number of webpack-specific concerns taken care of with a different approach (e.g. gulp doing concat and other things). I'm trying to isolate change as this isn't a small project, but rather a common SDK / Shell platform used by other teams, so ideally I would roll out these types of changes incrementally. Step 1 for me would be getting my package to a point where it fully supports a module loader pattern and integrating webpack as a replacement for the existing gulp tasks. Along the way, I would really like to maintain backwards compatibility until all consumers have snapped to the new model. Right now, I'm actually creating a script to "automatically" create a module layer in front of my global namespaces, but am running into issues like being unable to export my interfaces in the same way I export my classes. E.g. interfaces seem not to export in the below syntax:
|
@brphelps you can use a namespace to re-export your types. namespace reexports {
export SomeInterface extends SomeNamespace.SomeInterface {}
export const SomeClassImplementation = SomeNamespace.SomeClassImplementation;
}
export = reexports; |
That's pretty interesting, let me give that a try. I have a script I'm working on that is going through and generating these wrapper files, and this looks cleaner than the alternative (which would've required me to default exports the interfaces in their own file). I'll give it a try sometime today and follow up. |
@DanielRosenwasser : Which sucks. Is there a better way? |
why not just import CodeRedemptionStatus = ServiceDesk.Services.Fortification.CodeRedemptionStatus;
export {CodeRedemptionStatus }; |
That works! But I don't really understand why -- I didn't know you could "import X = <enum/ type/ ???>" . Is there a doc page I should look at? Thank you :). |
Does not seem that we have any documentation for import alias declarations on the handbook. filed microsoft/TypeScript-Handbook#598 to track that. you can find more info though in the spec: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#10.3.
|
I have something working at this point as I've had a few days to get back into it. It's a script that loads the AST of the global namespaced project, creates a graph for the namespace "nodes" and then creates corresponding files that wrap each node in their own file module and re-export moving vertically. While this seems like it would be an antipattern in file module loading, it will unblock us from moving towards a hybrid solution while our consumers start adopting our file module wrapper package. The script is pretty horrible code, but one thing it shows is that I think the whole process could be automated-- e.g. what if TS just had a switch (or a script) you could flip and it would generate file module adapter files to global namespace code...? :) |
@brphelps we are in a similar situation, it would be nice if you share the code. It does not matter how bad/good it is... |
This was one of the threads I kept bumping into when looking how to convert namespaces to modules. Unfortunately I couldn't follow the solution commented here as it seems only @brphelps knew what he was doing. Also I feel many steps were missed along the way. https://jorgeartieda.gitbook.io/typescript-from-namespaces-to-modules/ |
I wrote up a solution we ended up using here: https://www.geekytidbits.com/typescript-progressively-convert-namespaces-to-modules/. Also, I have an example repository of the approach here: https://github.com/bradymholt/ts-progressive-convert-namespace-modules. |
I've been working on a transformer which uses the typescript compiler api to automatically transform namespaces to modules. Will post a link to the repo shortly but if anyone is interested please get in touch. |
@Mknight492 Have you succeed? If yes, it will be cool to see your solution. |
I've experienced many times that people like myself struggle a lot with ts-namespaces and es-modules, especially when it comes down to try to work with both in the same project. I did some "structured" fiddling and found that on the Javascript-side after compilation, things appear to be quite simple, but can't be done in a standard way with typescript before compilation. I wrote about it here and I'd be happy to see some comments on that before I start creating a plugin or extension, that tweaks the compiled output. I expect to be missing some important parts... |
Is the code available? @Mknight492 I have to migrate an old project and it would be interesting not to develop the tool from scratch, although with |
Currently, the documentation section seems somewhat anemic on the best way to migrate out of global namespaces and into file modules. From what I can tell, that whole process is a not-well-supported there-be-dragons-here type of experience that seems to involve wholly converting your codebase and dependencies on your codebase in one fell swoop.
I'm writing this issue up as someone trying to do it, and struggling to find an incremental approach. If there is a known incremental approach, can that be shared and added to the documentation (I would be glad to get a PR with the doc changes)? If there isn't an incremental approach, is that something that could be treated as a missing feature?
The text was updated successfully, but these errors were encountered: