Closure Compiler renaming function in extern - google-closure-compiler

I'm trying to create an extern for Electron that gets used by the Google Closure Compiler. This is what my externs look like:
var electron = {};
electron.dialog = function() {};
electron.app = function() {};
electron.ipcRenderer = function() {};
electron.on = function() {};
electron.send = function() {};
electron.remote = function(){}; // {return{getGlobal: function(){}}};
electron.remote.getGlobal = function(a){};
electron.require = function() {};
electron.buildFromTemplate = function() {};
electron.popup = function() {};
electron.getCurrentWindow = function() {};
electron.showErrorBox = function() {};
electron.setTitle = function() {};
electron.setRepresentedFilename = function() {};
electron.showMessageBox = function() {};
electron.getPath = function() {};
electron.showSaveDialog = function() {};
electron.showOpenDialog = function() {};
var process = {
platform: {}
};
The problem I am having is that the compiler is not retaining the function name for the getGlobal function. My original source code looks like this:
const electron = require('electron');
electron.remote.getGlobal('sharedObject')
After running the compiler, it ends up looking like this:
var a = require("electron");
console.log(a.remote.D("sharedObject"));
a.remote.D should really be a.remote.getGlobal
How can I get the compiler to retain the getGlobal function name?

I changed my original source code to now look like this:
electron.remote'getGlobal'
The compiler will retain the name. This is because the compiler always keeps strings intact. The compile code now looks like this:
console.log(a.remote.getGlobal("sharedObject"));
It is still a mystery though why the compiler doesn't recognize the getGlobal function in the extern file. Maybe it can only handle functions that are nested one level deep from their root object (whereas here, it's two levels). Another reason may have to do with how the compiler carries out its multiphase compiling which is somewhat flawed in treating nested functions.

Related

Are there any issues in vue3 when using reactive function on data that also has methods (to get namespaced data and methods in template)?

Is it ok to use reactive function on data that also has methods. The reason for doing it is that otherwise I would have to use .value in template. My goal is to have nested (namespaced) method names in template as well. Would there be another way to get nested (namespaced) method and data names?
Excerpt from code (full example in codepen link below):
setup() {
const feature1 = function () {
const feature1Name = Vue.ref("feature1Default");
const feature1ChangeName = function () {
console.log("called", "feature1ChangeName");
feature1Name.value = "feature1Changed";
};
return { feature1Name, feature1ChangeName };
};
return { feature1: Vue.reactive(feature1()) }
}
https://codepen.io/realmerx/pen/abZqQYL?editors=1010
The functions are unnecessary, it is just a matter of formatting preference
instead of
const feature1 = function() {
const feature1Name = Vue.ref("feature1Default");
const feature1ChangeName = function() {
console.log("called", "feature1ChangeName");
feature1Name.value = "feature1Changed";
};
return { feature1Name, feature1ChangeName };
};
you could do
const feature1 = {
feature1Name: Vue.ref("feature1Default"),
feature1ChangeName: function() {
console.log("called", "feature1ChangeName");
feature1.feature1Name.value = "feature1Changed";
}
};
...but the important part in there seems to be that the ref is encapsulated in a reactive, which I suspect might add some overhead on the processing of reactivity.
You can get around that with a single reactive. You won't need to wrap it in additional reactivity or a function, and the code will work the same way (without any changes needed to the template). Note that the .value is missing from the assignment in the feature2ChangeName method.
const feature2 = Vue.reactive({
feature2Name: "feature2Default",
feature2ChangeName: () => {
feature2.feature2Name = "feature2Changed";
}
});
return {
feature2
};

JSC_POSSIBLE_INEXISTENT_PROPERTY Property never defined on error

How to solve this error?
===plugin.js
(function($) {
$.fn.myjqueryplugin = function(options) {};
$.fn.func1 = function() {
return this;
};
}(jQuery));
===base.js
$(document).ready(function() {
my_plugin = $("#myid").myjqueryplugin();
});
===hello.js
$(document).ready(function() {
my_plugin.func1();
});
===BUILD file
Properly define 3 js files with correct order.
Closure compiler error on hello.js
[JSC_POSSIBLE_INEXISTENT_PROPERTY] Property func1 never defined on *
How Closure compiler is able to find 'func1'?

gulpjs merge css and less

I'm trying to take my less files and convert them into css files, then take other css files and combine the two sets together to form one css file. I don't get any errors but the css file is never created. Is there a way to combine two different sets of css together to form one css file? Thanks in advance.
var gulp = require("gulp");
var gulpUtil = require("gulp-util");
var concat = require("gulp-concat");
var uglify = require("gulp-uglify");
var rename = require("gulp-rename");
var less = require("gulp-less");
var minify = require("gulp-minify-css");
var notify = require("gulp-notify");
var addsrc = require("gulp-add-src");
/*CSS Section*/
gulp.task("build-less", function () {
return gulp.src([
"Less/common.less",
"Less/box.less",
"Less/callout.less",
"Less/form-controls.less",
"Less/form.less",
"Less/scrollbar.less",
"Less/overflow-table.less",
"Less/button.less",
"Less/theme.less",
"Less/auto-complete.less"
]).pipe(less())
.pipe(addsrc([
"Css/bootstrap.css",
"Css/bootstrap-theme.css",
"Distribution/Styles/theme.css",
"Css/font-awesome.css",
"Css/font-awesome-animation.css",
"Css/animate.css",
"Css/bootstrap-select.css"
]))
.pipe(concat("dustball.min.css"))
.pipe(minify())
.pipe(gulp.dest("Destination/Styles"));
});
Just came across a plugin call merge2 and it worked. Below is the updated version.
var gulp = require("gulp");
var gulpUtil = require("gulp-util");
var concat = require("gulp-concat");
var uglify = require("gulp-uglify");
var rename = require("gulp-rename");
var less = require("gulp-less");
var minify = require("gulp-minify-css");
var notify = require("gulp-notify");
var merge = require("merge2");
/*CSS Section*/
gulp.task("build-less", function () {
var cssFiles = gulp.src([
"Css/bootstrap.css",
"Css/bootstrap-theme.css",
"Distribution/Styles/theme.css",
"Css/font-awesome.css",
"Css/font-awesome-animation.css",
"Css/animate.css",
"Css/bootstrap-select.css"
])
.pipe(concat("css-files.css"));
var lessCssFiles = gulp.src([
"Less/common.less",
"Less/box.less",
"Less/callout.less",
"Less/form-controls.less",
"Less/form.less",
"Less/scrollbar.less",
"Less/overflow-table.less",
"Less/button.less",
"Less/theme.less",
"Less/auto-complete.less"
])
.pipe(less())
.pipe(concat("less-files.css"));
return merge(cssFiles, lessCssFiles)
.pipe(concat("dustball.min.css"))
.pipe(minify())
.pipe(gulp.dest("Distribution/Styles"));
});

Browserify - multiple entry points

I am using Browserify within gulp. I am trying to compile down my tests to a single file as well. But unlike my main app, which I have working just fine, I am having trouble getting the tests to compile. The major difference is the tests have multiple entry points, there isn't one single entry point like that app. But I am getting errors fro Browserify that it can't find the entry point.
browserify = require 'browserify'
gulp = require 'gulp'
source = require 'vinyl-source-stream'
gulp.task 'tests', ->
browserify
entries: ['./app/js/**/*Spec.coffee']
extensions: ['.coffee']
.bundle
debug: true
.pipe source('specs.js')
.pipe gulp.dest('./specs/')
Below is a task I was able to build that seems to solve the problem. Basically I use an outside library to gather the files names as an array. And then pass that array as the entry points
'use strict;'
var config = require('../config');
var gulp = require('gulp');
var plumber = require('gulp-plumber');
var glob = require('glob');
var browserify = require('browserify');
var source = require('vinyl-source-stream');
gulp.task('tests', function(){
var testFiles = glob.sync('./spec/**/*.js');
return browserify({
entries: testFiles,
extensions: ['.jsx']
})
.bundle({debug: true})
.pipe(source('app.js'))
.pipe(plumber())
.pipe(gulp.dest(config.dest.development));
});
Here's an alternate recipe that fits more with the gulp paradigm using gulp.src()
var gulp = require('gulp');
var browserify = require('browserify');
var transform = require('vinyl-transform');
var concat = require('gulp-concat');
gulp.task('browserify', function () {
// use `vinyl-transform` to wrap around the regular ReadableStream returned by b.bundle();
// so that we can use it down a vinyl pipeline as a vinyl file object.
// `vinyl-transform` takes care of creating both streaming and buffered vinyl file objects.
var browserified = transform(function(filename) {
var b = browserify(filename, {
debug: true,
extensions: ['.coffee']
});
// you can now further configure/manipulate your bundle
// you can perform transforms, for e.g.: 'coffeeify'
// b.transform('coffeeify');
// or even use browserify plugins, for e.g. 'minifyiy'
// b.plugins('minifyify');
// consult browserify documentation at: https://github.com/substack/node-browserify#methods for more available APIs
return b.bundle();
});
return gulp.src(['./app/js/**/*Spec.coffee'])
.pipe(browserified)/
.pipe(concat('spec.js'))
.pipe(gulp.dest('./specs'));
});
gulp.task('default', ['browserify']);
For more details about how this work, this article that I wrote goes more in-depth: http://medium.com/#sogko/gulp-browserify-the-gulp-y-way-bb359b3f9623
For start, you can write a suite.js to require all the tests which you want to run and browserify them.
You can see two examples from my project https://github.com/mallim/sbangular.
One example for grunt-mocha-phantomjs
https://github.com/mallim/sbangular/blob/master/src/main/resources/js/suite.js
One example for protractor
https://github.com/mallim/sbangular/blob/master/src/main/resources/js/suite.js
This is just a start and I am sure there are more fancy ways available.
A little more complicated example to build files by glob pattern into many files with watching and rebuilding separated files. Not for .coffee, for es2015, but not a big difference:
var gulp = require("gulp");
var babelify = require("babelify");
var sourcemaps = require("gulp-sourcemaps");
var gutil = require("gulp-util");
var handleErrors = require("../utils/handleErrors.js");
var browserify = require("browserify");
var eventStream = require("event-stream");
var glob = require("glob");
var source = require("vinyl-source-stream");
var buffer = require("vinyl-buffer");
var watchify = require("watchify");
var SRC_PATH = "./src";
var BUILD_PATH = "./build";
var bundle = function (bundler, entryFilepath) {
console.log(`Build: ${entryFilepath}`);
return bundler.bundle()
.on("error", handleErrors)
.pipe(source(entryFilepath.replace(SRC_PATH, BUILD_PATH)))
.pipe(buffer())
.on("error", handleErrors)
.pipe(
process.env.TYPE === "development" ?
sourcemaps.init({loadMaps: true}) :
gutil.noop()
)
.on("error", handleErrors)
.pipe(
process.env.TYPE === "development" ?
sourcemaps.write() :
gutil.noop()
)
.on("error", handleErrors)
.pipe(gulp.dest("."))
.on("error", handleErrors);
};
var buildScripts = function (done, watch) {
glob(`${SRC_PATH}/**/[A-Z]*.js`, function (err, files) {
if (err) {
done(err);
}
var tasks = files.map(function (entryFilepath) {
var bundler = browserify({
entries: [entryFilepath],
debug: process.env.TYPE === "development",
plugin: watch ? [watchify] : undefined
})
.transform(
babelify,
{
presets: ["es2015"]
});
var build = bundle.bind(this, bundler, entryFilepath);
if (watch) {
bundler.on("update", build);
}
return build();
});
return eventStream
.merge(tasks)
.on("end", done);
});
};
gulp.task("scripts-build", function (done) {
buildScripts(done);
});
gulp.task("scripts-watch", function (done) {
buildScripts(done, true);
});
Complete code here https://github.com/BigBadAlien/browserify-multy-build

Meteor - How can I pass data between helpers and events for a template?

I'm a bit new to Meteor and something I'm having trouble with is reactive data -- particularly in instances where I need to change the data shown based on a mouse or keyboard event. Doing this kind of stuff the normal js way seems to give me trouble in meteor since everything I change gets re-rendered and reset constantly.
So, I thought I'd see if this would be a case in which I could use Meteor's Deps object, however I can't quite grasp it. Here's the code I'm using:
(function(){
var tenants = [];
var selectedTenant = 0;
var tenantsDep = new Deps.Dependency;
Template.tenantsBlock.tenantsList = function()
{
tenants = [];
var property = $properties.findOne({userId: Meteor.userId(), propertyId: Session.get('property')});
var tenancies = _Utils.resolveTenancies(property, true, null, true);
for(var i = 0; i < tenancies.length; i++)
{
if(tenancies[i].tenancyId == Session.get('tenancy'))
{
tenants = tenants.concat(tenancies[i].otherTenants, tenancies[i].primaryTenant);
}
}
tenants[selectedTenant].selected = 'Selected';
tenantsDep.changed();
return tenants;
};
Template.tenantsBlock.onlyOneTenant = function()
{
tenantsDep.depend();
return tenants.length > 1 ? '' : 'OneChild';
};
Template.tenantsBlock.phoneNumber = function()
{
tenantsDep.depend();
for(var i = 0; i < tenants[selectedTenant].details.length; i++)
if(_Utils.getDynamicContactIconClass(tenants[selectedTenant].details[i].key) == 'Phone')
return tenants[selectedTenant].details[i].value;
return null;
};
Template.tenantsBlock.emailAddress = function()
{
tenantsDep.depend();
for(var i = 0; i < tenants[selectedTenant].details.length; i++)
if(_Utils.getDynamicContactIconClass(tenants[selectedTenant].details[i].key) == 'Email')
return tenants[selectedTenant].details[i].value;
return null;
};
Template.tenantsBlock.addedDate = function()
{
tenantsDep.depend();
return _Utils.timeToDateString(tenants[selectedTenant].created);
};
Template.tenantsBlock.events({
'click .Name': function(e, template)
{
tenantsDep.depend();
var _this = e.currentTarget;
var tenantName = _this.innerHTML;
$(_this).addClass('Selected');
$(_this).siblings().removeClass('Selected');
for(var i = 0; i < tenants.length; i++)
{
if(tenants[i].name == tenantName)
tenants[i].selected = "Selected";
else
tenants[i].selected = '';
}
}
})
})();
^This seemed to be what they were getting at in the meteor documentation (http://docs.meteor.com/#deps_dependency) for dependency.changed() and dependency.depend(), but all this does is give me an infinite loop.
So can I modify the way I declare deps to get this to make data reactive? Is there a better way to do this all together?
UPDATE:
Although I was skeptical to do so, I've been inclined to try to use Session.set/Session.get in a localized way. So, the next time I have to do this, I'll just do
Session.set('tenantsBlock' {tenants: [], selectedTenant: 0});
and then just access this variable from within helpers and event maps related to Template.tenantsBlock. That way they all have real time access to the data and they all get re-run when the data changes. Here's what I converted this script into (sorry these are both so large):
(function()
{
Template.tenantsBlock.created = Template.tenantsBlock.destroyed =function()
{
_Utils.setSession('tenantsBlock', {
tenants: [],
selectedTenant: 0
})
};
Template.tenantsBlock.tenantsList = function()
{
var localContext = Session.get('tenantsBlock');
localContext.tenants = [];
var property = $properties.findOne({userId: Meteor.userId(), propertyId: Session.get('property')});
var tenancies = _Utils.resolveTenancies(property, true, null, true);
for(var i = 0; i < tenancies.length; i++)
{
if(tenancies[i].tenancyId == Session.get('tenancy'))
{
localContext.tenants = localContext.tenants.concat(tenancies[i].otherTenants, tenancies[i].primaryTenant);
break;
}
}
localContext.tenants[localContext.selectedTenant].selected = 'Selected';
Session.set('tenantsBlock', localContext);
return localContext.tenants;
};
Template.tenantsBlock.onlyOneTenant = function()
{
var localContext = Session.get('tenantsBlock');
return localContext.tenants.length > 1 ? '' : 'OneChild';
};
Template.tenantsBlock.phoneNumber = function()
{
var localContext = Session.get('tenantsBlock');
for(var i = 0; i < localContext.tenants[localContext.selectedTenant].details.length; i++)
if(_Utils.getDynamicContactIconClass(localContext.tenants[localContext.selectedTenant].details[i].key) == 'Phone')
return localContext.tenants[localContext.selectedTenant].details[i].value;
return null;
};
Template.tenantsBlock.emailAddress = function()
{
var localContext = Session.get('tenantsBlock');
var selectedTenantDetails = localContext.tenants[localContext.selectedTenant].details;
for(var i = 0; i < selectedTenantDetails.length; i++)
if(_Utils.getDynamicContactIconClass(selectedTenantDetails[i].key) == 'Mail')
return selectedTenantDetails[i].value;
return null;
};
Template.tenantsBlock.addedDate = function()
{
var localContext = Session.get('tenantsBlock');
return _Utils.timeToDateString(localContext.tenants[localContext.selectedTenant].created);
};
Template.tenantsBlock.events({
'click .Name': function(e, template)
{
var localContext = Session.get('tenantsBlock');
var _this = e.currentTarget;
var tenantName = _this.innerHTML;
for(var i = 0; i < localContext.tenants.length; i++)
{
if(localContext.tenants[i].name == tenantName)
{
localContext.tenants[i].selected = 'Selected';
localContext.selectedTenant = i;
}
else
{
localContext.tenants[i].selected = '';
}
}
Session.set('tenantsBlock', localContext);
}
})
})();
You'll have to overcome the old-school way of doing it :) Meteor is a lot simpler than you think. A good rule of thumb is that if you're using jQuery to manipulate any DOM elements, you're probably doing it wrong. Additionally, if you're accessing any data without using the collection API, you'd better have good reason to do so.
In your case, you don't need to code up any manual dependencies at all. Manual dependencies are rarely needed in most Meteor applications.
The first thing you need to do is put all your tenants inside a Meteor.Collection, which will make them easier to work with.
Tenants = new Meteor.Collection("tenants");
Your tenantsBlock template should look something like this (modulo some different html elements):
<template name="tenantsBlock">
<ol>
{{#each tenants}}
<li class="name {{selected}}">
<span>Primary Tenant: {{primaryTenant}}</span>
<span>Other Tenants: {{otherTenants}}</span>
<span>Phone Number: {{phoneNumber}}</span>
<span>Email Address: {{emailAddress}}</span>
<span>Added Date: {{addedDate}}</span>
</li>
{{/each}}
</ol>
</template>
Each document in Tenants should look something like the following:
{
primaryTenant: "Joe Blow",
otherTenants: "Mickey Mouse, Minnie Mouse",
phoneNumber: "555-234-5623",
emailAddress: "joe.blow#foo.com",
addedDate: "2005-10-30T10:45Z"
}
Then, all the code you would need is just for the selection/deselection, and you can delete everything else:
Template.tenantsBlock.tenants = function() {
return Tenants.find();
};
Template.tenantsBlock.selected = function() {
return Session.equals("selectedTenant", this._id);
};
Template.tenantsBlock.events({
'click .name': function(e) {
Session.set("selectedTenant", this._id);
}
});
Once again, I reiterate that you should never be doing DOM manipulations with Javascript when using Meteor. You just update your data and your templates will reactively update if everything is done correctly. Declare how you want your data to look, then change the data and watch the magic.
Meteor has really evolved since I posted this back in 2013. I thought
I should post a modern, superior method.
For a while now you've been able to create a ReactiveVar and now you can append those directly to templates. A ReactiveVar, similar to Session, is a reactive data store. ReactiveVar, however, holds only a single value (of any type).
You can add ReactiveVar to the client side of your project by running this in your terminal from your app's root directory:
$meteor add reactive-var
This javascript shows how you can pass the variable between the template's onCreated, onRendered, onDestroyed, events and helpers.
Template.myTemplate.onCreated = function() {
// Appends a reactive variable to the template instance
this.reactiveData = new ReactiveVar('Default Value');
};
Template.myTemplate.events({
'click .someButton': (e, template) => {
// Changes the value of the reactive variable for only this template instance
template.reactiveData.set('New Value');
},
});
Template.myTemplate.helpers({
theData: () => {
// Automatically updates view when reactive variable changes
return Template.instance().reactiveData.get();
},
});
This is superior for a few reasons:
It scopes the variable only to a single template instance. Particularly useful in cases where you might have a dozen instances of a template on a page, all requiring independent states.
It goes away when the template goes away. Using ReactiveVar or Session variables you will have to clear the variable when the template is destroyed (if it is even destroyed predictably).
It's just cleaner code.
Bonus Points: See ReactiveDict for cases in which you have many instances of a template on a page at once, but need to manage a handful of reactive variables and have those variables persist during the session.

Resources