How to expose Webpack-built library as function? - javascript

I'm working on a JavaScript library which is essentially a Vue.js component bundled together with Vue itself. The project was started via the vue-cli and is packaged with Webpack.
When this library gets imported via a <script> tag, I'd like it to expose a single function like:
Mapboard({div: '#some-div')
The library itself is currently just:
export default (opts) => console.log('hello')
I tweaked my Webpack config to set a library name:
module.exports = {
output: {
library: 'Mapboard'
}
}
which successfully exposes a global called Mapboard, but it's object, not a function:
{
__esModule: true,
default: function (e) {...}
}
Is there any way to prevent Webpack from exporting this as an ES6 module? I'd like it to just be the function exported by the module, as shown above.

Take a look to this webpack 2 official article: https://webpack.js.org/guides/author-libraries/
That is for ES5 and ES6, if you are building your library using TypeScript, you probably need extra configuration for typings...

http://siawyoung.com/coding/javascript/exporting-es6-modules-as-single-scripts-with-webpack.html
This article helps a lot.The core point to solve this problem is:
The fix for this is simple: simply write a file that imports those ES6 modules the ES6 way, but exports them in nodeJS module.exports style. Webpack will then handle them correctly!
That's right!Just change your function into :
function foo(opts) {
console.log('hello');
}
module.exports = foo;
And everything should work as expected.
NOTE:Code write like this may not work:
module.exports = function foo(opts) {
console.log('hello');
}

Related

Import Vanilla JavaScript library in Angular 6

I am getting following error when try to import existing Vanilla JavaScript library to Angular 6 component.
Please suggest how to fix it.
Syntax to import the library, I have written
import * as ExistingLibrary from 'app/main/libs/ExistingLibrary.js';
ExistingLibrary.doJob is not a function
External JavaScript library - ExistingLibrary.js
var ExistingLibrary = new (function () {
this.doJob = doJob;
function doJob(template, options) {
function f1(template, options) {}
function f2(template, options) {}
});
var ExistingLibrary = new (function () {
this.doJob = doJob;
function doJob(template, options) {
function f1(template, options) {}
function f2(template, options) {}
});
instead of this you need to export the functions like this:
export function doJob(template, options) { }
The existing library should probably be a module of some short, so that you module bundler can add it to your module system.
The first thing to look up is if the existing library has a npm package?
In that case you probably should use the NPM version of the existing library, or consider upgrading the existing library to a version that comes with module system.
You probably need something like
export const ExistingLibrary = ...
or
module.exports = ExistingLibrary =
If modifying the existing library is not possible, you'll have to configure your module bundler or JS build pipeline to handle libraries that do not include modules and/or require global this. E.g. using something like https://www.npmjs.com/package/webpack-raw-bundler

What is the equivalent of module.exports for ES6 Modules?

I have an existing package written in JavaScript that I'm trying to convert to TypeScript. My question is, what is the equivalent of module.exports for ES6 Modules?
For example if I have the following code:
module.exports = function () {
console.log("Hello World");
};
The only way I have found to do this in ES Modules is by doing something like the following.
const myFunc = function () {
console.log("Hello World");
};
export default myFunc;
// OR
export { myFunc };
Of course, this is not the same thing as module.exports = //....
Which leads to this problem. When I run this through TypeScript and get the outputted code, users of the package will either need to use an import defaultExport from "module-name"; statement (which isn't bad), or instead of being able to access it using require("module-name") they will have to use require("module-name").default (if they are using standard JS, not TypeScript). Which obviously is a breaking API change for standard JavaScript users.
Obviously, the goal here is to have no breaking API changes (and require no changes for customers code).
One more requirement to any solution. In other places in the package I need to be able to export a lot of different files as one object. So in the past I've been doing:
module.exports = {
"thing1": require("./a.js"),
"thing2": require("./b.js")
};
So I need to be able to export this as normal without requiring the user to do thing1.default. This one so far seems like the easier requirement since I'm exporting an object which ES Modules seems to handle really well. It's when you are exporting a singular (non-named) expression that it gets really tricky.
Is there a way to do this without having to make this a breaking change to the API?
The solution here is to use the TypeScript export = command. This allows you to use either require or import with the module.
export = function () {
console.log("Hello World");
};
import myModule from "./lib/myModule";
// OR
const myModule = require("./lib/myModule");
For more information the TypeScript documentation has a guide about Migrating from JavaScript, and specifically a section called Exporting from Modules.
Typescript recommends not to rewrite your code, but instead to configure it to allow js modules.
When we were working on converting a very large app from js to TS, we took the position of writing new code in TS, and slowly converting js into ts (but not spend a whole lot of time just converting).
We needed to enable few options in the tsconfig.json, such as :
{
...,
"compilerOptions" : {
...
"moduleResolution": "node",
"allowJs": true,
...
},
"include" [
"path/to/my/js",
]
}
you also need to update your webpack.config.js:
module.exports = {
// ... my settings
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
},
}
You might also have to declare require as a function (if you are getting errors such as require is undefined):
declare function require(path: string): any;
more info is available here :
https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html
Most accurate replacement for module.exports in typescript is export default
export default {
"thing1": require("./a.js"),
"thing2": require("./b.js")
}
then you can use them like below
import myThings from '.....'

Correct way to export/define functions in Electron's Renderer

I have a JS file that I'm importing into my Electron's "main" (or background process), app.js, using require (eg: const myJS = require("./pathToMyJS/myJS");)
Contents of myJS.js:
module.exports = {
mFunc: function mFunc(param1) {
...
}
};
And I can use mFunc in app.js as myJS.mFunc(param1); & everything's great.
Then, I tried to follow the same process for the "renderer" JS. So my renderer.js now imports const myOtherJS = require("./myJS/myOtherJS"); where this other JS file follows the exact same module.exports logic as myJS.
And the root HTML (app.html) declares the renderer as <script defer src="./renderer/renderer.js"></script>.
But on launch, I get:
Uncaught TypeError: Cannot set property 'exports' of undefined
at renderer.js? [sm]:34
Searching online, I came across this answer that mentions that the AMD way could be used instead of the commonJS way. So I tried the following: (not sure whether this is syntactically correct!)
define(
["renderer"],
function rFunc(param1) {
... }
)
But that fails with:
Uncaught ReferenceError: define is not defined
So what's the correct way to have functions defined for export when using them in the renderer? What I've been doing so far is just to write the functions in their own JS files (eg: function func1() { ...}) & declaring all of these files in the app.html as <script defer src="./funcFile1.js"></script>.
Turns out, I was just exporting incorrectly. modules.export was the point of failure as modules is undefined on the renderer.
Instead, if I do the following to export individual functions:
// ./myJS/myOtherJS.js
export function rFunc() { ...}
And then import into my renderer.js like:
import { rFunc } from './myJS/myOtherJS';
rFunc();
Things work as I originally expected.
This Google Developers Primer on modules was useful in understanding the concepts.
AMD is not provided by node.js by default. It's used by Require.js and other FWs. Here is a link on how you can use it with node:
https://requirejs.org/docs/node.html

Import JS web assembly into TypeScript

I'm trying to use wasm-clingo in my TypeScript React project. I tried to write my own d.ts file for the project:
// wasm-clingo.d.ts
declare module 'wasm-clingo' {
export const Module: any;
}
and import like this:
import { Module } from 'wasm-clingo';
but when I console.log(Module) it says undefined. What did I do wrong?
Notes:
clingo.js is the main js file.
index.html and index_amd.html are two example pages
Solution:
I solved the problem like this:
// wasm-clingo.d.ts
declare module 'wasm-clingo' {
const Clingo: (Module: any) => Promise<any>;
namespace Clingo {}
export = Clingo;
}
and
import * as Clingo from 'wasm-clingo';
Here's the source for this solution
I know you found a solution acceptable to you; however, you don't really have any types here, you just have Module declared as any, which gives you no typescript benefits at all. In a similar situation I used #types/emscripten, which provides full type definitions for web assembly modules compiled using emscripten. You simply need to do:
npm install --save-dev #types/emscripten
then change your tsconfig.json types array to add an entry for emscripten.
After that you can just write Module.ccall(...) etc. If you like you could of course write const Clingo = Module and then make calls against that if you want a more descriptive name than Module (which is a terrible name!).
You're welcome ;)
I think the issue is that wasm-clingo exports the module itself but import { Module } from 'wasm-clingo' expects a property.
Try
import Clingo_ from 'wasm-clingo';
const Clingo: typeof Clingo_ = (Clingo_ as any).default || Clingo_;

How does lazy module loading work in ES6

How can I lazily-load ES6 modules? By lazy, I mean I don't want to actually load modules that aren't needed. For example, here's something I can do with RequireJS:
function someEventHandler() {
var SomeModule = require('some-module'),
module = new SomeModule();
// ...
}
Something along the same lines doesn't appear to be possible using ES6 imports:
// Doesn't appear to be valid...
function someEventHandler() {
import SomeModule from 'some-module';
var module = new SomeModule();
// ...
}
Are there any viable techniques to only pull in dependencies when needed, using ES6 modules? Or is the only path to trace the full dependency graph and fetch everything up-front?
The import statement will only work in the very top of files, and it will load all of them. This is mainly to avoid potential issues with circular dependencies.
There will also be a way to do asynchronous loading; however the specification doesn't seem to be finalized yet. The ES6 module loader polyfill package uses a method called System.import(moduleName) that returns a promise and the final specification is likely to look similar:
function someEventHandler() {
System.import('some-module').then((SomeModule) => {
var module = new SomeModule();
// ...
})
}
Example for lazyloading jQuery in ES6 :
import('jquery').then((jquery) => {
// Because jquery is the module, here is the new syntax for using this
window.$ = jquery.default;
window.$('#id');
});

Categories

Resources