I have following dir structure
app
├── default.js
├── index.html
├── ObserverPattern
├── ConcreteObserver.mjs
├── ConcreteSubject.mjs
├── ObserverList.mjs
├── Observer.mjs
└── Subject.mjs
In index.html I have <script type="module" src="default.js"></script>
In default.js I have following code
import { Observer } from "./ObserverPattern/Observer.mjs";
import { ObserverList } from "./ObserverPattern/ObserverList.mjs";
import { Subject } from "./ObserverPattern/Subject.mjs";
import { ConcreteObserver } from "./ObserverPattern/ConcreteObserver.mjs";
import { ConcreteSubject } from "./ObserverPattern/ConcreteSubject.mjs";
In ConcreteObserver.mjs I have following code
class ConcreteObserver extends Observer {
constructor(element) {
super();
this.element = element;
}
update(value) {
this.element.checked = value;
}
}
export {
ConcreteObserver
};
When I run via local web server, I get error that Uncaught ReferenceError: Observer is not defined
If I then add import { Observer } from './Observer.mjs'; at top of ConcreteObserver.mjs then it works without error.
Is there a way to import all the modules in default.js so that I don't have to include import { Observer } from './Observer.mjs'; in every Concrete implementation?
Something like php composer autoload
Thanks
A good part of the point of modules is to isolate code so you don't end up with vast numbers of in-scope variables which aren't being used.
Since you seem to be running in a browser you could explicitly assigned values to the global object…
import { Observer } from "./ObserverPattern/Observer.mjs";
window.Observer = Observer;
And then use window.Observer elsewhere.
But that makes code hard to manage. Linters won't be able to catch errors where you failed to assign the variable properly. You risk race conditions where you try to read the value before you assign to the window object.
If you need to use Observer then import it where you need to use it.
One of the points of modules is that they're self-contained and the relationships between them are explicitly, statically defined (primarily; there is a dynamic form of import). There is no automatic import in ESM.
For your ConcreteObserver implementation to work as-is, Observer would have to be a global, and would have to be defined before ConcreteObserver is evaluated. That is something you could do, though my view is you shouldn't, by making Observer.mjs assign Observer to a property on globalThis (in modern environments, global on older Node.js or window in older browsers). But other than that, you're going to need at least one import in ConcreteObserver.mjs.
There are a couple of things you can do to keep the number of distinct import statements down, if that's a goal. For instance, you can group things together a bit more (Observer, ObserverList, and Subject all seem closely-related, so they might all go in a single module). But you'd still need that import in ConcreteObserver.mjs. It's also possible for one module to re-export things from other modules (for example: export { Something } from "./somewhere.mjs";), if you wanted to have a master module for all the ObserverPattern stuff that exported everything defined in the files that make it up. But again, you'd still need import in ConcreteObserver.mjs