Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

component-builder

Package Overview
Dependencies
Maintainers
1
Versions
61
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

component-builder - npm Package Compare versions

Comparing version 0.11.2 to 0.12.0

lib/build.js

27

History.md
0.12.0 / 2014-01-27
==================
* refactor to rework-style tree [BREAKING CHANGE]
0.11.2 / 2013-12-30

@@ -19,3 +24,3 @@ ==================

0.10.0 / 2013-10-17
0.10.0 / 2013-10-17
==================

@@ -25,3 +30,3 @@

0.9.2 / 2013-09-12
0.9.2 / 2013-09-12
==================

@@ -31,3 +36,3 @@

0.9.1 / 2013-09-06
0.9.1 / 2013-09-06
==================

@@ -37,3 +42,3 @@

0.9.0 / 2013-07-19
0.9.0 / 2013-07-19
==================

@@ -47,3 +52,3 @@

0.8.3 / 2013-06-06
0.8.3 / 2013-06-06
==================

@@ -55,10 +60,10 @@

0.8.2 / 2013-05-13
0.8.2 / 2013-05-13
==================
* fix .basename for root components not in a directory of the same config `.name`
* fix .basename for root components not in a directory of the same config `.name`
* fix improper asset rewrite url join on windows. Closes #94
* fix copy/link attempts to files that ENOENT
* fix copy/link attempts to files that ENOENT
0.8.1 / 2013-04-26
0.8.1 / 2013-04-26
==================

@@ -68,3 +73,3 @@

0.8.0 / 2013-04-24
0.8.0 / 2013-04-24
==================

@@ -74,3 +79,3 @@

0.7.1 / 2013-04-24
0.7.1 / 2013-04-24
==================

@@ -77,0 +82,0 @@

@@ -0,1 +1,2 @@

/**

@@ -5,24 +6,8 @@ * Module dependencies.

var fs = require('fs')
, cp = require('cp')
, path = require('path')
, join = path.join
, resolve = path.resolve
, dirname = path.dirname
, Batch = require('batch')
, debug = require('debug')('component:builder')
, Emitter = require('events').EventEmitter
, requirejs = require('component-require')
, read = fs.readFileSync
, exists = fs.existsSync
, mkdir = require('mkdirp')
, utils = require('./utils')
, dirname = path.dirname
, basename = path.basename
, resolveURL = require('url').resolve
, requires = require('requires')
, strtojs = require('string-to-js');
var Resolver = require('./resolver');
var Build = require('./build');
var Batch = require('batch');
/**
* Expose `Builder`.
* Expose `Builder`
*/

@@ -33,192 +18,34 @@

/**
* Initialize a new `Builder` with the given component `dir`.
*
* @param {String} dir
* @param {Builder} parent
* @api private
* Expose plugins
*/
function Builder(dir, parent) {
var self = this;
this._cache = {};
this._hooks = {};
this._files = {};
this._js = '';
this.urlPrefix = '';
this.copy = false;
this.dir = dir;
this.root = ! parent;
this.parent = parent;
this.globalLookupPaths = [];
this.lookupPaths = [];
this.ignored = {
scripts: [],
styles: [],
files: [],
images: [],
fonts: [],
templates: [],
json: []
};
Builder.copy = require('./plugins/copy');
Builder.symlink = require('./plugins/symlink');
Builder.concat = require('./plugins/concat');
Builder.commonjs = require('./plugins/commonjs');
Builder.rewriteUrls = require('./plugins/rewrite-urls');
// load config
this.config = this.json();
this.config.paths = this.config.paths || [];
this.basename = this.root
? this.config.name
: basename(dir);
// root level lookup paths are
// applied globally and affect
// all sub-components
if (this.root) {
self.addGlobalRelativeLookup(this.config.paths);
} else {
this.addRelativeLookup(this.config.paths);
}
// default lookup paths
if (this.root) self.addGlobalRelativeLookup('components');
// handle inheritance
this.on('dependency', this.inherit.bind(this));
}
/**
* Inherits from `Emitter.prototype`.
*/
Builder.prototype.__proto__ = Emitter.prototype;
/**
* Copy assets to the given `dir`.
* Initialize a new `Builder`.
*
* @param {String} dir
* @param {String} path
* @api public
*/
Builder.prototype.copyAssetsTo = function(dir){
this.assetsDest = dir;
};
function Builder(path){
if (!(this instanceof Builder)) return new Builder(path);
this.resolver = new Resolver(path);
this.batch = new Batch;
this.batch.concurrency(1);
}
/**
* Prefix css `url()`s with `str`. For example
* when building in development and serving from ./build
* you'll typically want "./" so they become relative
* and work well with `file://`. However when serving
* from your application you may want "/public", or
* no prefix at all such as `/mycomponent/images/foo.png`.
* Allow dev dependencies.
*
* @param {String} str
* @api public
*/
Builder.prototype.prefixUrls = function(str){
this.urlPrefix = str;
};
/**
* Inherit lookup paths, ignored, and asset dest dir.
*
* @param {Builder} dep
* @api private
*/
Builder.prototype.inherit = function(dep){
dep.copy = this.copy;
dep._cache = this._cache;
dep._hooks = this._hooks;
dep.ignored = this.ignored;
dep.prefixUrls(this.urlPrefix);
dep.copyAssetsTo(this.assetsDest);
dep.globalLookupPaths = this.globalLookupPaths;
if (this.sourceUrls) dep.addSourceURLs();
};
/**
* Enable "devDependencies" in the build.
*
* @api public
*/
Builder.prototype.development = function(){
debug('dev dependencies enabled');
this.dev = true;
};
/**
* Enable "copyFiles" in the build.
*
* @api public
*/
Builder.prototype.copyFiles = function(){
debug('copy files enabled');
this.copy = true;
};
/**
* Enable "sourceURLs" in the build.
*
* @api public
*/
Builder.prototype.addSourceURLs = function(){
this.sourceUrls = true;
};
/**
* Check if this build has deps.
*
* @return {Boolean}
* @api private
*/
Builder.prototype.hasDependencies = function(){
var conf = this.config;
if (conf.local) return true;
if (conf.dependencies) return true;
if (this.dev && conf.development) return true;
};
/**
* Return local dependencies object.
*
* @return {Object}
* @api public
*/
Builder.prototype.local = function(){
return this.config.local.reduce(function(obj, name){
obj[name] = '*';
return obj;
}, {});
};
/**
* Return dependencies.
*
* @return {Object}
* @api private
*/
Builder.prototype.dependencies = function(){
var conf = this.config;
var deps = conf.dependencies || {};
if (this.dev) merge(deps, conf.development || {});
if (conf.local) merge(deps, this.local());
return deps;
};
/**
* Add global lookup path(s).
*
* @param {String|Array} path
* @return {Builder}
* @api public
*/
Builder.prototype.addLookup = function(path){
this.globalLookupPaths = this.globalLookupPaths.concat(path);
this.resolver.development();
return this;

@@ -228,16 +55,10 @@ };

/**
* Add global lookup path(s) relative to this component's directory.
* Add global relative path.
*
* @param {String|Array} path
* @return {Builder}
* @param {String} path
* @api public
*/
Builder.prototype.addGlobalRelativeLookup = function(path){
if (Array.isArray(path)) {
path.forEach(this.addGlobalRelativeLookup.bind(this));
} else {
this.addLookup(resolve(this.dir, path));
}
Builder.prototype.path = function(path){
this.resolver.add(path);
return this;

@@ -247,324 +68,21 @@ };

/**
* Add lookup path(s) relative to this component's directory.
* Use the given `fn`.
*
* @param {String|Array} path
* @return {Builder}
* @api public
*/
Builder.prototype.addRelativeLookup = function(path){
if (Array.isArray(path)) {
path.forEach(this.addRelativeLookup.bind(this));
} else {
this.lookupPaths.push(resolve(this.dir, path));
}
return this;
};
/**
* Lookup component `name` using `.paths`
* and invoke `fn(err, dir)`.
*
* @param {String} name
* @param {String} fn
* @api public
*/
Builder.prototype.lookup = function(name, fn){
var cache = this._cache;
var paths = this.globalLookupPaths.concat(this.lookupPaths);
var self = this;
var i = 0;
function next() {
var path = paths[i++];
// no more paths
if (!path) return fn(new Error('failed to lookup "' + self.basename + '"\'s dependency "' + name + '"'));
// path
var dir = join(path, name);
var key = name + ':' + dir;
// check cache
var val = cache[key];
if (null != val) {
if (!val) return next();
return fn(null, val);
}
// lookup
debug('lookup %s', name);
var yes = exists(join(dir, 'component.json'));
if (yes) {
cache[key] = dir;
return fn(null, dir);
}
cache[key] = false;
next();
}
next();
};
/**
* Ignore the given component name(s) of `type`.
*
* This method is used internally to prevent
* buffering of duplicate components.
*
* @param {String} name
* @param {String} type
* @api public
*/
Builder.prototype.ignore = function(name, type){
if (!type) {
this.ignore(name, 'scripts');
this.ignore(name, 'styles');
return;
}
debug('ignore %j %s', name, type);
if (Array.isArray(name)) {
for (var i = 0; i < name.length; ++i) {
this.ignored[type].push(normalize(name[i]));
}
} else {
this.ignored[type].push(normalize(name));
}
};
/**
* Check if the builder is ignoring `name` and `type`.
*
* @param {String} name
* @param {String} type
* @return {Boolean}
* @api public
*/
Builder.prototype.ignoring = function(name, type){
if (!type) {
return this.ignoring(name, 'scripts')
&& this.ignoring(name, 'styles');
}
return ~this.ignored[type].indexOf(name);
};
/**
* Return a resolved path relative to this
* builder's dir.
*
* @param {String} file
* @return {String}
* @api public
*/
Builder.prototype.path = function(file){
return path.resolve(path.join(this.dir, file));
};
/**
* Add file `type` `filename` contents of `val`.
*
* @param {String} type
* @param {String} filename
* @param {String} val
* @return {Type}
* @api public
*/
Builder.prototype.addFile = function(type, filename, val){
debug('add %s "%s"', type, filename);
var files = this.config[type] || (this.config[type] = []);
files.push(filename);
this._files[filename] = val;
};
/**
* Add remove file `type` `filename` so it does not become
* part of the build output, for example when transpiling
* "list.styl" in the `.styles` array to "list.css", otherwise
* the contents of "list.styl" will become part of the output.
*
* @param {String} type
* @param {String} filename
* @param {String} val
* @return {Type}
* @api public
*/
Builder.prototype.removeFile = function(type, filename){
debug('remove %s "%s"', type, filename);
var files = this.config[type] || (this.config[type] = []);
var i = files.indexOf(filename);
if (~i) files.splice(i, 1);
};
/**
* Append the given `str` of javascript.
*
* @param {String} str
* @api public
*/
Builder.prototype.append = function(str){
this._js += str;
};
/**
* Return the configuration JSON.
*
* @return {Object}
* @api public
*/
Builder.prototype.json = function(){
var path = this.path('component.json');
debug('reading %s', path);
// TODO: cache
var str = read(path, 'utf8');
try {
var obj = JSON.parse(str);
} catch (err) {
err.message += ' in ' + path;
throw err;
}
utils.normalizeConfig(obj);
return obj;
};
/**
* Build and invoke `fn(err, res)`, where `res`
* is an object containing:
*
* - `css`
* - `js`
* - `images`
* - `fonts`
* - `files`
*
* NOTE: Batch maintains result ordering (res.shift()s here)
*
* @param {Function} fn
* @api public
* @api private
*/
Builder.prototype.build = function(fn){
Builder.prototype.use = function(fn){
var self = this;
var batch = new Batch;
debug('building %s', this.dir);
batch.push(this.buildScripts.bind(this));
batch.push(this.buildJson.bind(this));
batch.push(this.buildTemplates.bind(this));
batch.push(this.buildStyles.bind(this));
batch.push(this.buildImages.bind(this));
batch.push(this.buildFonts.bind(this));
batch.push(this.buildFiles.bind(this));
batch.end(function(err, res){
if (err) return fn(err);
var scripts = res.shift();
var json = res.shift();
var templates = res.shift();
var custom = self._js;
var js = [scripts, json, templates, custom].filter(empty).join('\n');
fn(null, {
js: js,
css: res.shift(),
images: res.shift(),
fonts: res.shift(),
files: res.shift(),
require: requirejs
});
this.batch.push(function(done){
fn(self._build, done);
});
};
/**
* Build `type` and invoke `fn`.
*
* @param {String} type
* @param {String} fn
* @param {String} process
* @api private
*/
Builder.prototype.buildType = function(type, fn, process){
var self = this;
var batch = new Batch;
var conf = this.config;
debug('building %s %s', this.basename, type);
// build dependencies
if (self.hasDependencies()) {
Object.keys(self.dependencies()).forEach(function(dep){
dep = normalize(dep);
// ignored
if (self.ignoring(dep, type)) return debug('ignoring %s', dep);
// ignore it so we dont have dups
self.ignore(dep, type);
// lookup dep
batch.push(function(done){
self.lookup(dep, function(err, dir){
if (err) return done(err);
debug('building dependency %s in %s', dep, dir);
var builder = new Builder(dir, self);
self.emit('dependency', builder);
builder.buildType(type, done, process);
});
});
});
}
// "before <type>" hook
self.performHook('before ' + type, function(err) {
if (err) return fn(err);
// build files
if (conf[type]) {
conf[type].forEach(function(file){
var path = self.path(file);
batch.push(function(done){
var val = self._files[file];
// on disk
if (null == val) {
debug('read file %s', path);
fs.readFile(path, 'utf8', function(err, str){
if (err) return fn(err);
done(null, process(self, file, str));
});
return
}
// fabricated
done(null, process(self, file, val));
});
});
}
batch.end(function(err, res){
if (err) return fn(err);
fn(null, res.join('\n'));
});
});
return this;
};
/**
* Build asset `type` and invoke `fn(err, paths)`.
* Build and call `fn(err, build)`.
*
* @param {String} type
* @param {Function} fn

@@ -574,395 +92,13 @@ * @api private

Builder.prototype.buildAsset = function(type, fn){
Builder.prototype.build = function(fn){
var self = this;
var conf = this.config;
var batch = new Batch;
debug('build asset %s', type);
// build dependencies
if (self.hasDependencies()) {
Object.keys(self.dependencies()).forEach(function(dep){
dep = normalize(dep);
// ignored
if (self.ignoring(dep, type)) return debug('ignoring %s', dep);
// ignore it so we dont have dups
self.ignore(dep, type);
// lookup dep
batch.push(function(done){
self.lookup(dep, function(err, dir){
if (err) return done(err);
var builder = new Builder(dir, self);
self.emit('dependency', builder);
builder.buildAsset(type, done);
});
});
});
}
// copy assets
if (conf[type]) {
conf[type].forEach(function(file){
var path = self.path(file);
var name = normalize(self.basename);
var dest = join(self.assetsDest, name, file);
batch.push(function(done){
self.copyTo(path, dest, done);
});
});
}
batch.end(function(err, res){
this.resolver.end(function(err, list){
if (err) return fn(err);
fn(null, res);
});
};
/**
* Copy `file` to `dest` and invoke `fn(err, path)`.
*
* @param {String} file
* @param {String} dest
* @param {Function} fn
* @api private
*/
Builder.prototype.copyTo = function(file, dest, fn){
var dir = dirname(dest)
, self = this
function done(err) {
if (err && 'EEXIST' == err.code) return fn(null, dest);
fn(err, dest);
}
debug('mkdir -p %s', dir);
mkdir(dir, function(err){
if (err) return fn(err);
fs.exists(file, function(exists){
if (!exists) return fn(new Error('Path does not exist: ' + file));
if (self.copy){
debug('cp %s -> %s', file, dest);
cp(file, dest, done);
} else {
debug('link %s -> %s', file, dest);
fs.symlink(file, dest, done);
}
self._build = new Build(list);
self._build.dev = self.dev;
self.batch.end(function(err){
if (err) return fn(err);
fn(null, self._build);
});
});
};
/**
* Build `.files` array and invoke `fn(err, paths)`.
*
* @param {Function} fn
* @api private
*/
Builder.prototype.buildFiles = function(fn){
this.buildAsset('files', fn);
};
/**
* Build `.images` array and invoke `fn(err, paths)`.
*
* @param {Function} fn
* @api private
*/
Builder.prototype.buildImages = function(fn){
this.buildAsset('images', fn);
};
/**
* Build `.fonts` array and invoke `fn(err, paths)`.
*
* @param {Function} fn
* @api private
*/
Builder.prototype.buildFonts = function(fn){
this.buildAsset('fonts', fn);
};
/**
* Build scripts and invoke `fn(err, js)`.
*
* @param {Function} fn
* @api private
*/
Builder.prototype.buildScripts = function(fn){
this.buildType('scripts', fn, register);
};
/**
* Build JSON and invoke `fn(err, js)`.
*
* @param {Function} fn
* @api private
*/
Builder.prototype.buildJson = function(fn){
this.buildType('json', fn, function(builder, file, str){
return register(builder, file, 'module.exports = ' + str);
});
};
/**
* Build templates and invoke `fn(err, str)`
*
* @param {Function} fn
* @api private
*/
Builder.prototype.buildTemplates = function(fn){
this.buildType('templates', fn, function(builder, file, str){
return register(builder, file, strtojs(str));
});
};
/**
* Build styles and invoke `fn(err, css)`.
*
* @param {Function} fn
* @api private
*/
Builder.prototype.buildStyles = function(fn){
var self = this;
this.buildType('styles', fn, rewriteUrls);
};
/**
* Use the given plugin `fn`.
*
* @param {Function} fn
* @return {Builder}
* @api public
*/
Builder.prototype.use = function(fn){
fn(this);
return this;
};
/**
* Perform hook `name` passing this builder
* to the hook callback, allowing the hook
* to work with the "current" builder instance
* specific to each component, _not_ the root
* builder.
*
* @param {String} name
* @return {Mixed}
* @api priate
*/
Builder.prototype.performHook = function(name, fn){
debug('invoke hook "%s"', name);
var hooks = this._hooks[name] || [];
var self = this;
var i = 0;
function next(err) {
if (err) return fn(err);
var hook = hooks[i++];
if (!hook) return fn();
if (hook.length > 1) return hook(self, next);
hook(self);
next();
}
next();
};
/**
* Define hook `name` with callback `fn()`.
*
* @param {String} name
* @param {String} fn
* @api private
*/
Builder.prototype.hook = function(name, fn){
debug('hook into "%s"', name);
this._hooks[name] = this._hooks[name] || [];
this._hooks[name].push(fn);
};
/**
* No-op processor function.
*/
function noop(builder, file, str){
return str;
}
/**
* Return a js string representing a commonjs
* client-side module with the given `builder`,
* `file` and `js`.
*
* NOTE: Here we special-case the root script so
* that for example if you are building "tip"
* to test, you may require('tip') instead of
* require('component-tip');
*
* TODO: ^ remove this special-casing for lazy-loading
*
* @param {Builder} builder
* @param {String} file
* @param {String} js
* @return {String}
* @api private
*/
function register(builder, file, js){
// strip ./
if ('./' == file.slice(0, 2)) file = file.slice(2);
var main = builder.config.main;
var isMain = main == file;
var orig = file;
// determine prefix
// TODO: should always be name / version combo
var prefix = builder.root
? builder.config.name
: builder.basename;
// prefix
file = prefix + '/' + file;
// rewrite requires for dependencies,
// and fix relative requires when .main
// is not index.js
js = requires(js, function(require){
var deps = builder.config.dependencies || {};
var path = require.path;
var name = canonicalized(path, deps) || path;
return 'require("' + name + '")';
});
// register the module
if (builder.sourceUrls) {
js = JSON.stringify(js + '//@ sourceURL=' + file);
js = js.replace(/\\n/g, '\\n\\\n');
js = 'require.register("' + file + '", Function("exports, require, module",\n'
+ js
+ '\n));';
} else {
js = 'require.register("' + file + '", function(exports, require, module){\n'
+ js
+ '\n});';
}
// main support
// TODO: dear god, this is nasty, doesnt belong
// in here but doing a separate tree walk is lame
// so screw it, works for now, need to rewrite this thing
// once we produce a dep tree first then we can completely
// remove all of this junk, as we'll know what all the mains
// are and can simply rewrite
if (isMain && 'index.js' != orig) {
js += '\nrequire.main("' + prefix + '", "' + orig + '")';
}
return js;
}
/**
* Return canonical name of `dep` in `deps`
* or undefined.
*
* @param {String} dep
* @param {Object} deps
* @return {String}
* @api private
*/
function canonicalized(dep, deps) {
for (var name in deps) {
if (dep == name.split('/')[1]) {
return normalize(name);
}
}
}
/**
* Return css with urls rewritten relative
* to the `.assetDest` directory. This allows
* the consumer to serve the asset destination
* directory (typically `./build`) to match
* the symlinks made.
*
* @param {Builder} builder
* @param {String} file
* @param {String} css
* @return {String}
* @api private
*/
function rewriteUrls(builder, file, css) {
function isAbsolute(url) {
return ~url.indexOf('://') || url[0] == '/';
}
function isData(url) {
return 0 == url.indexOf('data:');
}
function rewrite(_, url) {
var orig = 'url(' + url + ')';
url = utils.stripQuotes(url);
if (isData(url)) return orig;
if (isAbsolute(url)) return orig;
var name = normalize(builder.basename);
// trailing `/` needed to distinguish the URL as a folder
// for `url.resolve()`
var folder = [builder.urlPrefix, name, dirname(file)].filter(empty).join('/') + '/';
url = resolveURL(folder, url);
return 'url("' + url + '")';
}
return css.replace(/\burl *\(([^)]+)\)/g, rewrite);
}
/**
* Empty string filter.
*/
function empty(s) {
return '' != s;
}
/**
* Merge `b` into `a`.
*
* @param {Object} a
* @param {Object} b
* @return {Object} a
* @api private
*/
function merge(a, b) {
for (var key in b) a[key] = b[key];
return a;
}
/**
* Normalize package `name`.
*
* @param {String} name
* @return {String}
* @api private
*/
function normalize(name) {
return name.replace('/', '-');
}
{
"name": "component-builder",
"version": "0.11.2",
"version": "0.12.0",
"description": "Component build tool",

@@ -11,4 +11,4 @@ "keywords": [

"dependencies": {
"component-require": "1.0.1",
"batch": "0.2.1",
"component-require": "0.3.1",
"batch": "0.5.0",
"mkdirp": "0.3.4",

@@ -15,0 +15,0 @@ "debug": "*",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc