using external lib in angular-cli application - javascript

I would like to add an axternal javascript library I created. How can I do it?
Let's say I have a file, converter.js
that file looks like that:
module.export = {
myFunc: a=>a
}
and I copy it in ./lib folder
Then in .angular-cli.json I add this entry into app:
"scripts": ["../lib/converter.js"],
I can see in the source of html file the javascript file containing my file. Good.
But I don't understand how to use myFunc for example in app.component.ts...
Like doc said
Once you import a library via the scripts array, you should not import
it via a import statement in your TypeScript code (e.g. import * as $
from 'jquery';). If you do that, you'll end up with two different
copies of the library: one imported as a global library, and one
imported as a module.
...
If the global library you need to use does not have global typings,
you can also declare them manually in src/typings.d.ts as any:
declare var libraryName: any;
And I did: declare var myFunc: any in src/typings.d.ts
Nut nothing: trying to call myFunc (as global) but it was undefined.
Thank you

I think your declaration is correct, although I would prefer declare var myFunc: (any) => any;. I see that you use module.export in your library, may be for using it server-side in node.js. That's the syntax used by the SystemJS module loader. It was used in old Angular tutorials. Since Angular CLI switched to Webpack as a module loader, we don't need it anymore.
Try to write your function as following:
var myFunc = function (a) {
return a;
}
You have chosen to add your script to the "scripts" array in .angular-cli.json. That apparently works. But I don't like declaring functions on the global scope. There probably must be a better way of importing a library. Try to use the import way instead as R. Richards suggested.

Related

Importing a JS module in a Typescript file

I am developing a website in TS in which I have to call an unofficial API, that is https://www.npmjs.com/package/reverso-api.
The whole module is written in JS, and as described in the docs, the proper way to import the module in JS is
const Reverso = require('reverso-api');
const reverso = new Reverso();
Unluckily, I am developing in TypeScript. I have no idea how to import the module in my project in order to make it works. How can I do that?
If this package doesn't have a type definition, you can use a temporary shorthand declaration so TypeScript won't yell at you. This makes all imports from reverso-api have any type, which you might have guessed is not very safe, but it's all we have right now.
declare module 'reverso-api';
Reference: https://www.typescriptlang.org/docs/handbook/modules.html#shorthand-ambient-modules

Exporting outside webpack

This is just something I thought today and I didn't see a lot of information so I'm going to share this weird cases and how I personally solved them (if there's a better way please comment, but meanwhile this might help others ^^)
In a regular module, you would do something like this to export your function/library/object/data:
// regular NodeJS way:
module.exports = data;
// ES6 way
// (will get transpiled to the regular way using the module variable by webpack)
export data;
default export data;
When compiling the library usually babel or tsc are used, but if for any reason you want not only to compile (transpile) your library but also pack it using webpack, you will encounter this case.
As you know, in a webpack bundle the module variable is local to the bundle (every module/file gets wrapped with a function where module is a parameter = local variable), so nothing really gets exported outside the bundle, is just nicely managed by webpack.
That means that you can't also access the contents using the regular require/import methods.
In some case you might find necessary to export outside webpack. (i.e. you are trying to build a library using webpack and you want it to be accessible by other people). This basically means you need to access the original module variable, but webpack doesn't expose it like it happened with __non_webpack_require__.
See also: Importing runtime modules from outside webpack bundle
The solution is to create our own __non_webpack_module__ (as webpack does with __non_webpack_require__.
How I did it is using webpack.BannerPlugin to inject some code outside the bundle. This code is prepended to the build after the minification is done, so it's preserved safely.
In your webpack.config.js:
plugins: [
new BannerPlugin({
raw: true,
banner: `const __non_webpack_module__ = module;`,
}),
]
And again, if you are using TypeScript, in global.d.ts:
declare const __non_webpack_module__: NodeModule;
And now, you can do something like this in your code:
__non_webpack_module__.exports = /* your class/function/data/whatever */
This will allow to import it as usual from other files
Tip: You might want to look at BannerPlugin to check other options, like include or exclude so this variable is only generated on the desired files, etc.

How to declare reference to an existing namespace which is available from JavaScript bundle at runtime

I am writing a plugin for existing JavaScript app - Forge Autodesk.Viewing
After version 6 they have included THREE.js inside of their app bundle.
Right now I'm able to use it with my plugin like this:
declare var THREE:any;
but I lose all types, so I install three.js by:
npm install --save three
I'm able to use THREE, and import it, but I don't need to Import it as I already have it in my main app. What I need to do is to reference types, so I tried to do something like this:
declare var THREE:THREE;
//Cannot use namespace 'THREE' as a type.
Then I tried to:
/// <reference types='three' /> which works fine, but:
const planes:THREE.Plane[] = []; //this line is okey
planes.push(new THREE.Plane()); //but this says
//'THREE' refers to a UMD global,
// but the current file is a module.
// Consider adding an import instead.
Tsc insists that we should import it:
import * as THREE from 'three';
It compiles without any issues, but when I launch the app it crash, because it's trying to get one more instance of THREE.js, which I do not provide because I have it inside main app.
How to declare the correct reference and keep types to an namespace which is available at main JavaScript application?
There's a TypeScript definition file (.d.ts) for Forge Viewer that you should be able to use together with a THREE.js definition file: https://forge.autodesk.com/blog/typescript-definitions-forge-viewer-and-nodejs-client-sdk-now-available.
Ypu need to import THREE module:
Like this:
import * as THREE from 'three'; // or import THREE from 'three';
or
var THREE = require('Three').
and use webpack or other module loader (!)
If you want to manually include THREEJS distribution file and use TS bindings (type definitions) without modules (not recommended) - you can include Three.d.ts file to your project (with other THREEJS d.ts files) and use it with three slashes ref. For example:
/// <reference path="..\..\node_modules\#types\three\index.d.ts" />
Note: don't import THREE namespace with "import" or "require" in this case.
If you have issue such as:
'THREE' refers to a UMD global, but the current file is a module. Consider adding an import instead.
(about UMD)
You may try to use option in tsconfig.json:
"compilerOptions": {
"allowUmdGlobalAccess": true,
(about config options)
This will give compiler access to UMD global, so you do not need to import or reference such modules in that case.
And it's exact the case with three.js They alredy add THREE namespace as module to UMD global scope. So if you need to include this module you should import. If you want only reference you could use this option. If typescript doesn't recognize this option in config just update your typescript.
npm install typescript
Thank you dear SalientBrain and dear Petr Broz for your attention and help.

ES2015 modules, should I import / export everything?

I started using import and export in javascript recently, but I am a bit confused. I'm not exactly sure what I'm supposed to export, and what I'm supposed to keep local to the file. I don't know how to express this nice in english, so here is a small section from my code:
The /client/js/Inventory.js file, module:
import {
$,
renderHTML,
game
} from "../main.js";
const template = {
titanium: {
name: "Titanium",
description: "Description here..."
}
}
export default class Inventory {
constructor(inventory) {
this._name = inventory.name;
this._description = inventory.description;
}
get name() {return this._name}
get description() {return this._description}
generate(id) {
renderHTML("inventory, `
<div class="inventory" id="${id}">
content here...
</div>
`);
}
static make(id) {
game.inventory[id] = new Inventory(template[id]);
game.inventory[id].generate(id);
}
and my /client/main.js file looks something like this:
import Inventory from "./js/Inventory.js";
const $ = (id) => document.getElementById(id);
const renderHTML = (id, str) => $(id).insertAdjacentHTML("beforeend", str);
const game = {
inventory: {}
};
Inventory.make("titanium");
export {
$,
renderHTML,
game
};
Since I'm exporting game object from the main.js file, does that mean a new game object is being created in the Inventory.js file after it's imported, or does that mean that the Inventory.js file can now access main.js file's game object?
Since I'm calling the classes inside the main.js file, do I also need to export the template object from the Inventory.js file, and import it inside the main.js file?
Exporting only the class works just fine, but I don't get how is the main.js accessing the template if it's not exported? Does it look in the Inventory.js file if none was found in the main.js or?
Thanks!
It's pretty simple. Modules are a structured way of sharing code or data between different pieces of code.
You export any functions or data that you wish to share with other modules that will load your module.
If you have no intention of sharing it or no need to share it, don't export it. If you do need to share it or some other module needs to be able to access it, you export it. Export is the means of sharing with other other modules.
You import any properties from other modules that you need to use in this module.
For imports, you only import what you need now. No need to import something you "might" need in the future. You can always just add the import at the time you actually need it.
For exports, you only export what you specifically intend to share now. If you find a need to share more later, you can always add another export later.
Code that is only used within this module does not need to be exported. In fact, one of the benefits of modules is that you can maintain code privacy or protection in a module because other code cannot access anything in a module that is not exported or shared somehow via an export.
You can logically think of exports as the "api" for your module. This is what other modules can call in your module.
You can logically think of imports as you specifying what "apis" you want to use from other modules.
When everything is in the global namespace (as with the original browser design), then there was no explicit export or import. Everything declared at the top level was just public and shared. This caused all sorts of problems, particularly as projects got larger and there got to be more and more files and then got even more complicated when you started trying to use third party code.
The module system is a structured way of saying that, by default, everything is private. You then explicitly export only the things you want to share. And, then when someone wants to use your apis, they explicitly import the apis they want to use. Each module lives in it's own scope so has its own namespace. Modules also make a very natural testable unit.
Before the standarization of Javascript modules, developers had build a whole bunch of different conventions to try to work-around the large flat global namespace in Javascript. It was not uncommon to encounter multiple conventions in the same project if you were using 3rd party libraries. For developers not trying to use a convention to solve this, code could get pretty messy with lots of potential for variable naming conflicts, accidental replacement of functions and a generally undocumented web of dependencies between files, etc... The standardized module design attempts to provide one common way of addressing these issues and, in the process, also make it a lot easier to write reusable, testable, shareable code.
Since I'm exporting game object from the main.js file, does that mean a new game object is being created in the Inventory.js file after it's imported, or does that mean that the Inventory.js file can now access main.js file's game object?
It means that Inventory.js can now export the one game object that was created in main.js. There is no implicit copying when you export or import.
Since I'm calling the classes inside the main.js file, do I also need to export the template object from the Inventory.js file, and import it inside the main.js file?
You only need to export things from inventory.js that you directly need to reference from some other file. Since main.js does not need to reference the template variable directory, there is no need to export it. The act of importing from inventory.js loads and runs that module. That makes the template variable available to all the code inside inventory.js which is all your code needs. So, no need to export it.
Exporting only the class works just fine, but I don't get how is the main.js accessing the template if it's not exported? Does it look in the Inventory.js file if none was found in the main.js or?
Exporting the Inventory class allows any other module to use that class and all its methods. The process of importing anything from inventory.js cause the module itself to get loaded so all the variables defined within inventory.js are active inside of inventory.js. When you create an Inventory object via the exported class, you are running code in inventory.js that has access to all the data in that module.
Thing of import as two steps. First load the module that is referenced (if it's not already loaded). Then, fetch the exports that you requested imports for.

In Javascript AMD, why is it useful to define a module without a name?

A named module makes sense for me:
define('myModule', ['dep1', 'dep2'], function (dep1, dep2) {
//Define the module value by returning a value.
return function () {};
});
When I want to use this module, I can use require to import it:
require('myModule', function(myModule){})
However, what I can't understand is the anonymous module like this (from requireJS examples):
define(['dep1', 'dep2'], function (dep1, dep2) {
//Define the module value by returning a value.
return function () {};
});
Is the code above used to define an anonymous module? If so, how can this module be used/imported/refered by other modules? Does anyone have ideas about this?
If you scroll down a bit on that page you linked, it says
Notice that the above module does not declare a name for itself. This is what makes the module very portable. It allows a developer to place the module in a different path to give it a different ID/name. The AMD loader will give the module an ID based on how it is referenced by other scripts.
So the module will in fact get a name, based on how you load the file that contains it.
I guess the idea is that you develop with "anonymous" modules (one per file), and then have a build tool that bundles them all up (giving them names in the process).

Categories