Skip to content

Commit

Permalink
[TIMOB-25890] Windows: Support ES6 imports (#279)
Browse files Browse the repository at this point in the history
* module builder should include hooks
remove old module zip

* runCmake is no longer needed

* Support ES6-style imports
  • Loading branch information
infosia authored and hansemannn committed Mar 28, 2018
1 parent 29b1902 commit cb481c7
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 65 deletions.
Binary file removed windows/dist/hyperloop-windows-2.2.0.zip
Binary file not shown.
60 changes: 47 additions & 13 deletions windows/hooks/hyperloop.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ exports.cliVersion = '>=3.2';
ejs = require('ejs'),
path = require('path'),
spawn = require('child_process').spawn,
wrench = require('wrench');
wrench = require('wrench'),
babel_types = require('babel-types');

// State
var state = {};
Expand Down Expand Up @@ -78,10 +79,24 @@ exports.cliVersion = '>=3.2';
builder.native_types || (builder.native_types = {});
builder.native_events || (builder.native_events = {});

// Store relation between local variable name and class name
var native_specifiers = {};

// For typical require calls:
// Look for CallExpression with callee Identifier whose name property is "require"
//
// For imports like this: import MessageDialog from 'Windows.UI.Popups.MessageDialog';
// Look for ImportDeclaration whose 'source' property is a Literal with 'value' property holds the package name ('Windows.UI.Popups.MessageDialog')
// 'specifiers' property is an array holding multiple elements of type ImportSpecifier
// Each has an 'imported' and 'local' property is an Identifier whose 'name' is the imported class name ("MessageDialog")
// Variant on this may be: import { MessageDialog as MyLocalName } where 'imported' would be 'MessageDialog', 'local' would be 'MyLocalName'
//
// import 'module-name';
// This can be ignored in our case as nothing is imported locally, so basically it's like a require with no assign, imported only for running/side-effects.
traverse(ast, {
// ES5-style require calls
CallExpression: {
enter: function(path) {

// if we're calling require with one string literal argument...
// FIXME What if it is a requires, but not a string? What if it is a dynamically built string?
if (types.isIdentifier(path.node.callee, { name: 'require' }) &&
Expand All @@ -106,22 +121,41 @@ exports.cliVersion = '>=3.2';
types.isNewExpression(binding.path.node.init) && // and it's assigned from a 'new' expression
types.isIdentifier(binding.path.node.init.callee) // and the type is an identifier
) {
var ctor = path.scope.getBinding(binding.path.node.init.callee.name); // and it's the constructor variable
var detectedConstructorType = null;
var localname = binding.path.node.init.callee.name;
var ctor = path.scope.getBinding(localname); // and it's the constructor variable
if (ctor && ctor.path.node.init && ctor.path.node.init.arguments && ctor.path.node.init.arguments.length > 0) {
var detectedConstructorType = ctor.path.node.init.arguments[0].value; // record the type of the constructor
if (t_.hasWindowsAPI(detectedConstructorType)) {
var native_event = {
name: event_name,
type: detectedConstructorType,
signature: event_name + '_' + detectedConstructorType.replace(/\./g, '_')
};
builder.native_events[native_event.signature] = native_event;
logger.info('Detected native API event: ' + native_event.name + ' for ' + detectedConstructorType);
}
detectedConstructorType = ctor.path.node.init.arguments[0].value; // record the type of the constructor
} else if (native_specifiers[localname]) {
detectedConstructorType = native_specifiers[localname];
}
if (detectedConstructorType != null && t_.hasWindowsAPI(detectedConstructorType)) {
var native_event = {
name: event_name,
type: detectedConstructorType,
signature: event_name + '_' + detectedConstructorType.replace(/\./g, '_')
};
builder.native_events[native_event.signature] = native_event;
logger.info('Detected native API event: ' + native_event.name + ' for ' + detectedConstructorType);
}
}
}
}
},
// ES6+-style imports
ImportDeclaration: function(p) {
const nodeSource = p.node.source;
if (nodeSource && babel_types.isStringLiteral(nodeSource)) { // module name is a string literal
// Found an import that acts the same as a require...
let className = nodeSource.value;
if (t_.hasWindowsAPI(className)) {
for (let i = 0; i < p.node.specifiers.length; i++) {
logger.info("Detected native API reference: " + className);
builder.native_types[className] = {name: className};
native_specifiers[p.node.specifiers[i].local.name] = className;
}
}
}
}
});

Expand Down
71 changes: 19 additions & 52 deletions windows/plugins/hooks/hyperloop.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
/**
* Hyperloop ® builder for Windows
* Copyright (c) 2018 by Appcelerator, Inc.
* All Rights Reserved. This library contains intellectual
* property protected by patents and/or patents pending.
*
*
* THIS IS A PLUGIN FOR "BUILDING HYPERLOOP MODULE" BINARY
* THIS IS NOT A PLUGIN HOOK FOR BUILDING TITANIUM APP!
*/
var spawn = require('child_process').spawn,
async = require('async'),
path = require('path'),
fs = require('fs'),
ejs = require('ejs'),
appc = require('node-appc');
appc = require('node-appc'),
wrench = require('wrench');

function isVS2017(data) {
if (data.windowsInfo && data.windowsInfo.selectedVisualStudio) {
Expand All @@ -22,12 +33,6 @@ exports.init = function(logger, config, cli, nodeappc) {
function(next) {
generateCMakeList(data, next);
},
function(next) {
runCmake(data, 'WindowsStore', 'Win32', '10.0', next);
},
function(next) {
runCmake(data, 'WindowsStore', 'ARM', '10.0', next);
},
];

data.projectDir = cli.argv['project-dir'];
Expand Down Expand Up @@ -81,10 +86,13 @@ exports.init = function(logger, config, cli, nodeappc) {
});
});

var sharedInitHook = path.join(data.projectDir, '..', 'hooks', 'hyperloop-init.js');
if (fs.existsSync(sharedInitHook)) {
fs.createReadStream(sharedInitHook).pipe(fs.createWriteStream(path.join(data.projectDir, 'hooks', 'hyperloop-init.js')));
}
// Copy hooks
var hooksFrom = path.join(data.projectDir, 'hooks'),
hooksTo = path.join(data.projectDir, 'build', 'hyperloop', data.manifest.version, 'hooks')
fs.existsSync(hooksTo) || wrench.mkdirSyncRecursive(hooksTo);
wrench.copyDirSyncRecursive(hooksFrom, hooksTo, {
forceDelete: true
});

callback(null, data);
});
Expand Down Expand Up @@ -118,47 +126,6 @@ function generateCMakeList(data, next) {

}

function runCmake(data, platform, arch, sdkVersion, next) {
var logger = data.logger,
generatorName = (isVS2017(data) ? 'Visual Studio 15 2017' : 'Visual Studio 14 2015') + (arch==='ARM' ? ' ARM' : ''),
cmakeProjectName = (sdkVersion === '10.0' ? 'Windows10' : platform) + '.' + arch,
cmakeWorkDir = path.resolve(__dirname,'..','..',cmakeProjectName);

logger.debug('Run CMake on ' + cmakeWorkDir);

if (!fs.existsSync(cmakeWorkDir)) {
fs.mkdirSync(cmakeWorkDir);
}

var p = spawn(path.join(data.titaniumSdkPath,'windows','cli','vendor','cmake','bin','cmake.exe'),
[
'-G', generatorName,
'-DCMAKE_SYSTEM_NAME=' + platform,
'-DCMAKE_SYSTEM_VERSION=' + sdkVersion,
'-DCMAKE_BUILD_TYPE=Debug',
path.resolve(__dirname,'..','..')
],
{
cwd: cmakeWorkDir
});
p.on('error', function(err) {
logger.error(cmake);
logger.error(err);
});
p.stdout.on('data', function (data) {
logger.info(data.toString().trim());
});
p.stderr.on('data', function (data) {
logger.warn(data.toString().trim());
});
p.on('close', function (code) {
if (code != 0) {
process.exit(1); // Exit with code from cmake?
}
next();
});
}

function buildSolution(data, dest, platform, buildConfig, callback) {
var slnFile = path.join(dest, platform, 'HyperloopInvocation.sln');
runNuGet(data, slnFile, function(err) {
Expand Down

0 comments on commit cb481c7

Please sign in to comment.