I have made several CustomElement, like buttons, drawer etc...
The drawer depends on buttons.
With javascript import / export modules dependencies are resolved by the browser and the js files needed are loaded, but with CustomElement their is no import of the others CustomElements needed as they are defined via customElements.define('my-drawer', Drawer);
How do you handle those dependencies ? do you add fake import export so that the browser resolve the dependencies for you? or do you bundle them together even if they may not be all needed?
You can import the child component into the parent and check if custom element exists before defining it so you won't get an error for multiple custom elements with the same name.
In button.js (assuming is the child):
if(!customElements.get('my-button')) customElements.define('my-button')
In drawer.js (parent) just import the button.js
you import those dependencies and register those elements; for each once imported and registered any instance of an element becomes 'defined' and the class code exercised, that's why my-complex imports them in the example below; they only need to be imported once, importing more doesn't hurt anything, that's why every class that has dependencies imports them, and that's why each class self-registers, to manage the boilerplate;
// my-button.js
export default class MyButton extends HTMLElement{}
customemElements.get('my-button') ?? customElements.define('my-button', MyButton);
// my-thing.js
export default class MyThing extends HTMLElement{}
customemElements.get('my-thing') ?? customElements.define('my-thing', MyThing);
// my-complex.js
import './my-thing.js';
import './my-button.js';
export default class MyComplex extends HTMLElement{}
// use <my-button> and <my-thing> anywhere, including here
customemElements.get('my-complex') ?? customElements.define('my-complex', MyComplex);
don't worry about any optimizations, ES modules do that for you, and if you bundle let the bundler handle it--it's not worth considering; just add all the imports each feature needs; ESM does the rest of the work--which together with serviceworkers and caching is a beautiful, easy evolution of the platform; we can actually get away from complex build processes and let the browser do it's job
Related
In React when you wanna import components from other files we use:
import ComponentName from 'somePlace';
That works fine, but I wanna know if there is a way to import the content of a file instead of the exports. I wanna put all import statements for components in a single file (e.g. imports.js) and have a statement like:
import './import.js' to all documents;
so all my components are automatically imported everywhere.
Is there a way to do that?
Globally import modules? Not really, no. And neither should you need to.
A hacky "solution" would be assigning all imports to the global context (e.g. window in the browser) so it's accessible that way. That's possible, but definitely not recommended. It'll also prevent your bundler (most likely Webpack) from optimizing a lot of code.
Apart from the technical aspect, there are other reasons not to do so. If you specify the imports in each file, you know exactly what imports that file needs and under what variables it is imported as for that file.
If you still want to simplify importing the same components over and over again, you can have this setup:
imports.js
// For default exports
import ComponentA from 'wherever';
export { ComponentA };
// For regular exports
//import { ComponentB } from 'wherever';
export { ComponentB } from 'wherever';
// For whole modules
//import * as wherever from 'wherever';
export * as wherever from 'wherever';
someOtherFile.js
// Either import as a namespace
import * as Imports from './imports';
console.log([
Imports.ComponentA,
Imports.ComponentB,
Imports.wherever.someFieldFromTheWhereverModule,
]);
// Or partial import
import { ComponentA, ComponentB } from './imports';
Having a C# / C++ packaging structure in mind, how do I achieve to have JS-classes in individual files all imported as a single name space?
My currently working solution is to use an additional "package"-script file, which then encapsulates all classes. See the code below.
It seems as this includes a lot of overhead (updating the package-script-exports with every new class, importing this script in every new class).
ClassA.js:
import * as MyPackage from "../MyPackage.js";
export default class ClassA {/* some Class Code */}
export {
ClassA
};
MyPackage.js:
import ClassA from "./module/ClassA.js";
import ClassB from "./module/ClassB.js";
export {
ClassA,ClassB
}
script.js:
import * as MyPackage from "./MyPackage.js"
let a = new MyPackage.ClassA();
What would be a best practice for that?
My currently working solution is to use an additional "package"-script file, which then encapsulates all classes.
Yes, this is the best practice. The "package" script file is typically called index.js placed in the directory of the package, as that's what the folder path is resolved to when importing.
importing this script in every new class
No, you should not do that. It introduces a circular dependency. As long as your module doesn't depend on any of the other classes, it should not import anything. There is no "package declaration", the module is a standalone file with its own dependencies.
Notice also that you shouldn't export your class twice from the module, the default export is enough. So you'd use
// mypackage/classA.js:
export default class ClassA {
/* some Class Code */
}
// mypackage/index.js:
export { default as ClassA } from "./classA.js";
export { default as ClassB } from "./classB.js";
// your solution of importing, then exporting works as well.
// script.js:
import * as MyPackage from "./mypackage";
const a = new MyPackage.ClassA();
// or
import { ClassA } from "./mypackage";
const a = new ClassA();
For cases like this,
/ACollectionOfTinyComponent/index.js
import Container from './Container';
export {
Container,
};
So the index.js becomes a directory to include other small parts without having to write each Component Name out all the time.
So in this case, we can import a component in another component like following:
import {Container} from './ACollectionOfTinyComponent'
//then use Container in the code here
Is this a bad practice? Since if I have airbnb linter enable then I got error linting of
Prefer default export import/prefer-default-export
it asks me to add default but that will cause compile error
I found out that because I added only ONE import & export for the index.js. But if I add more than one its fine!
For example, if I do
import Container from './Container';
import Ezeewei from './Ezeewei';
export {
Container,
Ezeewei,
};
Note that I added one more import of Ezeewei.
Then the linting rule will pass!
it asks me to add default but that will cause compile error
You must be using the wrong syntax for exporting a default.
export { name1 as default, … };
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#Syntax
import {Container} ... Is this a bad practice?
Yes. If you have a single entry point into a component (like a a class), you should export it as the default rather than a named export. The designers of the language gave default imports and exports a special syntax to promote it as the primary use case.
http://2ality.com/2014/09/es6-modules-final.html#default-exports-are-favored
Say I have these imports:
import clearLineReporter from '../modules/clear-line-reporter';
import karmaReporter from '../modules/karma-reporter';
import metaTestReporter from '../modules/meta-test-reporter';
import stdReporter from '../modules/std-reporter';
import tapJSONReporter from '../modules/tap-json-reporter';
import tapReporter from '../modules/tap-reporter';
import webSocketReporter from '../modules/websocket-reporter';
these must be referenced like I do above, in other words, I obviously can't do this:
const imports = {
stdReporter: import(...),
tapJSONReporter: import(...),
...
webSocketReporter: import(...)
}
Is there any way I can reference imported files through some form of reflection? Because it seems like I can't group them together to reference them somehow.
Instead of import syntax, I could use require(), but I am wondering if there is some way I can do some dynamic things with import statements, for example reference them all dynamically, such that if I add or remove an import, I don't have to change any other code.
There is a great answer to this question that I discovered by asking a different question, here:
exporting imports as a namespace with TypeScript
Create a file name grouped-modules.ts for example, where you want to simply list only the modules and export each one.
export {default as clearLineReporter} from '../modules/clear-line-reporter';
export {default as karmaReporter} from '../modules/karma-reporter';
export {default as metaTestReporter} from '../modules/meta-test-reporter';
...
export {default as stdReporter} from '../modules/std-reporter';
export {default as tapJSONReporter} from '../modules/tap-json-reporter';
Then in your module you can just do :
import * as mods from './grouped-modules'
export {mods}
It will export both types and values in a namespace called s. You can then import them using :
import {mods} from 'your-module'
const anObject: mods.clearLineReporter = ...;
This allows you to dynamically group your modules into one variable.
Is there any way I can reference imported files through some form of reflection?
Answer is dependent on environment, meant in questing, because import statement can be ES native modules implementation in browser, or babel-ed to require statements in node.js, or compile-time resolved bindings in webpack.
So, in each case there is solution to do something reflection. In node.js with babel-ed code import is just require wrapper, so any information is available there.
In browser with native ES modules, all requests to them can be served via ServiceWorker, so it can provide necessary information about fetched ES modules. Also in browser ES modules can be dynamically imported that way: https://matthewphillips.info/posts/loading-app-with-script-module
Most interesting part is webpack: compile-time resolve and semi-reflection can be achieved by externals resolver in functional style (https://webpack.js.org/configuration/externals/#function), and runtime by load module API (https://webpack.js.org/api/module-variables/#webpack_modules-webpack-specific- )
I have a unit test using jasmine that needs to import the classes it is going to test, so I include them at the top of the file:
///<reference path="./player.ts" name="PlayerModule" />
import PlayerModule = require("./player");
However!
When I come to creating an instance of the class
var player = new PlayerModule.Player(playerData);
... I get an error Module has not been loaded yet [player]
The player class use the exports
export class Player {
//code
}
How to I import other classes into classes for use whereby I can instantiate them, in typescript?
You could import your module and the respective Player class via
import {Player} from './player';
const player = new Player(playerData);
Is it possible that you use "module": "amd" in your tsconfig.json?
Edit: Overlooked your requirejs tag :) Could you add some information about your Jasmine / build configuration?