I'm trying to make a generator that I can then extend to form similar sub-generators
Here's my base generator:
var generators = require('yeoman-generator');
var VolumeAdderBase = module.exports = generators.Base.extend({
initializing: function() {
/* ... */
},
prompting: function () {
/* ... */
},
writing: function () {
/* ... */
}
});
Here's an example sub-generator:
var VolumeAdderBase = require('../../utils/VolumeAdderBase.js');
// console.log(VolumeAdderBase);
module.exports = VolumeAdderBase.extend({
fileType: "tomcat script",
containerName: "tomcat",
containerVolumeLocation: "/opt/tomcat/client-conf/"
});
When I try to run my sub-generator, it does nothing at all. No errors, no nothing.
When I dump the VolumeAdderBase object, there are plenty of methods on there, but they are all the Base ones. The ones defined in VolumeAdderBase are missing.
Am I missing something here? Or is there a better way to create similar sub-generators?
yeoman-generator is only going to run top level methods (Object.getOwnPropertyNames(prototype)). It doesn't go deeper in the prototype; that's by design. If Yeoman was to dig in the prototype, you couldn't use methods like this.destinationPath() or any other helpers as they'd all be schedule to be run - which would just break.
We have plans to support mixin in the future. But that's not currently the case.
As a fix, you can manually assign these methods:
VolumeAdderBase.prototype.prompting = VolumeAdderBase.prototype.prompting;
// etc...
Related
I want module sync-fetch to be accessible globally without need to import in each component and be named as simple fetch.
Also I want to extend it with custom method then.
Now in rollup.config.js there are:
export default {
...
output: {
...
intro: `const fetch = require('sync-fetch');
fetch.Response.prototype.then = function(foo) {
return foo(this);
}`
},
};
And it works, but looks dangerous) Is intro is the only way to do it?
If you want to make it seem less dangerous, you could put that code in a file and then return the contents of it in a function. The output.intro option also takes a function that returns the code as a string.
{
output: {
intro: () => require('fs/promises').readFile('path/to/the/file.js', 'utf-8')
}
}
This question is about a Kotlin JS project which uses the Kotlin Frontend Plugin.
I want to use some UI components from the Vaadin Components library.
I have two questions about this:
(1) What would be the best way to include web components in Kotlin JS
=> for my complete code, see the link to the source below. In summary the relevant details are:
build.gradle.kts
kotlinFrontend {
npm {
dependency("#vaadin/vaadin-grid")
}
}
vaadin.grid.Imports.kt
#file:JsModule("#vaadin/vaadin-grid")
#file:JsNonModule
package vaadin.grid
external class GridElement {
companion object
}
Why the companion object? I need it for the workaround (see below).
foo.kt
fun main() {
document.getElementById("container")!!.append {
vaadin_grid {
attributes["id"] = "grid"
}
}
initUI()
}
fun initUI() {
// Force the side-effects of the vaadin modules. Is there a better way?
console.log(GridElement)
val grid = document.querySelector("#grid") /* ?? as GridElement ?? */
}
The console.log is the ugly workaround trick I want to avoid. If I don't do anything with GridElement then it's just not included in my bundle.
The vaadin_grid DSL is defined as a custom kotlinx.html tag which is unrelated code.
(2) I want to keep my code as typed as possible to avoid asDynamic but when I cast the HTMLElement to a Vaadin Element I get ClassCastExceptions (because GridElement is undefined).
For example I want to write something like this:
val grid : GridElement = document.querySelector("#grid") as GridElement
grid.items = ... // vs grid.asDynamic().items which does work
Here is how I define the external GridElement
vaadin/button/Imports.kt
#file:JsModule("#vaadin/vaadin-grid")
#file:JsNonModule
package vaadin.grid
import org.w3c.dom.HTMLElement
abstract external class GridElement : HTMLElement {
var items: Array<*> = definedExternally
}
build/node_modules/#vaadin/vaadin-grid/src/vaadin-grid.js
...
customElements.define(GridElement.is, GridElement);
export { GridElement };
Source example
To run:
From the root of the git repo:
./gradlew 05-kt-frontend-vaadin:build && open 05-kt-frontend-vaadin/frontend.html
I found the answer(s)
For the first question
(1) What would be the best way to include web components in Kotlin JS
Instead of the console.log to trigger the side effects I use require(...)
external fun require(module: String): dynamic
fun main() {
require("#vaadin/vaadin-button")
require("#vaadin/vaadin-text-field")
require("#vaadin/vaadin-grid")
...
}
(credits to someone's answer on the kotlin-frontend-plugin list)
(2) I want to keep my code as typed as possible to avoid asDynamic
Instead of importing #vaadin/vaadin-grid I have to import the file which actually exposes the element. Then it seems to work and I can even add generics to my GridElement:
#file:JsModule("#vaadin/vaadin-grid/src/vaadin-grid")
#file:JsNonModule
package vaadin.grid
import org.w3c.dom.HTMLElement
abstract external class GridElement<T> : HTMLElement {
var items: Array<out T> = definedExternally
}
This way I was able to get rid of all the asDynamics
val firstNameField = document.querySelector("#firstName") as TextFieldElement?
val lastNameField = document.querySelector("#lastName") as TextFieldElement?
val addButton = document.querySelector("#addButton") as ButtonElement?
val grid = document.querySelector("#grid") as GridElement<Person>?
val initialPeople: Array<out Person> = emptyArray()
grid?.items = initialPeople
addButton?.addEventListener("click", {
// Read the new person's data
val person = Person(firstNameField?.value, lastNameField?.value)
// Add it to the items
if(grid != null){
val people = grid.items
grid.items = people.plus(person)
}
// Reset the form fields
firstNameField?.value = ""
lastNameField?.value = ""
})
I can't understand how to use the createDummyGenerator() function when testing a generator that relies on external generators.
I have tried:
test.js:
...
return helpers.run(require.resolve('../generators/app'))
.withGenerators([
[helpers.createDummyGenerator(), 'license:app'],
])
.then(() => {
assert.textEqual('true', 'true')
});
...
index.js:
...
default() {
this.composeWith('license:app', { name: 'foo' });
}
...
This makes the test fail because it can't find a generator for license:app. I have generator-license in my package.json as a dependency.
I also tried the following:
test.js:
...
beforeEach(() => {
jest.mock('generator-license/app', () => {
const helpers = require('yeoman-test');
return helpers.createDummyGenerator();
});
}
...
index.js:
...
default() {
this.composeWith(require.resolve('generator-license/app', { name: 'foo' }));
}
...
This doesn't mock the generator at all, and it uses the actual generator-license code, which makes the test fail because not all prompts are supplied (some are meant to be asked by the license generator)
How am I supposed to use the createDummyGenerator() helper to completely stub out the license generator?
Well, I feel like an idiot... I had a typo in another test that didn't mock the module, and that's what was making the test suite fail... Nevermind, nothing to see here :)
Perhaps this is the wrong approach to my problem, but that is why I'm here. In the code below is a sample of a JavaScript module pattern with sub-modules. As I build this, I realize that some sub-modules need to "call" each other's methods.
I know that it would be wrong to use the full call admin.subModuleB.publicB_2(); but its the only way since the IIFE functions cannot call "themselves" until instatiated, ex. "module" is not available in the primary namespace, etc...
My thought is that this pattern is incorrect for my situation. The purpose of the module encapsulation is to keep things private unless reveled. So what would be a better pattern?
var module = (function($, window, document, undefined) {
return {
subModuleA : (function() {
var privateA = 100;
return {
// We have access to privateA
publicA_1 : function() {
console.log(privateA);
// How do I use a method from publicB_1
// the only way is:
module.subModuleB.publicB_2();
// but I don't want to use "module"
},
publicA_2 : function() {
console.log(privateA);
}
}
})(),
subModuleB : (function() {
var privateB = 250;
return {
// We have access to privateB
publicB_1 : function() {
console.log(privateB);
},
publicB_2 : function() {
console.log(privateB);
// I have access to publicB_1
this.publicB_1();
}
}
})()
}
})(jQuery, window, document);
What you actually have is an issue with dependencies. Sub module A has a dependency on Sub module B. There are two solutions that come to mind.
Define both modules as their own variables inside the function closure, but return them together in a single object.
What you actually want is instantiable classes where Class A has a dependency on Class B.
Since solution #1 is the closest to your current code, let's explore that first.
Define Both Modules Separately Inside the Closure
var module = (function($, window, document, undefined) {
var SubModuleA = function() {
var privateA = 100;
return {
// We have access to privateA
publicA_1 : function() {
console.log(privateA);
// Refer to SubModuleB via the private reference inside your "namespace"
SubModuleB.publicB_2();
// but I don't want to use "module"
},
publicA_2 : function() {
console.log(privateA);
}
};
}();
var SubModuleB = function() {
var privateB = 250;
return {
// We have access to privateB
publicB_1 : function() {
console.log(privateB);
},
publicB_2 : function() {
console.log(privateB);
// I have access to publicB_1
this.publicB_1();
}
};
}();
// Return your module with two sub modules
return {
subModuleA : SubModuleA,
subModuleB : SubModuleB
};
})(jQuery, window, document);
This allows you to refer to your two sub modules using local variables to your module's closure (SubModuleA and SubModuleB). The global context can still refer to them as module.subModuleA and module.subModuleB.
If Sub Module A uses Sub Module B, it begs the question of whether or not Sub Module B needs to be revealed to the global context at all.
To be honest, this is breaking encapsulation because not all the functionality of Sub Module A exists in Sub Module A. In fact, Sub Module A cannot function correctly without Sub Module B.
Given your particular case, the Module Pattern seems to be an Anti Pattern, that is, you are using the wrong tool for the job. In reality, you have two classifications of objects that are interdependent. I would argue that you need "classes" (JavaScript Constructor functions) and traditional OOP practices.
Use JavaScript Constructor Functions ("classes")
First, let's refactor your "module" into two classes:
var module = (function($, window, document, undefined) {
function ClassA(objectB) {
var privateA = 100;
this.publicA_1 = function() {
console.log(privateA);
objectB.publicB_2();
};
this.publicA_2 = function() {
console.log(privateA);
};
}
function ClassB() {
var privateB = 250;
this.publicB_1 = function() {
console.log(privateB);
};
this.publicB_2 = function() {
console.log(privateB);
this.publicB_1();
};
}
// Return your module with two "classes"
return {
ClassA: ClassA,
ClassB: ClassB
};
})(jQuery, window, document);
Now in order to use these classes, you need some code to generate the objects from the constructor functions:
var objectA = new module.ClassA(new module.ClassB());
objectA.publicA_1();
objectA.publicA_2();
This maximizes code reuse, and because you are passing an instance of module.ClassB into the constructor of module.ClassA, you are decoupling those classes from one another. If you don't want outside code to be managing dependencies, you can always tweak ClassA thusly:
function ClassA() {
var privateA = 100,
objectB = new ClassB();
this.publicA_1 = function() {
console.log(privateA);
objectB.publicB_2();
};
this.publicA_2 = function() {
console.log(privateA);
};
}
Now you can refer to module.ClassB using the name within the function closure: ClassB. The advantage here is that outside code does not have to give module.ClassA all of its dependencies, but the disadvantage is that you still have ClassA and ClassB coupled to one another.
Again, this begs the question of whether or not the global context needs ClassB revealed to it.
I have been studying John Papa's pluralsight course on SPA.
In his main.js, he gave a name to each js library which is included in the bundle.
(function () {
var root = this;
define3rdPartyModules();
function define3rdPartyModules() {
// These are already loaded via bundles.
// We define them and put them in the root object.
define('jquery', [], function () { return root.jQuery; });
define('ko', [], function () { return root.ko; });
define('amplify', [], function () { return root.amplify; });
define('infuser', [], function () { return root.infuser; });
define('moment', [], function () { return root.moment; });
define('sammy', [], function () { return root.Sammy; });
define('toastr', [], function () { return root.toastr; });
define('underscore', [], function () { return root._; });
}
})();
But what is the root here?
By doing so, we can call those short names in the define statement:
define('vm.session',
['ko', 'datacontext', 'config', 'router', 'messenger', 'sort'],
function (ko, datacontext, config, router, messenger, sort) {
Current, I don't know how to do that. So my working define statement is ugly:
define('vm.admin.outfitters',
['/Scripts/lib/jquery-1.8.1.js', '/Scripts/lib/jsrender.js', ...], function(){...
I know there's gotta be a better way. All those js files have been included in the script bundle already. How can I reference those scripts?
RE: root
RequireJS and the AMD ready libraries remove the objects from the global scope (things like ko). Some plugins want them in global scope, so we can either shim those plugins or pop the objects back in global scope. The latter is what is happening in this code. It's being done for the plugins for Knockout primarily.
RE: your define statements
The first parameter is the name of of the module, so you are fine there. THe second parameter is the list of modules that RequireJS is aware of. The 3rd parameter is the matching variable to represent it. So in your code you might have something like this ...
define('vm.admin.outfitters',
['jquery', 'jsrender'], function($, jsrender) {