summaryrefslogtreecommitdiff
path: root/node_modules/locutus/_util/util.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/locutus/_util/util.js')
-rw-r--r--node_modules/locutus/_util/util.js622
1 files changed, 622 insertions, 0 deletions
diff --git a/node_modules/locutus/_util/util.js b/node_modules/locutus/_util/util.js
new file mode 100644
index 0000000..c5f3956
--- /dev/null
+++ b/node_modules/locutus/_util/util.js
@@ -0,0 +1,622 @@
+'use strict';
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var globby = require('globby');
+var path = require('path');
+var fs = require('fs');
+var async = require('async');
+var YAML = require('js-yaml');
+var mkdirp = require('mkdirp');
+var debug = require('depurar')('locutus');
+var indentString = require('indent-string');
+var _ = require('lodash');
+var esprima = require('esprima');
+
+var Util = function () {
+ function Util(argv) {
+ _classCallCheck(this, Util);
+
+ if (!argv) {
+ argv = [];
+ }
+ this.__src = path.dirname(__dirname);
+ this.__root = path.dirname(path.dirname(__dirname));
+ this.__test = path.dirname(path.dirname(__dirname)) + '/test';
+
+ this.globals = {};
+
+ this.pattern = [this.__src + '/**/**/*.js', '!**/index.js', '!**/_util/**'];
+ this.concurrency = 8;
+ this.authorKeys = ['original by', 'improved by', 'reimplemented by', 'parts by', 'bugfixed by', 'revised by', 'input by'];
+
+ this.langDefaults = {
+ c: {
+ order: 1,
+ function_title_template: '[language]\'s [category].[function] in JavaScript',
+ human: 'C',
+ packageType: 'header file',
+ inspiration_urls: ['<a href="http://en.cppreference.com/w/c/numeric/math">the C math.h documentation</a>', '<a href="https://sourceware.org/git/?p=glibc.git;a=tree;f=math;hb=HEAD">the C math.h source</a>'],
+ function_description_template: 'Here’s what our current JavaScript equivalent to <a href="http://en.cppreference.com/w/c/numeric/[category]/[function]">[language]\'s [function] found in the [category].h header file</a> looks like.'
+ },
+ golang: {
+ order: 2,
+ function_title_template: '[language]\'s [category].[function] in JavaScript',
+ human: 'Go',
+ packageType: 'package',
+ inspiration_urls: ['<a href="https://golang.org/pkg/strings/">Go strings documentation</a>', '<a href="https://golang.org/src/strings/strings.go">Go strings source</a>', '<a href="https://golang.org/src/strings/example_test.go">Go strings examples source</a>', '<a href="http://gophersjs.com">GopherJS</a>'],
+ function_description_template: 'Here’s what our current JavaScript equivalent to <a href="https://golang.org/pkg/[category]/#[function]">[language]\'s [category].[function]</a> looks like.'
+ },
+ python: {
+ order: 3,
+ function_title_template: '[language]\'s [category].[function] in JavaScript',
+ human: 'Python',
+ packageType: 'module',
+ inspiration_urls: ['<a href="https://docs.python.org/3/library/string.html">the Python 3 standard library string page</a>'],
+ function_description_template: 'Here’s what our current JavaScript equivalent to <a href="https://docs.python.org/3/library/[category].html#[category].[function]">[language]\'s [category].[function]</a> looks like.'
+ },
+ ruby: {
+ order: 4,
+ function_title_template: '[language]\'s [category].[function] in JavaScript',
+ human: 'Ruby',
+ packageType: 'module',
+ inspiration_urls: ['<a href="http://ruby-doc.org/core-2.2.2/Math.html">the Ruby core documentation</a>'],
+ function_description_template: 'Here’s what our current JavaScript equivalent to <a href="http://ruby-doc.org/core-2.2.2/[category].html#method-c-[function]">[language]\'s [category].[function]</a> looks like.'
+ },
+ php: {
+ order: 5,
+ function_title_template: '[language]\'s [function] in JavaScript',
+ human: 'PHP',
+ packageType: 'extension',
+ inspiration_urls: ['<a href="http://php.net/manual/en/book.strings.php">the PHP string documentation</a>', '<a href="https://github.com/php/php-src/blob/master/ext/standard/string.c#L5338">the PHP string source</a>', '<a href="https://github.com/php/php-src/blob/master/ext/standard/tests/strings/str_pad_variation1.phpt">a PHP str_pad test</a>'],
+ function_description_template: 'Here’s what our current JavaScript equivalent to <a href="http://php.net/manual/en/function.[functiondashed].php">[language]\'s [function]</a> looks like.',
+ alias: ['/categories/', '/categories/array/', '/categories/bc/', '/categories/ctype/', '/categories/datetime/', '/categories/exec/', '/categories/filesystem/', '/categories/funchand/', '/categories/i18n/', '/categories/index/', '/categories/info/', '/categories/json/', '/categories/math/', '/categories/misc/', '/categories/net/', '/categories/network/', '/categories/pcre/', '/categories/strings/', '/categories/url/', '/categories/var/', '/categories/xdiff/', '/categories/xml/', '/functions/index/', '/functions/', '/packages/', '/packages/index/']
+ }
+ };
+
+ this.allowSkip = argv.indexOf('--noskip') === -1;
+
+ this._reindexBuffer = {};
+ this._injectwebBuffer = {};
+ }
+
+ _createClass(Util, [{
+ key: 'injectweb',
+ value: function injectweb(cb) {
+ var self = this;
+ this._runFunctionOnAll(this._injectwebOne, function (err) {
+ if (err) {
+ return cb(err);
+ }
+ for (var indexHtml in self._injectwebBuffer) {
+ debug('writing: ' + indexHtml);
+ fs.writeFileSync(indexHtml, self._injectwebBuffer[indexHtml], 'utf-8');
+ }
+ });
+ }
+ }, {
+ key: 'reindex',
+ value: function reindex(cb) {
+ var self = this;
+ self._reindexBuffer = {};
+ self._runFunctionOnAll(self._reindexOne, function (err) {
+ if (err) {
+ return cb(err);
+ }
+ for (var indexJs in self._reindexBuffer) {
+ var requires = self._reindexBuffer[indexJs];
+ requires.sort();
+ debug('writing: ' + indexJs);
+ fs.writeFileSync(indexJs, requires.join('\n') + '\n', 'utf-8');
+ }
+ });
+ }
+ }, {
+ key: 'writetests',
+ value: function writetests(cb) {
+ this._runFunctionOnAll(this._writetestOne, cb);
+ }
+ }, {
+ key: '_runFunctionOnAll',
+ value: function _runFunctionOnAll(runFunc, cb) {
+ var self = this;
+
+ var q = async.queue(function (fullpath, callback) {
+ self._load.bind(self, fullpath, {}, function (err, params) {
+ if (err) {
+ return callback(err);
+ }
+
+ runFunc.bind(self, params, callback)();
+ })();
+ }, self.concurrency);
+
+ debug({
+ pattern: self.pattern
+ });
+ var files = globby.sync(self.pattern);
+
+ q.push(files);
+
+ q.drain = cb;
+ }
+ }, {
+ key: '_reindexOne',
+ value: function _reindexOne(params, cb) {
+ var fullpath = this.__src + '/' + params.filepath;
+ var dir = path.dirname(fullpath);
+ var basefile = path.basename(fullpath, '.js');
+ var indexJs = dir + '/index.js';
+
+ var module = basefile;
+ if (basefile === 'Index2') {
+ module = 'Index';
+ }
+
+ if (!this._reindexBuffer[indexJs]) {
+ this._reindexBuffer[indexJs] = [];
+ }
+
+ var line = 'module.exports[\'' + module + '\'] = require(\'./' + basefile + '\')';
+ this._reindexBuffer[indexJs].push(line);
+ return cb(null);
+ }
+ }, {
+ key: '_injectwebOne',
+ value: function _injectwebOne(params, cb) {
+ var authors = {};
+ this.authorKeys.forEach(function (key) {
+ if (params.headKeys[key]) {
+ authors[key] = _.flattenDeep(params.headKeys[key]);
+ }
+ });
+
+ var langPath = [this.__root, '/website/source/', params.language].join('');
+
+ var langIndexPath = langPath + '/index.html';
+ var catPath = langPath + '/' + params.category;
+ var catIndexPath = catPath + '/' + 'index.html';
+ var funcPath = catPath + '/' + params.func_name + '.html';
+
+ if (!this._injectwebBuffer[langIndexPath]) {
+ var langTitle = '';
+ langTitle += this.langDefaults[params.language].human + ' ';
+ langTitle += this.langDefaults[params.language].packageType + 's ';
+ langTitle += ' in JavaScript';
+
+ var langData = Object.assign({}, this.langDefaults[params.language], {
+ warning: 'This file is auto generated by `npm run web:inject`, do not edit by hand',
+ type: 'language',
+ layout: 'language',
+ language: params.language,
+ title: langTitle
+ });
+ this._injectwebBuffer[langIndexPath] = '---' + '\n' + YAML.safeDump(langData).trim() + '\n' + '---' + '\n';
+ }
+
+ if (!this._injectwebBuffer[catIndexPath]) {
+ var catTitle = '';
+ catTitle += this.langDefaults[params.language].human + '\'s ';
+ catTitle += params.category + ' ';
+ catTitle += this.langDefaults[params.language].packageType + ' ';
+ catTitle += ' in JavaScript';
+
+ var catData = {
+ warning: 'This file is auto generated by `npm run web:inject`, do not edit by hand',
+ type: 'category',
+ layout: 'category',
+ language: params.language,
+ category: params.category,
+ title: catTitle
+ };
+ this._injectwebBuffer[catIndexPath] = '---' + '\n' + YAML.safeDump(catData).trim() + '\n' + '---' + '\n';
+ }
+
+ var functionTitle = this.langDefaults[params.language].function_title_template.replace(/\[language]/g, this.langDefaults[params.language].human).replace(/\[category]/g, params.category).replace(/\[function]/g, params.func_name).replace(/\[functiondashed]/g, params.func_name.replace(/_/g, '-'));
+
+ var functionDescription = this.langDefaults[params.language].function_description_template.replace(/\[language]/g, this.langDefaults[params.language].human).replace(/\[category]/g, params.category).replace(/\[function]/g, params.func_name).replace(/\[functiondashed]/g, params.func_name.replace(/_/g, '-'));
+
+ var funcData = {
+ warning: 'This file is auto generated by `npm run web:inject`, do not edit by hand',
+ examples: (params.headKeys.example || []).map(function (lines, i) {
+ return lines.join('\n');
+ }),
+ estarget: (params.headKeys.estarget || []).map(function (lines, i) {
+ return lines.join('\n');
+ }).join('\n').trim() || 'es5',
+ returns: (params.headKeys.returns || []).map(function (lines, i) {
+ return lines.join('\n');
+ }),
+ dependencies: [],
+ authors: authors || {},
+ notes: (params.headKeys.note || []).map(function (lines, i) {
+ return lines.join('\n');
+ }),
+ type: 'function',
+ layout: 'function',
+ title: functionTitle,
+ description: functionDescription,
+ function: params.func_name,
+ category: params.category,
+ language: params.language,
+ permalink: params.language + '/' + params.category + '/' + params.func_name + '/',
+ alias: ['/functions/' + params.language + '/' + params.func_name + '/', '/functions/' + params.category + '/' + params.func_name + '/', '/' + params.language + '/' + params.func_name + '/']
+ };
+
+ if (params.language === 'php') {
+ funcData.alias.push('/functions/' + params.func_name + '/');
+ }
+
+ var buf = '---' + '\n' + YAML.safeDump(funcData).trim() + '\n' + '---' + '\n';
+
+ buf += '{% codeblock lang:javascript %}' + params.code + '{% endcodeblock %}';
+
+ mkdirp(path.dirname(funcPath), function (err) {
+ if (err) {
+ throw new Error('Could not mkdir for ' + funcPath + '. ' + err);
+ }
+ fs.writeFile(funcPath, buf, 'utf-8', cb);
+ });
+ }
+ }, {
+ key: '_addRequire',
+ value: function _addRequire(name, relativeSrcForTest) {
+ return ['var ', name, ' = require(\'', relativeSrcForTest, '\') // eslint-disable-line no-unused-vars,camelcase'].join('');
+ }
+ }, {
+ key: '_writetestOne',
+ value: function _writetestOne(params, cb) {
+ var self = this;
+
+ if (!params.func_name) {
+ throw new Error('No func_name in ' + JSON.stringify(params));
+ }
+ if (!params.headKeys) {
+ throw new Error('No headKeys in ' + params.func_name);
+ }
+ if (!params.headKeys.example) {
+ throw new Error('No example in ' + params.func_name);
+ }
+
+ var basename = path.basename(params.filepath);
+ var subdir = path.dirname(params.filepath);
+ var testpath = this.__test + '/languages/' + subdir + '/test-' + basename;
+ var testdir = path.dirname(testpath);
+ var relativeSrcForTestDir = path.relative(testdir, self.__src);
+ var relativeTestFileForRoot = path.relative(self.__root, testpath);
+
+ // console.log(relativeSrcForTestDir)
+ // process.exit(1)
+
+ var testProps = '';
+ if (params.headKeys.test) {
+ testProps = params.headKeys.test[0][0];
+ }
+
+ var describeSkip = '';
+ if (self.allowSkip && testProps.indexOf('skip-all') !== -1) {
+ describeSkip = '.skip';
+ }
+
+ var codez = [];
+
+ codez.push('// warning: This file is auto generated by `npm run build:tests`');
+ codez.push('// Do not edit by hand!');
+
+ // Add globals
+ for (var global in self.globals) {
+ codez.push('var ' + global + ' = ' + self.globals[global]);
+ }
+
+ // Set timezone for testing dates
+ // Not ideal: http://stackoverflow.com/questions/8083410/how-to-set-default-timezone-in-node-js
+ codez.push('process.env.TZ = \'UTC\'');
+
+ codez.push('var ' + 'expect' + ' = require(\'chai\').expect');
+
+ // Add language-wide dependencies
+ // @todo: It would be great if we could remove this
+ if (params.language === 'php') {
+ codez.push(self._addRequire('ini_set', relativeSrcForTestDir + '/' + 'php/info/ini_set'));
+ codez.push(self._addRequire('ini_get', relativeSrcForTestDir + '/' + 'php/info/ini_get'));
+ if (params.func_name === 'localeconv') {
+ codez.push(self._addRequire('setlocale', relativeSrcForTestDir + '/' + 'php/strings/setlocale'));
+ }
+ if (params.func_name === 'i18n_loc_get_default') {
+ codez.push(self._addRequire('i18n_loc_set_default', relativeSrcForTestDir + '/' + 'php/i18n/i18n_loc_set_default'));
+ }
+ }
+
+ // Add the main function to test
+ codez.push(self._addRequire(params.func_name, relativeSrcForTestDir + '/' + params.filepath));
+
+ codez.push('');
+
+ codez.push(['describe', describeSkip, '(\'src/', params.filepath, ' (tested in ', relativeTestFileForRoot, ')\', function () {'].join(''));
+
+ // Run each example
+ for (var i in params.headKeys.example) {
+ if (!params.headKeys.returns[i] || !params.headKeys.returns[i].length) {
+ throw new Error('There is no return for example ' + i, test, params);
+ }
+
+ var humanIndex = parseInt(i, 10) + 1;
+ var itSkip = '';
+ if (self.allowSkip && testProps.indexOf('skip-' + humanIndex) !== -1) {
+ itSkip = '.skip';
+ }
+
+ codez.push([' it', itSkip, '(\'should pass example ', humanIndex, '\', function (done) {'].join(''));
+
+ var body = [];
+
+ var testExpected = params.headKeys.returns[i].join('\n');
+
+ body.push('var expected = ' + testExpected);
+
+ // Execute line by line (see date.js why)
+ // We need result be the last result of the example code
+ for (var j in params.headKeys.example[i]) {
+ if (parseInt(j, 10) === params.headKeys.example[i].length - 1) {
+ // last action gets saved
+ body.push('var result = ' + params.headKeys.example[i][j].replace('var $result = ', ''));
+ } else {
+ body.push(params.headKeys.example[i][j]);
+ }
+ }
+
+ body.push('expect(result).to.deep.equal(expected)');
+ body.push('done()');
+
+ codez.push(indentString(body.join('\n'), ' ', 4));
+
+ codez.push(' })');
+ }
+
+ codez.push('})');
+ codez.push('');
+
+ var code = codez.join('\n');
+
+ // Write to disk
+ mkdirp(testdir, function (err) {
+ if (err) {
+ throw new Error(err);
+ }
+ debug('writing: ' + testpath);
+ fs.writeFile(testpath, code, 'utf-8', cb);
+ });
+ }
+
+ // Environment-specific file opener. function name needs to
+ // be translated to code. The difficulty is in finding the
+ // category.
+
+ }, {
+ key: '_opener',
+ value: function _opener(fileOrName, requesterParams, cb) {
+ var self = this;
+ var pattern;
+
+ var language = requesterParams.language || '*';
+
+ if (path.basename(fileOrName, '.js').indexOf('.') !== -1) {
+ // periods in the basename, like: unicode.utf8.RuneCountInString or strings.sprintf
+ pattern = self.__src + '/' + language + '/' + fileOrName.replace(/\./g, '/') + '.js';
+ } else if (fileOrName.indexOf('/') === -1) {
+ // no slashes, like: sprintf
+ pattern = self.__src + '/' + language + '/*/' + fileOrName + '.js';
+ } else if (fileOrName.substr(0, 1) === '/') {
+ // absolute path, like: /Users/john/code/locutus/php/strings/sprintf.js
+ pattern = fileOrName;
+ } else {
+ // relative path, like: php/strings/sprintf.js
+ pattern = self.__src + '/' + fileOrName;
+ }
+
+ pattern = pattern.replace('golang/strings/Index.js', 'golang/strings/Index2.js');
+ debug('loading: ' + pattern);
+ var files = globby.sync(pattern, {});
+
+ if (files.length !== 1) {
+ var msg = 'Found ' + files.length + ' occurances of ' + fileOrName + ' via pattern: ' + pattern;
+ return cb(new Error(msg));
+ }
+
+ var filepath = files[0];
+
+ if (path.basename(filepath) === 'index.js') {
+ return cb(null);
+ }
+
+ if (!filepath) {
+ return cb(new Error('Could not find ' + pattern));
+ }
+
+ fs.readFile(filepath, 'utf-8', function (err, code) {
+ if (err) {
+ return cb(new Error('Error while opening ' + filepath + '. ' + err));
+ }
+ return cb(null, filepath, code);
+ });
+ }
+ }, {
+ key: '_load',
+ value: function _load(fileOrName, requesterParams, cb) {
+ var self = this;
+ self._opener(fileOrName, requesterParams, function (err, fullpath, code) {
+ if (err) {
+ return cb(err);
+ }
+
+ var filepath = path.relative(self.__src, fullpath);
+ self._parse(filepath, code, cb);
+ });
+ }
+ }, {
+ key: '_findDependencies',
+ value: function _findDependencies(fileOrName, requesterParams, dependencies, cb) {
+ var self = this;
+
+ if (!requesterParams.headKeys['depends on'] || !requesterParams.headKeys['depends on'].length) {
+ if (cb) {
+ cb(null, {});
+ }
+ return;
+ }
+
+ var i;
+ var depCodePath;
+ var loaded = 0;
+ for (i in requesterParams.headKeys['depends on']) {
+ depCodePath = requesterParams.headKeys['depends on'][i][0];
+
+ self._load(depCodePath, requesterParams, function (err, params) {
+ if (err) {
+ return cb(err);
+ }
+
+ dependencies[depCodePath] = params;
+ self._findDependencies(depCodePath, params, dependencies);
+
+ if (cb && ++loaded === requesterParams.headKeys['depends on'].length) {
+ cb(null, dependencies);
+ }
+ });
+ }
+ }
+ }, {
+ key: '_parse',
+ value: function _parse(filepath, code, cb) {
+ if (!code) {
+ return cb(new Error('Unable to parse ' + filepath + '. Received no code'));
+ }
+
+ if (filepath.indexOf('/') === -1) {
+ return cb(new Error('Parse only accepts relative filepaths. Received: \'' + filepath + '\''));
+ }
+
+ var parts = filepath.split('/');
+ var language = parts.shift();
+ var codepath = parts.join('.');
+ var name = parts.pop();
+ var category = parts.join('.');
+
+ var ast = esprima.parseScript(code, { comment: true, loc: true, range: true });
+
+ // find module.exports in the code
+ var moduleExports = ast.body.filter(function (node) {
+ try {
+ var leftArg = node.expression.left;
+ var rightArg = node.expression.right;
+
+ return leftArg.object.name === 'module' && leftArg.property.name === 'exports' && rightArg.type === 'FunctionExpression' && rightArg.id.type === 'Identifier' && !!rightArg.id.name;
+ } catch (err) {
+ return false;
+ }
+ });
+
+ // if file contains more than one export, fail
+ if (moduleExports.length !== 1) {
+ return cb(Error('File ' + filepath + ' is allowed to contain exactly one module.exports'));
+ }
+
+ // get the only export
+ var exp = moduleExports[0];
+
+ // look for function name and param list
+ var funcName = exp.expression.right.id.name;
+ var funcParams = exp.expression.right.params.map(function (p) {
+ return p.name;
+ });
+
+ // remember the lines where the function is defined
+ var funcLoc = exp.expression.right.loc;
+
+ // since comments are not included in the AST
+ // but are offered in ast.comments
+ // remember the location of first function body statement/expression
+ var firstFuncBodyElementLoc = exp.expression.right.body.body[0].loc;
+
+ // get all line comments which are located between function signature definition
+ // and first function body element
+ var headComments = ast.comments.filter(function (c) {
+ return c.type === 'Line' && c.loc.start.line >= funcLoc.start.line && c.loc.end.line <= firstFuncBodyElementLoc.start.line;
+ }).map(function (c) {
+ return c.value.trim();
+ });
+
+ if (headComments.length === 0) {
+ var msg = 'Unable to parse ' + filepath + '. Did not find any comments in function definition';
+ return cb(new Error(msg));
+ }
+
+ var headKeys = this._headKeys(headComments);
+
+ var params = {
+ headKeys: headKeys,
+ name: name,
+ filepath: filepath,
+ codepath: codepath,
+ // code: code,
+ language: language,
+ category: category,
+ func_name: funcName,
+ func_arguments: funcParams
+ };
+
+ this._findDependencies(filepath, params, {}, function (err, dependencies) {
+ if (err) {
+ return cb(err);
+ }
+
+ params.dependencies = dependencies;
+ return cb(null, params);
+ });
+ }
+ }, {
+ key: '_headKeys',
+ value: function _headKeys(headLines) {
+ var i;
+ var keys = {};
+ var match = [];
+ var dmatch = [];
+ var key = '';
+ var val = '';
+ var num = 0;
+
+ for (i in headLines) {
+ if (!(match = headLines[i].match(/^\s*\W?\s*([a-z 0-9]+)\s*:\s*(.*)\s*$/))) {
+ continue;
+ }
+ key = match[1];
+ val = match[2];
+
+ if (dmatch = key.match(/^(\w+)\s+(\d+)$/)) {
+ // Things like examples and notes can be grouped
+ key = dmatch[1];
+ num = dmatch[2] - 1;
+ } else {
+ num = 0;
+ }
+
+ if (!keys[key]) {
+ keys[key] = [];
+ }
+ if (!keys[key][num]) {
+ keys[key][num] = [];
+ }
+ keys[key][num].push(val);
+ }
+
+ return keys;
+ }
+ }]);
+
+ return Util;
+}();
+
+module.exports = Util;
+//# sourceMappingURL=util.js.map \ No newline at end of file