问题
I have a fairly large app (~650 files) that currently has a mixed implementation of ES6 modules and legacy global namespace internal modules and classes.
I want to move to 100% ES6 modules.
The iterative approach
To get there, I need to start by converting all the global objects to ES6 modules by adding the "export" keyword.
As soon as I add "export", that global object is no longer global, and each file that uses that object now has a compiler error "object not found"(i.e. Cannot read property 'xxx' of undefined ).
To fix this, an "import" must be added to the file, thus turning it into a ES6 module, which in turn "de-globalizes" all the objects in that file, causing new "object not found" errors in other files..
In short, this iterative approach doesn't seem like it will work well.
Possible "one fell swoop" Solution:
- Iterate over all ts files
- Programmatically add
export
to all top level classes, modules and variables. - Gather these into a list
- Programmatically add
- Generate a barrel (globalBarrel) file, re-exporting all the exports gathered in the previous step.
- Iterate over all ts files again
- To each ts file, add
import {list, of, symbols, exported, by, the, barrel,file} from "./globalBarrel"
to each file
- To each ts file, add
- Compile clean?
- There will probably be some circular dependency issues to clean up. Do that.
- As we edit each file in the future, use the vsCode " Organize Imports" command to remove the many unneeded.
As @jfriend000 said:
Taking non-modular code and trying to programmatically convert it to be in modules is just going to create a mess.
But getting everything into modules all at once will make the true modularization process easier.
Question
Is this the the best way to go about this? Any suggestions?
回答1:
Is this the the best way to go about this? Any suggestions?
I doubt this is even a good way and certainly not the best way.
Taking non-modular code and trying to programmatically convert it to be in modules is just going to create a mess. I'm sure it could be done, but it hasn't really gained anything. It's just made things messier than they probably already were. You will have just shoe-horned a non-modular architecture into a mess of non-modular, highly cross dependent modules. Your architecture will not have improved and the code will now be more complicated, not less complicated.
The better approach would be to incrementally redesign areas of functionality to actually be "modular". Create an interface for an area of functionality, package that in a module and then convert all users of that functionality to import the interface and use it. This can literally be done, one functional area at a time. You don't have to break the whole thing at once and then try to put it all back together.
Getting rid of shared globals requires rethinking the design. Just packaging a bunch of shared globals into a module that you now make everyone import does not really improve the design at all - in fact, it probably even makes things worse. Code is just as intertwined and dependent as it was before.
You can do this modular redesign incrementally, one functional area of the code at a time, rather than create a big giant mess in one fell swoop that doesn't actually improve the architecture of your code.
If I were attacking this problem, I'd probably create a top level diagram of all the major points of functionality in the program and make sure every one of the 650 files was represented somewhere in the diagram. Then, I'd start incrementally picking off the easiest pieces to modularize (the ones with the cleanest interface and the least spaghetti dependencies). As you get more and more things out and into modules, the remaining part starts to get less complicated. If there really are access to globals interwoven all over the code, then you will really have to rethink parts of that design, either creating objects that contain the data and passing those around or moving what was global data into relevant modules and providing exported accessors for that data.
回答2:
To get there, I need to start by converting all the global objects to ES6 modules by adding the "export" keyword.
As soon as I add "export", that global object is no longer global, and each file that uses that object now has a compiler error "object not found"(i.e. Cannot read property 'xxx' of undefined ).
To fix this, an "import" must be added to the file, thus turning it into a ES6 module, which in turn "de-globalizes" all the objects in that file, causing new "object not found" errors in other files..
Right. The trick to avoid this vicious cycle is, each time you convert a file to a module, also assign that module to a global variable that you can reference from other non-module files without turning them into modules. This takes some boilerplate; this open suggestion would reduce the boilerplate slightly.
I assume you must currently be using some kind of bundling tool to ensure that all your non-module files are loaded and contribute to a single global scope so they can access each other's definitions. Start by creating a file with the following code and configure your bundler to ensure that it runs first:
namespace Modules {
// Force `Modules` to be created at runtime.
(() => {})();
}
Now if you started with:
// a.ts
namespace A {
export const a = 42;
}
// b.ts
namespace B {
console.log(A.a);
}
you could transition to:
// a.ts
export const a = 42;
import * as A_ from "./a";
declare global {
namespace Modules {
export import A = A_;
}
}
Modules.A = A_;
// b.ts
namespace B {
console.log(Modules.A.a);
}
and b.ts
will be able to access Modules.A
with full type information without becoming a module itself (modulo load order issues with your bundler).
来源:https://stackoverflow.com/questions/52632810/converting-a-large-typescript-project-to-es6-modules