Skip to content

Commit

Permalink
Make babel-node a standalone package (#6023)
Browse files Browse the repository at this point in the history
* Make babel-node a standalone package

* New package `babel-node` previously `babel-cli/bin/babel-node`

* updates
  • Loading branch information
existentialism authored and hzoo committed Jul 30, 2017
1 parent e32042f commit 6d965c0
Show file tree
Hide file tree
Showing 30 changed files with 627 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -23,6 +23,7 @@ package-lock.json
!/packages/babel-runtime/helpers/es6/toArray.js
/packages/babel-register/test/.babel
/packages/babel-cli/test/tmp
/packages/babel-node/test/tmp
/packages/*/lib
.nyc_output
/babel.sublime-workspace
3 changes: 3 additions & 0 deletions packages/babel-node/.npmignore
@@ -0,0 +1,3 @@
src
test
node_modules
17 changes: 17 additions & 0 deletions packages/babel-node/README.md
@@ -0,0 +1,17 @@
# babel-node

> A Babel-powered Node.js CLI
babel-node is a CLI that works exactly the same as the Node.js CLI, with the added benefit of compiling with Babel presets and plugins before running it.

## Install

```sh
npm install --save-dev babel-node
```

## Usage

```sh
babel-node [options] [ -e script | script.js ] [arguments]
```
3 changes: 3 additions & 0 deletions packages/babel-node/bin/babel-node.js
@@ -0,0 +1,3 @@
#!/usr/bin/env node

require("../lib/babel-node");
34 changes: 34 additions & 0 deletions packages/babel-node/package.json
@@ -0,0 +1,34 @@
{
"name": "babel-node",
"version": "7.0.0-alpha.17",
"description": "Babel command line",
"author": "Sebastian McKenzie <sebmck@gmail.com>",
"homepage": "https://babeljs.io/",
"license": "MIT",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-node",
"keywords": [
"6to5",
"babel",
"es6",
"transpile",
"transpiler",
"babel-cli",
"compiler"
],
"dependencies": {
"babel-core": "7.0.0-alpha.17",
"babel-register": "7.0.0-alpha.17",
"babel-polyfill": "7.0.0-alpha.17",
"commander": "^2.8.1",
"fs-readdir-recursive": "^1.0.0",
"lodash": "^4.2.0",
"output-file-sync": "^2.0.0",
"v8flags": "^3.0.0"
},
"devDependencies": {
"babel-helper-fixtures": "7.0.0-alpha.17"
},
"bin": {
"babel-node": "./bin/babel-node.js"
}
}
186 changes: 186 additions & 0 deletions packages/babel-node/src/_babel-node.js
@@ -0,0 +1,186 @@
import commander from "commander";
import Module from "module";
import { inspect } from "util";
import path from "path";
import repl from "repl";
import * as babel from "babel-core";
import vm from "vm";
import "babel-polyfill";
import register from "babel-register";

import pkg from "../package.json";

const program = new commander.Command("babel-node");

function collect(value, previousValue): Array<string> {
// If the user passed the option with no value, like "babel-node file.js --presets", do nothing.
if (typeof value !== "string") return previousValue;

const values = value.split(",");

return previousValue ? previousValue.concat(values) : values;
}

/* eslint-disable max-len */
program.option("-e, --eval [script]", "Evaluate script");
program.option("-p, --print [code]", "Evaluate script and print result");
program.option(
"-o, --only [globs]",
"A comma-separated list of glob patterns to compile",
collect,
);
program.option(
"-i, --ignore [globs]",
"A comma-separated list of glob patterns to skip compiling",
collect,
);
program.option(
"-x, --extensions [extensions]",
"List of extensions to hook into [.es6,.js,.es,.jsx,.mjs]",
collect,
);
program.option("-w, --plugins [string]", "", collect);
program.option("-b, --presets [string]", "", collect);
/* eslint-enable max-len */

program.version(pkg.version);
program.usage("[options] [ -e script | script.js ] [arguments]");
program.parse(process.argv);

register({
extensions: program.extensions,
ignore: program.ignore,
only: program.only,
plugins: program.plugins,
presets: program.presets,
});

const replPlugin = ({ types: t }) => ({
visitor: {
ModuleDeclaration(path) {
throw path.buildCodeFrameError("Modules aren't supported in the REPL");
},

VariableDeclaration(path) {
if (path.node.kind !== "var") {
throw path.buildCodeFrameError(
"Only `var` variables are supported in the REPL",
);
}
},

Program(path) {
if (path.get("body").some(child => child.isExpressionStatement())) return;

// If the executed code doesn't evaluate to a value,
// prevent implicit strict mode from printing 'use strict'.
path.pushContainer(
"body",
t.expressionStatement(t.identifier("undefined")),
);
},
},
});

const _eval = function(code, filename) {
code = code.trim();
if (!code) return undefined;

code = babel.transform(code, {
filename: filename,
presets: program.presets,
plugins: (program.plugins || []).concat([replPlugin]),
}).code;

return vm.runInThisContext(code, {
filename: filename,
});
};

if (program.eval || program.print) {
let code = program.eval;
if (!code || code === true) code = program.print;

global.__filename = "[eval]";
global.__dirname = process.cwd();

const module = new Module(global.__filename);
module.filename = global.__filename;
module.paths = Module._nodeModulePaths(global.__dirname);

global.exports = module.exports;
global.module = module;
global.require = module.require.bind(module);

const result = _eval(code, global.__filename);
if (program.print) {
const output = typeof result === "string" ? result : inspect(result);
process.stdout.write(output + "\n");
}
} else {
if (program.args.length) {
// slice all arguments up to the first filename since they're babel args that we handle
let args = process.argv.slice(2);

let i = 0;
let ignoreNext = false;
args.some(function(arg, i2) {
if (ignoreNext) {
ignoreNext = false;
return;
}

if (arg[0] === "-") {
const parsedArg = program[arg.slice(2)];
if (parsedArg && parsedArg !== true) {
ignoreNext = true;
}
} else {
i = i2;
return true;
}
});
args = args.slice(i);

// make the filename absolute
const filename = args[0];
if (!path.isAbsolute(filename)) {
args[0] = path.join(process.cwd(), filename);
}

// add back on node and concat the sliced args
process.argv = ["node"].concat(args);
process.execArgv.unshift(__filename);

Module.runMain();
} else {
replStart();
}
}

function replStart() {
repl.start({
prompt: "> ",
input: process.stdin,
output: process.stdout,
eval: replEval,
useGlobal: true,
});
}

function replEval(code, context, filename, callback) {
let err;
let result;

try {
if (code[0] === "(" && code[code.length - 1] === ")") {
code = code.slice(1, -1); // remove "(" and ")"
}

result = _eval(code, filename);
} catch (e) {
err = e;
}

callback(err, result);
}
98 changes: 98 additions & 0 deletions packages/babel-node/src/babel-node.js
@@ -0,0 +1,98 @@
/**
* This tiny wrapper file checks for known node flags and appends them
* when found, before invoking the "real" _babel-node(1) executable.
*/

import getV8Flags from "v8flags";
import path from "path";

let args = [path.join(__dirname, "_babel-node")];

let babelArgs = process.argv.slice(2);
let userArgs;

// separate node arguments from script arguments
const argSeparator = babelArgs.indexOf("--");
if (argSeparator > -1) {
userArgs = babelArgs.slice(argSeparator); // including the --
babelArgs = babelArgs.slice(0, argSeparator);
}

/**
* Replace dashes with underscores in the v8Flag name
* Also ensure that if the arg contains a value (e.g. --arg=true)
* that only the flag is returned.
*/
function getNormalizedV8Flag(arg) {
const matches = arg.match(/--(.+)/);

if (matches) {
return `--${matches[1].replace(/-/g, "_")}`;
}

return arg;
}

getV8Flags(function(err, v8Flags) {
babelArgs.forEach(function(arg) {
const flag = arg.split("=")[0];

switch (flag) {
case "-d":
args.unshift("--debug");
break;

case "debug":
case "--debug":
case "--debug-brk":
case "--inspect":
args.unshift(arg);
break;

case "-gc":
args.unshift("--expose-gc");
break;

case "--nolazy":
args.unshift(flag);
break;

default:
if (
v8Flags.indexOf(getNormalizedV8Flag(flag)) >= 0 ||
arg.indexOf("--trace") === 0
) {
args.unshift(arg);
} else {
args.push(arg);
}
break;
}
});

// append arguments passed after --
if (argSeparator > -1) {
args = args.concat(userArgs);
}

try {
const kexec = require("kexec");
kexec(process.argv[0], args);
} catch (err) {
if (err.code !== "MODULE_NOT_FOUND") throw err;

const child_process = require("child_process");
const proc = child_process.spawn(process.argv[0], args, {
stdio: "inherit",
});
proc.on("exit", function(code, signal) {
process.on("exit", function() {
if (signal) {
process.kill(process.pid, signal);
} else {
process.exit(code);
}
});
});
}
});
@@ -0,0 +1,4 @@
{
"args": ["--eval", "console.log([1, 2, 3].map(x => x * x));"],
"stdout": "[ 1, 4, 9 ]"
}
@@ -0,0 +1 @@
console.log([1, 2, 3].map(x => x * x));
@@ -0,0 +1,4 @@
{
"args": ["foo", "--extensions", ".bar"],
"stdout": "[ 1, 4, 9 ]"
}
@@ -0,0 +1,4 @@
{
"args": ["--print", "--eval", "([1, 2, 3].map(x => x * x))"],
"stdout": "[ 1, 4, 9 ]"
}
@@ -0,0 +1 @@
console.log(process.argv[2]);
@@ -0,0 +1,4 @@
{
"args": ["bar", "foo"],
"stdout": "foo"
}
@@ -0,0 +1,2 @@
var foo = () => console.log("foo");
foo();
@@ -0,0 +1,3 @@
{
"args": ["foo"]
}
@@ -0,0 +1 @@
foo
@@ -0,0 +1,2 @@
var foo = () => console.log("foo");
foo();
@@ -0,0 +1,3 @@
{
"args": ["bar"]
}
@@ -0,0 +1 @@
foo

0 comments on commit 6d965c0

Please sign in to comment.