Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit 2be1a2e16da2eecdc50a1524c1a8fcf293d14219 @millermedeiros committed Nov 19, 2012
@@ -0,0 +1,2 @@
+node_modules/
+
@@ -0,0 +1,63 @@
+# nodefy
+
+convert AMD modules into a node.js compatible format.
+
+## How?
+
+this tool uses [Esprima](http://esprima.org/) to parse the code and replace
+`define()` calls, doing the less amount of changes as possible to the code.
+
+### Input
+
+```js
+define(['foo', '../bar/baz'], function(foo, baz){
+
+ var lorem = 'ipsum';
+
+ return {
+ log : function(){
+ console.log(lorem);
+ }
+ };
+
+});
+```
+
+### Output
+
+```js
+ var foo = require('foo');
+ var baz = require('../bar/baz');
+
+ var lorem = 'ipsum';
+
+ module.exports = {
+ log : function(){
+ console.log(lorem);
+ }
+ };
+```
+
+
+## Inspiration / Why?
+
+I couldn't find any tool that did what I wanted - convert AMD modules into
+plain node.js - so I decided to code my own. There are alternatives but they
+all add more complexity than I would like.
+
+This project was created mainly because of the
+[amd-utils](http://millermedeiros.github.com/amd-utils/) project, since many
+methods are useful on both environments.
+
+The name was inpired by
+[browserify](https://github.com/substack/node-browserify).
+
+
+## Alternatives
+
+ - [amdefine](https://github.com/jrburke/amdefine)
+ - [UMD](https://github.com/umdjs/umd)
+ - [r.js](https://github.com/jrburke/r.js)
+ - [uRequire](https://github.com/anodynos/uRequire)
+
+
@@ -0,0 +1,17 @@
+#!/usr/bin/env node
+
+var nodefy = require('../index');
+var cli = require('commander');
+
+cli
+ .description('convert AMD modules into node.js compatible modules.')
+ .usage('[options] [globs...]')
+ .option('-o,--output <folder>', 'Output folder. If omitted it will output to stdout.')
+
+cli.parse(process.argv);
+
+if (cli.args) {
+ var globs = cli.args.join();
+ // nodefy
+}
+
@@ -0,0 +1,29 @@
+
+
+var _parser = require('./src/parser');
+var _converter = require('./src/converter');
+
+
+/**
+ * Convert AMD-style JavaScript string into node.js compatible module
+ * @param String raw
+ * @return String
+ */
+exports.parse = _parser.parse;
+
+/**
+ * Read file content and output into destination path.
+ * @param String inputPath
+ * @param String outputPath
+ * @param Function callback
+ */
+exports.convert = _converter.convert;
+
+/**
+ * Read glob content and output files into output folder
+ * @param Glob inputGlob Glob that matches files that should be converted
+ * @param String outputFolder
+ * @param Function callback
+ */
+exports.batchConvert = _converter.batchConvert;
+
@@ -0,0 +1,34 @@
+{
+ "name": "nodefy",
+ "version": "0.1.0",
+ "description": "convert AMD modules into a node.js compatible format",
+ "main": "index.js",
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "test": "./node_modules/.bin/jasmine-node test"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/millermedeiros/nodefy.git"
+ },
+ "keywords": [ "amd", "cjs", "modules", "convert" ],
+ "author": {
+ "name" : "Miller Medeiros",
+ "url" : "http://blog.millermedeiros.com/"
+ },
+ "dependencies" : {
+ "esprima" : ">= 1.0 <1.2",
+ "glob" : "~3.1.14",
+ "async" : "~0.1.22",
+ "commander" : "~1.1.0"
+ },
+ "devDependencies" : {
+ "jasmine-node" : "~1.0.26"
+ },
+ "engine" : {
+ "node" : ">= 0.8"
+ },
+ "license": "MIT"
+}
@@ -0,0 +1,58 @@
+var _fs = require('fs');
+var _path = require('path');
+
+var _glob = require('glob');
+var _async = require('async');
+
+var _parser = require('./parser');
+
+
+
+/**
+ * Read file content and output into destination path.
+ */
+exports.convert = function(inputPath, outputPath, callback){
+ _fs.readFile(inputPath, function(err, content){
+ if (err) {
+ callback(err);
+ return;
+ }
+ content = _parser.parse( content.toString() );
+ safeCreateDir(outputPath, function(err){
+ if (err) {
+ callback(err);
+ return;
+ }
+ _fs.writeFile(outputPath, content, callback);
+ });
+ });
+};
+
+
+function safeCreateDir(filePath, callback){
+ var dir = _path.dirname(filePath);
+ if (! _fs.existsSync(dir) ) {
+ _fs.mkdir(dir, callback);
+ } else {
+ callback(null);
+ }
+}
+
+
+/**
+ * Read folder content and output files into output folder
+ */
+exports.batchConvert = function(inputGlob, outputFolder, callback){
+ _glob( inputGlob, function(err, files){
+ if (err) {
+ callback(err);
+ return;
+ }
+ // convert all files in parallel
+ _async.forEach(files, function(filePath, cb){
+ var outputPath = _path.join(outputFolder, _path.basename(filePath));
+ exports.convert(filePath, outputPath, cb);
+ }, callback);
+ });
+};
+
@@ -0,0 +1,128 @@
+
+var esprima = require('esprima');
+
+
+var MAGIC_DEPS = {
+ 'exports' : true,
+ 'module' : true,
+ 'require' : true
+};
+
+
+
+
+/**
+ * Convert AMD-style JavaScript string into node.js compatible module
+ * @param String raw
+ * @return String
+ */
+exports.parse = function(raw){
+ var output = '';
+ var ast = esprima.parse(raw, {
+ range: true,
+ raw: true
+ });
+
+ var defines = ast.body.filter(isDefine);
+
+ if ( defines.length > 1 ){
+ throw new Error('Each file can have only a single define call. Found "'+ defines.length +'"');
+ }
+
+ var def = defines[0];
+ var args = def.expression['arguments'];
+ var factory = getFactory( args );
+
+ // do replacements in-place to avoid modifying the code more than needed
+ output += raw.substring( 0, def.range[0] ); // anything before define
+ output += getRequires(args, factory); // add requires
+ output += getBody(raw, factory.body); // module body
+ output += raw.substring( def.range[1], raw.length ); // anything after define
+
+ return output;
+};
+
+
+function getRequires(args, factory){
+ var deps = getDependenciesNames( args );
+
+ var params = getParams( factory ).map(function(param, i){
+ return {
+ name : param,
+ dep : deps[i]
+ };
+ });
+
+ // only do it for dependencies that have a matching param
+ // also skip "magic" dependencies used on simplified CJS
+ params = params.filter(function(param){
+ return param.dep && !MAGIC_DEPS[param.dep];
+ });
+
+ var requires = params.map(function(param){
+ return 'var '+ param.name +' = require(\''+ param.dep +'\');';
+ });
+
+ return requires.join('\n');
+}
+
+
+function getDependenciesNames(args){
+ var arr = args.filter(function(arg){
+ return arg.type === 'ArrayExpression';
+ });
+
+ var deps = [];
+
+ if (arr.length) {
+ deps = arr[0].elements.map(function(el){
+ return el.value;
+ });
+ }
+
+ return deps;
+}
+
+
+function getParams(factory){
+ return factory.params.map(function(param){
+ return param.name;
+ });
+}
+
+
+function isDefine(node){
+ return node.type === 'ExpressionStatement' &&
+ node.expression.type === 'CallExpression' &&
+ node.expression.callee.type === 'Identifier' &&
+ node.expression.callee.name === 'define';
+}
+
+
+function getFactory(args){
+ return args.filter(function(arg){
+ return arg.type === 'FunctionExpression';
+ })[0];
+}
+
+
+function getBody(raw, factoryBody){
+ var returnStatement = factoryBody.body.filter(function(node){
+ return node.type === 'ReturnStatement';
+ })[0];
+
+ var body = '';
+
+ if (returnStatement) {
+ body += raw.substring( factoryBody.range[0] + 1, returnStatement.range[0] );
+ // "return ".length === 7 so we add "6" to returnStatement start
+ body += 'module.exports ='+ raw.substring( returnStatement.range[0] + 6, factoryBody.range[1] - 1 );
+ } else {
+ // if using exports or module.exports or just a private module we
+ // simply return the factoryBody content
+ body = raw.substring( factoryBody.range[0] + 1, factoryBody.range[1] - 1 );
+ }
+
+ return body;
+}
+
Oops, something went wrong.

0 comments on commit 2be1a2e

Please sign in to comment.