Skip to content

Commit

Permalink
Merge pull request #386 from aws/npm-browserifiable
Browse files Browse the repository at this point in the history
Allow SDK to be be browserified without custom scripts

Closes #383
  • Loading branch information
AdityaManohar committed Oct 16, 2014
2 parents fd841cf + 6b4b3bb commit 6602c26
Show file tree
Hide file tree
Showing 12 changed files with 2,494 additions and 2,629 deletions.
1 change: 0 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ configuration
configuration.sample
coverage
dist
dist-tools
doc
doc-src
eslint-rules
Expand Down
167 changes: 57 additions & 110 deletions dist-tools/browser-builder.js
Original file line number Diff line number Diff line change
@@ -1,142 +1,89 @@
#!/usr/bin/env node

var fs = require('fs');
var util = require('util');
var path = require('path');

var CacheStrategy = require('./strategies/cache');
var DefaultStrategy = require('./strategies/default');
var AWS = require('../');

var defaultServices = 'cloudwatch,cognitoidentity,cognitosync,dynamodb,kinesis,elastictranscoder,s3,sqs,sns,sts';
var sanitizeRegex = /[^a-zA-Z0-9,-]/;
var license = [
'// AWS SDK for JavaScript v' + AWS.VERSION,
'// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.',
'// License at https://sdk.amazonaws.com/js/BUNDLE_LICENSE.txt'
].join('\n') + '\n';

function Builder(options) {
this.setDefaultOptions(options);
this.serviceCode = [];
this.builtServices = {};
this.buildStrategy = this.options.cache ?
new CacheStrategy(this) : new DefaultStrategy(this);
function minify(code) {
var uglify = require('uglify-js');
var minified = uglify.minify(code, {fromString: true});
return minified.code;
}

Builder.prototype.setDefaultOptions = function(options) {
this.options = options || {};
this.options.libPath = this.options.libPath || this.getRootPath();
this.options.cacheRoot = this.options.cacheRoot ||
path.join(this.options.libPath, 'dist-tools', 'cache');
this.options.cache = this.options.cache || false;
this.options.writeCache = this.options.writeCache || false;
this.options.minify = this.options.minify || false;
this.options.minifyOptions = this.options.minifyOptions || {compress: false};
};

Builder.prototype.getRootPath = function() {
return path.join(__dirname, '..');
};

Builder.prototype.cachePath = function(path) {
var fullPath = this.options.cacheRoot;
if (path) {
fullPath += '/' + path + (this.options.minify ? '.min' : '') + '.js';
}

return fullPath;
};

Builder.prototype.cacheExists = function(path) {
return fs.existsSync(this.cachePath(path));
};

Builder.prototype.buildService = function(name, usingDefaultServices) {
var match = name.match(/^(.+?)(?:-(.+?))?$/);
var service = match[1], version = match[2] || 'latest';
var contents = [];
var lines, err;

if (!this.builtServices[service]) {
this.builtServices[service] = {};

lines = this.buildStrategy.getServiceHeader(service);
if (lines === null) {
if (!usingDefaultServices) {
err = new Error('Invalid module: ' + service);
err.name = 'InvalidModuleError';
throw err;
}
} else {
contents.push(lines);
function stripComments(code) {
var lines = code.split(/\r?\n/);
var multiLine = false;
lines = lines.map(function (line) {
var rLine = line;
if (line.match(/^\s*\/\//)) {
rLine = null;
} else if (line.match(/^\s*\/\*/)) {
multiLine = true;
rLine = null;
}
}

if (!this.builtServices[service][version]) {
this.builtServices[service][version] = true;

lines = this.buildStrategy.getService(service, version);
if (lines === null) {
if (!usingDefaultServices) {
err = new Error('Invalid module: ' + service + '-' + version);
err.name = 'InvalidModuleError';
throw err;
if (multiLine) {
var multiLineEnd = line.match(/\*\/(.*)/);
if (multiLineEnd) {
multiLine = false;
rLine = multiLineEnd[1];
} else {
rLine = null;
}
} else {
contents.push(lines);
}
}

return contents.join('\n');
};
return rLine;
}).filter(function(l) { return l !== null; });

Builder.prototype.addServices = function(services) {
var usingDefaultServices = false;
if (!services) {
usingDefaultServices = true;
services = defaultServices;
}
if (services.match(sanitizeRegex)) {
throw new Error('Incorrectly formatted service names');
var newCode = lines.join('\n');
newCode = newCode.replace(/\/\*\*[\s\S]+?Copyright\s+.+?Amazon[\s\S]+?\*\//g, '');
return newCode;
}

function build(options, callback) {
if (arguments.length === 1) {
callback = options;
options = {};
}

var invalidModules = [];
var stsIncluded = false;
services.split(',').sort().forEach(function(name) {
if (name.match(/^sts\b/) || name === 'all') stsIncluded = true;
try {
this.serviceCode.push(this.buildService(name, usingDefaultServices));
} catch (e) {
if (e.name === 'InvalidModuleError') invalidModules.push(name);
else throw e;
}
}.bind(this));
var img = require('browserify/node_modules/insert-module-globals');
img.vars.process = function() { return '{browser:true}'; };

if (!stsIncluded) {
this.serviceCode.push(this.buildService('sts'));
}
if (options.services) process.env.AWS_SERVICES = options.services;

if (invalidModules.length > 0) {
throw new Error('Missing modules: ' + invalidModules.join(', '));
}
var browserify = require('browserify');
var brOpts = { basedir: path.resolve(__dirname, '..') };
browserify(brOpts).add('./').ignore('domain').bundle(function(err, data) {
if (err) return callback(err);

return this;
};
var code = (data || '').toString();
if (options.minify) code = minify(code);
else code = stripComments(code);

Builder.prototype.build = function(callback) {
this.buildStrategy.getCore(function(err, core) {
callback(err, err ? null : (core + ';' + this.serviceCode.join('\n')));
}.bind(this));
code = license + code;
callback(null, code);
});
};

// run if we called this tool directly
if (require.main === module) {
var options = {
minify: process.env.MINIFY ? true : false,
cache: process.env.CACHE ? true : false,
writeCache: process.env.WRITE_CACHE ? true : false,
cacheRoot: process.env.CACHE_ROOT,
libPath: process.env.LIB_PATH
services: process.argv[2] || process.env.SERVICES,
minify: process.env.MINIFY ? true : false
};
var services = process.argv[2] || process.env.SERVICES;
new Builder(options).addServices(services).build(function (err, code) {
build(options, function(err, code) {
if (err) console.error(err.message);
else console.log(code);
});
}

module.exports = Builder;
build.license = license;
module.exports = build;
149 changes: 149 additions & 0 deletions dist-tools/service-collector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
var fs = require('fs');
var util = require('util');
var path = require('path');

var AWS = require('../');
var apis = require('../lib/api_loader');

var defaultServices = 'cloudwatch,cognitoidentity,cognitosync,dynamodb,kinesis,elastictranscoder,s3,sqs,sns,sts';
var sanitizeRegex = /[^a-zA-Z0-9,-]/;

var serviceClasses = {};
Object.keys(AWS).forEach(function(name) {
if (AWS[name].serviceIdentifier) {
serviceClasses[AWS[name].serviceIdentifier] = AWS[name];
}
});

function getServiceHeader(service) {
if (service === 'all') {
return Object.keys(serviceClasses).map(function(service) {
return getServiceHeader(service);
}).join('\n');
}

if (!serviceClasses[service]) return null;
var versions = serviceClasses[service].apiVersions.map(function(version) {
return version.indexOf('*') >= 0 ? null : version;
}).filter(function(c) { return c !== null; });

var file = util.format(
'AWS.apiLoader.services[\'%s\'] = {};\n' +
'AWS.%s = AWS.Service.defineService(\'%s\', %s);\n',
service, apis.serviceName(service), service, util.inspect(versions));
var svcPath = path.join(__dirname, '..', 'lib', 'services', service + '.js');
if (fs.existsSync(svcPath)) {
file += 'require(\'./services/' + service + '\');\n';
}

return file;
}

function getService(service, version) {
if (service === 'all') {
return Object.keys(serviceClasses).map(function(service) {
var out = serviceClasses[service].apiVersions.map(function(version) {
if (version.indexOf('*') >= 0) return null;
return getService(service, version);
}).filter(function(c) { return c !== null; }).join('\n');

return out;
}).join('\n');
}

var svc, api;
if (!serviceClasses[service]) {
return null;
}

try {
var ClassName = serviceClasses[service];
svc = new ClassName({apiVersion: version, endpoint: 'localhost'});
api = apis.load(service, svc.api.apiVersion);
} catch (e) {
return null;
}

var line = util.format(
'AWS.apiLoader.services[\'%s\'][\'%s\'] = %s;',
service, svc.api.apiVersion, JSON.stringify(api));

return line;
}

function ServiceCollector(services) {
var builtServices = {};

function buildService(name, usingDefaultServices) {
var match = name.match(/^(.+?)(?:-(.+?))?$/);
var service = match[1], version = match[2] || 'latest';
var contents = [];
var lines, err;

if (!builtServices[service]) {
builtServices[service] = {};

lines = getServiceHeader(service);
if (lines === null) {
if (!usingDefaultServices) {
err = new Error('Invalid module: ' + service);
err.name = 'InvalidModuleError';
throw err;
}
} else {
contents.push(lines);
}
}

if (!builtServices[service][version]) {
builtServices[service][version] = true;

lines = getService(service, version);
if (lines === null) {
if (!usingDefaultServices) {
err = new Error('Invalid module: ' + service + '-' + version);
err.name = 'InvalidModuleError';
throw err;
}
} else {
contents.push(lines);
}
}

return contents.join('\n');
}

var serviceCode = '';
var usingDefaultServices = false;
if (!services) {
usingDefaultServices = true;
services = defaultServices;
}
if (services.match(sanitizeRegex)) {
throw new Error('Incorrectly formatted service names');
}

var invalidModules = [];
var stsIncluded = false;
services.split(',').sort().forEach(function(name) {
if (name.match(/^sts\b/) || name === 'all') stsIncluded = true;
try {
serviceCode += buildService(name, usingDefaultServices) + '\n';
} catch (e) {
if (e.name === 'InvalidModuleError') invalidModules.push(name);
else throw e;
}
});

if (!stsIncluded) {
serviceCode += buildService('sts') + '\n';
}

if (invalidModules.length > 0) {
throw new Error('Missing modules: ' + invalidModules.join(', '));
}

return serviceCode;
}

module.exports = ServiceCollector;
Loading

0 comments on commit 6602c26

Please sign in to comment.