nativeloop
Version:
⭐ Axway Amplify module for using nativeloop with Appcelerator Titanium SDK Framework
410 lines (346 loc) • 13.1 kB
JavaScript
;
/***
* __ _ __ __
* ____ ___ ____ / /_ (_)/ /___ / /_ ___ _____ ____
* / __ `__ \ / __ \ / __ \ / // // _ \ / __ \ / _ \ / ___// __ \
* / / / / / // /_/ // /_/ // // // __// / / // __// / / /_/ /
* /_/ /_/ /_/ \____//_.___//_//_/ \___//_/ /_/ \___//_/ \____/
*
* mobile solutions for everyday heroes
*
* @file core module for {nativeloop}, a framework for building native mobile apps using nodejs style javascript.
* @module nativeloop/core
* @author Brenton House <brenton.house@gmail.com>*
* @version 1.0.0
* @since 1.0.0
* @copyright Copyright (c) 2017 by Superhero Studios Incorporated. All Rights Reserved.
* @license Licensed under the terms of the MIT License (MIT)
*
*/
const _ = require( 'lodash' );
const path = require( 'path' );
const resolve = require( 'resolve' );
const paths = require( 'global-paths' );
const hjson = require( 'hjson' );
const utils = require( './utils' );
// var config;
var _event;
const debug = require( 'debug' );
const log = debug( 'nativeloop' );
const warn = debug( 'nativeloop' );
debug.log = console.info.bind( console );
const alloyParser = require( './alloy-parser' );
const nativeloop_widgets = require( './widgets' );
var CONST;
var CU;
var U;
function handler( params ) {
init( params );
}
module.exports = handler;
/**
* @function init
* @summary Initialize the core handler
* @param {object} params - Parameters to initialize core
* @since 1.0.0
*/
var init = function( params ) {
// console.debug("params: " + JSON.stringify(params, null, 2));
CONST = params.constants;
CU = params.compilerUtils;
U = params.utils;
// var x = U.getWidgetDirectories.toString();
// console.error('x.length: ' + JSON.stringify(x.length, null, 2));
// console.error('U.getWidgetDirectories: ' + U.getWidgetDirectories.toString());
// console.error('U.getWidgetDirectories.findWidgetAsNodeModule: ' + U.getWidgetDirectories.findWidgetAsNodeModule.toString());
params.resolve.sync = _.wrap( params.resolve.sync, function( func, id, opts ) {
console.error( 'you are here → resolve.sync: ' + id );
var response;
try {
response = func( id, opts );
console.error( 'you are here → resolve.sync: after 1st' );
console.error( 'response: ' + JSON.stringify( response, null, 2 ) );
return response;
} catch( error ) {
// var regex = new RegExp( CONST.NPM_WIDGET_PREFIX + '([^\/]*)\/widget' );
var regex = new RegExp( CONST.NPM_WIDGET_PREFIX + '(.*)\/widget' );
var found = regex.exec( id );
console.error( 'found[1]: ' + JSON.stringify( found[ 1 ], null, 2 ) );
if( found && found[ 1 ] ) {
console.error( 'you are here → resolve.sync: before 2nd -- ' + found[ 1 ] );
response = func( found[ 1 ] + '/widget', opts );
console.error( 'you are here → resolve.sync: after 2nd' );
return response;
}
}
} );
params.jsonlint.parse = _.wrap( params.jsonlint.parse, function( func, input, opts ) {
debug.error( 'you are here → jsonlint.parse: ' );
debug.warn( 'input: ' + JSON.stringify( input, null, 2 ) );
var result = func( input );
return require( './fix-config' ).addWidgets( { input: result, logger: debug } );
// return hjson.parse( input, opts );
} );
U.getWidgetDirectories = _.wrap( U.getWidgetDirectories, function( func, options ) {
console.log( "********* getWidgetDirectories **********" );
return func( options );
} );
// var widgetParser = {
// parse: ( node, state ) => {
// if( !node.getAttribute( 'src' ) && state.widgetId ) {
// node.setAttribute( 'src', state.widgetId );
// }
// console.error( 'you are here → inside widgetParser' );
// node.nodeName = 'Require';
// node.setAttribute( 'type', 'widget' );
// let response = params.requireParser.parse( node, state );
// return response;
// }
// }
params.baseParser.parse = alloyParser.parse( {
baseParser: params.baseParser,
defaultParser: params.defaultParser,
widgetParser: params.widgetParser,
// widgetParser: widgetParser,
} );
// params.requireParser.parse = _.wrap( params.requireParser.parse, ( func, node, state ) => {
// console.error('you are here → inside the FAKE require parser');
// let response = func( node, state );
// // console.error( node );
// // console.error( node.attributes);
// // console.error( state );
// // console.error('node.getAttribute("__nativeloop"): ' + JSON.stringify(node.getAttribute('__nativeloop'), null, 2));
// // console.error( 'response: ' + JSON.stringify( response, null, 2 ) );
// if( node.getAttribute( '__nativeloop' ) && response.parent && response.parent.symbol ) {
// console.error( 'you are here → fixing parent.symbol' );
// // response.parent.symbol = response.parent.symbol.replace( /\.getViewEx[^\)]*\)/, '' );
// response.code = response.code.replace( /\.getViewEx[^\)]*\)/g, '' );
// }
// console.error( 'response.code: ' + JSON.stringify( response.code, null, 2 ) );
// console.error( 'response.parent: ' + JSON.stringify( response.parent, null, 2 ) );
// return response;
// } );
U.XML.getAlloyFromFile = handler.getAlloyFromFile;
console.log( "********* WRAPPING uglifyjs.parse **********" );
params.uglifyjs.parse = _.wrap( params.uglifyjs.parse, function( func, code, options ) {
console.log( "********* PRE:PARSE **********" );
var params = {
code: code
};
executeScripts( "preparse", params );
return func( params.code, options );
} );
alloyParser.init( {
CONST: CONST,
CU: CU,
U: U,
nativeloop_widgets: nativeloop_widgets,
} );
params.task( "pre:load", handler.preload );
params.task( "pre:compile", handler.precompile );
params.task( "post:compile", handler.postcompile );
params.task( "compile:app.js", handler.appjs );
}
/**
* @function getAlloyFromFile
* @summary summary
* @param {string} filename - description
* @since 1.0.0
* @returns {object} - description
*/
handler.getAlloyFromFile = function( filename ) {
// console.trace("***** INSIDE getAlloyFromFile()");
var doc = U.XML.parseFromFile( filename );
var docRoot = doc.documentElement;
if( _.toLower( docRoot.nodeName ) === "nativeloop" ) {
docRoot.nodeName = CONST.ROOT_NODE;
docRoot.setAttribute( "module", "/nativeloop" );
} else if( docRoot.nodeName === CONST.ROOT_NODE.toLowerCase() ) {
docRoot.nodeName = CONST.ROOT_NODE;
}
// Make sure the markup has a top-level <Alloy> tag
else if( docRoot.nodeName !== CONST.ROOT_NODE ) {
exports.die( [
'Invalid view file "' + filename + '".',
'All view markup must have a top-level <Alloy> tag'
] );
}
return docRoot;
};
// handler.preparse = function ( func ) {
// console.log( "********* WRAPPING uglifyjs.parse **********" );
// return _.wrap( func, function ( func, code, options ) {
// console.log( "********* PRE:PARSE **********" );
// var params = {
// code: code
// };
// executeScripts( "preparse", params );
// return func( params.code, options );
// } );
// }
// handler.alloyParser = alloyParser.parse;
/**
* @function splitTasks
* @summary summary
* @param {object} tasks - description
* @since 1.0.0
* @returns {object} - description
*/
function splitTasks( tasks ) {
var results = [];
if( !_.isArray( tasks ) ) {
tasks = [ tasks ];
}
// handler.logger.trace("splitting tasks...");
// handler.logger.trace("tasks: " + JSON.stringify(tasks, null, 2));
_.forEach( tasks, function( task ) {
if( _.isArray( task.events ) && !_.isEmpty( task.events ) ) {
// handler.logger.trace("found events to split");
_.forEach( task.events, function( event ) {
var splitTask = _.cloneDeep( task );
splitTask.events = event;
results.push( splitTask );
} );
} else {
results.push( task );
}
} );
return results;
}
var _init = _.once( function() {
loadConfig();
configureTasks();
} );
/**
* @function loadTasks
* @summary summary
* @since 1.0.0
* @returns {object} - description
*/
function loadTasks() {
var tasks = _.cloneDeep( _.get( handler.config, "nativeloop.tasks", [] ).concat( require( "./core_tasks" ) ) );
// handler.logger.debug("loaded Tasks: " + JSON.stringify(tasks, null, 2));
return tasks;
}
/**
* @function configureTasks
* @summary summary
* @param {object} tasks - description
* @since 1.0.0
* @returns {object} - description
*/
function configureTasks( tasks ) {
tasks = tasks || loadTasks();
var configuredTasks = [];
var importedTasks = [];
// handler.logger.trace("tasks coming into configureTasks(): " + JSON.stringify(tasks, null, 2));
_.forEach( tasks, function( task ) {
// handler.logger.trace("task: " + JSON.stringify(task, null, 2));
if( _.isString( task ) ) {
handler.logger.trace( "getting default tasks for module: " + task );
var target = require( resolve.sync( task, {
basedir: handler.event.dir.project
} ) );
importedTasks = importedTasks.concat( target.tasks || [] );
// handler.logger.error("target.tasks: " + JSON.stringify(target.tasks, null, 2));
// handler.logger.warn("imported tasks: " + JSON.stringify(importedTasks, null, 2));
return true;
} else {
// handler.logger.trace("adding task: " + JSON.stringify(task, null, 2));
configuredTasks.push( _.defaults( task, {
weight: 1000,
platforms: [ "ios", "android", "mobileweb", "windows" ]
} ) );
}
} );
if( !_.isEmpty( importedTasks ) ) {
handler.logger.debug( "Configuring importedTasks" )
configuredTasks = configuredTasks.concat( configureTasks( importedTasks ) );
} else {
handler.configuredTasks = splitTasks( configuredTasks );
// handler.logger.trace("handler.configuredTasks: " + JSON.stringify(handler.configuredTasks, null, 2));
}
return configuredTasks;
}
/**
* @function loadConfig
* @summary Load alloy config file and set it to property in handler
* @since 1.0.0
*/
var loadConfig = function() {
handler.logger.debug( "Loading alloy config file" );
handler.config = require( path.join( handler.event.dir.resourcesPlatform, "alloy", "CFG" ) );
// handler.logger.trace(JSON.stringify(handler.event, null, 2));
// handler.logger.trace(JSON.stringify(handler.config, null, 2));
}
Object.defineProperty( handler, "event", {
get: function() {
return _event;
},
set: function( event ) {
_event = event;
event.dir.resourcesPlatform = path.join( event.dir.resources, event.alloyConfig.platform === 'ios' ? 'iphone' : event.alloyConfig.platform );
},
enumerable: true,
configurable: false
} );
/**
* @function executeScripts
* @summary summary
* @param {string} eventName - description
* @param {object} params - description
* @since 1.0.0
*/
function executeScripts( eventName, params ) {
// handler.logger.trace("task to execute: " + JSON.stringify(handler.configuredTasks, null, 2));
// handler.logger.trace("handler.configuredTasks: " + JSON.stringify(handler.configuredTasks, null, 2));
var tasks = _.sortBy( _.filter( handler.configuredTasks || [], ( task ) => {
return task.events === eventName && _.includes( task.platforms, handler.event.alloyConfig.platform );
} ), "weight" );
params = params || {};
_.forEach( tasks, function( task ) {
//TODO: Check to make sure task is an object...
var taskParams = {
event: handler.event,
config: handler.config,
logger: handler.logger,
code: params.code,
};
_.defaults( taskParams, task );
handler.logger.debug( "executing task: " + task.module );
// handler.logger.debug("taskParams: " + JSON.stringify(taskParams, null, 2));
var target = require( resolve.sync( task.module, {
basedir: handler.event.dir.project,
paths: paths(),
} ) );
_.isFunction( target.execute ) && target.execute( taskParams );
if( taskParams.code ) {
// handler.logger.trace(taskParams.code);
params.code = taskParams.code;
}
} );
}
var events = [ "preload", "precompile", "postcompile", "appjs" ];
_.forEach( events, function( eventName ) {
handler[ eventName ] = function( event, logger ) {
// handler.logger = logger;
debug.trace = logger.trace.bind( logger );
debug.debug = logger.debug.bind( logger );
debug.info = logger.info.bind( logger );
debug.warn = logger.warn.bind( logger );
debug.error = logger.error.bind( logger );
handler.logger = debug;
handler.event = event;
_init();
handler.logger.warn( "********************* STARTING EVENT: " + eventName + " ***************************" );
handler.logger = logger;
// debug.trace = logger.trace.bind(logger);
// debug.warn = logger.debug.bind(logger);
// debug.info = logger.info.bind(logger);
// debug.warn = logger.warn.bind(logger);
// debug.error = logger.error.bind(logger);
// handler.logger = debug;
executeScripts( eventName );
handler.logger.warn( "********************* FINISHED EVENT: " + eventName + " ***************************" );
}
} );