Comparing version 1.2.0 to 1.3.0
330
lib/one.js
@@ -1,19 +0,16 @@ | ||
var templating = require('./templating'), | ||
render = require('./render'), | ||
functools = require('functools'), | ||
compose = functools.compose, | ||
combiner = require('combiner'), | ||
genpkg = require('genpkg'), | ||
path = require('path'), | ||
fs = require('fs'), | ||
readFile = fs.readFile, | ||
var templating = require('./templating'), | ||
render = require('./render'), | ||
logging = require('./logging'), | ||
server = require('./server'), | ||
installDict = require('./install_dict'); | ||
genpkg = require('genpkg'), | ||
path = require('path'), | ||
fs = require('fs'), | ||
const DEPENDENCY_BLACK_LIST = [ | ||
'one' | ||
]; | ||
logging = require('./logging'), | ||
server = require('./server'), | ||
installDict = require('./install_dict'), | ||
dependencies = require('./dependencies'), | ||
packages = require('./packages'), | ||
modules = require('./modules'); | ||
var slice = Array.prototype.slice; | ||
@@ -23,8 +20,10 @@ | ||
logging.trace('Building package from "%s"', options.manifestPath); | ||
logging.info('Given build options: %s', Object.keys(options).join(', ')); | ||
options.id = templating.idGenerator(); | ||
loadPkgFromManifestPath(options.manifestPath, undefined, options, function(error, pkg){ | ||
if(error) return callback(error); | ||
packages.loadFromManifestPath(options.manifestPath, undefined, options, function(error, pkg){ | ||
if(error) { | ||
callback(error); | ||
return; | ||
} | ||
@@ -34,279 +33,5 @@ render(pkg, options, function(error, sourceCode){ | ||
}); | ||
}); | ||
} | ||
function collectDeps(pkg, options, callback){ | ||
var deps = [], | ||
declaredDepObj = pkg.manifest.dependencies, | ||
declaredDepList, | ||
next; | ||
if(declaredDepObj){ | ||
declaredDepList = Object.keys(declaredDepObj).filter(function(name){ | ||
return DEPENDENCY_BLACK_LIST.indexOf(name) == -1; | ||
}); | ||
} | ||
if(!declaredDepList || !declaredDepList.length){ | ||
return callback(undefined, deps); | ||
} | ||
(function iter(i){ | ||
if(i>=declaredDepList.length){ | ||
logging.debug('Loaded %d dependencies under the package "%s"',declaredDepList.length, pkg.manifest.name); | ||
callback(undefined, deps); | ||
return; | ||
} | ||
next = iter.bind(null, i+1); | ||
var dp = declaredDepList[i], | ||
manifestPath = path.join(pkg.wd, 'node_modules/', dp, '/package.json'); | ||
logging.debug('Loading the dependency in "'+manifestPath+'" for the package "'+pkg.name+'"'); | ||
if(pkg.pkgDict[dp]){ | ||
next(); | ||
return; | ||
} | ||
loadPkgFromManifestPath(manifestPath, pkg, options, function(error, subpkg){ | ||
if(error){ | ||
logging.warn(error); | ||
next(); | ||
return; | ||
} | ||
deps.push(subpkg); | ||
next(); | ||
}); | ||
})(0); | ||
} | ||
function collectModules(pkg, callback){ | ||
logging.debug('Collect modules for the package "'+pkg.name+'"'); | ||
var dirs = [], | ||
base = '', | ||
join = path.join.bind(undefined, pkg.wd), | ||
lib = join('lib'); | ||
if(pkg.dirs && pkg.dirs.lib){ | ||
base = join(pkg.dirs.lib); | ||
dirs.push(base); | ||
} else if (pkg.manifest.main) { | ||
base = pkg.wd; | ||
dirs.indexOf(lib) == -1 && dirs.push(lib); | ||
dirs.push(join(pkg.manifest.main + ( /\.js$/.test(pkg.manifest.main) ? '' : '.js' ))); | ||
} else { | ||
base = pkg.wd; | ||
dirs.push(join('index.js')); | ||
dirs.push(join('lib')); | ||
} | ||
logging.debug('The directories to search:',dirs); | ||
compose.async(combiner.findFiles, | ||
combiner.includeDirectories, | ||
combiner.flatten, | ||
function(filenames,callback){ | ||
callback(undefined,filenames.filter(filterFilename)); | ||
}, | ||
function(filenames, callback){ | ||
logging.debug('Found '+filenames.length+' file(s) under the package "'+pkg.name+'"'); | ||
var modules = []; | ||
(function next(i){ | ||
if(i>=filenames.length){ | ||
logging.debug('Loaded %d module(s) under the package "%s"',filenames.length,pkg.name); | ||
callback(undefined, modules); | ||
return; | ||
} | ||
loadModule(filenames[i], function(error, module){ | ||
if(error){ | ||
logging.error('Failed to load the module "'+filenames[i]+'"'); | ||
callback(error); | ||
return; | ||
} | ||
module.filename = module.filename.replace(base+'/', ''); | ||
module.filename.indexOf('/') > 0 && ( module.filename = module.filename.replace(base, '') ); | ||
module.id = module.filename.replace(/\.js$/,''); | ||
if(!error) modules.push(module); | ||
next(i+1); | ||
}); | ||
})(0); | ||
})(dirs,callback); | ||
} | ||
function filterFilename(filename,callback){ | ||
return /\.js$/.test(filename); | ||
} | ||
function loadManifest(path, callback){ | ||
logging.debug('Loading the manifest @ "'+path+'"'); | ||
var manifest, | ||
manifestSource; | ||
readFile(path, function(error, bf){ | ||
if(error){ | ||
logging.error('Failed to read the file "'+path+'"'); | ||
return callback(error); | ||
} | ||
logging.debug('Parsing the manifest @ "'+path+'"'); | ||
if(!error){ | ||
manifestSource = bf.toString(); | ||
try { | ||
manifest = JSON.parse(manifestSource); | ||
logging.debug('Manifest file "%s" loaded and parsed successfully.', path); | ||
} catch(exc) { | ||
logging.error('Failed to parse the manifest @ "'+path+'"'); | ||
error = exc; | ||
} | ||
} | ||
callback(error, manifest); | ||
}); | ||
} | ||
var loadModule = (function(){ | ||
var template; | ||
return function loadModule(filename, callback){ | ||
logging.debug('Loading module "'+filename+'"'); | ||
readFile(filename, function(error, bf){ | ||
if(error) return callback(error); | ||
var content = bf.toString(), | ||
name = moduleName(filename); | ||
if(content.substring(0,2) == '#!'){ | ||
content = content.replace(/\#\!.+\n/, ''); | ||
} | ||
callback(undefined, { | ||
'name':name, | ||
'filename':filename, | ||
'path':filename, | ||
'content':content | ||
}); | ||
}); | ||
}; | ||
})(); | ||
function loadPkgFromManifestPath(manifestPath, parentPkg, options, callback){ | ||
logging.debug('Loading package from manifest path "%s"', manifestPath); | ||
loadManifest(manifestPath, function(error, manifest){ | ||
if(error){ | ||
callback(error); | ||
return; | ||
} | ||
var pkg = createPkg(path.normalize(path.dirname(manifestPath)), manifest, parentPkg, options); | ||
loadPkg(pkg, options, callback); | ||
}); | ||
} | ||
function loadPkg(pkg, options, callback){ | ||
logging.debug('Loading the package "%s"', pkg.manifest.name); | ||
collectDeps(pkg, options, function(error, deps){ | ||
if(error){ | ||
logging.error('An unexpected error occurred during collecting dependencies of the package "'+pkg.name+'".'); | ||
logging.error(error); | ||
callback(error); | ||
return; | ||
} | ||
logging.debug('Found '+deps.length+' dependencies for the package "'+pkg.name+'"'); | ||
pkg.dependencies = deps; | ||
collectModules(pkg, function(error, modules){ | ||
if(error){ | ||
logging.error('An unexpected error occurred during collecting modules of the package "'+pkg.name+'".'); | ||
logging.error(error); | ||
callback(error); | ||
return; | ||
} | ||
logging.debug('Collected '+modules.length+' modules for the package "'+pkg.name+'"'); | ||
pkg.modules = modules; | ||
var i = modules.length, m, mainModulePath; | ||
while(i-->0){ | ||
m = modules[i]; | ||
pkg.modulesDict[m.path] = m; | ||
} | ||
if(pkg.manifest.main){ | ||
mainModulePath = path.join(pkg.wd, pkg.manifest.main + ( /\.js$/.test(pkg.manifest.main) ? '' : '.js' )); | ||
pkg.main = pkg.modulesDict[mainModulePath]; | ||
pkg.mainModuleId = pkg.main.name; | ||
} | ||
logging.info('%s loaded.', pkg.name); | ||
callback(error, pkg); | ||
}); | ||
}); | ||
} | ||
function moduleName(filename){ | ||
var m = filename.match(/([^\/\.]+)\.js$/); | ||
return !m ? undefined : m[1]; | ||
} | ||
function createPkg(wd, manifest, parentPkg, options){ | ||
!options && ( options = {} ); | ||
var pkg = { | ||
'id':options.id && options.id() || ( options.id = templating.idGenerator() ), | ||
'dependencies':undefined, | ||
'dirs':manifest.directories || {}, | ||
'main':undefined, | ||
'manifest':manifest, | ||
'modules':undefined, | ||
'modulesDict':{}, | ||
'name':manifest.name, | ||
'parent': parentPkg, | ||
'pkgDict': parentPkg ? parentPkg.pkgDict : {}, | ||
'wd': wd | ||
}; | ||
pkg.pkgDict[pkg.name] = pkg; | ||
return pkg; | ||
} | ||
function quiet(y){ | ||
@@ -321,3 +46,4 @@ logging.setLevel('ERROR'); | ||
logging.error('Failed to write the target file "'+target+'"'); | ||
return callback(error); | ||
callback(error); | ||
return; | ||
} | ||
@@ -355,3 +81,4 @@ | ||
if(i>=len || error){ | ||
return callback(error); | ||
callback(error); | ||
return; | ||
} | ||
@@ -364,3 +91,4 @@ | ||
logging.error('Unknown package "%s" ', pkgName); | ||
return callback(new Error('Unknown package "%s"', pkgName)); | ||
callback(new Error('Unknown package "%s"', pkgName)); | ||
return; | ||
} | ||
@@ -392,12 +120,6 @@ | ||
'build': build, | ||
'collectDeps': collectDeps, | ||
'collectModules': collectModules, | ||
'createPkg': createPkg, | ||
'filterFilename': filterFilename, | ||
'loadManifest': loadManifest, | ||
'loadModule': loadModule, | ||
'loadPkg': loadPkg, | ||
'loadPkgFromManifestPath': loadPkgFromManifestPath, | ||
'dependencies': dependencies, | ||
'packages': packages, | ||
'modules': modules, | ||
'logging': logging, | ||
'moduleName': moduleName, | ||
'quiet': quiet, | ||
@@ -404,0 +126,0 @@ 'publish': publish, |
{ | ||
"name":"one", | ||
"version":"1.2.0", | ||
"version":"1.3.0", | ||
"description":"Transform NodeJS packages into single stand-alone script files.", | ||
@@ -5,0 +5,0 @@ "author":"Azer Koculu <azer@kodfabrik.com>", |
OneJS is a command-line utility for converting CommonJS packages to single, stand-alone JavaScript | ||
files that can be run on other JavaScript platforms such as web browsers, unity, silkjs etc. | ||
files that can be run on web browsers. | ||
# Motivation | ||
* **Reusability** OneJS aims to let developers run NodeJS modules and packages on all environments able to execute JavaScript. | ||
* **Elegant Modularization** OneJS lets web projects benefit from CommonJS, an excellent proposal that gives us a very well designed way to structure JavaScript source codes. | ||
* **NPM** It eventually makes it possible for web projects to use NPM. Which is a great tool that makes controlling dependencies even fun! | ||
* **No Spaghetti Code** No awkward headers, no framework-specific definitions which become deprecated in a few years. | ||
* **Reliable code generation** OneJS doesn't change your source code. It only generates a container environment that simply emulates NodeJS environment. | ||
* **Unobtrusive Code** OneJS generates puts all content into an isolated JS object. | ||
* **Reusability** OneJS let coders use NodeJS libraries in client-side projects. | ||
* **Elegant Modularization** Modules and packages specs of CommonJS are what web apps exactly needs: a very well designed way to structure JavaScript code. | ||
* **NPM** OneJS moves the revolution of NPM one step forward and makes it available for client-side projects! | ||
* **No Spaghetti Code** No awkward headers, no framework-specific definitions. | ||
* **Reliable code generation** OneJS doesn't change your source code. It generates a container that emulates a simple NodeJS environment. | ||
* **Unobtrusive Code** OneJS puts all the content into an isolated JS object. | ||
![](http://oi41.tinypic.com/aw2us3.jpg) | ||
### Examples | ||
* See the example project included in this repository | ||
* See MultiplayerChess.com's source code. | ||
* MultiplayerChess.com ([Source Code](https://github.com/azer/multiplayerchess.com/tree/master/frontend) - [Output](http://multiplayerchess.com/mpc.js) ) | ||
* [boxcars](https://github.com/azer/boxcars) | ||
@@ -21,40 +24,43 @@ # Install | ||
* Tip: Pass -g parameter to install it globally. * | ||
# First Steps | ||
## Testing Example Project | ||
It's an easy way to give OneJS a test-drive. Example project is located at this repository; | ||
## Creating the Bundle Script | ||
```bash | ||
$ git clone git@github.com:azer/onejs.git | ||
$ cd onejs | ||
$ npm install | ||
$ cd example-project | ||
``` | ||
OneJS walks the modules and dependencies defined by package.json files. To create your bundle, just go a project directory and type `onejs build` command: | ||
It's a non-functional NodeJS project with some dummy dependencies under node_modules directory. The built file will contain all the packages under node_modules directory; | ||
``` | ||
$ ../bin/onejs build package.json bundle.js | ||
$ onejs build package.json bundle.js | ||
``` | ||
Now we're willing to test the code OneJS generated for us. Quickest way might be requiring it from NodeJS; | ||
## Experimenting the Bundle Script | ||
The output OneJS generates can be used by NodeJS, too. It's the easiest way of making sure if the output works or not. | ||
``` | ||
> var exampleProject = require('./bundle'); | ||
> exampleProject.main() // calls main module of the package, returns its exports | ||
> exampleProject.main() // calls main module, returns its exports | ||
> exampleProject.require('./b') // each package object has a require method available for external calls | ||
``` | ||
To test it on web browsers, OneJS has a "server" command that builds the source code and start serving it at localhost:1338. | ||
In the case what you need is to try it in web browsers, onejs has a "server" option that'll publish the source code at `localhost:1338` let you debug the output with Firebug Lite easily; | ||
```bash | ||
``` | ||
$ ../bin/onejs server example-project/package.json | ||
``` | ||
You can simply go to that URL and inspect the content of "exampleProject" object, using Firebug Lite. The whole source code with | ||
dependencies (if exists) is wrapped by it. It also provides an external API for the possible clients, containing some methods | ||
such as require, main, stdin, stdout, stderror. | ||
## Using NodeJS Modules | ||
Many modules of the standard NodeJS library is able to be used by web projects, as well. OneJS has an 'install' command that converts demanded remote NodeJS module to a package on the fly: | ||
```javascript | ||
> onejs install assert path url | ||
``` | ||
The reference of available modules that you can install: https://github.com/azer/onejs/blob/master/lib/install_dict.js | ||
## Process | ||
OneJS includes a simple emulation of [NodeJS' process](http://nodejs.org/api/process.html). (Pass --noprocess if you don't need it) | ||
```javascript | ||
> exampleProject.require('dependency'), exampleProject.require('./b'); | ||
@@ -66,11 +72,6 @@ > exampleProject.lib.process.stdout.write("Hello World"); | ||
# Projects Using OneJS | ||
# Troubleshooting | ||
* [MultiplayerChess.com](http://github.com/azer/multiplayerchess.com) | ||
* [HighKick](http://github.com/azer/highkick) | ||
# Beginner's Guide | ||
FIXME | ||
# API Reference | ||
FIXME | ||
* The most common issue of a OneJS output is to lack some dependencies. In that case, make sure that the library is located under `node_modules/` properly. | ||
* Enabling verbose mode might be helpful: `onejs build package.json --verbose` | ||
* See the content of `projectName.map` object if it contains the missing dependency |
@@ -113,3 +113,3 @@ var assert = require('assert'), | ||
assert.equal(p.dependencies.length, 2); | ||
assert.equal(p.dependencies.length, 3); | ||
@@ -120,3 +120,3 @@ callback(); | ||
function test_packageTree(mod, callback){ | ||
assert.equal(mod.map.main.dependencies.length, 2); | ||
assert.equal(mod.map.main.dependencies.length, 3); | ||
assert.equal(mod.map.main.dependencies[0].name, 'dependency'); | ||
@@ -123,0 +123,0 @@ assert.equal(mod.map.main.dependencies[1].name, 'sibling'); |
@@ -30,7 +30,20 @@ var one = require('../lib/one'), | ||
one.build({ 'manifestPath':'example-project/package.json' }, function(error, sourceCode){ | ||
if(error) return callback(error); | ||
if(error) { | ||
callback(error); | ||
return; | ||
} | ||
one.save('tmp/built.js', sourceCode, function(error){ | ||
if(error) return callback(error); | ||
if(error) { | ||
callback(error); | ||
return; | ||
} | ||
kick({ module:require('./build'), 'silent': false, 'name':'built file', 'target':'../tmp/built.js' },function(error,result){ | ||
if(error) return callback(error); | ||
if(error) { | ||
callback(error); | ||
return; | ||
} | ||
callback(result.fail ? new Error('Build tests failed') : undefined); | ||
@@ -42,3 +55,3 @@ }); | ||
function test_collectDeps(callback){ | ||
function test_dependencies(callback){ | ||
var pkg = { | ||
@@ -56,11 +69,23 @@ 'name':'example-project', | ||
one.collectDeps(pkg, { id:templating.idGenerator() }, function(error, deps){ | ||
if(error) return callback(error); | ||
assert.equal(deps.length, 2); | ||
assert.equal(deps[0].name, 'dependency'); | ||
assert.equal(deps[0].parent, pkg); | ||
assert.equal(deps[0].dependencies[0].name, 'subdependency'); | ||
assert.equal(deps[0].dependencies[0].parent, deps[0]); | ||
assert.equal(deps[1].name, 'sibling'); | ||
callback(); | ||
one.dependencies(pkg, { id:templating.idGenerator() }, function(error, deps){ | ||
if(error){ | ||
callback(error); | ||
return; | ||
} | ||
try { | ||
assert.equal(deps.length, 3); | ||
assert.ok(verifyListContent( deps.map(function(el){ return el.name; }), ['dependency', 'sibling', 'assert'])); | ||
var dependency = deps.filter(function(el){ return el.name == 'dependency' })[0]; | ||
assert.equal(dependency.dependencies[0].name, 'subdependency'); | ||
assert.equal(dependency.dependencies[0].parent, deps[0]); | ||
callback(); | ||
} catch(exc) { | ||
callback(exc); | ||
} | ||
}); | ||
@@ -78,3 +103,3 @@ } | ||
function test_loadPkg(callback){ | ||
one.loadPkgFromManifestPath('example-project/package.json', undefined, { id:templating.idGenerator(), 'azer':1 }, function(error, pkg){ | ||
one.packages.loadFromManifestPath('example-project/package.json', undefined, { id:templating.idGenerator(), 'azer':1 }, function(error, pkg){ | ||
if(error) return callback(error); | ||
@@ -88,3 +113,3 @@ | ||
assert.equal(pkg.manifest.name, 'example-project'); | ||
assert.equal(pkg.dependencies.length, 2); | ||
assert.equal(pkg.dependencies.length, 3); | ||
assert.equal(pkg.main.filename, 'a.js'); | ||
@@ -94,3 +119,3 @@ | ||
assert.equal(pkgDict.length, 4); | ||
assert.equal(pkgDict.length, 5); | ||
assert.equal(pkgDict[0], 'example-project'); | ||
@@ -117,4 +142,4 @@ assert.equal(pkgDict[1], 'dependency'); | ||
function test_collectModules(callback){ | ||
one.collectModules({ 'name':'example-project', 'dirs':{'lib':'lib'}, 'wd':'example-project/' }, function(error, modules){ | ||
function test_modules(callback){ | ||
one.modules({ 'name':'example-project', 'dirs':{'lib':'lib'}, 'wd':'example-project/' }, function(error, modules){ | ||
@@ -128,3 +153,3 @@ if(error){ | ||
one.collectModules({ 'name': 'subdependency', 'manifest':{ 'main':'i' }, 'wd':'example-project/node_modules/dependency/node_modules/subdependency/' }, function(error, modules){ | ||
one.modules({ 'name': 'subdependency', 'manifest':{ 'main':'i' }, 'wd':'example-project/node_modules/dependency/node_modules/subdependency/' }, function(error, modules){ | ||
@@ -151,7 +176,7 @@ if(error){ | ||
for(var i = -1, len=legalPaths.length; ++i < len; ){ | ||
assert.ok(one.filterFilename(legalPaths[i])); | ||
assert.ok(one.modules.filterFilename(legalPaths[i])); | ||
}; | ||
for(var i = -1, len=illegalPaths.length; ++i < len; ){ | ||
assert.ok(!one.filterFilename(illegalPaths[i])); | ||
assert.ok(!one.modules.filterFilename(illegalPaths[i])); | ||
}; | ||
@@ -163,3 +188,3 @@ | ||
function test_loadModule(callback){ | ||
one.loadModule('example-project/lib/a.js', function(error, module){ | ||
one.modules.load('example-project/lib/a.js', function(error, module){ | ||
try { | ||
@@ -177,9 +202,9 @@ assert.equal(module.name, 'a'); | ||
function test_moduleName(callback){ | ||
assert.equal(one.moduleName('foo.js'),'foo'); | ||
assert.equal(one.moduleName('foo/bar/qux.js'),'qux'); | ||
assert.equal(one.moduleName('foo')); | ||
assert.equal(one.moduleName('foo/bar/qux')); | ||
assert.equal(one.moduleName('foo.js/bar.js/qux')); | ||
assert.equal(one.moduleName('foo.js/bar.js/qux.js.')); | ||
assert.equal(one.moduleName('qux/quux/c-orge.js'),'c-orge'); | ||
assert.equal(one.modules.fixname('foo.js'),'foo'); | ||
assert.equal(one.modules.fixname('foo/bar/qux.js'),'qux'); | ||
assert.equal(one.modules.fixname('foo')); | ||
assert.equal(one.modules.fixname('foo/bar/qux')); | ||
assert.equal(one.modules.fixname('foo.js/bar.js/qux')); | ||
assert.equal(one.modules.fixname('foo.js/bar.js/qux.js.')); | ||
assert.equal(one.modules.fixname('qux/quux/c-orge.js'),'c-orge'); | ||
callback(); | ||
@@ -204,3 +229,3 @@ } | ||
function test_loadManifest(callback){ | ||
one.loadManifest('example-project/package.json', function(error, manifest){ | ||
one.packages.manifest('example-project/package.json', function(error, manifest){ | ||
assert.equal(manifest.name, "example-project"); | ||
@@ -258,4 +283,4 @@ assert.equal(manifest.main, "./lib/a"); | ||
'test_build':test_build, | ||
'test_collectDeps':test_collectDeps, | ||
'test_collectModules':test_collectModules, | ||
'test_dependencies':test_dependencies, | ||
'test_modules':test_modules, | ||
'test_filterFilename':test_filterFilename, | ||
@@ -262,0 +287,0 @@ 'test_flattenPkgTree':test_flattenPkgTree, |
@@ -13,3 +13,3 @@ var kick = require('highkick'), | ||
kick({ module:require('./main'), name:' main' }, function(error, result){ | ||
kick({ module:require('./main'), name:' main', 'ordered': true }, function(error, result){ | ||
if(error) throw error; | ||
@@ -16,0 +16,0 @@ }); |
@@ -410,4 +410,28 @@ var exampleProject = (function(global, undefined){ | ||
}); | ||
exampleProject.pkg(1, function(parent){ | ||
return { | ||
'id':5, | ||
'name':'assert', | ||
'main':undefined, | ||
'mainModuleId':'assert', | ||
'dependencies':[], | ||
'modules':[], | ||
'parent':parent | ||
}; | ||
}); | ||
exampleProject.module(5, function(parent){ | ||
return { | ||
'id':'assert', | ||
'pkg':parent, | ||
'wrapper':function(module, exports, global, Buffer, process, require, undefined){ | ||
exports.assert = true; | ||
} | ||
}; | ||
}); | ||
if(typeof module != 'undefined' && module.exports ){ | ||
module.exports = exampleProject; | ||
} |
56622
53
1585
11