I like to implement like a namespacing something like this as example:
const { SMS } = require('./custom-sdk')
const list = SMS.List();
let data = list.getData("ABC");
console.log(data)
I am completely stuck how to implement this, what do I need to do design this kind of API methods.
I have tried like this would which would be in custom-sdk.js file:
module.exports = {
SMS: function() {
// ...
}
};
Would something like the following nesting work?
module.exports = {
SMS: {
List: function() {
return {
getData: function(arg) {
// get that data
}
}
}
}
};
Which I think would allow you to do SMS.List().getData('ABC'). That said, this seems overly nested, unless you just simplified it for the SO question. I would suggest to only use functions when necessary (to take an argument or to instantiate a service) and prefer just a plain object when possible:
module.exports = {
SMS: {
List: {
getData: function(arg) {
// get that data
}
}
}
};
Related
I know it's a duplicate but I don't understand the other posts: I'm doing an exercise on freeCodeCamp that I don't understand, it's about modules: What's the advantage of doing this:
const motionModule = (function() {
return {
isCuteMixin: function(obj) {
obj.isCute = function() {
return true;
};
},
singMixin: function(obj) {
obj.sing = function() {
console.log("Singing to an awesome tune");
};
}
};
})();
instead of this:
const motionModule = {
isCuteMixin: function(obj) {
obj.isCute = function() {
return true;
};
},
singMixin: function(obj) {
obj.sing = function() {
console.log("Singing to an awesome tune");
};
}
};
One advantage is you can emulate private variables and methods which are not accessible from outside the returned object. This helps keeping the data and functionality together and avoids corruption of global namespace.
const motionModule = (function() {
let song = 'My song'; // private variable
function singTheSong() {
// private method
}
return {
isCuteMixin: function(obj) {
obj.isCute = function() {
return true;
};
},
singMixin: function(obj) {
obj.sing = function() {
console.log("Singing to an awesome tune" + song);
singTheSong();
};
}
};
})();
// cannot call singTheSong from here
Within a module, you would often want the methods to be able to access each other and shared variables. To do this in your 2nd example, you need to attach them to the object and access them via the this keyword, and also (within the mixin creating functions) use arrow functions to ensure this refers to the right object.
const motionModule = {
song: "La La La",
sing: function() {
console.log(this.song);
},
singMixin: function(obj) {
obj.sing = () => {
console.log(`Singing ${this.song}`);
};
}
};
const a = {};
motionModule.sing();
motionModule.singMixin(a);
a.sing();
Modern ES6 class declarations also require you to work in this way.
class MotionModule {
song = "La La La";
sing() {
console.log(this.song);
}
singMixin(obj) {
obj.sing = () => {
console.log(`Singing ${this.song}`);
};
}
}
const motionModule = new MotionModule();
motionModule.sing();
const a = {};
motionModule.singMixin(a);
a.sing();
As shown in another answer, the first example (an immediately invoked function expression) allows you to access other variables and methods defined within the module without using this, and gives you greater control over which methods and variables are accessible from outside the module.
Does anyone know how to use the javascript-obfuscator (or similar) in Ember ?
I guess it needs to be called inside ember-cli-build.js but I don't know where and how.
Thank you in advance for any help :)
I don't think there's a really straightforward answer to this. If you're running on embroider, then all your Javascript will be handled by webpack, so you could try using webpack-obfuscator -- in your ember-cli-build.js something like
return require('#embroider/compat').compatBuild(app, Webpack, {
plugins: [
new WebpackObfuscator(/*whatever args*/)
],
rules: [
{
test: /\.js$/,
enforce: 'post',
use: {
loader: WebpackObfuscator.loader,
}
}
]
});
The other options I know of would be to write a broccoli plugin. What you're doing is analogous to what ember-cli-terser does -- post-process Javascript files before they get concatenated together -- so you could use that as reference.
You would need to write a broccoli plugin that actually performs the transformations (the analog is broccoli-terser-sourcemap) and an Ember addon to hook it into ember-cli's build pipeline (the analog is ember-cli-terser).
Broccoli plugin
Looking at broccoli-terser-sourcemap's index.js, which is only 114 lines of code, I would think you could adapt it to something like this:
module.exports = class TerserWriter extends Plugin {
constructor(_inputNodes, options = {}) {
let inputNodes = Array.isArray(_inputNodes) ? _inputNodes : [_inputNodes];
super(inputNodes, {
name: options.name,
annotation: options.annotation,
needsCache: false,
});
this.options = defaults(options, {
obfuscator: {},
});
let exclude = this.options.exclude;
if (Array.isArray(exclude)) {
this.excludes = new MatcherCollection(exclude);
} else {
this.excludes = MatchNothing;
}
}
async build() {
let pendingWork = [];
this.inputPaths.forEach(inputPath => {
walkSync(inputPath).forEach(relativePath => {
if (relativePath.slice(-1) === '/') {
return;
}
let inFile = path.join(inputPath, relativePath);
let outFile = path.join(this.outputPath, relativePath);
fs.mkdirSync(path.dirname(outFile), { recursive: true });
if (this._isJSExt(relativePath) && !this.excludes.match(relativePath)) {
pendingWork.push(() => this.processFile(inFile, outFile, relativePath, this.outputPath));
} else {
symlinkOrCopy.sync(inFile, outFile);
}
});
});
for (let fn of pendingWork) {
await fn();
}
}
_isJSExt(relativePath) {
return relativePath.slice(-3) === '.js' || relativePath.slice(-4) === '.mjs';
}
async processFile(inFile, outFile, relativePath, outDir) {
let input = await readFile(inFile).toString();
let result = obfuscate(input, this.options.obfuscator);
await writeFile(outFile, result.getObfuscatedCode());
}
};
You could also do the worker pooling this that broccoli-terser-sourcemaps does, and if you care about source maps you'd need to handle them as well, but broccoli-terser-sourcemaps does just that, so you could use it as reference.
ember-cli addon
ember-cli-terser has even less code -- looking at its index.js, you could adapt it to something like
'use strict';
module.exports = {
name: require('./package').name,
included(app) {
this._super.included.apply(this, arguments);
let defaultOptions = {
enabled: app.env === 'production',
obfuscator: {
// default `javascript-obfuscator` options
},
};
let addonOptions = app.options['ember-cli-obfuscator'];
this._obfuscatorOptions = Object.assign({}, defaultOptions, addonOptions);
},
postprocessTree(type, tree) {
if (this._obfuscatorOptions.enabled === true && type === 'all') {
// Import the plugin code above
const Obfuscator = require('./broccoli-obfuscator');
return new Obfuscator(tree, this._obfuscatorOptions);
} else {
return tree;
}
}
};
Then you'd have to install the above addon in your app (it could be an in-repo addon), and it should do its thing!
This would definitely take some doing, but what you're doing is so similar to what ember-cli-terser is doing, just using the obfuscator API instead of the terser API, that you have a really good starting point.
BUT, if embroider is an option for you, I'd definitely try that route first because it might just be a matter of configuration, rather than writing a bunch of code.
I don't really know how to describe this, but I'll try explain it.
I want to be able to call func1() and func2(), but going through handler() in a module.
I want it in a way where calling module.exported1("foo") will call handler(func1, "foo"), in turn calling func1("foo"). The issue I'm having is that if I export 'exported1' as handler(func1), I can't pass any arguments exported1 was called with (As far as I know). Is there a workaround for this?
NOTE: It is a module, and I need it to be exported without the user needing to provide func1 and func2 to handler().
function func1(args) {
...
}
function func2(args) {
...
}
function handler(func, args) {
return func()
}
module.exports = {
exported1 = handler(func1, ...),
exported2 = handler(func2, ...)
}
Not sure I get why to use this pattern, but I am sure there is more to the code and guess you could do the following:
function func1(args) {
console.info(`func1 ${args}`);
}
function func2(args) {
console.info(`func2 ${args}`);
}
function handler(func, args) {
return func(args);
}
module.exports = {
exported1: (args) => {
return handler(func1, (args));
},
exported2: (args) => {
return handler(func2, (args));
},
};
You just need to export the function:
module.exports = {
exported = handler
}
Or, just:
exports.exported = handler
Now, after import, you can call with parameters:
exported(func1,...)
exported(func2,...)
After reading your edited question, I think you want to do something like this but I'm not pretty sure:
function handler(func) {
// you can replace it with function(args) { instead of arrow function
return (args) => {
return func(args)
}
}
module.exports = {
exported1 = handler(func1),
exported2 = handler(func2)
}
exported1(args)
I'm new to VueJs and currently trying to load some data only once and make it globally available to all vue components. What would be the best way to achieve this?
I'm a little bit stuck because the global variables occasionally seem to become null and I can't figure out why.
In my main.js I make three global Vue instance variables:
let globalData = new Vue({
data: {
$serviceDiscoveryUrl: 'http://localhost:40000/api/v1',
$serviceCollection: null,
$clientConfiguration: null
}
});
Vue.mixin({
computed: {
$serviceDiscoveryUrl: {
get: function () { return globalData.$data.$serviceDiscoveryUrl },
set: function (newUrl) { globalData.$data.$serviceDiscoveryUrl = newUrl; }
},
$serviceCollection: {
get: function () { return globalData.$data.$serviceCollection },
set: function (newCollection) { globalData.$data.$serviceCollection = newCollection; }
},
$clientConfiguration: {
get: function () { return globalData.$data.$clientConfiguration },
set: function (newConfiguration) { globalData.$data.$clientConfiguration = newConfiguration; }
}
}
})
and in my App.vue component I load all the data:
<script>
export default {
name: 'app',
data: function () {
return {
isLoading: true,
isError: false
};
},
methods: {
loadAllData: function () {
this.$axios.get(this.$serviceDiscoveryUrl)
.then(
response => {
this.$serviceCollection = response.data;
let configurationService = this.$serviceCollection.services.find(obj => obj.key == "ProcessConfigurationService");
this.$axios.get(configurationService.address + "/api/v1/clientConfiguration").then(
response2 => {
this.$clientConfiguration = response2.data;
}
);
this.isLoading = false;
})
}
},
created: function m() {
this.loadAllData();
}
}
</script>
But when I try to access the $clientConfiguration it seems to be null from time to time and I can't figure out why. For example when I try to build the navigation sidebar:
beforeMount: function () {
let $ = JQuery;
let clients = [];
if (this.$clientConfiguration === null)
console.error("client config is <null>");
$.each(this.$clientConfiguration, function (key, clientValue) {
let processes = [];
$.each(clientValue.processConfigurations, function (k, processValue) {
processes.push(
{
name: processValue.name,
url: '/process/' + processValue.id,
icon: 'fal fa-project-diagram'
});
});
clients.push(
{
name: clientValue.name,
url: '/client/' + clientValue.id,
icon: 'fal fa-building',
children: processes
});
});
this.nav.find(obj => obj.name == 'Processes').children = clients;
The most likely cause is that the null is just the initial value. Loading the data is asynchronous so you'll need to wait for loading to finish before trying to create any components that rely on that data.
You have an isLoading flag, which I would guess is your attempt to wait for loading to complete before showing any components (maybe via a suitable v-if). However, it currently only waits for the first request and not the second. So this:
this.$axios.get(configurationService.address + "/api/v1/clientConfiguration").then(
response2 => {
this.$clientConfiguration = response2.data;
}
);
this.isLoading = false;
would need to be:
this.$axios.get(configurationService.address + "/api/v1/clientConfiguration").then(
response2 => {
this.$clientConfiguration = response2.data;
this.isLoading = false;
}
);
If it isn't that initial value that's the problem then you need to figure out what is setting it to null. That should be prety easy, just put a debugger statement in your setter:
$clientConfiguration: {
get: function () { return globalData.$data.$clientConfiguration },
set: function (newConfiguration) {
if (!newConfiguration) {
debugger;
}
globalData.$data.$clientConfiguration = newConfiguration;
}
}
Beyond the problem with the null, if you're using Vue 2.6+ I would suggest taking a look at Vue.observable, which is a simpler way of creating a reactive object than creating a new Vue instance.
Personally I would probably implement all of this by putting a reactive object on Vue.prototype rather than using a global mixin. That assumes that you even need the object to be reactive, if you don't then this is all somewhat more complicated than it needs to be.
Task: I want to create a dynamic class by a given JSON Object in ES6.
After a lot of reading in the MDN web docs and much stackoverflow questions i'm totally confused how to get this work.
JSON Object
{
constructor: {
name: "someName",
},
getter: {
function1: () => "someOutput",
function2: () => false,
}
}
While I tried to solve the problem I figured out how to create dynamic getter methods by using "Proxy" or "defineProperty" but how i should handle the constructor?? :(
I hope someone can help me with a hint or an example.
Thanks in advance
You can add constructor to your class created by Proxy using Proxy's "construct" handler method:
const jsonObj = {
constructor: {
name: "someName",
},
getter: {
function1: () => "someOutput",
function2: () => false,
}
}
function baseClass(obj) {
for(i in obj){
this[i] = obj[i]
}
}
const handler = {
construct(target, args) {
return new target(jsonObj.constructor);
}
};
const NewClass = new Proxy(baseClass, handler);