Commit 01c7d52e authored by Andrew Kelley's avatar Andrew Kelley

Imported Upstream version 0.0.1

parents
/node_modules
The MIT License (Expat)
Copyright (c) 2014 Andrew Kelley
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# browserify-lite
browserify, minus some of the advanced features and heavy dependencies.
* No builtin Node.js shims.
* Naive AST tokenization for require instead of true AST parsing.
- Require statements must be outside all braces. This includes functions,
control statements, and objects.
* Only supports a single entry file and the `--outfile` parameter,
nothing else.
* Minimal dependencies.
#!/usr/bin/node
var fs = require('fs');
var browserifyLite = require('./');
var entrySourcePath = process.argv[2];
var outputFileParam = process.argv[3];
var outBundlePath = process.argv[4];
if (outputFileParam !== '--outfile') {
console.error("Expected second param to be --outfile");
process.exit(1);
}
if (!outBundlePath || !entrySourcePath) {
console.error("Expected first arg to be source path and third arg to be out bundle path.");
process.exit(1);
}
browserifyLite.createBundle(entrySourcePath, outBundlePath, function(err) {
if (err) throw err;
});
var fs = require('fs');
var path = require('path');
var Pend = require('pend');
exports.extractRequires = extractRequires;
exports.createBundle = createBundle;
function createBundle(entrySourcePath, outBundlePath, cb) {
// data structure that is filled up with canonical source path as the key,
// source code as the value.
var sources = {};
// describes the dependency graph. key is canonical source path, value is
// array of canonical source path dependencies
var deps = {};
// each file has its own map of how require statements resolve.
var depMap = {};
var sourceQueue = [];
requireResolve(entrySourcePath, process.cwd(), function(err, resolvedPath) {
if (err) return cb(err);
sourceQueue.push(resolvedPath);
collectDependencies(function(err) {
if (err) return cb(err);
render(resolvedPath, function(err, output) {
if (err) return cb(err);
fs.writeFile(outBundlePath, output, cb);
});
});
});
function collectDependencies(cb) {
var canonicalSourcePath = sourceQueue.shift();
if (!canonicalSourcePath) return cb();
if (sources[canonicalSourcePath]) return collectDependencies(cb);
fs.readFile(canonicalSourcePath, {encoding: 'utf8'}, function(err, source) {
if (err) return cb(err);
sources[canonicalSourcePath] = source;
deps[canonicalSourcePath] = {};
depMap[canonicalSourcePath] = {};
var pend = new Pend();
extractRequires(source, function(err, requireList) {
if (err) return cb(err);
requireList.forEach(function(requireItem) {
pend.go(function(cb) {
requireResolve(requireItem, path.dirname(canonicalSourcePath), function(err, canonicalDepPath) {
if (err) return cb(err);
deps[canonicalSourcePath][canonicalDepPath] = true;
depMap[canonicalSourcePath][requireItem] = canonicalDepPath;
sourceQueue.push(canonicalDepPath);
cb();
});
});
});
pend.wait(function(err) {
if (err) return cb(err);
collectDependencies(cb);
});
});
});
}
function render(entrySourcePath, cb) {
var modules = Object.keys(sources);
var aliases = {};
modules.forEach(function(canonicalSourcePath, index) {
aliases[canonicalSourcePath] = index;
});
modules.forEach(function(canonicalSourcePath, index) {
var thisDepMap = depMap[canonicalSourcePath]
for (var depSourcePath in thisDepMap) {
thisDepMap[depSourcePath] = aliases[thisDepMap[depSourcePath]];
}
});
var out =
"(function(modules, cache, entry) {\n" +
" req(entry);\n" +
" function req(name) {\n" +
" if (cache[name]) return cache[name].exports;\n" +
" var m = cache[name] = {exports: {}};\n" +
" modules[name][0].call(m.exports, modRequire, m, m.exports);\n" +
" return m.exports;\n" +
" function modRequire(alias) {\n" +
" var id = modules[name][1][alias];\n" +
" if (!id) throw new Error(\"Cannot find module \" + alias);\n" +
" return req(id);\n" +
" }\n" +
" }\n" +
"})({";
modules.forEach(function(canonicalSourcePath) {
out += aliases[canonicalSourcePath] + ": [function(require,module,exports){\n";
out += sources[canonicalSourcePath];
out += "\n}, " + JSON.stringify(depMap[canonicalSourcePath]) + "],";
});
out += "}, {}, " + aliases[entrySourcePath] + ");\n";
cb(null, out);
}
}
function tokenizeSource(source) {
var tokens = [];
var inQuote = false;
var braceCount = 0;
var quoteType;
var qEscape = false;
var token = "";
var inLineComment = false;
var inMultiLineComment = false;
var startComment = false;
var endComment = false;
for (var i = 0; i < source.length; i += 1) {
var c = source[i];
if (inQuote) {
if (qEscape) {
token += c;
qEscape = false;
} else if (c === "\\") {
qEscape = true;
} else if (c === quoteType) {
inQuote = false;
if (braceCount === 0) {
tokens.push(token);
}
token = "";
} else {
token += c;
}
} else if (inLineComment) {
if (c === "\n") {
inLineComment = false;
}
} else if (inMultiLineComment) {
if (c === '*') {
endComment = true;
} else if (c === '/') {
if (endComment) {
inMultiLineComment = false;
endComment = false;
}
} else {
endComment = false;
}
} else if (c === "\"" || c === "'") {
startComment = false;
if (token) tokens.push(token);
token = "";
inQuote = true;
quoteType = c;
qEscape = false;
} else if (c === '{') {
startComment = false;
if (token) tokens.push(token);
token = "";
braceCount += 1;
} else if (c === '}') {
startComment = false;
braceCount -= 1;
} else if (c === '/') {
if (startComment) {
if (token) tokens.push(token);
token = "";
inLineComment = true;
startComment = false;
} else {
startComment = true;
}
} else if (c === '*' && startComment) {
if (token) tokens.push(token);
token = "";
inMultiLineComment = true;
startComment = false;
} else if (braceCount === 0) {
if (/\W/.test(c)) {
if (token) tokens.push(token);
token = "";
}
if (/\S/.test(c)) {
token += c;
}
} else {
startComment = false;
}
}
if (token) tokens.push(token);
return tokens;
}
var stateCount = 0;
var STATE_WANT_REQUIRE = stateCount++;
var STATE_WANT_LPAREN = stateCount++;
var STATE_WANT_STR = stateCount++;
var STATE_WANT_RPAREN = stateCount++;
function extractRequires(source, cb) {
var tokens = tokenizeSource(source);
var requiresList = [];
var state = STATE_WANT_REQUIRE;
var requireName;
for (var i = 0; i < tokens.length; i += 1) {
var token = tokens[i];
if (state === STATE_WANT_REQUIRE && token === 'require') {
state = STATE_WANT_LPAREN;
} else if (state === STATE_WANT_LPAREN && token === '(') {
state = STATE_WANT_STR;
} else if (state === STATE_WANT_STR) {
requireName = token;
state = STATE_WANT_RPAREN;
} else if (state === STATE_WANT_RPAREN && token === ')') {
state = STATE_WANT_REQUIRE;
requiresList.push(requireName);
}
}
cb(null, requiresList);
}
function requireResolve(pkg, basedir, cb) {
if (/^[.\/]/.test(pkg)) {
requireResolvePath(path.resolve(basedir, pkg), cb);
} else {
requireResolveModule(pkg, basedir, cb);
}
}
function requireResolveModule(pkg, basedir, cb) {
var globalSearchPaths = process.env.NODE_PATH ? process.env.NODE_PATH.split(path.delimiter) : [];
var localSearchPaths = [];
var parts = basedir.split(path.sep);
var it = "/";
for (var i = 0; i < parts.length; i += 1) {
it = path.join(it, parts[i]);
localSearchPaths.unshift(path.join(it, "node_modules"));
}
var searchPaths = localSearchPaths.concat(globalSearchPaths);
var index = 0;
trySearchPath();
function trySearchPath() {
var searchPath = searchPaths[index];
if (!searchPath) return cb(new Error("module not found"));
requireResolvePath(path.resolve(searchPath, pkg), function(err, resolvedFilename) {
if (!err) return cb(null, resolvedFilename);
index += 1;
trySearchPath();
});
}
}
function requireResolvePath(filename, cb) {
resolveFile(filename, function(err, resolvedFilename) {
if (!err) return cb(null, resolvedFilename);
resolveFile(filename + '.js', function(err, resolvedFilename) {
if (!err) return cb(null, resolvedFilename);
resolveFile(filename + '.json', function(err, resolvedFilename) {
if (!err) return cb(null, resolvedFilename);
resolveDirectory(filename, cb);
});
});
});
}
function resolveFile(filename, cb) {
fs.stat(filename, function(err, stat) {
if (err) return cb(err);
if (stat.isDirectory()) return cb(new Error("directory"));
cb(null, filename);
});
}
function resolveDirectory(dirname, cb) {
var packageJsonPath = path.resolve(dirname, "package.json");
fs.readFile(packageJsonPath, {encoding: 'utf8'}, function(err, packageJsonStr) {
var packageJson;
try {
packageJson = JSON.parse(packageJsonStr);
} catch (err) {
cb(err);
return;
}
var filename;
if (packageJson.main) {
filename = path.resolve(dirname, packageJson.main);
resolveFile(filename, tryIndex);
} else {
tryIndex(new Error("no main found in package.json"));
}
function tryIndex(err) {
if (!err) return cb(null, filename);
filename = path.resolve(dirname, "index.js");
resolveFile(filename, function(err) {
if (!err) return cb(null, filename);
filename = path.resolve(dirname, "index.json");
resolveFile(filename, cb);
});
}
});
}
{
"name": "browserify-lite",
"version": "0.0.1",
"description": "browserify, minus some of the advanced features and heavy dependencies.",
"main": "index.js",
"scripts": {
"test": "node test.js"
},
"bin": {
"browserify-lite": "./cli.js"
},
"author": "Andrew Kelley <superjoe30@gmail.com>",
"license": "MIT",
"dependencies": {
"pend": "~1.1.3"
},
"repository": {
"type": "git",
"url": "git://github.com/andrewrk/browserify-lite.git"
},
"bugs": {
"url": "https://github.com/andrewrk/browserify-lite/issues"
}
}
var assert = require('assert');
var browserifyLite = require('./');
var extractRequiresTests = [
{
name: "basic",
source: "require('./code')",
output: [
"./code"
],
},
{
name: "multiple",
source:
"var EventEmitter = require('./event_emitter');\n" +
"var inherits = require('./inherits');\n" +
"var uuid = require('./uuid');\n" +
"var MusicLibraryIndex = require('music-library-index');\n" +
"var keese = require(\"keese\");\n" +
"var curlydiff = require('curlydiff');\n",
output: [
"./event_emitter",
"./inherits",
"./uuid",
"music-library-index",
"keese",
"curlydiff",
],
},
{
name: "trick",
source: "require('./code');\nvar a = \"require('foo');\";\nrequire(\"../morecode\");",
output: [
"./code",
"../morecode",
],
},
{
name: "unescape",
source: "require('./code');\nvar a = \"require(\\\"foo\\\");\";\nrequire(\"../morecode\");",
output: [
"./code",
"../morecode",
],
},
{
name: "spaces",
source: "var foo = require ( 'derp ' ) ;\n",
output: [
"derp ",
],
},
{
name: "ignore braces",
source: "var foo = require('derp'); { require('ignore-this'); } require('this-ok')\n",
output: [
"derp",
"this-ok",
],
},
{
name: "ignore comments",
source: "/* var foo = require('derp');*/ { require('ignore-this'); } require('this-ok') // require('also-ignore-this'); \n require('this-also-ok')",
output: [
"this-ok",
"this-also-ok",
],
},
];
process.stderr.write("extract requires tests:\n");
extractRequiresTests.forEach(function(extractRequiresTest) {
process.stderr.write(extractRequiresTest.name + "...");
browserifyLite.extractRequires(extractRequiresTest.source, function(err, requiresList) {
if (err) throw err;
assert.deepEqual(extractRequiresTest.output, requiresList);
process.stderr.write("OK\n");
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment