diff --git a/bin/react-scripts.js b/bin/react-scripts.js
index 355d49f16f0fda03d6a7a835685a8df1373549a9..d943832c3e8a6784026a5af58af26fdb92f4aa50 100644
--- a/bin/react-scripts.js
+++ b/bin/react-scripts.js
@@ -7,6 +7,7 @@ switch (script) {
 case 'build':
 case 'start':
 case 'eject':
+case 'run':
   spawn(
     'node',
     [require.resolve('../scripts/' + script)].concat(args),
diff --git a/package.json b/package.json
index 85e93dc849a09c8a46e8f51a7912867c2311ae5d..cb4470ef0ebc55333c7d9cd1fda9461f51803a34 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
     "fs-extra": "0.30.0",
     "html-webpack-plugin": "2.22.0",
     "json-loader": "0.5.4",
+    "null-loader": "0.1.1",
     "opn": "4.0.2",
     "postcss-loader": "0.9.1",
     "promise": "7.1.1",
diff --git a/scripts/eject.js b/scripts/eject.js
index 60ec9b9a5916a2d58c7e540155787b7885f8546c..393631d630fb180079c35ef6b671bf0ba876c5d5 100644
--- a/scripts/eject.js
+++ b/scripts/eject.js
@@ -51,6 +51,7 @@ prompt('Are you sure you want to eject? This action is permanent. [y/N]', functi
     path.join('config', 'webpack.config.dev.js'),
     path.join('config', 'webpack.config.prod.js'),
     path.join('scripts', 'build.js'),
+    path.join('scripts', 'run.js'),
     path.join('scripts', 'start.js'),
     path.join('scripts', 'openChrome.applescript')
   ];
diff --git a/scripts/init.js b/scripts/init.js
index a26ad799cfa46a61f57d0960bd3fc8845154e82d..19bc47850fe9f07a3bbd6e1f20349e92b4e5f622 100644
--- a/scripts/init.js
+++ b/scripts/init.js
@@ -25,7 +25,7 @@ module.exports = function(appPath, appName, verbose, originalDirectory) {
 
   // Setup the script rules
   appPackage.scripts = {};
-  ['start', 'build', 'eject'].forEach(function(command) {
+  ['start', 'build', 'eject', 'run'].forEach(function(command) {
     appPackage.scripts[command] = 'react-scripts ' + command;
   });
 
diff --git a/scripts/run.js b/scripts/run.js
new file mode 100644
index 0000000000000000000000000000000000000000..39f36d5bd54e4d025a25747e6360eca64321d9d3
--- /dev/null
+++ b/scripts/run.js
@@ -0,0 +1,115 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+var fs = require('fs');
+var os = require('os');
+var path = require('path');
+var webpack = require('webpack');
+
+var paths = require('../config/paths');
+
+// Hacky command-line parsing. All flags are just ignored and there
+// may be no args.
+// TODO: support passing args to the script we're running
+var nonflags = process.argv.filter(function(x) { return x[0] !== '-'; });
+if (nonflags.length != 3) {
+  console.error('Usage: react-scripts run foo.js');
+  console.error(
+    'foo.js is your script, resolved relative to the src directory.')
+  process.exit(1);
+}
+var scriptName = nonflags[2];
+var pathToEntry = path.join(paths.appSrc, scriptName);
+
+// Figure out what all the node_modules are.
+// We don't want to compile the node_modules, but we still want to
+// do a commonjs require for them.
+var nodeModules = {};
+fs.readdirSync(paths.appNodeModules).filter(function(x) {
+  return ['.bin'].indexOf(x) === -1;
+}).forEach(function(mod) {
+  nodeModules[mod] = 'commonjs ' + mod;
+});
+
+// Construct our script in a temporary file.
+var outputPath = os.tmpDir();
+var outputFile = 'entry.js';
+
+// Configure webpack.
+// TODO: support sourcemaps
+// TODO: figure out if we want to automatically polyfill fetch
+var config = {
+  entry: [
+    pathToEntry,
+  ],
+  target: 'node',
+  output: {
+    libraryTarget: 'commonjs2',
+    path: outputPath,
+    filename: outputFile
+  },
+  externals: nodeModules,
+  resolve: {
+    extensions: ['', '.js', '.json'],
+  },
+  resolveLoader: {
+    root: paths.ownNodeModules,
+    moduleTemplates: ['*-loader']
+  },
+  module: {
+    loaders: [
+      {
+        test: /\.js$/,
+        include: paths.appSrc,
+        loader: 'babel',
+        query: require('../config/babel.dev')
+      },
+      {
+        test: /\.css$/,
+        include: [paths.appSrc, paths.appNodeModules],
+        loader: 'null'
+      },
+      {
+        test: /\.json$/,
+        include: [paths.appSrc, paths.appNodeModules],
+        loader: 'json'
+      },
+      {
+        test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2)$/,
+        include: [paths.appSrc, paths.appNodeModules],
+        loader: 'null',
+      },
+      {
+        test: /\.(mp4|webm)$/,
+        include: [paths.appSrc, paths.appNodeModules],
+        loader: 'null'
+      }
+    ]
+  },
+};
+
+// Compile the script
+webpack(config).run(function(err, stats) {
+  if (err) {
+    console.error('error:', err.code, err.message);
+    console.error(err);
+    process.exit(2);
+  }
+  if (stats.hasErrors()) {
+    var errors = stats.toJson().errors;
+    console.error('stats has', errors.length, 'errors:');
+    for (var i = 0; i < errors.length; i++) {
+      console.error(errors[i]);
+    }
+    process.exit(3);
+  }
+
+  var scriptPath = path.resolve(outputPath, outputFile);
+  require(scriptPath);
+});