Format helpers.js (#205)

* Format helpers.js with prettier

* Adding docs on how to run prettier
This commit is contained in:
Manatsawin Hanmongkolchai 2017-09-16 00:12:09 +07:00 committed by Craig Peterson
parent 7c015bd56c
commit 4bffc37d15
5 changed files with 274 additions and 247 deletions

1
.gitignore vendored
View file

@ -10,3 +10,4 @@ docs/_site
powershell.log
zones/
*.env
node_modules

3
.prettierrc Normal file
View file

@ -0,0 +1,3 @@
tabWidth: 4
singleQuote: true
trailingComma: es5

View file

@ -60,7 +60,12 @@ is the Javascript file that defines `dnsconfig.js`'s functions like
`A()` and `MX()`. Look at the definition of A, MX and CAA for good
examples to use as a base.
Please add the function alphabetically with the others.
Please add the function alphabetically with the others. Also, please run
[prettier](https://github.com/prettier/prettier) on the file to ensure
your code conforms to our coding standard:
npm install prettier
node_modules/.bin/prettier --write pkg/js/helpers.js
## Step 3: Search for `#rtype_variations`

View file

@ -1,143 +1,153 @@
"use strict";
'use strict';
var conf = {
registrars: [],
dns_providers: [],
domains: []
domains: [],
};
var defaultArgs = [];
function initialize(){
function initialize() {
conf = {
registrars: [],
dns_providers: [],
domains: []
domains: [],
};
defaultArgs = [];
}
function NewRegistrar(name,type,meta) {
function NewRegistrar(name, type, meta) {
if (type) {
type == "MANUAL";
type == 'MANUAL';
}
var reg = {name: name, type: type, meta: meta};
var reg = { name: name, type: type, meta: meta };
conf.registrars.push(reg);
return name;
}
function NewDnsProvider(name, type, meta) {
if ((typeof meta === 'object') && ('ip_conversions' in meta)) {
meta.ip_conversions = format_tt(meta.ip_conversions)
if (typeof meta === 'object' && 'ip_conversions' in meta) {
meta.ip_conversions = format_tt(meta.ip_conversions);
}
var dsp = {name: name, type: type, meta: meta};
var dsp = { name: name, type: type, meta: meta };
conf.dns_providers.push(dsp);
return name;
}
function newDomain(name,registrar) {
return {name: name, registrar: registrar, meta:{}, records:[], dnsProviders: {}, defaultTTL: 0, nameservers:[]};
function newDomain(name, registrar) {
return {
name: name,
registrar: registrar,
meta: {},
records: [],
dnsProviders: {},
defaultTTL: 0,
nameservers: [],
};
}
function processDargs(m, domain) {
// for each modifier, if it is a...
// function: call it with domain
// array: process recursively
// object: merge it into metadata
if (_.isFunction(m)) {
m(domain);
} else if (_.isArray(m)) {
for (var j in m) {
processDargs(m[j], domain)
}
} else if (_.isObject(m)) {
_.extend(domain.meta,m);
} else {
throw "WARNING: domain modifier type unsupported: "+ typeof m + " Domain: "+ domain.name;
// for each modifier, if it is a...
// function: call it with domain
// array: process recursively
// object: merge it into metadata
if (_.isFunction(m)) {
m(domain);
} else if (_.isArray(m)) {
for (var j in m) {
processDargs(m[j], domain);
}
} else if (_.isObject(m)) {
_.extend(domain.meta, m);
} else {
throw 'WARNING: domain modifier type unsupported: ' +
typeof m +
' Domain: ' +
domain.name;
}
}
// D(name,registrar): Create a DNS Domain. Use the parameters as records and mods.
function D(name,registrar) {
var domain = newDomain(name,registrar);
for (var i = 0; i< defaultArgs.length; i++){
processDargs(defaultArgs[i],domain)
}
for (var i = 2; i<arguments.length; i++) {
var m = arguments[i];
processDargs(m, domain)
function D(name, registrar) {
var domain = newDomain(name, registrar);
for (var i = 0; i < defaultArgs.length; i++) {
processDargs(defaultArgs[i], domain);
}
conf.domains.push(domain)
for (var i = 2; i < arguments.length; i++) {
var m = arguments[i];
processDargs(m, domain);
}
conf.domains.push(domain);
}
// DEFAULTS provides a set of default arguments to apply to all future domains.
// Each call to DEFAULTS will clear any previous values set.
function DEFAULTS(){
function DEFAULTS() {
defaultArgs = [];
for (var i = 0; i<arguments.length; i++) {
for (var i = 0; i < arguments.length; i++) {
defaultArgs.push(arguments[i]);
}
}
// TTL(v): Set the TTL for a DNS record.
function TTL(v) {
if (_.isString(v)){
if (_.isString(v)) {
v = stringToDuration(v);
}
return function(r) {
r.ttl = v;
}
};
}
function stringToDuration(v){
function stringToDuration(v) {
var matches = v.match(/^(\d+)([smhdwny]?)$/);
if (matches == null){
throw v + " is not a valid duration string"
if (matches == null) {
throw v + ' is not a valid duration string';
}
unit = "s"
if (matches[2]){
unit = matches[2]
unit = 's';
if (matches[2]) {
unit = matches[2];
}
v = parseInt(matches[1])
var u = {"s":1, "m":60, "h":3600}
u["d"] = u.h * 24
u["w"] = u.d * 7
u["n"] = u.d * 30
u["y"] = u.d * 365
v = parseInt(matches[1]);
var u = { s: 1, m: 60, h: 3600 };
u['d'] = u.h * 24;
u['w'] = u.d * 7;
u['n'] = u.d * 30;
u['y'] = u.d * 365;
v *= u[unit];
return v
return v;
}
// DefaultTTL(v): Set the default TTL for the domain.
function DefaultTTL(v) {
if (_.isString(v)){
if (_.isString(v)) {
v = stringToDuration(v);
}
return function(d) {
d.defaultTTL = v;
}
};
}
function makeCAAFlag(value){
return function(record){
function makeCAAFlag(value) {
return function(record) {
record.caaflag |= value;
};
}
// CAA_CRITICAL: Critical CAA flag
var CAA_CRITICAL = makeCAAFlag(1<<0);
var CAA_CRITICAL = makeCAAFlag(1 << 0);
// DnsProvider("providerName", 0)
// DnsProvider("providerName", 0)
// nsCount of 0 means don't use or register any nameservers.
// nsCount not provider means use all.
function DnsProvider(name, nsCount){
if(typeof nsCount === 'undefined'){
function DnsProvider(name, nsCount) {
if (typeof nsCount === 'undefined') {
nsCount = -1;
}
return function(d) {
d.dnsProviders[name] = nsCount;
}
};
}
// A(name,ip, recordModifiers...)
@ -151,18 +161,14 @@ var ALIAS = recordBuilder('ALIAS');
// CAA(name,tag,value, recordModifiers...)
var CAA = recordBuilder('CAA', {
// TODO(tlim): It should be an error if value is not 0 or 128.
args: [
['name', _.isString],
['tag', _.isString],
['value', _.isString],
],
transform: function(record, args, modifiers){
// TODO(tlim): It should be an error if value is not 0 or 128.
args: [['name', _.isString], ['tag', _.isString], ['value', _.isString]],
transform: function(record, args, modifiers) {
record.name = args.name;
record.caatag = args.tag;
record.target = args.value;
},
modifierNumber: function(record, value){
modifierNumber: function(record, value) {
record.caaflags |= value;
},
});
@ -182,7 +188,7 @@ var SRV = recordBuilder('SRV', {
['port', _.isNumber],
['target', _.isString],
],
transform: function(record, args, modifiers){
transform: function(record, args, modifiers) {
record.name = args.name;
record.srvpriority = args.priority;
record.srvweight = args.weight;
@ -200,7 +206,7 @@ var TLSA = recordBuilder('TLSA', {
['matchingtype', _.isNumber],
['target', _.isString], //recordBuilder needs a "target" argument
],
transform: function(record, args, modifiers){
transform: function(record, args, modifiers) {
record.name = args.name;
record.tlsausage = args.usage;
record.tlsaselector = args.selector;
@ -219,20 +225,20 @@ var MX = recordBuilder('MX', {
['priority', _.isNumber],
['target', _.isString],
],
transform: function(record, args, modifiers){
transform: function(record, args, modifiers) {
record.name = args.name;
record.mxpreference = args.priority;
record.target = args.target;
},
});
function checkArgs(checks, args, desc){
if (args.length < checks.length){
throw(desc)
function checkArgs(checks, args, desc) {
if (args.length < checks.length) {
throw desc;
}
for (var i = 0; i< checks.length; i++){
if (!checks[i](args[i])){
throw(desc+" - argument "+i+" is not correct type")
for (var i = 0; i < checks.length; i++) {
if (!checks[i](args[i])) {
throw desc + ' - argument ' + i + ' is not correct type';
}
}
}
@ -243,51 +249,46 @@ var NS = recordBuilder('NS');
// NAMESERVER(name,target)
function NAMESERVER(name, target) {
return function(d) {
d.nameservers.push({name: name, target: target})
}
d.nameservers.push({ name: name, target: target });
};
}
function format_tt(transform_table) {
// Turn [[low: 1, high: 2, newBase: 3], [low: 4, high: 5, newIP: 6]]
// into "1 ~ 2 ~ 3 ~; 4 ~ 5 ~ ~ 6"
var lines = []
for (var i=0; i < transform_table.length; i++) {
var ip = transform_table[i];
var newIP = ip.newIP;
if (newIP){
if(_.isArray(newIP)){
newIP = _.map(newIP,function(i){return num2dot(i)}).join(",")
}else{
newIP = num2dot(newIP);
// Turn [[low: 1, high: 2, newBase: 3], [low: 4, high: 5, newIP: 6]]
// into "1 ~ 2 ~ 3 ~; 4 ~ 5 ~ ~ 6"
var lines = [];
for (var i = 0; i < transform_table.length; i++) {
var ip = transform_table[i];
var newIP = ip.newIP;
if (newIP) {
if (_.isArray(newIP)) {
newIP = _.map(newIP, function(i) {
return num2dot(i);
}).join(',');
} else {
newIP = num2dot(newIP);
}
}
}
var newBase = ip.newBase;
if (newBase){
if(_.isArray(newBase)){
newBase = _.map(newBase,function(i){return num2dot(i)}).join(",")
}else{
newBase = num2dot(newBase);
var newBase = ip.newBase;
if (newBase) {
if (_.isArray(newBase)) {
newBase = _.map(newBase, function(i) {
return num2dot(i);
}).join(',');
} else {
newBase = num2dot(newBase);
}
}
var row = [num2dot(ip.low), num2dot(ip.high), newBase, newIP];
lines.push(row.join(' ~ '));
}
var row = [
num2dot(ip.low),
num2dot(ip.high),
newBase,
newIP
]
lines.push(row.join(" ~ "))
}
return lines.join(" ; ")
return lines.join(' ; ');
}
// IMPORT_TRANSFORM(translation_table, domain)
var IMPORT_TRANSFORM = recordBuilder('IMPORT_TRANSFORM', {
args: [
['translation_table'],
['domain'],
['ttl', _.isNumber],
],
transform: function(record, args, modifiers){
args: [['translation_table'], ['domain'], ['ttl', _.isNumber]],
transform: function(record, args, modifiers) {
record.name = '@';
record.target = args.domain;
record.meta['transform_table'] = format_tt(args.translation_table);
@ -297,21 +298,21 @@ var IMPORT_TRANSFORM = recordBuilder('IMPORT_TRANSFORM', {
// PURGE()
function PURGE(d) {
d.KeepUnknown = false
d.KeepUnknown = false;
}
// NO_PURGE()
function NO_PURGE(d) {
d.KeepUnknown = true
d.KeepUnknown = true;
}
/**
* @deprecated
*/
function getModifiers(args,start) {
function getModifiers(args, start) {
var mods = [];
for (var i = start;i<args.length; i++) {
mods.push(args[i])
for (var i = start; i < args.length; i++) {
mods.push(args[i]);
}
return mods;
}
@ -326,12 +327,9 @@ function getModifiers(args,start) {
* applied before this function. It should mutate the given record.
* @param {function=} opts.applyModifier Function to apply modifiers to the record
*/
function recordBuilder(type, opts){
function recordBuilder(type, opts) {
opts = _.defaults({}, opts, {
args: [
['name', _.isString],
['target'],
],
args: [['name', _.isString], ['target']],
transform: function(record, args, modifiers) {
// record will have modifiers already applied
@ -357,21 +355,30 @@ function recordBuilder(type, opts){
}
_.extend(record.meta, mod);
} else {
throw "ERROR: Unknown modifier type";
throw 'ERROR: Unknown modifier type';
}
}
},
});
return function(){
return function() {
var parsedArgs = {};
var modifiers = [];
if (arguments.length < opts.args.length) {
var argumentsList = opts.args.map(function(item){
return item[0];
}).join(', ');
throw type + " record requires " + opts.args.length + " arguments (" + argumentsList + "). Only " + arguments.length + " were supplied";
var argumentsList = opts.args
.map(function(item) {
return item[0];
})
.join(', ');
throw type +
' record requires ' +
opts.args.length +
' arguments (' +
argumentsList +
'). Only ' +
arguments.length +
' were supplied';
return;
}
@ -381,8 +388,11 @@ function recordBuilder(type, opts){
var value = arguments[i];
if (argDefinition.length > 1) {
// run validator if supplied
if(!argDefinition[1](value)){
throw type + " record " + argDefinition[0] + " argument validation failed";
if (!argDefinition[1](value)) {
throw type +
' record ' +
argDefinition[0] +
' argument validation failed';
}
}
parsedArgs[argDefinition[0]] = value;
@ -393,7 +403,7 @@ function recordBuilder(type, opts){
modifiers.push(arguments[i]);
}
return function(d){
return function(d) {
var record = {
type: type,
meta: {},
@ -412,30 +422,41 @@ function recordBuilder(type, opts){
/**
* @deprecated
*/
function addRecord(d,type,name,target,mods) {
function addRecord(d, type, name, target, mods) {
// if target is number, assume ip address. convert it.
if (_.isNumber(target)) {
target = num2dot(target);
}
var rec = {type: type, name: name, target: target, ttl:d.defaultTTL, priority: 0, meta:{}};
var rec = {
type: type,
name: name,
target: target,
ttl: d.defaultTTL,
priority: 0,
meta: {},
};
// for each modifier, decide based on type:
// - Function: call is with the record as the argument
// - Object: merge it into the metadata
// - Number: IF MX record assume it is priority
if (mods) {
for (var i = 0; i< mods.length; i++) {
var m = mods[i]
for (var i = 0; i < mods.length; i++) {
var m = mods[i];
if (_.isFunction(m)) {
m(rec);
} else if (_.isObject(m)) {
//convert transforms to strings
if (m.transform && _.isArray(m.transform)){
m.transform = format_tt(m.transform)
}
_.extend(rec.meta,m);
_.extend(rec.meta,m);
//convert transforms to strings
if (m.transform && _.isArray(m.transform)) {
m.transform = format_tt(m.transform);
}
_.extend(rec.meta, m);
_.extend(rec.meta, m);
} else {
console.log("WARNING: Modifier type unsupported:", typeof m, "(Skipping!)");
console.log(
'WARNING: Modifier type unsupported:',
typeof m,
'(Skipping!)'
);
}
}
}
@ -445,69 +466,66 @@ function addRecord(d,type,name,target,mods) {
//ip conversion functions from http://stackoverflow.com/a/8105740/121660
// via http://javascript.about.com/library/blipconvert.htm
function IP(dot)
{
function IP(dot) {
var d = dot.split('.');
return ((((((+d[0])*256)+(+d[1]))*256)+(+d[2]))*256)+(+d[3]);
// prettier-ignore
return ((((((+d[0]) * 256) + (+d[1])) * 256) + (+d[2])) * 256) + (+d[3]);
}
function num2dot(num)
{
if(num === undefined){
return "";
function num2dot(num) {
if (num === undefined) {
return '';
}
if (_.isString(num)){
return num
if (_.isString(num)) {
return num;
}
var d = num%256;
for (var i = 3; i > 0; i--)
{
num = Math.floor(num/256);
d = num%256 + '.' + d;
var d = num % 256;
for (var i = 3; i > 0; i--) {
num = Math.floor(num / 256);
d = num % 256 + '.' + d;
}
return d;
}
// Cloudflare aliases:
// Meta settings for individual records.
var CF_PROXY_OFF = {'cloudflare_proxy': 'off'}; // Proxy disabled.
var CF_PROXY_ON = {'cloudflare_proxy': 'on'}; // Proxy enabled.
var CF_PROXY_FULL = {'cloudflare_proxy': 'full'}; // Proxy+Railgun enabled.
var CF_PROXY_OFF = { cloudflare_proxy: 'off' }; // Proxy disabled.
var CF_PROXY_ON = { cloudflare_proxy: 'on' }; // Proxy enabled.
var CF_PROXY_FULL = { cloudflare_proxy: 'full' }; // Proxy+Railgun enabled.
// Per-domain meta settings:
// Proxy default off for entire domain (the default):
var CF_PROXY_DEFAULT_OFF = {'cloudflare_proxy_default': 'off'};
var CF_PROXY_DEFAULT_OFF = { cloudflare_proxy_default: 'off' };
// Proxy default on for entire domain:
var CF_PROXY_DEFAULT_ON = {'cloudflare_proxy_default': 'on'};
var CF_PROXY_DEFAULT_ON = { cloudflare_proxy_default: 'on' };
// CUSTOM, PROVIDER SPECIFIC RECORD TYPES
function _validateCloudFlareRedirect(value){
if(!_.isString(value)){
function _validateCloudFlareRedirect(value) {
if (!_.isString(value)) {
return false;
}
return value.indexOf(",") === -1;
return value.indexOf(',') === -1;
}
var CF_REDIRECT = recordBuilder("CF_REDIRECT", {
var CF_REDIRECT = recordBuilder('CF_REDIRECT', {
args: [
["source", _validateCloudFlareRedirect],
["destination", _validateCloudFlareRedirect],
['source', _validateCloudFlareRedirect],
['destination', _validateCloudFlareRedirect],
],
transform: function(record, args, modifiers){
record.name = "@";
record.target = args.source + "," + args.destination;
transform: function(record, args, modifiers) {
record.name = '@';
record.target = args.source + ',' + args.destination;
},
});
var CF_TEMP_REDIRECT = recordBuilder("CF_TEMP_REDIRECT", {
var CF_TEMP_REDIRECT = recordBuilder('CF_TEMP_REDIRECT', {
args: [
["source", _validateCloudFlareRedirect],
["destination", _validateCloudFlareRedirect],
['source', _validateCloudFlareRedirect],
['destination', _validateCloudFlareRedirect],
],
transform: function(record, args, modifiers){
record.name = "@";
record.target = args.source + "," + args.destination;
transform: function(record, args, modifiers) {
record.name = '@';
record.target = args.source + ',' + args.destination;
},
});

View file

@ -190,75 +190,75 @@ var _escData = map[string]*_escFile{
"/helpers.js": {
local: "pkg/js/helpers.js",
size: 14087,
size: 14682,
modtime: 0,
compressed: `
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
AAD//4nEKeNaOQAA
`,
},