JSDoc: Is conditional ignore possible? - javascript

Is there a way to ignore some documented symbols conditionally?
I'd like to do something like this (pseudo-code):
/**
* #ignore if dev
*/
var a = 42;
/**
* #ignore if prod
*/
var b = 24;
In this example I would like to have my JSDoc generator to only document var a if I configured my generator to dev and vice versa for var b.
Is this possible?

You can implement your own jsdoc plugin to test an ignore condition and set its value in doclet.ignore property. Setting it to true will prevent the doclet being processed - not adding it to the final documentation.
exports.defineTags = function(dictionary) {
var env = require('jsdoc/env');
/*
* Usage: #customIgnore prod, test
*/
dictionary.defineTag('customIgnore', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
var i;
// "envParams" is a property of your jsdoc.json
var environments = env.conf.envParams;
// Will hold "prod, test"
var tagValue = tag.value;
var conditionValues = tag.value.split(",");
for (i = 0; i < conditionValues.length && !doclet.ignore; i++) {
if (environments.indexOf(conditionValues[i].trim()) !== -1) {
doclet.ignore = true;
}
}
}
});
};

Related

Using fast-json-patched for document versioning and timelining

I'm attempting to create a revisable document, with the edits stored as fast-json-patched objects.
The rough outline is :
const jsonpatch = require('fast-json-patch');
/**
* Generate a doc, with all the state changes recorded as
* diffed objects.
* #param {object} docParams The parameters to generate the doc from
* #returns {object} the doc
*/
function generateDoc(docParams) {
const defaults = {
docnumber: 'TS99999',
edits: 1,
};
// create a complete set of properties to work from
const { docnumber, edits } = {
...defaults,
...docParams,
};
// basic docs setup
const doc = {
parameters: { docnumber, edits },
history: [],
state: { docnumber, notes: [] },
};
// update the doc 'edits' times
for (let edit = 0; edit < edits; edit++) {
// clone to preserve the current state
const currentState = JSON.parse(JSON.stringify(doc.state));
// add at least one note, up to every edit
const notesToAdd = Math.ceil(Math.random() * 5);
for (let i = 0; i < notesToAdd; i++) {
doc.state.notes.push('Note: ' + Math.ceil(Math.random() * 500));
}
doc.history.push(jsonpatch.compare(currentState, doc.state));
}
return doc;
}
/**
* Set the current doc state to the state at the spercifier point in time
* #param {object} doc The doc to update
* #param {integer} edit The point in time to use
* #returns {boolean} Was the doc set updated?
*/
function timetravel(doc, editPoint) {
if (
doc.parameters.currentedit === editPoint ||
editPoint > doc.parameters.edits ||
editPoint < 1
) {
return false; // don't travel too far into the future or past!
}
const patchesToApply = doc.history.slice(0, editPoint);
const patches = [].concat.apply([], patchesToApply);
let newDoc = {};
newDoc = jsonpatch.applyPatch(newDoc, patches).newDocument;
doc.state = newDoc;
doc.parameters.currentedit = editPoint;
return true;
}
// Testing....
const doc = generateDoc({
docnumber: 'TS99999',
edits: 5,
});
console.log(doc);
timetravel(doc, 2);
console.log(doc);
Clearly my understanding of what should be happening is wrong, as I get the following error...
/Users/dd/Code/patchtesting/node_modules/fast-json-patch/commonjs/core.js:14
obj[key] = this.value;
just at the jsonpatch.applyPatch line.
I've tried alternative approaches:
// seems to be one popular suggestion...
const patchesToApply = doc.history.slice(0, editPoint);
const patches = [].concat.apply([], patchesToApply);
doc.state = patches.reduce(jsonpatch.applyReducer, {});
doc.parameters.currentedit = editPoint;
or...
// Trying to see the effect of applying a single patch at a time...
patches.forEach(patch => {
console.log(patch);
newDoc = jsonpatch.applyPatch(newDoc, [patch]).newDocument;
console.log(newDoc);
});
The patches that are generated make sense, I just can't seem to apply them :-(
Sorry, it was a basic coding / fencepost-ish error:
...
// basic docs setup
const doc = {
parameters: { docnumber, edits },
history: [],
state: { docnumber, notes: [] },
};
// ADDED: need to record the initial state
doc.history.push(jsonpatch.compare({}, doc.state))
// update the doc 'edits' times
...
;

Using dropbox-api-content-hasher without node

I have used dropbox-api-content-hasher (gitrepo here) within a node.js environment for an Electron project with great success, but now I need to use it in a Phonegap/cordova project and I've no idea how to replace the following:
const fs = require('fs');
const dch = require('dropbox-content-hasher');
... with vanilla javascript so I can use it!
I have included the <script type="text/javascript" src="js/dropbox-content-hasher.js"></script> but I need to access the dch variable to do the following:
const hasher = dch.create();
and then access hasher the rest of what I need to do. I don't have the experience on how to convert it across, any ideas?
I am building in phonegap rather than cordova cli as I'm more experienced in this approach.
EDIT:
This is a fiddle of my attempt based on Alex's answer but I'm getting 'CryptoJS is not a constructor' in the web console. I don't know how to bring in the cryptojs library!
Fiddle here
I just needed the same and adapted the example from the dropbox repo.
I use the crypto-js library. The code is in ES6 and is used in a quasar-framework app (which btw. might be interesting for you, due to phonegap/cordova):
/**
* Computes a hash using the same algorithm that the Dropbox API uses for the
* the "content_hash" metadata field.
*
* The `digest()` method returns a hexadecimal-encoded version
* of the digest of the hash.
* The "content_hash" field in the Dropbox API is a hexadecimal-encoded version
* of the digest.
*
* Example:
*
* import DropboxContentHasher from 'dropboxContentHasher'
*
* let hasher = new DropboxContentHasher()
* hasher.update(content) // content is some string or CryptoJS.lib.WordArray
* return hasher.digest()
*/
import cryptoJS from 'crypto-js'
const BLOCK_SIZE = 4 * 1024 * 1024
class DropboxContentHasher {
constructor () {
this._overallHasher = cryptoJS.algo.SHA256.create()
this._blockHasher = cryptoJS.algo.SHA256.create()
this._blockPos = 0
}
update (data) {
let offset = 0
while (offset < data.length) {
if (this._blockPos === BLOCK_SIZE) {
this._overallHasher.update(this._blockHasher.finalize())
this._blockHasher = cryptoJS.algo.SHA256.create()
this._blockPos = 0
}
let spaceInBlock = BLOCK_SIZE - this._blockPos
let inputPartEnd = Math.min(data.length, offset + spaceInBlock)
let inputPartLength = inputPartEnd - offset
this._blockHasher.update(data.slice(offset, inputPartEnd))
this._blockPos += inputPartLength
offset = inputPartEnd
}
}
digest () {
if (this._blockPos > 0) {
this._overallHasher.update(this._blockHasher.finalize())
this._blockHasher = null
}
let r = this._overallHasher.finalize().toString()
this._overallHasher = null // Make sure we can't use this object anymore.
return r
}
}
export default DropboxContentHasher
Simple JavaScript implementation using crypto-js:
function computeContentHash(content) {
const BLOCK_SIZE = 4 * 1024 * 1024;
let hash = CryptoJS.algo.SHA256.create();
for (let p = 0; p < content.length; p += BLOCK_SIZE) {
let chunk = content.substr(p, BLOCK_SIZE);
hash.update(CryptoJS.SHA256(chunk));
}
return hash.finalize().toString();
}

clone javascript function, closure scope

I have this closure :
function CFetchNextData(ofs, pag, fetchFunction) {
var offset = ofs;
var limit = pag;
return function(options, cb) {
//do stuff to create params
fetchFunction(params, cb);
offset += limit;
};
}
I then create a variable this way:
var fetchInfo = CFetchNextData(0, 10, specificFetchFunction);
fetchInfo(options, myCB);
So that everytime I call fetchInfo, pagination is automatically set to the next set of data. That works great, althought
I'd like to have multiple instance of : "fetchInfo", each one having its own scope.
var A = fetchInfo; // I'd like a clone with its own scope, not a copy
var B = fetchInfo; // I'd like a clone with its own scope, not a copy
I could do:
var A = new CFetchNextData(ofs, pag, fetchFunction);
var B = new CFetchNextData(ofs, pag, fetchFunction);
But obviously I would have to setup "ofs" and "pag" each time, whereas by cloning fetchInfo, I'd have a stable pagination, set only once and for good.
Do you know how to achieve that ?
Thanks in advance
There isn't a concept of cloning a function in JavaScript. You need to call CFetchNextData (or another function) multiple times if you want to create multiple closures.
You could have CFetchNextData return a factory function instead of returning the actual function. But I'm not sure that's really an improvement.
function CFetchNextDataFactory(ofs, pag, fetchFunction) {
return function() {
var offset = ofs;
var limit = pag;
return function(options, cb) {
//do stuff to create params
fetchFunction(params, cb);
offset += limit;
};
};
}
var fetchInfoFactory = CFetchNextData(0, 10, specificFetchFunction);
var A = fetchInfoFactory();
var B = fetchInfoFactory();
This may not answer all of your question but just to pitch in , you could try assigning your parameters to a default / fallback value which will allow you to avoid setting ofs and pag each declaration . Below is a prototype of what I came up with . Its using oop :
class CFetchNextData {
constructor(ofs, pag){
this.OFS = 1; //default value
this.PAG = 10; //default value
this.ofs = ofs;
this.pag = pag;
if(ofs == null || ofs == undefined){
this.ofs = this.OFS;
}
if(pag = null || pag == undefined){
this.pag = this.PAG;
}
}
fetchInfo(){
var data = this.ofs += this.pag;
return data;
}
}
var task1 = new CFetchNextData(); // Falls back to default values..
var task2 = new CFetchNextData(32,31); // Uses values from specified in args...
document.write(task1.fetchInfo() + "\n")
document.write(task2.fetchInfo())
Hope this helps...

mocha cannot create an instance of my function

Just starting out with mocha and cannot for the life of me figure out why it thinks Helper is undefined at the indicated line/columns below:
test.js
var assert = require('assert'),
helper = require('../src/js/helper.js');
describe('helper', function() {
describe('#parseValue', function() {
it('should return number of minutes for a properly formatted string', function() {
assert.equal(1501, (new Helper()).parseValue('1d 1h 1m', 'when'));
^^^^^^^^^^^^
});
});
});
helper.js
(function(exports) {
'use strict';
function Helper(opts) {
this.opts = opts || {};
/**
* Parse a value based on its type and return a sortable version of the original value
*
* #param {string} val input value
* #param {string} type type of input value
* #returns {mixed} sortable value corresponding to the input value
*/
this.parseValue = function(val, type) {
switch (type) {
case 'when':
var d = val.match(/\d+(?=d)/),
h = val.match(/\d+(?=h)/),
m = val.match(/\d+(?=m)/);
if (m)
m = parseInt(m, 10);
if (h)
m += parseInt(h, 10) * 60;
if (d)
m += parseInt(d, 10) * 1440;
val = m;
break;
default:
break;
}
return val;
};
}
exports.helper = Helper;
})(this);
I wrote a quick test in the browser without mocha to ensure my helper.js functions were accessible and it worked fine, so I really am at a loss. I am running this directly on my server by calling mocha from the command line in my directory.
You never define Helper in test.js—only helper on this line:
var helper = require('../src/js/helper.js');
Use the lower case helper that you defined.
By the way, you might want to change your exports line in helper.js from this:
exports.helper = Helper;
To this:
exports.Helper = Helper;
Then use helper in test.js like so:
assert.equal(1501, (new helper.Helper()).parseValue('1d 1h 1m', 'when'));
Or just do something like this:
var Helper = require('../src/js/helper.js').Helper;

js build object path in property assignment

is there a way to automatically create subobjects in an assignment after construction, i.e.
var obj = {};
obj.a.b.c=13;
the above gives me a "obj.a is undefined" error
i wrote a function to do this, but wondered if there was an easier way
_setObjectProperty(obj,13,['a','b','c']);
function _setObjectProperty(obj,value,loc)
{
if(loc.length>1) {
obj[loc[0]] = obj[loc[0]] || {};
_setObjectProperty(obj[loc[0]],value,loc.splice(1));
}
else if(loc.length===1) {
obj[loc[0]]=value;
}
}
No, there's no built in way to do this in JavaScript. The only way is to create your own function like you did. If you want the convenience of the dot operator/notation you can use the following function:
var set = function(path, value, root) {
var segments = path.split('.'),
cursor = root || window,
segment,
i;
for (i = 0; i < segments.length - 1; ++i) {
segment = segments[i];
cursor = cursor[segment] = cursor[segment] || {};
}
return cursor[segments[i]] = value;
};
set("a.b.c", 2);
console.log(a.b.c) // => 2

Categories