UNPKG

6.48 kBJavaScriptView Raw
1#!/usr/bin/env node
2var fs = require('fs');
3var path = require('path');
4
5var _ = require('lodash');
6var Mocha = require('mocha');
7var colors = require('colors');
8var rimraf = require('rimraf');
9var mochaNotifier = require('mocha-notifier-reporter');
10
11var build = require('../lib/build');
12var makeBuild = require('../lib/makeBuild').makeBuild;
13var configs = require('../lib/configs');
14var getWebpackEntryForTest = require('../lib/getWebpackEntryForTest');
15var uploadToSentry = require('../lib/uploadToSentry');
16
17var argv = require('yargs')
18 .alias('b', 'blueprintsPath')
19 .describe('b', 'path to a raw-config via a node file with moduel.exports = config')
20 .default('b', './blueprints.config.js')
21 .alias('p', 'production')
22 .describe('p', 'enable production settings for the default build configs')
23 .default('p', false)
24 .alias('c', 'client')
25 .describe('c', 'use the default client build, assumes you have an entry point to a client at ~/lib/client.[some es6.js or .js or .jsx]')
26 .default('c', false)
27 .alias('s', 'server')
28 .describe('s', 'use the default server build, assumes you have an entry point to a server at ~/lib/server[some es6.js or .js or .jsx]')
29 .default('s', false)
30 .alias('a', 'clientAndServer')
31 .describe('a', '[DEFAULT=true] use both a client and a server build. checks if you have an extend build and applies it.')
32 .default('a', true)
33 .alias('w', 'watch')
34 .describe('w', '[DEFAULT=false] force watching of all builds')
35 .default('w', false)
36 .alias('i', 'ignoreBlueprints')
37 .describe('ignore the blueprints.config.js file in the current directory and use defaults')
38 .default('i', false)
39 .alias('t', 'runTest')
40 .describe('search for test files and run them')
41 .default('t', false)
42 .argv;
43
44function loadBlueprintsFromPath(filePath, isProduction) {
45 try {
46 console.log('...loading blueprints from', filePath)
47 var builds = require(path.resolve(filePath));
48
49 // build configuration files are written in js and can be:
50 // a) a function that takes isProduction (boolean) and returns an array of builds
51 // b) object with property named extensions, to extend / override default builds
52 // c) an array of builds
53 // The array is most straightforward and the function seems infinitely
54 // more useful than the extensions object, and easier to understand. I'd
55 // like to deprecate the extensions object if its not being used in many places.
56 if (typeof builds === 'function') {
57 builds = builds(isProduction);
58 } else if (!Array.isArray(builds)) {
59 if (builds.extensions === true) {
60 return { extensions: _.omit(builds, 'extensions') };
61 }
62 builds = [builds];
63 }
64
65 return { builds };
66 } catch (e) {
67 console.log(colors.red('Error in loading blueprints'), e);
68 process.exit(1);
69 }
70}
71
72function loadDefaultConfigs(options) {
73 console.log('...using default configs');
74 if (options.runTest) {
75 console.log('...Setting up tests:');
76 var config = _.merge(
77 {},
78 configs.getDefaultTestingConfig(),
79 { webpack: { entry: getWebpackEntryForTest('./') } }
80 );
81 return [ config ];
82
83 } else if (options.client) {
84 console.log('...client');
85 return [ configs.getClientConfig(options.production) ];
86
87 } else if (options.server) {
88 console.log('...server');
89 return [ configs.getServerConfig(options.production) ];
90
91 } else if (options.clientAndServer) {
92 console.log('...both');
93 return [
94 configs.getClientConfig(options.production),
95 configs.getServerConfig(options.production),
96 ];
97 }
98}
99
100
101function makeConfig(options) {
102 var builds;
103 var extensions = {};
104
105 if (options.blueprintsPath && !options.ignoreBlueprints && !options.runTest) {
106 var blueprints = loadBlueprintsFromPath(options.blueprintsPath, options.production);
107
108 if (blueprints.extensions) {
109 extensions = blueprints.extensions;
110
111 } else if (blueprints.builds && blueprints.builds.length) {
112 builds = blueprints.builds;
113 }
114 }
115
116 if (!builds) {
117 builds = loadDefaultConfigs(options);
118 }
119
120 if (options.watch) {
121 extensions.watch = true;
122 }
123
124 return builds.reduce(function(namedBuilds, build) {
125 namedBuilds[build.name] = makeBuild(_.merge(build, extensions));
126 return namedBuilds;
127 }, {});
128}
129
130function shouldUploadToSentry(sentryProject, buildName) {
131 return (
132 sentryProject &&
133 argv.production &&
134 WHITELISTED_BUILD_NAMES.indexOf(buildName) > -1 &&
135 process.env.SENTRY_KEY &&
136 process.env.SENTRY_RELEASE_BASE_URL
137 );
138}
139
140console.log('...Reading Blueprints', argv.blueprintsPath);
141console.log('...cwd', process.cwd());
142
143var config = makeConfig(argv);
144var JS_REGEX = /.*\.js(\.map)?$/;
145// sentry does not currently support source maps for the server
146// so there is no point add server builds here.
147// see: https://github.com/getsentry/sentry/issues/2632
148var WHITELISTED_BUILD_NAMES = ['ProductionClient'];
149
150build(config, function(buildName, stats) {
151 if (stats.errors && stats.errors.length > 0 && !argv.watch) {
152 console.log(colors.red('ERROR IN BUILD. Aborting.'));
153 process.exit(1);
154 }
155
156 // upload to Sentry if applicable
157 var build = config[buildName];
158 if (shouldUploadToSentry(build.sentryProject, buildName)) {
159 var buildPath = build.webpackConfig.output.path;
160 var assets = stats.assets
161 .filter(function(a) { return JS_REGEX.test(a.name); })
162 .map(function(a) { return path.join(buildPath, a.name); });
163
164 uploadToSentry(build.sentryProject, build.sentryOrg, build.release, assets);
165 }
166
167 if (argv.runTest) {
168 console.log(colors.magenta(
169 '\n ******************************' +
170 '\n * RUNNING TESTS *' +
171 '\n ******************************'
172 ));
173
174 m = new Mocha({ reporter: mochaNotifier.decorate('spec') });
175 stats.assets.forEach(function(asset) {
176 m.addFile('./.test/' + asset.name);
177 });
178 m.run();
179
180 // we want to remove these from the require cache while we have path
181 // references to them to ensure they get tested on the next rebuild
182 m.files.forEach(function(filePath) {
183 delete require.cache[require.resolve(path.resolve(filePath))];
184 });
185 }
186});
187
188// Hacky way to handle webpacks file output
189process.on('SIGINT', function() {
190 if (argv.runTest) {
191 var testDirectory = configs.getDefaultTestingConfig().webpack.output.path;
192 rimraf(path.resolve(testDirectory), {}, process.exit);
193 } else {
194 process.exit();
195 }
196});