Webpack: reference bundled components from outside bundle - javascript

I have a class inside my bundled app that I want to allow users of the app to extend.
Here's what the definition of the bundled class looks like:
import * as d3 from 'd3';
class VizPlugin {
constructor(options) {
this.options = options;
}
build() {
}
...
}
export default VizPlugin;
The application is a full client/server nodejs app, installed on a customers server. After the app is bundled/deployed/installed on the server, I want the customer to be able to extend the app and add their own custom modules as extensions/plugins, like this:
import VizPlugin from './js/viz-plugin'; //there is no viz-plugin.js because it's bundled
class ScatterPlot extends VizPlugin {
constructor(options) {
super(options);
}
build() {
//I'd like to also use the reference to d3 from VizPlugin here
}
...
}
export default ScatterPlot;
They would put their javascript code in a directory along side the other bundled client javascript and import from that. Basically, there needs to be a named file called "viz-plugin.js" that can be imported from "scatter-plot.js".

Add d3 to your class, and give the extender some way of using it:
import * as d3 from 'd3';
class VizPlugin {
constructor(options) {
this.options = options;
this.d3 = d3;
}
useD3 (callback) {
callback(this, this.d3);
}
build() {
}
...
}
module.exports = VizPlugin as VizPlugin;

You can use the webpack SplitChunksPlugin and give names to individual modules by using splitChunks.name.
The following code will look for the viz-plugin.js module and tell it to keep it's name:
optimization: {
splitChunks: {
name(module, chunks, cacheGroupKey) {
const moduleId = module.identifier();
if( moduleId && moduleId.indexOf('viz-plugin.js') >= 0 ) {
return 'viz-plugin'
}
return null;
}
}
},
Now, in the dist/build output, there will be a file called "viz-plugin.js" that you can extend, as such:
import VizPlugin from './dist/js/viz-plugin';
class ScatterPlot extends VizPlugin {
constructor(options) {
super(options);
}
build() {
//I'd like to also use the reference to d3 from VizPlugin here
}
...
}
export default ScatterPlot;

Related

Default export new instance of class

Will the snippets below produce new instance every time it's imported?
// 1st implementation
class ConnectionManager {
...
}
export default new ConnectionManager();
// 2nd implementation
class ConnectionManager {
...
}
const connectionManager = new ConnectionManager();
export default connectionManager;
If yes, how can I get the same instance in every import?
ES6 modules follow single instance pattern. That is, the instance is created when the module is loaded.
Here is an article about it.
// File: yolo.js
class Yolo {}
export let yolo = new Yolo();
// File: laser.js
import { yolo } from "./yolo.js";
// yolo is a single instance of Yolo class
// File: cat.js
import { yolo } from "./yolo.js";
// same yolo as in laster.js
It should be the same.
The following example uses both the implementations, imports them into 2 different files, and imports them all into single index file. Everytime an instance is created, we generate a random value for the class, and log its creation.
// ConnectionManagerImpl1.ts
class ConnectionManagerImpl1 {
public value;
constructor() {
this.value = Math.random().toString(36).substring(7);
console.log(`New ConnectionManagerImpl1 instance created: ${this.value}`)
}
}
export default new ConnectionManagerImpl1();
// ConnectionManagerImpl2.ts
class ConnectionManagerImpl2 {
public value;
constructor() {
this.value = Math.random().toString(36).substring(7);
console.log(`New ConnectionManagerImpl2 instance created: ${this.value}`)
}
}
const connectionManagerImpl2 = new ConnectionManagerImpl2();
export default connectionManagerImpl2;
// import1.ts
import connectionManagerImpl1 from './ConnectionManagerImpl1';
import connectionManagerImpl2 from './ConnectionManagerImpl2';
export { connectionManagerImpl1, connectionManagerImpl2 };
// import2.ts
import connectionManagerImpl1 from './ConnectionManagerImpl1';
import connectionManagerImpl2 from './ConnectionManagerImpl2';
export { connectionManagerImpl1, connectionManagerImpl2 };
// index.ts
import * as import1 from './import1';
import * as import2 from './import2';
console.log(import1)
console.log(import2)
console.log("Done")
Ran the above setup using tsc --module 'commonjs' * && node index.js
Output:
New ConnectionManagerImpl1 instance created: ddt3re
New ConnectionManagerImpl2 instance created: uv5z6
{ connectionManagerImpl1: ConnectionManagerImpl1 { value: 'ddt3re' },
connectionManagerImpl2: ConnectionManagerImpl2 { value: 'uv5z6' } }
{ connectionManagerImpl1: ConnectionManagerImpl1 { value: 'ddt3re' },
connectionManagerImpl2: ConnectionManagerImpl2 { value: 'uv5z6' } }
Done
As you can see, only 1 instance of ConnectionManagerImpl1 and ConnectionManagerImpl2 were created. So, both the implementation should create only 1 instance.
The export statement is used when creating JavaScript modules to export functions, objects, or primitive values from the module so they can be used by other programs with the import statement.
There are two different types of export, named and default. You can have multiple named exports per module but only one default export.
export default class ConnectionManager { .. }
Or
class ConnectionManager {
...
}
export default connectionManager;

Expose node_module using both import and require

I wrote a node module and I'm trying to understand how I would go about allowing users to either require or import the module depending on their environment.
At the moment I'm exposing my classes like this:
/src/index.ts
import { Class1 } from './Class1';
import { Class2 } from './Class2 ';
module.exports = {
Class1,
Class2
}
gets compiled to
/lib/index.js
var Class1_1 = require("./Class1")
var Class2_1 = require("./Class2 ")
module.exports = {
Class1: Class1_1.Class1
Class2: Class2_1.Class2
}
which works allows require but not import
const { Class1, Class2 } = require('my-module');
I've been googling everything I can think of but can't get any examples or pointers.

Webpack adding ".default" to Dexie Plugin import

I'm using Dexie, and am trying to use the dexie-relationships addon (https://github.com/ignasbernotas/dexie-relationships). However, after including it, and trying to use it, webpack adds a .default to it, which results in an error as there is no .default on the module.
Code:
import Dexie from 'dexie';
import relationships from 'dexie-relationships'
export class AppDatabase extends Dexie {
constructor(){
super('AppDatabase', {addons: [relationships]});
}
}
What webpack outputs:
const dexie_1 = __webpack_require__(30);
const dexie_relationships_1 = __webpack_require__(57);
class AppDatabase extends dexie_1.default {
constructor() {
super('AppDatabase', { addons: [dexie_relationships_1.default] });
}
}
The dexie_relationships_1.default doesn't work as dexie_relationships_1 is the function that's supposed to be passed.
Why is it doing this, and what can I do to correct this behavior?

Reference another internal module implicitly in typescript

Say I have
List.ts:
module Helper {
export class List{
}
}
Parser.ts:
module Helper {
export class Parser {
}
}
Now I have another module, and every time I want to use "List", I need to say Helper.List. Is it possible to just say something like:
import Helper;
module Data {
export interface DataRepository {
getRange() : List<string>;
}
}
So that every time I want to use List, I can just type List instead of List.Helper? I know you can do:
import List = Helper.List;
But is something like
import * from Helper;
possible?
That is only possible when you use external modules (which are recommended). However, you can do the following:
module Helper {
export class List<T> {
}
}
module Helper {
export class Parser {
}
}
module Data {
export interface DataRepository {
getRange() : Helper.List<string>;
}
}
Or
module Data {
type StringList = Helper.List<string>;
export interface DataRepository {
getRange() : StringList;
}
}

Typescript, cant load internal module

I m actually learning typescript, and I m facing some problems with internal modules.
In fact, I have three files :
index.ts in which I start my app
///<reference path='RouteManager.ts'/>
import RouteManager = RestifyRouting.RouteManager;
var myManager = new RouteManager();
myManager.init("superpath");
RouteManager.ts that manage my REST routes
///<reference path='RouteParser.ts'/>
module RestifyRouting {
export class RouteManager {
routeParser:RouteParser;
constructor() {
}
public init(filePath) {
this.routeParser = new RouteParser();
this.routeParser.register("zfaf","callback");
console.log(filePath);
}
}
}
RouteParser which has to parse some string to get some informations
module RestifyRouting {
export class RouteParser {
constructor() {
}
public register(path, callback) {
console.log('super register');
}
}
}
I have a gulp file that creates my .js and d.ts files and it works great, except for the index.js file. The compiler tells my that RestifyRouting (which is my internal module) is undefined and I dont know why...
Can you help me ?
PS : every files are in the same folder, it's just a learning application.
Thanks for advance
As of TypeScript 1.5 the module syntax is aligned with ES6 module syntax and that is what I have been using as well...
You can remove any references to TypeScript modules and just export the classes directly
index.ts
import { RouteManager } from './RouteManager';
var myManager = new RouteManager();
myManager.init("superpath");
RouteManager.ts
import { RouteParser } from './RouteParser';
export class RouteManager {
routeParser:RouteParser;
constructor() {}
public init(filePath) {
this.routeParser = new RouteParser();
this.routeParser.register("zfaf","callback");
console.log(filePath);
}
}
RouteParser.ts
export class RouteParser {
constructor() {}
public register(path, callback) {
console.log('super register');
}
}
Keeping modules
If you'd like to keep using internal modules then you have to be sure to export your module as well as the classes inside the module.
// RouteManager.ts
export module RestifyRouting {
export class RouteManager{}
}
//index.ts
import { RestifyRouting } from './RouteManager';
//usage
var manager = new RestifyRouting.RouteManager();
Something to keep in mind is that you will not be able to import multiple items into the the same name.
// i.e.
import { RestifyRouting } from './RouteManager';
import { RestifyRouting } from './RouteParser';
NOTES
the {} syntax in the import statement can allow multiple imports
{ Class1, Class2 }
The {} can be skipped if you exporting a default:
//Source (foo.ts):
export default class Foo{}
//Reference:
import Foo from './foo';
//usage:
class User {
foo: Foo;
}

Categories

Resources