This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
297 lines
7.9 KiB
297 lines
7.9 KiB
/* eslint-disable no-console */ |
|
import Async from 'async'; |
|
import fs from 'fs'; |
|
import * as Handlebars from './handlebars'; |
|
import {basename} from 'path'; |
|
import {SourceMapConsumer, SourceNode} from 'source-map'; |
|
|
|
|
|
module.exports.loadTemplates = function(opts, callback) { |
|
loadStrings(opts, function(err, strings) { |
|
if (err) { |
|
callback(err); |
|
} else { |
|
loadFiles(opts, function(err, files) { |
|
if (err) { |
|
callback(err); |
|
} else { |
|
opts.templates = strings.concat(files); |
|
callback(undefined, opts); |
|
} |
|
}); |
|
} |
|
}); |
|
}; |
|
|
|
function loadStrings(opts, callback) { |
|
let strings = arrayCast(opts.string), |
|
names = arrayCast(opts.name); |
|
|
|
if (names.length !== strings.length |
|
&& strings.length > 1) { |
|
return callback(new Handlebars.Exception('Number of names did not match the number of string inputs')); |
|
} |
|
|
|
Async.map(strings, function(string, callback) { |
|
if (string !== '-') { |
|
callback(undefined, string); |
|
} else { |
|
// Load from stdin |
|
let buffer = ''; |
|
process.stdin.setEncoding('utf8'); |
|
|
|
process.stdin.on('data', function(chunk) { |
|
buffer += chunk; |
|
}); |
|
process.stdin.on('end', function() { |
|
callback(undefined, buffer); |
|
}); |
|
} |
|
}, |
|
function(err, strings) { |
|
strings = strings.map((string, index) => ({ |
|
name: names[index], |
|
path: names[index], |
|
source: string |
|
})); |
|
callback(err, strings); |
|
}); |
|
} |
|
|
|
function loadFiles(opts, callback) { |
|
// Build file extension pattern |
|
let extension = (opts.extension || 'handlebars').replace(/[\\^$*+?.():=!|{}\-\[\]]/g, function(arg) { return '\\' + arg; }); |
|
extension = new RegExp('\\.' + extension + '$'); |
|
|
|
let ret = [], |
|
queue = (opts.files || []).map((template) => ({template, root: opts.root})); |
|
Async.whilst(() => queue.length, function(callback) { |
|
let {template: path, root} = queue.shift(); |
|
|
|
fs.stat(path, function(err, stat) { |
|
if (err) { |
|
return callback(new Handlebars.Exception(`Unable to open template file "${path}"`)); |
|
} |
|
|
|
if (stat.isDirectory()) { |
|
opts.hasDirectory = true; |
|
|
|
fs.readdir(path, function(err, children) { |
|
/* istanbul ignore next : Race condition that being too lazy to test */ |
|
if (err) { |
|
return callback(err); |
|
} |
|
children.forEach(function(file) { |
|
let childPath = path + '/' + file; |
|
|
|
if (extension.test(childPath) || fs.statSync(childPath).isDirectory()) { |
|
queue.push({template: childPath, root: root || path}); |
|
} |
|
}); |
|
|
|
callback(); |
|
}); |
|
} else { |
|
fs.readFile(path, 'utf8', function(err, data) { |
|
/* istanbul ignore next : Race condition that being too lazy to test */ |
|
if (err) { |
|
return callback(err); |
|
} |
|
|
|
if (opts.bom && data.indexOf('\uFEFF') === 0) { |
|
data = data.substring(1); |
|
} |
|
|
|
// Clean the template name |
|
let name = path; |
|
if (!root) { |
|
name = basename(name); |
|
} else if (name.indexOf(root) === 0) { |
|
name = name.substring(root.length + 1); |
|
} |
|
name = name.replace(extension, ''); |
|
|
|
ret.push({ |
|
path: path, |
|
name: name, |
|
source: data |
|
}); |
|
|
|
callback(); |
|
}); |
|
} |
|
}); |
|
}, |
|
function(err) { |
|
if (err) { |
|
callback(err); |
|
} else { |
|
callback(undefined, ret); |
|
} |
|
}); |
|
} |
|
|
|
module.exports.cli = function(opts) { |
|
if (opts.version) { |
|
console.log(Handlebars.VERSION); |
|
return; |
|
} |
|
|
|
if (!opts.templates.length && !opts.hasDirectory) { |
|
throw new Handlebars.Exception('Must define at least one template or directory.'); |
|
} |
|
|
|
if (opts.simple && opts.min) { |
|
throw new Handlebars.Exception('Unable to minimize simple output'); |
|
} |
|
|
|
const multiple = opts.templates.length !== 1 || opts.hasDirectory; |
|
if (opts.simple && multiple) { |
|
throw new Handlebars.Exception('Unable to output multiple templates in simple mode'); |
|
} |
|
|
|
// Force simple mode if we have only one template and it's unnamed. |
|
if (!opts.amd && !opts.commonjs && opts.templates.length === 1 |
|
&& !opts.templates[0].name) { |
|
opts.simple = true; |
|
} |
|
|
|
// Convert the known list into a hash |
|
let known = {}; |
|
if (opts.known && !Array.isArray(opts.known)) { |
|
opts.known = [opts.known]; |
|
} |
|
if (opts.known) { |
|
for (let i = 0, len = opts.known.length; i < len; i++) { |
|
known[opts.known[i]] = true; |
|
} |
|
} |
|
|
|
const objectName = opts.partial ? 'Handlebars.partials' : 'templates'; |
|
|
|
let output = new SourceNode(); |
|
if (!opts.simple) { |
|
if (opts.amd) { |
|
output.add('define([\'' + opts.handlebarPath + 'handlebars.runtime\'], function(Handlebars) {\n Handlebars = Handlebars["default"];'); |
|
} else if (opts.commonjs) { |
|
output.add('var Handlebars = require("' + opts.commonjs + '");'); |
|
} else { |
|
output.add('(function() {\n'); |
|
} |
|
output.add(' var template = Handlebars.template, templates = '); |
|
if (opts.namespace) { |
|
output.add(opts.namespace); |
|
output.add(' = '); |
|
output.add(opts.namespace); |
|
output.add(' || '); |
|
} |
|
output.add('{};\n'); |
|
} |
|
|
|
opts.templates.forEach(function(template) { |
|
let options = { |
|
knownHelpers: known, |
|
knownHelpersOnly: opts.o |
|
}; |
|
|
|
if (opts.map) { |
|
options.srcName = template.path; |
|
} |
|
if (opts.data) { |
|
options.data = true; |
|
} |
|
|
|
let precompiled = Handlebars.precompile(template.source, options); |
|
|
|
// If we are generating a source map, we have to reconstruct the SourceNode object |
|
if (opts.map) { |
|
let consumer = new SourceMapConsumer(precompiled.map); |
|
precompiled = SourceNode.fromStringWithSourceMap(precompiled.code, consumer); |
|
} |
|
|
|
if (opts.simple) { |
|
output.add([precompiled, '\n']); |
|
} else { |
|
if (!template.name) { |
|
throw new Handlebars.Exception('Name missing for template'); |
|
} |
|
|
|
if (opts.amd && !multiple) { |
|
output.add('return '); |
|
} |
|
output.add([objectName, '[\'', template.name, '\'] = template(', precompiled, ');\n']); |
|
} |
|
}); |
|
|
|
// Output the content |
|
if (!opts.simple) { |
|
if (opts.amd) { |
|
if (multiple) { |
|
output.add(['return ', objectName, ';\n']); |
|
} |
|
output.add('});'); |
|
} else if (!opts.commonjs) { |
|
output.add('})();'); |
|
} |
|
} |
|
|
|
if (opts.map) { |
|
output.add('\n//# sourceMappingURL=' + opts.map + '\n'); |
|
} |
|
|
|
output = output.toStringWithSourceMap(); |
|
output.map = output.map + ''; |
|
|
|
if (opts.min) { |
|
output = minify(output, opts.map); |
|
} |
|
|
|
if (opts.map) { |
|
fs.writeFileSync(opts.map, output.map, 'utf8'); |
|
} |
|
output = output.code; |
|
|
|
if (opts.output) { |
|
fs.writeFileSync(opts.output, output, 'utf8'); |
|
} else { |
|
console.log(output); |
|
} |
|
}; |
|
|
|
function arrayCast(value) { |
|
value = value != null ? value : []; |
|
if (!Array.isArray(value)) { |
|
value = [value]; |
|
} |
|
return value; |
|
} |
|
|
|
/** |
|
* Run uglify to minify the compiled template, if uglify exists in the dependencies. |
|
* |
|
* We are using `require` instead of `import` here, because es6-modules do not allow |
|
* dynamic imports and uglify-js is an optional dependency. Since we are inside NodeJS here, this |
|
* should not be a problem. |
|
* |
|
* @param {string} output the compiled template |
|
* @param {string} sourceMapFile the file to write the source map to. |
|
*/ |
|
function minify(output, sourceMapFile) { |
|
try { |
|
// Try to resolve uglify-js in order to see if it does exist |
|
require.resolve('uglify-js'); |
|
} catch (e) { |
|
if (e.code !== 'MODULE_NOT_FOUND') { |
|
// Something else seems to be wrong |
|
throw e; |
|
} |
|
// it does not exist! |
|
console.error('Code minimization is disabled due to missing uglify-js dependency'); |
|
return output; |
|
} |
|
return require('uglify-js').minify(output.code, { |
|
fromString: true, |
|
outSourceMap: sourceMapFile, |
|
inSourceMap: JSON.parse(output.map) |
|
}); |
|
}
|
|
|