I was going through the codebase of an old project that uses Ember-CLI 1.13 and found something strange.
There are many helpers that are not directly used inside templates but are used in component js files by importing into them. One such example is
//..helpers/my-helper.js
export function func1 (param1, param2) {
//return something;
}
export function func2 (param1, param2) {
//return something;
}
export function func3 (param1, param2) {
//return something;
}
export default Ember.Helper.helper(func1);
export default Ember.Helper.helper(func2);
export default Ember.Helper.helper(func3);
And inside a component js file, I could see the above helpers being imported and used.
//../components/my-component.js
import Ember from "ember";
import { func1 } from '../helpers/my-helper';
import { func2 } from '../helpers/my-helper';
import { func3 } from '../helpers/my-helper';
I have few questions:
Shouldn't we create a utility instead of a helper in this case?
Is it ok to include many functions in a single helper file?
Are the imports inside the component file necessary?
Shouldn't we create a utility instead of a helper in this case?
Yes, but sometimes programmers are lazy or very restricted in time (even though moving function to utilities doesn't look like time-consuming task)
Is it ok to include many functions in a single helper file?
Yes, it is fine to have many functions in file and export them, but as far as I know, only default export will work in templates as helper. And I am 99% sure that not having default export will lead to build error.
Are the imports inside the component file necessary?
If these imports are used in component's code, then they are necessary. Otherwise, no.
Related
I am learning to use webpack for first time. I am really stuck with something that was never a problem with gulp, that is the way it scopes different files.
I have a file that contains multiple standalone functions (helpers.js).
function a() {
}
function b() {
}
function c() {
}
Based on my reading here do I need to import each function separately? How do I import the whole file?
From the example in link:
a.js:
export var a = 4;
b.js
import { a } from "./b.js";
var b = a+1;
console.debug(b);
I suspect that you have used some concatenation plugin for gulp and your scope was "shared".
With ES6 modules, you have to define exactly what functions to export and import because the scope of each file is separate.
So in your example, we can do it this way:
Create default export in helpers.js and define what data to export.
helpers.js
function a(){}
function b(){}
function c(){}
export default {a,b,c}
And import data this way:
import myHelpers from 'helpers'
Then to use helper a, you will need to call myHelpers.a()
Another approach is to create "named" exports
helpers.js:
export function a(){}
export function b(){}
export function c(){}
To import all data use:
import * as myHelpers from 'helpers'
Then similar to the previous example, call -> myHelpers.a()
But typing "myHelpers" is not always convenient, so here we can use named exports additional benefit - you can import it by a name, so we can do it like this:
import {a,b,c} from 'helpers'
Then you can call a()
You have to type all the names you want to import. There is no "shortcut" for that.
Why this way?
better control on what exactly you import to your code, optional "tree shaking"
no conflicts between imported modules
Webpack - ProvidePlugin
Ok, but what if you use those helpers really often? Do you need to import them everywhere? Technically - yes. But Webpack can automate that for us, have a look on Webpack - ProvidePlugin which automatically loads modules instead of having to import them everywhere.
For Webpack 3, if you go with the first solution it will look like this:
new webpack.ProvidePlugin({
helpers:['path/to/module/helpers', 'default'],
}),
That will make helpers available like a "global", and you will be able to use helpers.a()
I'm using web pack to package a library.
We have multiple ES6 classes, in this fashion:
/src/Lib.js
import HelperClass from './HelperClass.js';
class Lib {
method1() {...}
}
/src/HelperClass.js
class HelperClass {
doSth() {...}
}
Packaging with webpack works, we end up with one file lib.js that contains Lib and HelperClass as var Lib = ....
How can I hide the HelperClass from the global namespace (e.g. make it a private class) with webpack?
UPDATE:
Now I'm running into an issue with importing the HelperClass! I uploaded a sample project https://github.com/benmarten/webpack_es6_test
This line:
__WEBPACK_IMPORTED_MODULE_0__Helper_js___default.a.doSth();
results in:
[Error] TypeError: __WEBPACK_IMPORTED_MODULE_0__Helper_js___default.a.doSth is not a function. (In '__WEBPACK_IMPORTED_MODULE_0__Helper_js___default.a.doSth()', '__WEBPACK_IMPORTED_MODULE_0__Helper_js___default.a.doSth' is undefined)
method1 (lib.js:92)
Global Code (index.htm:6)
When creating a library with webpack you expose everything that is exported in your entry point, everything else is not accessible from outside but you can use it within your code. If you want to use anything from another file, you still need to export it, because the files are still modules. Just because there is an export, does not mean it becomes a global. Only the exports in the entry specified in the webpack.config.js will be exposed.
Export the Helper in HelperClass.js:
class Helper {
static doSth() {
console.log('helper:doSth');
}
}
export default Helper;
Then import it in Lib.js:
import Helper from './HelperClass.js';
class Lib {
static method1() {
Helper.doSth();
}
}
export default Lib;
Now the default export of your bundle will be the Lib class and you can use Helper inside it without exposing it.
You should also read the Authoring Libraries Guides.
I am having a hard time understanding exporting and importing stuff in typescript, how should this one be constructed for example?
src/functions/handle.ts:
export default function handle() {
// do something here.
console.log("It is handled");
}
src/functions/index.ts:
import handle from './handle';
export default {
handle
};
src/run.ts:
import allFunctions from './functions';
console.log(allFunctions);
If I run node dist/run.js (I am compiling into distdirectory) after compiling, I get undefined. But if I use
import allFunctions from './functions/'
(notice the "/" at the end)
it references to an object that includes the exported function.
I also tried using export * from './handle' but the result is the same.
What is the correct way to accomplish this?
I guess what you are trying to achieve is to re-export all your functions from a single file so you just have to import that file to access them all
First of all, you cannot re-export default exports, so you will have to go for named exports. Anyway, avoid default exports, they do not provide much benefits and confuse everyone.
src/functions/handle.ts:
export function handle() {
// do something here.
console.log("It is handled");
}
src/functions/index.ts::
export * from './handle';
export * from './anotherFile';
...
src/run.ts:
import * as allFunctions from './functions/index'
I was wondering what is the best practice to import a module's function/class in another module that the module itself needs to invoke/initialize its own function/class before being imported into another module? I don't know if I could ask my question clearly enough! So let's put it in an example.
This is my module:
// myModule.js
class MyModule {
constructor() {
// do sth
}
}
let myModule = new MyModule();
And this is how I like to import it in another module:
import MyModule from './myModule';
This actually works fine! But as you can see, in the myModule.js file I didn't export default my MyModule class because that's not the only thing that is happening in the myModule.js file! I'm also initializing the class after defining it... (I know that even if I have set my class as export default the initializing would still work fine while the module is imported somewhere else...)
So, without setting anything as exported inside of our module, or with setting the class as the export default, everything works fine when the module has been imported somewhere else... So far so good! But I'm looking for a best practice if there's one!
So here are my questions regarding such cases:
Is it ok to import a module that doesn't have anything for export?
Shall we set the class as the export default, although we are doing some more works outside of the class in the module (The initializing job that is happening after defining the class)?
Or maybe is it good to do the initializing job in another function and then export both, the class and the function, and then invoke the function to do the initializing job in the imported module?
Thanks a lot everyone! I really appreciate any helps regarding this :)
How about offering to import the class or the instance of it? Like:
// export class itself
export class MyModule {
constructor() {
// do sth
}
}
// export instance of MyModule directly
export default new MyModule();
// export a factory function if you need more work to be done
// before the instance is created
export function myModuleFactory(...args) { // define e.g. arguments to be passed to constructor
// ... do stuff
const myModule = new MyModule(...args);
// ... do more stuff
return myModule;
}
So you could do:
// import instance
import instance from './myModule';
// or class
import { MyModule } from './myModule';
// or import factory
import { myModuleFactory } from './myModule';
What to do, depeneds on what you want to accomplish with your module. If you want your app use one shared instance of a MyModule class object, you’d export and import the instance like shown above. If you want to create multiple instances in different contexts, you’d export the class itself or a factory function to return a new instance.
To keep it even more clean, you’d keep the class in yet another separate file and import it into the module providing the factory/instantiation.
Update
To answer your first question: You can import modules that don’t have any export defined. The module will be loaded and its logic executed. The thing is that as long as it won’t change global variables (like window in web development) it won’t have any effect as everything inside the module happens within an isolated scope. As you may have guessed, having modules change global vars is far from best practice.
Thanks a lot guys for your answers. I really appreciate that. Actually I got really confused on the project that I'm working on, so maybe I couldn't express what I'm trying to do...
But anyway, I write my own answers to my questions, maybe someone else find them useful:
It's always a good practice to have a export default or export for a module that we're going to write. Because every piece of code tends to have some results, right? So in a module, we should consider what we're going to achieve at the end and then export that. Export your outputs, the things that you expect your module to provide when it is going to be imported somewhere else.
If your module is a single class, it's good to export default it. Otherwise, as I said in the first answer, it all depends in what you're going to achieve in your module and what is your results. Export all the results, utility functions, etc...
You may like to do that too! But first think about your use case. As soon as the module is imported somewhere else, all the codes inside of it will be executed. So do whatever you like to do and then export the end results.
I am using TypeScript 1.6 with ES6 modules syntax.
My files are:
test.ts:
module App {
export class SomeClass {
getName(): string {
return 'name';
}
}
}
main.ts:
import App from './test';
var a = new App.SomeClass();
When I am trying to compile the main.ts file I get this error:
Error TS2306: File 'test.ts' is not a module.
How can I accomplish that?
Extended - to provide more details based on some comments
The error
Error TS2306: File 'test.ts' is not a module.
Comes from the fact described here http://exploringjs.com/es6/ch_modules.html
17. Modules
This chapter explains how the built-in modules work in ECMAScript 6.
17.1 Overview
In ECMAScript 6, modules are stored in files. There is exactly one
module per file and one file per module. You have two ways of
exporting things from a module. These two ways can be mixed, but it is
usually better to use them separately.
17.1.1 Multiple named exports
There can be multiple named exports:
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
...
17.1.2 Single default export
There can be a single default export. For example, a function:
//------ myFunc.js ------
export default function () { ··· } // no semicolon!
Based on the above we need the export, as a part of the test.js file. Let's adjust the content of it like this:
// test.js - exporting es6
export module App {
export class SomeClass {
getName(): string {
return 'name';
}
}
export class OtherClass {
getName(): string {
return 'name';
}
}
}
And now we can import it in these three ways:
import * as app1 from "./test";
import app2 = require("./test");
import {App} from "./test";
And we can consume imported stuff like this:
var a1: app1.App.SomeClass = new app1.App.SomeClass();
var a2: app1.App.OtherClass = new app1.App.OtherClass();
var b1: app2.App.SomeClass = new app2.App.SomeClass();
var b2: app2.App.OtherClass = new app2.App.OtherClass();
var c1: App.SomeClass = new App.SomeClass();
var c2: App.OtherClass = new App.OtherClass();
and call the method to see it in action:
console.log(a1.getName())
console.log(a2.getName())
console.log(b1.getName())
console.log(b2.getName())
console.log(c1.getName())
console.log(c2.getName())
Original part is trying to help to reduce the amount of complexity in usage of the namespace
Original part:
I would really strongly suggest to check this Q & A:
How do I use namespaces with TypeScript external modules?
Let me cite the first sentence:
Do not use "namespaces" in external modules.
Don't do this.
Seriously. Stop.
...
In this case, we just do not need module inside of test.ts. This could be the content of it adjusted test.ts:
export class SomeClass
{
getName(): string
{
return 'name';
}
}
Read more here
Export =
In the previous example, when we consumed each validator, each module only exported one value. In cases like this, it's cumbersome to work with these symbols through their qualified name when a single identifier would do just as well.
The export = syntax specifies a single object that is exported from the module. This can be a class, interface, module, function, or enum. When imported, the exported symbol is consumed directly and is not qualified by any name.
we can later consume it like this:
import App = require('./test');
var sc: App.SomeClass = new App.SomeClass();
sc.getName();
Read more here:
Optional Module Loading and Other Advanced Loading Scenarios
In some cases, you may want to only load a module under some conditions. In TypeScript, we can use the pattern shown below to implement this and other advanced loading scenarios to directly invoke the module loaders without losing type safety.
The compiler detects whether each module is used in the emitted JavaScript. For modules that are only used as part of the type system, no require calls are emitted. This culling of unused references is a good performance optimization, and also allows for optional loading of those modules.
The core idea of the pattern is that the import id = require('...') statement gives us access to the types exposed by the external module. The module loader is invoked (through require) dynamically, as shown in the if blocks below. This leverages the reference-culling optimization so that the module is only loaded when needed. For this pattern to work, it's important that the symbol defined via import is only used in type positions (i.e. never in a position that would be emitted into the JavaScript).
Above answers are correct. But just in case...
Got same error in VS Code. Had to re-save/recompile file that was throwing error.
How can I accomplish that?
Your example declares a TypeScript < 1.5 internal module, which is now called a namespace. The old module App {} syntax is now equivalent to namespace App {}. As a result, the following works:
// test.ts
export namespace App {
export class SomeClass {
getName(): string {
return 'name';
}
}
}
// main.ts
import { App } from './test';
var a = new App.SomeClass();
That being said...
Try to avoid exporting namespaces and instead export modules (which were previously called external modules). If needs be you can use a namespace on import with the namespace import pattern like this:
// test.ts
export class SomeClass {
getName(): string {
return 'name';
}
}
// main.ts
import * as App from './test'; // namespace import pattern
var a = new App.SomeClass();
In addition to A. Tim's answer there are times when even that doesn't work, so you need to:
Rewrite the import string, using the intellisense. Sometimes this fixes the issue
Restart VS Code
I had this issue and I had forgotten to export the Class.
In addition to Tim's answer, this issue occurred for me when I was splitting up a refactoring a file, splitting it up into their own files.
VSCode, for some reason, indented parts of my [class] code, which caused this issue. This was hard to notice at first, but after I realised the code was indented, I formatted the code and the issue disappeared.
for example, everything after the first line of the Class definition was auto-indented during the paste.
export class MyClass extends Something<string> {
public blah: string = null;
constructor() { ... }
}
Just in case this may works for you as it did form me, i had this files
//server.ts
class Server{
...
}
exports.Server = Server
//app.ts
import {Server} from './server.ts'
And this actually raised an error but i changed server.ts to
//server.ts
export class Server{
...
}
and it worked 😎👌
Note: i am using this config
"target": "esnext",
"module": "commonjs",
I faced the same issue in a module that has no exports. I used it for side-effects only. This is what the TypeScript docs say about importing side-effects modules:
Though not recommended practice, some modules set up some global state that can be used by other modules. These modules may not have any exports, or the consumer is not interested in any of their exports. To import these modules, use:
import "./my-module.js";
In that situation, you can fix the "File is not a module" error by simply exporting an empty object:
// side-effects stuff
export default {};
I faced the same issue ("File is not a module error") for import js in vue component
import handleClientLoad from "../../../public/js/calendar.js"
I do this and solve it
// #ts-ignore
import handleClientLoad from "../../../public/js/calendar.js"
The file needs to add Component from core hence add the following import to the top
import { Component } from '#angular/core';