Skip to content

Commit 6602c26

Browse files
committed
Merge pull request #386 from aws/npm-browserifiable
Allow SDK to be be browserified without custom scripts Closes #383
2 parents fd841cf + 6b4b3bb commit 6602c26

12 files changed

+2494
-2629
lines changed

.npmignore

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ configuration
77
configuration.sample
88
coverage
99
dist
10-
dist-tools
1110
doc
1211
doc-src
1312
eslint-rules

dist-tools/browser-builder.js

+57-110
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,89 @@
11
#!/usr/bin/env node
22

33
var fs = require('fs');
4+
var util = require('util');
45
var path = require('path');
56

6-
var CacheStrategy = require('./strategies/cache');
7-
var DefaultStrategy = require('./strategies/default');
7+
var AWS = require('../');
88

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

12-
function Builder(options) {
13-
this.setDefaultOptions(options);
14-
this.serviceCode = [];
15-
this.builtServices = {};
16-
this.buildStrategy = this.options.cache ?
17-
new CacheStrategy(this) : new DefaultStrategy(this);
15+
function minify(code) {
16+
var uglify = require('uglify-js');
17+
var minified = uglify.minify(code, {fromString: true});
18+
return minified.code;
1819
}
1920

20-
Builder.prototype.setDefaultOptions = function(options) {
21-
this.options = options || {};
22-
this.options.libPath = this.options.libPath || this.getRootPath();
23-
this.options.cacheRoot = this.options.cacheRoot ||
24-
path.join(this.options.libPath, 'dist-tools', 'cache');
25-
this.options.cache = this.options.cache || false;
26-
this.options.writeCache = this.options.writeCache || false;
27-
this.options.minify = this.options.minify || false;
28-
this.options.minifyOptions = this.options.minifyOptions || {compress: false};
29-
};
30-
31-
Builder.prototype.getRootPath = function() {
32-
return path.join(__dirname, '..');
33-
};
34-
35-
Builder.prototype.cachePath = function(path) {
36-
var fullPath = this.options.cacheRoot;
37-
if (path) {
38-
fullPath += '/' + path + (this.options.minify ? '.min' : '') + '.js';
39-
}
40-
41-
return fullPath;
42-
};
43-
44-
Builder.prototype.cacheExists = function(path) {
45-
return fs.existsSync(this.cachePath(path));
46-
};
47-
48-
Builder.prototype.buildService = function(name, usingDefaultServices) {
49-
var match = name.match(/^(.+?)(?:-(.+?))?$/);
50-
var service = match[1], version = match[2] || 'latest';
51-
var contents = [];
52-
var lines, err;
53-
54-
if (!this.builtServices[service]) {
55-
this.builtServices[service] = {};
56-
57-
lines = this.buildStrategy.getServiceHeader(service);
58-
if (lines === null) {
59-
if (!usingDefaultServices) {
60-
err = new Error('Invalid module: ' + service);
61-
err.name = 'InvalidModuleError';
62-
throw err;
63-
}
64-
} else {
65-
contents.push(lines);
21+
function stripComments(code) {
22+
var lines = code.split(/\r?\n/);
23+
var multiLine = false;
24+
lines = lines.map(function (line) {
25+
var rLine = line;
26+
if (line.match(/^\s*\/\//)) {
27+
rLine = null;
28+
} else if (line.match(/^\s*\/\*/)) {
29+
multiLine = true;
30+
rLine = null;
6631
}
67-
}
6832

69-
if (!this.builtServices[service][version]) {
70-
this.builtServices[service][version] = true;
71-
72-
lines = this.buildStrategy.getService(service, version);
73-
if (lines === null) {
74-
if (!usingDefaultServices) {
75-
err = new Error('Invalid module: ' + service + '-' + version);
76-
err.name = 'InvalidModuleError';
77-
throw err;
33+
if (multiLine) {
34+
var multiLineEnd = line.match(/\*\/(.*)/);
35+
if (multiLineEnd) {
36+
multiLine = false;
37+
rLine = multiLineEnd[1];
38+
} else {
39+
rLine = null;
7840
}
79-
} else {
80-
contents.push(lines);
8141
}
82-
}
8342

84-
return contents.join('\n');
85-
};
43+
return rLine;
44+
}).filter(function(l) { return l !== null; });
8645

87-
Builder.prototype.addServices = function(services) {
88-
var usingDefaultServices = false;
89-
if (!services) {
90-
usingDefaultServices = true;
91-
services = defaultServices;
92-
}
93-
if (services.match(sanitizeRegex)) {
94-
throw new Error('Incorrectly formatted service names');
46+
var newCode = lines.join('\n');
47+
newCode = newCode.replace(/\/\*\*[\s\S]+?Copyright\s+.+?Amazon[\s\S]+?\*\//g, '');
48+
return newCode;
49+
}
50+
51+
function build(options, callback) {
52+
if (arguments.length === 1) {
53+
callback = options;
54+
options = {};
9555
}
9656

97-
var invalidModules = [];
98-
var stsIncluded = false;
99-
services.split(',').sort().forEach(function(name) {
100-
if (name.match(/^sts\b/) || name === 'all') stsIncluded = true;
101-
try {
102-
this.serviceCode.push(this.buildService(name, usingDefaultServices));
103-
} catch (e) {
104-
if (e.name === 'InvalidModuleError') invalidModules.push(name);
105-
else throw e;
106-
}
107-
}.bind(this));
57+
var img = require('browserify/node_modules/insert-module-globals');
58+
img.vars.process = function() { return '{browser:true}'; };
10859

109-
if (!stsIncluded) {
110-
this.serviceCode.push(this.buildService('sts'));
111-
}
60+
if (options.services) process.env.AWS_SERVICES = options.services;
11261

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

117-
return this;
118-
};
67+
var code = (data || '').toString();
68+
if (options.minify) code = minify(code);
69+
else code = stripComments(code);
11970

120-
Builder.prototype.build = function(callback) {
121-
this.buildStrategy.getCore(function(err, core) {
122-
callback(err, err ? null : (core + ';' + this.serviceCode.join('\n')));
123-
}.bind(this));
71+
code = license + code;
72+
callback(null, code);
73+
});
12474
};
12575

12676
// run if we called this tool directly
12777
if (require.main === module) {
12878
var options = {
129-
minify: process.env.MINIFY ? true : false,
130-
cache: process.env.CACHE ? true : false,
131-
writeCache: process.env.WRITE_CACHE ? true : false,
132-
cacheRoot: process.env.CACHE_ROOT,
133-
libPath: process.env.LIB_PATH
79+
services: process.argv[2] || process.env.SERVICES,
80+
minify: process.env.MINIFY ? true : false
13481
};
135-
var services = process.argv[2] || process.env.SERVICES;
136-
new Builder(options).addServices(services).build(function (err, code) {
82+
build(options, function(err, code) {
13783
if (err) console.error(err.message);
13884
else console.log(code);
13985
});
14086
}
14187

142-
module.exports = Builder;
88+
build.license = license;
89+
module.exports = build;

dist-tools/service-collector.js

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
var fs = require('fs');
2+
var util = require('util');
3+
var path = require('path');
4+
5+
var AWS = require('../');
6+
var apis = require('../lib/api_loader');
7+
8+
var defaultServices = 'cloudwatch,cognitoidentity,cognitosync,dynamodb,kinesis,elastictranscoder,s3,sqs,sns,sts';
9+
var sanitizeRegex = /[^a-zA-Z0-9,-]/;
10+
11+
var serviceClasses = {};
12+
Object.keys(AWS).forEach(function(name) {
13+
if (AWS[name].serviceIdentifier) {
14+
serviceClasses[AWS[name].serviceIdentifier] = AWS[name];
15+
}
16+
});
17+
18+
function getServiceHeader(service) {
19+
if (service === 'all') {
20+
return Object.keys(serviceClasses).map(function(service) {
21+
return getServiceHeader(service);
22+
}).join('\n');
23+
}
24+
25+
if (!serviceClasses[service]) return null;
26+
var versions = serviceClasses[service].apiVersions.map(function(version) {
27+
return version.indexOf('*') >= 0 ? null : version;
28+
}).filter(function(c) { return c !== null; });
29+
30+
var file = util.format(
31+
'AWS.apiLoader.services[\'%s\'] = {};\n' +
32+
'AWS.%s = AWS.Service.defineService(\'%s\', %s);\n',
33+
service, apis.serviceName(service), service, util.inspect(versions));
34+
var svcPath = path.join(__dirname, '..', 'lib', 'services', service + '.js');
35+
if (fs.existsSync(svcPath)) {
36+
file += 'require(\'./services/' + service + '\');\n';
37+
}
38+
39+
return file;
40+
}
41+
42+
function getService(service, version) {
43+
if (service === 'all') {
44+
return Object.keys(serviceClasses).map(function(service) {
45+
var out = serviceClasses[service].apiVersions.map(function(version) {
46+
if (version.indexOf('*') >= 0) return null;
47+
return getService(service, version);
48+
}).filter(function(c) { return c !== null; }).join('\n');
49+
50+
return out;
51+
}).join('\n');
52+
}
53+
54+
var svc, api;
55+
if (!serviceClasses[service]) {
56+
return null;
57+
}
58+
59+
try {
60+
var ClassName = serviceClasses[service];
61+
svc = new ClassName({apiVersion: version, endpoint: 'localhost'});
62+
api = apis.load(service, svc.api.apiVersion);
63+
} catch (e) {
64+
return null;
65+
}
66+
67+
var line = util.format(
68+
'AWS.apiLoader.services[\'%s\'][\'%s\'] = %s;',
69+
service, svc.api.apiVersion, JSON.stringify(api));
70+
71+
return line;
72+
}
73+
74+
function ServiceCollector(services) {
75+
var builtServices = {};
76+
77+
function buildService(name, usingDefaultServices) {
78+
var match = name.match(/^(.+?)(?:-(.+?))?$/);
79+
var service = match[1], version = match[2] || 'latest';
80+
var contents = [];
81+
var lines, err;
82+
83+
if (!builtServices[service]) {
84+
builtServices[service] = {};
85+
86+
lines = getServiceHeader(service);
87+
if (lines === null) {
88+
if (!usingDefaultServices) {
89+
err = new Error('Invalid module: ' + service);
90+
err.name = 'InvalidModuleError';
91+
throw err;
92+
}
93+
} else {
94+
contents.push(lines);
95+
}
96+
}
97+
98+
if (!builtServices[service][version]) {
99+
builtServices[service][version] = true;
100+
101+
lines = getService(service, version);
102+
if (lines === null) {
103+
if (!usingDefaultServices) {
104+
err = new Error('Invalid module: ' + service + '-' + version);
105+
err.name = 'InvalidModuleError';
106+
throw err;
107+
}
108+
} else {
109+
contents.push(lines);
110+
}
111+
}
112+
113+
return contents.join('\n');
114+
}
115+
116+
var serviceCode = '';
117+
var usingDefaultServices = false;
118+
if (!services) {
119+
usingDefaultServices = true;
120+
services = defaultServices;
121+
}
122+
if (services.match(sanitizeRegex)) {
123+
throw new Error('Incorrectly formatted service names');
124+
}
125+
126+
var invalidModules = [];
127+
var stsIncluded = false;
128+
services.split(',').sort().forEach(function(name) {
129+
if (name.match(/^sts\b/) || name === 'all') stsIncluded = true;
130+
try {
131+
serviceCode += buildService(name, usingDefaultServices) + '\n';
132+
} catch (e) {
133+
if (e.name === 'InvalidModuleError') invalidModules.push(name);
134+
else throw e;
135+
}
136+
});
137+
138+
if (!stsIncluded) {
139+
serviceCode += buildService('sts') + '\n';
140+
}
141+
142+
if (invalidModules.length > 0) {
143+
throw new Error('Missing modules: ' + invalidModules.join(', '));
144+
}
145+
146+
return serviceCode;
147+
}
148+
149+
module.exports = ServiceCollector;

0 commit comments

Comments
 (0)