usage of JS plugins with webpack - jquery-plugins

I am trying to reconfigure my Symfony 3 project to use webpack encore instead of normal asstets. I am a little stuck with this.
For one part of the application I developed some JS plugins which inherits from Kube (a CSS and JS Framework I am unsing). Here is an exmaple:
(function (Kube) {
Kube.Assignment = function (element, options) {
this.namespace = 'assignment';
this.defaults = {
//...
};
Kube.apply(this, arguments);
this.start();
};
Kube.Assignment.prototype = {
start: function () {
//...
}
};
Kube.Assignment.inherits(Kube.Distribution);
Kube.Plugin.create('Assignment');
Kube.Plugin.autoload('Assignment');
}(Kube));
Unfortunatly Kube is not recognized.
The module 'imperavi-kube' is installed via yarn and is imported via
let Kube = require('imperavi-kube');
I am getting the following error: TypeError: Kube.Assignment.inherits is not a function
Propably this is not a issue of the Kube Framework but somthing about handeling JS plugins with webpack.
Inside the Kube module there is a class 'Kube' defined with a prototype. The Prototype ends with window.Kube = Kube;

Try to import Kube like this:
import * as Kube from <your path> or <alias> or <module name>
I may be wrong, but as far as I remember sometimes it works a bit differently.

Related

Client-only Nuxt 3 D3 plugin

I'm trying to use the D3 extension in a Nuxt 3 project and for that I created a d3.client.js file in the plugins/ directory.
import * as d3 from "d3";
import { defineNuxtPlugin } from '#app'
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.use(d3)
})
However, when I try to use it gives me a 500 Internal Server Error document is not defined.
<script>
import * as d3 from "d3";
export default {
name: "globe",
created() {
d3.select("#globe");
}
}
</script>
How can I solve this?
d3.select() uses document.querySelector() under the hood. Since you're working server side, you don't have access to document yet. So you'll need to mock it or to avoid using it.
You can avoid using it all together by passing an element instead of a string to d3.select(), as it will then create a functioning d3 selection without running document.querySelector(). And since every other chained .select() or .selectAll() uses previousSelection.querySelector(), you can just continue from there.
If you do not have access to the DOM element directly, you might want to mock document. This article suggests using JSDOM:
import { JSDOM } from 'jsdom';
// create a new JSDOM instance for d3-selection to use
const document = new JSDOM().window.document;
d3.select(document.body)
.append('div');
I managed to solve it by using the d3.select with a Vue reference.
const globe = d3.select(this.$refs.globe)

How to Use nativescript-autocomplete plugin with nativescript angular?

I am not able to make plugin work with angular project template .GitHub shows only code in native and XML .Sample plugin code works but unfortunately no angular support or help given. I am not able show on angular template.
relevant code i am using
detail.component.ts
registerElement("AutoComplete", () => require("nativescript-autocomplete").AutoComplete);
public list :Array = ['1','2','3','4','567'] ;
public itemTapped(args){
console.log("tapped");
}
detail.component.html
<AutoComplete items=""{{list}}"" itemTap="itemTapped($event)"> </AutoComplete>
i am getting exception on console while page loads and autocompletion doesnt work
this.items.forEach is not a function inside plugin code .that line is with definition of AutoComplete.prototype.itemsUpdate inside autocomplete.android.js plugin source
Debugging into plugin source it breaks at initialization time :
'AutoComplete.prototype.itemsUpdate = function (items) {
var arr = Array.create(java.lang.String, this.items.length);
this.items.forEach(function (item, index) {
arr[index] = item;
});
var ad = new android.widget.ArrayAdapter(app.android.context, android.R.layout.simple_list_item_1, arr);
this._android.setAdapter(ad);
};'
In detail.component.html
<AutoComplete [items]="list" (itemTap)="itemTapped($event)"> </AutoComplete>
in details.component.ts add
public list:any= ['1','2','3','4','567'] ;
itemTapped(ev){
//console.log(ev); your code
}
Issue in npm version. Clone the repository.
Replace all the files in node_modules/nativescript-autocomplete ,expect screenshot, demo folders and git related files. And try the solution

Jasmine Unit testing define library that was imported as a global variable

I have a project that uses pdfMake to generate a PDF. To use it I include the file in my index.html
<script src='js/pdfmake.js'></script>
<script src='js/vfs_fonts.js'></script>
Inside pdfmake.js it declares global["pdfMake"] which then allows me to use the library in my service.
pdfService:
pdfMake.createPdf(docDefinition).download(fileName);
Everything works great but when I tried to test ths method in my service I get an error that the test can't find the variable pdfMake. That makes sense considering it's loaded by index.html.
How can I replace this library with a mock in my test?
I've tried using a spy but since makePdf isn't a function that doesn't work. spyOn(service, 'makePdf').
I tried just setting it as a variable but that also didn't work and I get: Strict mode forbids implicit creation of global property 'pdfMake'
pdfMake = {
createPdf: jasmine.createSpy('createPdf').and.returnValue({
download: jasmine.createSpy('download')
}
}
I got the same problem and solved inserting the pdfMake mock on global variable window inside the unit test. So, in your case will be something like this:
window.pdfMake = {
createPdf: jasmine.createSpy('createPdf')
.and.returnValue({
download: jasmine.createSpy('download')
}),
};
I just fixed this issue by making below changes-
Declare pdfMake variable globally in your .ts file like-
declare var pdfMake;
And then mock the pdfMake function in your .spec file like this-
window['pdfMake'] = {
createPdf: function (param) {
return {
open: function () {
return true;
},
download: function () {
return true;
}
};
}
};

Problems with defining modules using AMD in Mocha

While writing tests I got bug TypeError: $.extend is not a function on toastr plugin that we are using. It seems that jQuery is not picked up properly, even tho is working normally in browser.
In our main mock file we imported jQuery and bind it to global windows object and it's accessible across whole application (but toastr plugin), even while testing in mocha:
import jsdom from 'jsdom';
import $ from 'jquery';
import chai from 'chai';
import chaiImmutable from 'chai-immutable';
import React from 'react';
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>');
const win = doc.defaultView;
global.document = doc;
global.window = win;
global.expect = chai.expect;
global.$ = $(win);
global.jquery = $(win);
global.jQuery = $(win);
Object.keys(window).forEach((key) => {
if (!(key in global)) {
global[key] = window[key];
}
});
chai.use(chaiImmutable);
So while taking closer look at toastr I noticed this:
; (function (define) {
define(['jquery'], function ($) {
// this function is called on inizialization
function getOptions() {
return $.extend({}, getDefaults(), toastr.options);
}
It takes jquery from node_modules directly and then defines object $ in function scope, that means that it's ignoring window.$ (which is working normally even in here).
Therefore logging $ will return function, and logging $.anyMethodFromjQuery ($.extend) will return undefined.
In the end I tried logging $.prototype, in the browser it will return me jQuery object while in mocha it returns empty object {}
So in the end it define didn't created prototype in mocha environment and I cannot add one line of code $ = window.$; in plugin, since no one should edit a plugin + we are using npm.
Is there any solution for this?
You're running into trouble because you are loading code that should be loaded by JSDom outside of it. While it is possible in some cases to load code in Node and then pass it to the window that JSDom creates, that's a brittle approach, as you've discovered. Whenever I use JSDom for things other than write-and-toss cases, I load every script that would normally be loaded by a browser through JSDom. This avoids running into the issue you ran into. Here's a proof-of-concept based on the code you've shown in the question. You'll see that toastr.getContainer() runs fine, because Toastr has been loaded by JSDom. If you try to use the same code with an import toastr from "toastr" you'll get the same problem you ran into.
import jsdom from 'jsdom';
import $ from 'jquery';
import path from "path";
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>', {
features: {
FetchExternalResources: ["script"],
ProcessExternalResources: ["script"]
}
});
const win = doc.defaultView;
global.document = doc;
global.window = win;
global.$ = $(win);
global.jquery = $(win);
global.jQuery = $(win);
window.addEventListener("error", function () {
console.log("ERROR");
});
const script = document.createElement("script");
script.src = path.join(__dirname, "./node_modules/toastr/toastr.js");
document.head.appendChild(script);
// The load will necessarily be asynchronous, so we have to wait for it.
script.addEventListener("load", function () {
console.log("LOADED");
console.log(window.toastr);
// We do this here so that `toastr` is also picked up.
Object.keys(window).forEach((key) => {
if (!(key in global)) {
global[key] = window[key];
}
});
toastr.getContainer();
});
Note that the code hung when I tried calling toastr.info(...). I took a look at the code of Toastr but it is not clear to me what causes the problem. There are features of browsers that JSDom is unable to emulate. It is possible that Toastr is dependent on such features. In the past, I've sometimes had to switch test suites away from JSDom due to its limitations.

TypeScript dynamic loading of AMD module ends in "Could not find symbol '...'

No, this topic won't answer my question and NO, the solution is not simply importing Command in the nav.ts file. nav.ts is one of many viewModel-files and they will be loaded dynamically on demand. The only problem is to set the parameter's type in the constructor of the class. (Type has to be "Command")
In the following class, which will be loaded by require.js, the method viewModel() requires a new class dynamically. In this case NavViewModel .
command.ts
export class Command {
...
public viewModel(name: string, callback: Function) {
require(["noext!boot/getViewModel/" + name], function (viewModel) {
callback(viewModel);
});
}
}
This is the class which will be fetched by viewModel():
nav.ts
export class NavViewModel extends kendo.Router {
constructor(command: Command) {
super();
this.route('/:name', function (name) {
command.view(name, $('div.content'));
});
this.start();
}
}
EDIT:
Here is the entry-point (requested in comment 2)
main.ts (EntryPoint)
import lib = require("command");
var cmd = new lib.Command();
cmd.viewModel('nav', function (o) {
cmd.view('nav', $('div.header'), function () {
kendo.bind($('.header .nav'), new o.NavViewModel(cmd));
});
});
/EDIT
The Problem:
Visual Studio will throw the error TS2095: Could not find symbol 'Command', because the "Command" class ist not defined in this Module.
The program works fine if the "Command"-Type will be removed from the NavViewModel constructor. Is there any solution to reference the Command class in the NavViewModel?
This won't work:
/// <reference path="../../Scripts/command.ts" />
When using RequireJS, the import statement should be the full path from the root of the application.
I also use a slightly different export syntax
command.ts
class command {
...
}
export = command;
main.ts
// I'm assuming the Scripts folder is at the root of the application
import Command = require('Scripts/command');
var cmd = new Command();
Note
I'm using Typescript 0.9.1.1. I can't upgrade my machine to 0.9.5 as a large internal application is affected by some breaking changes between versions

Resources