UNPKG

@aplus/node

Version:

⭐ Axway Amplify module for using Node.js code with Appcelerator Titanium SDK

263 lines (233 loc) 7.94 kB
"use strict"; /*** * __ _ __ __ * ____ ___ ____ / /_ (_)/ /___ / /_ ___ _____ ____ * / __ `__ \ / __ \ / __ \ / // // _ \ / __ \ / _ \ / ___// __ \ * / / / / / // /_/ // /_/ // // // __// / / // __// / / /_/ / * /_/ /_/ /_/ \____//_.___//_//_/ \___//_/ /_/ \___//_/ \____/ * * mobile solutions for everyday heroes * * @file * Fix resolve issues by replacing functionality * * @module * @aplus/node/resolver/resolve-fix * * @author * Brenton House <brenton.house@gmail.com> * * @copyright * Copyright (c) 2016 by Superhero Studios Incorporated. All Rights Reserved. * * @license * Licensed under the terms of the MIT License (MIT) * Please see the license.md included with this distribution for details. * */ var _ = require("lodash"); var wrench = require("wrench"); var path = require("path"); var fs = require("fs-extra"); var minimatch = require('minimatch'); /** * * * @param {string} rootpath - Directory path to start searching for files * @param {Object} registry - Object used to store files/modules found * @param {string[]} registry.files - Files found in search * @param {string[]} registry.directories - Directories found in search * @param {Object[]} registry.core - Core modules to use in app * @param {Object[]} registry.fallback - Modules to use in case of reference to missing module * @param {string[]} [params.includes] - Array of glob patterns to match files to be included in search * @param {Object} [logger=console] - Alloy logger object */ module.exports = function(rootpath, registry, includes, logger) { _.defaults(registry, { files: [], directories: [], core: [], fallback: [], }); logger = logger || console; includes = includes || ["**/*.js", "**/*.json"]; /** * Inject necessary code into the file app.js */ function injectCode() { var fullpath = path.join(rootpath, "app.js"); var source = fs.readFileSync(fullpath, 'utf8'); var test = /\/\/ALLOY-RESOLVER/.test(source); logger.trace("CODE INJECTED ALREADY: " + test); if(!test) { source = source.replace(/(var\s+Alloy[^;]+;)/g, "$1\n//ALLOY-RESOLVER\nvar process=require('/process');\nAlloy.resolve=new (require('/resolver'))().resolve;\n"); fs.writeFileSync(fullpath, source); } } /** * Fix module resolution in all found javascript files */ function fixFiles() { logger.trace("inside fixFiles()"); _.each(registry.files, function(file) { var fullpath = path.join(rootpath, file); var basepath = path.posix.dirname(file); var basefile = path.posix.resolve(file); var source = fs.readFileSync(fullpath, 'utf8'); logger.trace("fixing file: " + fullpath); var requireRegex = /(require)\s*\(((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*)\)/g; var staticRequireRegex = /(require)(?:\(\s*['"])([^'"]+)(?:['"]\s*\))/g; source = source.replace(requireRegex, function($1, $2, $3) { var requestedModule = $2; if(staticRequireRegex.test($1)) { var staticRequireSource = $1; staticRequireSource = staticRequireSource.replace(staticRequireRegex, function($1, $2, $3) { var resolved_path = resolver.resolve($3, basepath); return 'require("' + resolved_path + '")'; }); return staticRequireSource; } else { return 'require(Alloy.resolve(' + $3 + ', "' + basepath + '"))'; } }); fs.writeFileSync(fullpath, source, { mode: 0o755 }); }); } /** * Replace backslashes for cross-platform usage * Adapted from https://github.com/sindresorhus/slash * * @param {string} intput - value needing to have backslashes replaced in. * @returns {string} */ function replaceBackSlashes(input) { var isExtendedLengthPath = /^\\\\\?\\/.test(input); var hasNonAscii = /[^\x00-\x80]+/.test(input); if(isExtendedLengthPath || hasNonAscii) { return input; } return input.replace(/\\/g, '/'); }; /** * Find all files that match extension criteria * * @param {string} rootpath - Absolute path of the directory from which file search will begin * @param {string[]|string} [patterns="**"] - Pattern(s) to be used when attempting to match files found * @returns {string[]} - Matched file paths */ function findFiles(rootpath, patterns) { logger.trace("inside findFiles()"); var patterns = patterns || ['**']; if(_.isString(patterns)) { patterns = [patterns]; } var files = _.map(wrench.readdirSyncRecursive(rootpath), function(filename) { return path.posix.sep + replaceBackSlashes(filename); }); var matchedFiles = match(files, patterns, { nocase: true, matchBase: true, dot: true, }); return _.filter(matchedFiles, function(file) { return !fs.statSync(path.join(rootpath, file)).isDirectory(); }) || []; }; /** * Find items in array that match a set of patterns * Adapted from https://github.com/sindresorhus/multimatch * * @param {string[]} list * @param {string[]|string} patterns * @param {Object} options * @returns {string[]} */ function match(list, patterns, options) { list = list || []; patterns = patterns || []; if(_.isString(patterns)) { patterns = [patterns]; } if(list.length === 0 || patterns.length === 0) { return []; } options = options || {}; return patterns.reduce(function(ret, pattern) { var process = _.union if(pattern[0] === '!') { pattern = pattern.slice(1); process = _.difference; } return process(ret, minimatch.match(list, pattern, options)); }, []); }; /** * Find and process all files */ function loadFiles() { logger.trace("inside loadFiles()"); var allfiles = findFiles(rootpath, includes); var filepaths = _.filter(allfiles, function(filepath) { return !/.+(package\.json)/.test(filepath); }); _.forEach(filepaths, function(filepath) { registry.files.push(filepath); }); var packagepaths = _.filter(allfiles, function(filepath) { return(/.+(package\.json)/.test(filepath)); }); _.forEach(packagepaths, function(filepath) { var content = fs.readFileSync(path.posix.join(rootpath, filepath), 'utf8'); var json = JSON.parse(content); if(json.main) { registry.directories.push({ id: path.posix.dirname(filepath), path: path.posix.resolve(path.posix.join(path.posix.dirname(filepath), json.main)) }); } }); var indexpaths = _.filter(allfiles, function(filepath) { return(/.+(index\.js)/.test(filepath)); }); _.forEach(indexpaths, function(filepath) { var existingdir = _.find(registry.directories, function(dir) { return dir.id === path.posix.dirname(filepath); }); if(!existingdir) { registry.directories.push({ id: path.posix.dirname(filepath), path: filepath }); } }); return registry; }; /** * Write registry file to resolver.js */ function writeRegistry() { logger.trace("inside writeRegistry()"); var filepath = path.join(rootpath, "resolver.js"); var content = fs.readFileSync(filepath, 'utf8'); var regex = /(var\s+registry\s+=\s+)[^;]*(;)/g; var modified = content.replace(regex, "$1" + JSON.stringify(registry) + "$2"); fs.writeFileSync(filepath, modified); } fs.copySync(path.join(__dirname, "resolver.js"), path.join(rootpath, "resolver.js")); fs.copySync(path.join(__dirname, "path.js"), path.join(rootpath, "path.js")); fs.copySync(path.join(__dirname, "process.js"), path.join(rootpath, "process.js")); loadFiles(); var r = require("./resolver"); var resolver = new r(registry, logger); registry = resolver.registry; injectCode(); fixFiles(); writeRegistry(); Object.defineProperty(this, "registry", { get: function() { return _.clone(registry); }, enumerable: true, configurable: false }); }