Comparing version 0.1.0 to 0.1.1
@@ -8,35 +8,2 @@ var fs = require('fs'), | ||
var styles = { | ||
//styles | ||
'bold' : ['\033[1m', '\033[22m'], | ||
'italic' : ['\033[3m', '\033[23m'], | ||
'underline' : ['\033[4m', '\033[24m'], | ||
'inverse' : ['\033[7m', '\033[27m'], | ||
//grayscale | ||
'white' : ['\033[37m', '\033[39m'], | ||
'grey' : ['\033[90m', '\033[39m'], | ||
'black' : ['\033[30m', '\033[39m'], | ||
//colors | ||
'blue' : ['\033[34m', '\033[39m'], | ||
'cyan' : ['\033[36m', '\033[39m'], | ||
'green' : ['\033[32m', '\033[39m'], | ||
'magenta' : ['\033[35m', '\033[39m'], | ||
'red' : ['\033[31m', '\033[39m'], | ||
'yellow' : ['\033[33m', '\033[39m'] | ||
}, | ||
levelMap = { debug: 1, info: 2, warn: 3, error: 4 }; | ||
function style(str, style) { | ||
return styles[style][0] + str + styles[style][1]; | ||
} | ||
Minilog | ||
.pipe(Minilog.backends.nodeConsole) | ||
.format(function(name, level, args) { | ||
var colors = { debug: 'blue', info: 'cyan', warn: 'yellow', error: 'red' }; | ||
return (name ? style(name +' ', 'grey') : '') | ||
+ (level ? style(level, colors[level]) + ' ' : '') | ||
+ args.join(' '); | ||
}); | ||
var requireCode = fs.readFileSync(__dirname + '/require.js', 'utf8') | ||
@@ -55,2 +22,3 @@ .replace(/\/*([^/]+)\/\n/g, '') | ||
options || (options = { }); | ||
// Package | ||
@@ -66,2 +34,3 @@ this.build = new Package(); | ||
this.code = {}; | ||
this._concatFiles = []; | ||
}; | ||
@@ -159,6 +128,7 @@ | ||
Renderer.prototype.render = function(onDone){ | ||
var self = this; | ||
this._render(function(out) { | ||
// place replaced modules into modules[0] | ||
Object.keys(out.replaced).forEach(function(key) { | ||
out.modules[0][key] = '{ exports: ' + out.replaced[key] + ' }\n'; | ||
out.modules[0][key] = '{ exports: ' + out.replaced[key] + ' }'; | ||
}); | ||
@@ -177,7 +147,11 @@ // place injected code into modules[0] | ||
}); | ||
return str + 'require.modules['+counter+'] = { ' + keys.join(',') + '};\n'; | ||
return str + 'require.modules['+counter+'] = { ' + keys.join(',\n') + '};\n'; | ||
}, '') | ||
// name to export | ||
+ out.exportLine + '\n' | ||
+ '}());'); | ||
+ '}());' | ||
+ self._concatFiles.map(function(fpath) { | ||
log.debug('Concatenating', fpath); | ||
return fs.readFileSync(fpath); | ||
}).join('')); | ||
}); | ||
@@ -220,3 +194,8 @@ }; | ||
Renderer.concat = Renderer.prototype.concat = function(arr, callback) { | ||
Renderer.prototype.concat = function(filepath) { | ||
this._concatFiles.push(filepath); | ||
return this; | ||
}; | ||
Renderer.concat = function(arr, callback) { | ||
var data = ''; | ||
@@ -223,0 +202,0 @@ function run(callable) { |
@@ -21,3 +21,3 @@ var fs = require('fs'), | ||
// take all the .js files and concatenate them | ||
re: new RegExp('.*\.js$'), | ||
re: new RegExp('.*\\.js$'), | ||
handler: function(opts, done) { | ||
@@ -88,3 +88,4 @@ return done(opts.filename, fs.readFileSync(opts.filename, 'utf8')); | ||
var handlers = [].concat(globalHandlers, this.handlers); | ||
var handlers = [].concat(globalHandlers, this.handlers), | ||
sizes = []; | ||
@@ -110,3 +111,4 @@ this.files.forEach(function(filename) { | ||
opts.relativeFilename = opts.relative(filename); | ||
log.debug('Processing ('+self.name + '): ' +filename + ' as ' + opts.relative(filename)); | ||
log.debug('Processing ('+(self.name ? self.name : 'root') + '): ' +filename + ' as ' + opts.relative(filename)); | ||
sizes.push({ filename: opts.relative(filename), size: fs.statSync(filename).size }); | ||
matching[0].handler(opts, function(filename, source) { | ||
@@ -123,6 +125,14 @@ var relpath = relative(filename); | ||
// serial execution for tasks | ||
series(tasks, function() { onDone(result); }); | ||
series(tasks, function() { | ||
// summarize sizes | ||
var total = sizes.reduce(function(prev, item) { return prev + item.size}, 0); | ||
sizes.sort(function(a, b) { return b.size - a.size; }); | ||
sizes.forEach(function(item, index) { | ||
log.info('' + (index+1) + '. ' + item.filename + ' '+ (item.size / 1024).toFixed(2) +'k (' + Math.floor( item.size / total * 100 )+ ' %)'); | ||
}); | ||
log.info('Package total: ' + (total / 1024).toFixed(2) +'k'); | ||
onDone(result); | ||
}); | ||
}; | ||
Package.prototype.render = function(result, onDone) { | ||
@@ -129,0 +139,0 @@ var self = this, |
{ | ||
"name": "gluejs", | ||
"description": "Build CommonJS modules for the browser via a chainable API", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"author": { | ||
@@ -19,6 +19,10 @@ "name": "Mikito Takada", | ||
], | ||
"main": "lib/glue.js", | ||
"main": "lib/index.js", | ||
"bin": { | ||
"gluejs": "./bin/gluejs" | ||
}, | ||
"dependencies": { | ||
"package-json-resolver": "git://github.com/mixu/package-json-resolver.git#master", | ||
"minilog": "0.0.4" | ||
"minilog": "0.0.4", | ||
"argsparser": "0.0.6" | ||
}, | ||
@@ -25,0 +29,0 @@ "devDependencies": { |
211
readme.md
# gluejs | ||
A CommonJS-based build system with a chainable API | ||
Build CommonJS modules for the browser via a chainable API | ||
@@ -20,56 +20,85 @@ ### Features | ||
new Glue() | ||
.basepath('./lib') // output paths are relative to this | ||
.include('./lib') // includes all files in the dir | ||
.exclude(new RegExp('.+\.test\.js')) // excludes .test.js | ||
.replace({ | ||
'jquery': 'window.$', // binds require('jquery') to window.$ | ||
'Chat': 'window.Chat' | ||
}) | ||
.export('App') // the package is output as window.App | ||
.render(function (err, txt) { | ||
// send the package as a response to a HTTP request | ||
res.setHeader('content-type', 'application/javascript'); | ||
res.end(txt); | ||
// or write the result to a file | ||
fs.writeFile('./app.js', txt); | ||
}); | ||
```javascript | ||
new Glue() | ||
.basepath('./lib') // output paths are relative to this | ||
.include('./lib') // includes all files in the dir | ||
.exclude(new RegExp('.+\\.test\\.js')) // excludes .test.js | ||
.replace({ | ||
'jquery': 'window.$', // binds require('jquery') to window.$ | ||
'Chat': 'window.Chat' | ||
}) | ||
.export('App') // the package is output as window.App | ||
.render(function (err, txt) { | ||
// send the package as a response to a HTTP request | ||
res.setHeader('content-type', 'application/javascript'); | ||
res.end(txt); | ||
// or write the result to a file | ||
fs.writeFile('./app.js', txt); | ||
}); | ||
``` | ||
## Using the resulting file | ||
To use the resulting file, just include the build result: | ||
<script src="app.js"></script> | ||
<script> | ||
console.log(window.App); // single external interface to the package | ||
</script> | ||
The require() statements inside the package work just like under Node, yet none of the internals are leaked into the the global namespace. | ||
gluejs does not export a global "require()" function in the browser; this means that it is compatible with other code since all details are hidden and only a single interface is exported (main file's ```module.exports```). The reasons behind this are documented in much more detail in my book, "[Single page applications in depth](http://singlepageappbook.com/maintainability1.html)". | ||
An additional benefit is that you only need one HTTP request to load a package, and that the resulting files can be redistributed (e.g. to a non-Node web application) without worry. | ||
## Including files / directories, excluding by regexp | ||
```include(path)```: If the path is a file, include it. If the path is a directory, include all files in it recursively. | ||
`.include(path)`: If the path is a file, include it. If the path is a directory, include all files in it recursively. | ||
```exclude(regexp)```: excludes all files matching the regexp from the build. Evaluated just before rendering the build so it applies to all files. | ||
`.exclude(regexp)`: excludes all files matching the regexp from the build. Evaluated just before rendering the build so it applies to all files. | ||
## Including npm packages | ||
gluejs can also include dependencies from npm. You have to first ```npm install``` the packages, then gluejs reads, wraps and includes them in your build. This is done recursively, so the dependencies of dependencies are also packaged. | ||
`npm(name, [searchFrom])`: includes a single package from a directory. The package is searched from `searchFrom+"/node_modules/"` - the default value for searchFrom is the basepath for the build. The dependency is then available via require(name). | ||
`npm(pathToPackageJson)`: includes all dependencies from the package.json file. Note that things under "devDependencies" are not included. The dependencies are searched starting from pathToPackageJson. Each dependency is accessible via its name. | ||
Sub-dependencies are also automatically bundled, as long as they've been installed by npm. Since the require() semantics are the same as in Node, subdependencies can depend on different versions of the same module without conflicting with each other. | ||
## Setting default values | ||
.defaults({ | ||
// all relative include() paths are resolved relative to this path | ||
reqpath: '', | ||
```javascript | ||
.defaults({ | ||
// all relative include() paths are resolved relative to this path | ||
reqpath: '', | ||
// strip this string from each path | ||
// (e.g. /foo/bar/baz.js with '/foo' becomes 'bar/baz.js') | ||
basepath: '', | ||
// strip this string from each path | ||
// (e.g. /foo/bar/baz.js with '/foo' becomes 'bar/baz.js') | ||
basepath: '', | ||
// main file, preset default is index.js | ||
main: 'index.js', | ||
// main file, preset default is index.js | ||
main: 'index.js', | ||
// name for the variable under window to which the package is exported | ||
export: '', | ||
// name for the variable under window to which the package is exported | ||
export: '', | ||
// binds require('jquery') to window.$ | ||
replace: { 'jquery': 'window.$' } | ||
}) | ||
// binds require('jquery') to window.$ | ||
replace: { 'jquery': 'window.$' } | ||
}) | ||
``` | ||
## Outputting | ||
```.export(name)```: sets the export name (e.g. export('Foo') => window.Foo = require('index.js'); ) | ||
`.export(name)`: sets the export name (e.g. export('Foo') => window.Foo = require('index.js'); ) | ||
```.render(function(err, text){ ...})```: renders the result | ||
`.render(function(err, text){ ...})`: renders the result | ||
## Watching files for changes | ||
```.watch(function(err, text){ ...})```: renders and adds file watchers on the files. | ||
`.watch(function(err, text){ ...})`: renders and adds file watchers on the files. | ||
When a file in the build changes, the ```watch()``` callback will be called again with the new build result. | ||
When a file in the build changes, the `watch()` callback will be called again with the new build result. | ||
@@ -96,8 +125,10 @@ Note that this API is a bit clunky: | ||
```replace(module, code)```: Meant for replacing a module with a single variable or expression. Examples: | ||
`.replace(module, code)`: Meant for replacing a module with a single variable or expression. Examples: | ||
// define require('jquery') as window.$ | ||
.replace('jquery', 'window.$'); | ||
// define require('debug') as the function below | ||
.replace('debug', function debug() { return debug() }); | ||
```javascript | ||
// define require('jquery') as window.$ | ||
.replace('jquery', 'window.$'); | ||
// define require('debug') as the function below | ||
.replace('debug', function debug() { return debug() }); | ||
``` | ||
@@ -112,3 +143,5 @@ ## Source URLs | ||
.set('debug', true) | ||
```javascript | ||
.set('debug', true) | ||
``` | ||
@@ -123,28 +156,31 @@ Note that source URLs require that scripts are wrapped in a eval block, which is a bit ugly, so you probably don't want that in production mode. | ||
To specify a handler, call ```handler(regexp, function(opts, done) { ... })``` | ||
To specify a handler, call `handler(regexp, function(opts, done) { ... })` | ||
Here is an example: | ||
var Template = require('templating-library'); | ||
var extensionRe = new RegExp('(.+)\.tpl$'); | ||
new Glue() | ||
.include('./fixtures/mixed_content/') | ||
.handler(extensionRe, function(opts, done) { | ||
var wrap = opts.wrap, filename = opts.filename; | ||
var out = Template.precompile( | ||
fs.readFileSync(filename).toString() | ||
); | ||
done(wrap(filename.replace(extensionRe, '$1.js'), out)); | ||
}) | ||
.render(function(err, txt) { | ||
console.log(txt); | ||
done(); | ||
}); | ||
```javascript | ||
var Template = require('templating-library'); | ||
var extensionRe = new RegExp('(.+)\\.tpl$'); | ||
new Glue() | ||
.include('./fixtures/mixed_content/') | ||
.handler(extensionRe, function(opts, done) { | ||
var wrap = opts.wrap, filename = opts.filename; | ||
var out = Template.precompile( | ||
fs.readFileSync(filename).toString() | ||
); | ||
done(filename.replace(extensionRe, '$1.js'), out); | ||
}) | ||
.render(function(err, txt) { | ||
console.log(txt); | ||
done(); | ||
}); | ||
``` | ||
In fact, internally, the ".js" extension handler is just: | ||
.handler(new RegExp('.*\.js$'), function(opts, done) { | ||
return done(opts.wrap(opts.filename, | ||
fs.readFileSync(opts.filename, 'utf8'))); | ||
}); | ||
```javascript | ||
.handler(new RegExp('.*\.js$'), function(opts, done) { | ||
return done(opts.filename, fs.readFileSync(opts.filename, 'utf8')); | ||
}); | ||
``` | ||
@@ -161,3 +197,2 @@ Handler params: | ||
- relativeFilename: the file name relative to the gluejs basepath | ||
- wrap: a function(filename, content) which wraps the content string inside a anonymous function, just like normal JS files. | ||
- second param (done): a callback(string) which should be called with the return value - this allows for async calls inside the handler. | ||
@@ -171,3 +206,5 @@ | ||
.define('model', 'require("./model")'); | ||
```javascript | ||
.define('model', 'require("./model")'); | ||
``` | ||
@@ -178,9 +215,11 @@ Don't use require('model') in files inside the ./model directory, since that may introduce a circular dependency (e.g. model/a -> model/index -> model/a). | ||
```define(module, code)```: Meant for writing a full module. The difference here is that while replace() code is not wrapped in a closure while define() code is. | ||
`.define(module, code)`: Meant for writing a full module. The difference here is that while replace() code is not wrapped in a closure while define() code is. | ||
.define('index.js', [ 'module.exports = {', | ||
[ (hasBrowser ? " browser: require('./backends/browser_console.js')" : undefined ), | ||
(hasLocalStorage ? " localstorage: require('./backends/browser_localstorage.js')" : undefined ) | ||
].filter(function(v) { return !!v; }).join(',\n'), | ||
'};'].join('\n')); | ||
```javascript | ||
.define('index.js', [ 'module.exports = {', | ||
[ (hasBrowser ? " browser: require('./backends/browser_console.js')" : undefined ), | ||
(hasLocalStorage ? " localstorage: require('./backends/browser_localstorage.js')" : undefined ) | ||
].filter(function(v) { return !!v; }).join(',\n'), | ||
'};'].join('\n')); | ||
``` | ||
@@ -193,17 +232,23 @@ The example above generates a index.js file depending on hasBrowser and hasLocalStorage. | ||
var packageA = new Glue() | ||
.basepath('./fixtures/') | ||
.export('Foo') | ||
.include('./fixtures/lib/foo.js'); | ||
var packageB = new Glue() | ||
.basepath('./fixtures/') | ||
.export('Bar') | ||
.include('./fixtures/lib/bar.js'); | ||
```javascript | ||
var packageA = new Glue() | ||
.basepath('./fixtures/') | ||
.export('Foo') | ||
.include('./fixtures/lib/foo.js'); | ||
var packageB = new Glue() | ||
.basepath('./fixtures/') | ||
.export('Bar') | ||
.include('./fixtures/lib/bar.js'); | ||
Glue.concat([packageA, packageB], function(err, txt) { | ||
console.log(txt); | ||
}); | ||
Glue.concat([packageA, packageB], function(err, txt) { | ||
console.log(txt); | ||
}); | ||
``` | ||
## TODO | ||
## A few notes about npm dependencies | ||
.npm(file.json): includes a package.json | ||
The main file is determined by looking at the "main" key in package.json and resolution follows the require() rules as documented in the Node API docs. | ||
Only files ending with .js are included in the builds, since require() only works with .js, .json and .node files (the last one being for compiled native modules). | ||
The .npmignore file is honored. It works like a .gitignore file. This is the preferred way of excluding files and directories from npm dependencies according to ```man npm developers```. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
28934
10
479
247
1
0
3
+ Addedargsparser@0.0.6
+ Addedargsparser@0.0.6(transitive)