Socket
Socket
Sign inDemoInstall

node-module-concat

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-module-concat - npm Package Compare versions

Comparing version 1.5.0 to 2.0.0-beta

lib/moduleConcatStream.js

311

index.js
/* node-module-concat
Node.js module concatenation library
This library exposes a single function that concatenates Node.js modules
within a project. This can be used to obfuscate an entire project into a
single file. It can also be used to write client-side JavaScript code where
each file is written just like a Node.js module.
Known limitations:
- Dynamic `require()` statements don't work
(i.e. `require("./" + variable)`)
- `require.resolve` calls are not modified
- `require.cache` statements are not modified
CommonJS module concatenation library
*/
const fs = require("fs"),
ModuleConcatStream = require("./lib/moduleConcatStream");
var fs = require("fs")
, path = require("path")
, stride = require("stride");
// Read main header/footer and header/footer for each file in the project
var header = fs.readFileSync(__dirname + "/lib/header.js").toString("utf8")
, footer = fs.readFileSync(__dirname + "/lib/footer.js").toString("utf8")
, fileHeader = fs.readFileSync(__dirname + "/lib/fileHeader.js").toString("utf8")
, fileFooter = fs.readFileSync(__dirname + "/lib/fileFooter.js").toString("utf8");
/* Concatenate all modules within a project.
The procedure works like this:
0.) A special header file is added to the `outputFile`. See
`./lib/header.js` for details.
1.) Read the entry module, as specified by its path.
2.) Scan the file for `require("...")` or `require('...')` statements.
Note: Dynamic require statements such as `require("./" + b)` are not
matched.
3.) If the fixed path specified by the `require("...")` statement is a
relative or absolute filesystem path (i.e. it begins with "./", "../",
or "/"), then that file is added to the project and recursively scanned
for `require` statements as in step #2. Additionally, the file is given
an unique identifier, and the `require("...")` statement is replaced
with `__require(id)` where `id` is the unique identifier. `__require`
is explained in more detail in `./lib/header.js`.
4.) In addition, any reference to `__dirname` or `__filename` is replaced
with `__getDirname(...)` and `__getFilename(...)`, which are explained
in `./lib/header.js`.
Note: __dirname and __filename references are replaced with paths
relative to the `outputFile` at the time of concatenation.
Therefore, if you move `outputFile` to a different path, the
__dirname and __filename reference will also change, but it will
still be the relative path at the time of concatenation. If you
don't know what I mean and you are having issues, please just read
./lib/header.js and look at the contents of the `outputFile`.
5.) Finally, the modified file is wrapped with a header and footer to
encapsulate the module within its own function. The wrapped code is
then written to the `outputFile`.
6.) Once all of the modules are written to the `outputFile`, a footer is
added to the `outputFile` telling Node to require and execute the entry
module.
Any source file added to the project has:
- A prepended header (./lib/fileHeader.js)
- An appended footer (./lib/fileFooter.js)
- Certain `require` statements replaced with `__require`
- All `__dirname` and `__filename` references replaced with
`__getDirname(...)` and `__getFilename(...)` references.
`modConcat(entryModule, outputFile, [options,] cb)`
- `entryModule` - the path to the entry point of the project to be
concatenated. This might be an `index.js` file, for example.
- `outputFile` - the path where the concatenated project file will be
written.
- `options` - Optional. An Object containing any of the following:
- `outputStreamOptions` - Options passed to `fs.createWriteStream` call
when the `outputFile` is opened for writing. Defaults to `null`.
- `excludeFiles` - An Array of files that should be excluded from the
project even if they were referenced by a `require(...)`.
Note: These `require` statements should probably be wrapped in a
try, catch block to prevent uncaught exceptions.
- `includeNodeModules` - Set to `true` if node_modules should be
included in the `outputFile`
- `cb` - Callback of the form `cb(err, files)` where `files` is an Array
of files that have been included in the project.
*/
module.exports = function concat(entryModule, outputFile, opts, cb) {
// Reorganize arguments
function modConcat(entryModule, outputPath, opts, cb) {
// Re-organize arguments
if(typeof opts === "function") {

@@ -92,199 +13,51 @@ cb = opts;

}
opts = opts || {};
// Ensure that all paths have been resolved
if(opts.excludeFiles) {
for(var i = 0; i < opts.excludeFiles.length; i++) {
opts.excludeFiles[i] = path.resolve(opts.excludeFiles[i]);
}
}
// Ensure that `includeNodeModules` will work properly
var _nodeModulePaths;
if(opts.includeNodeModules) {
try {
_nodeModulePaths = require("module")._nodeModulePaths;
if(typeof _nodeModulePaths !== "function") {
opts.includeNodeModules = false;
// Create a Promise that resolves once the concatenation is complete
let p = new Promise((resolve, reject) => {
opts = opts || {};
opts.outputPath = outputPath;
// Create output stream and ModuleConcatStream
let out = fs.createWriteStream(outputPath);
out.on("error", reject);
let project = new ModuleConcatStream(entryModule, opts);
project.on("error", reject);
// Pipe to outputPath and resolve to stats when complete
out.on("finish", () => {
try {
resolve(project.getStats() )
} catch(e) {
reject(e);
}
} catch(err) {
opts.includeNodeModules = false;
}
}
// A list of all of the files read and included in the output thus far
var files = [];
// A list of all of the native modules not included in the output thus far
var nativeModules = [];
// The file descriptor pointing to the `outputFile`
var fd;
stride(function writeMainHeader() {
var cb = this;
// Open WriteStream
var out = fs.createWriteStream(outputFile, opts.outputStreamOptions);
out.on("open", function(_fd) {
// Save the file descriptor
fd = _fd;
// Write main header
fs.write(fd, header, null, "utf8", cb);
});
}, function processEntryModule() {
// Add entry module to the project
files.push(path.resolve(entryModule) );
addToProject(fd, files[0], this);
}, function writeMainFooter() {
// Write main footer
fs.write(fd, footer, null, "utf8", this);
}).once("done", function(err) {
if(fd) {
fs.close(fd, function(closeErr) {
cb(err || closeErr, files, nativeModules);
});
} else {
cb(err, files, nativeModules);
}
project.pipe(out);
setTimeout(() => project.pause(), 1);
});
function getPathRelativeToOutput(filePath) {
return path.relative(path.dirname(outputFile), filePath);
// Call `cb` if it was provided; otherwise return the Promise
if(typeof cb === "function") {
p.then((data) => {
process.nextTick(() => cb(null, data) );
}).catch((err) => {
process.nextTick(() => cb(err) );
});
} else {
return p;
}
function addToProject(fd, filePath, cb) {
// Keep track of the current `files` length; we need it later
var lengthBefore = files.length;
stride(function writeHeader() {
// Write module header
fs.write(fd, fileHeader
.replace(/\$\{id\}/g, files.indexOf(filePath) )
.replace(/\$\{path\}/g, filePath), null, "utf8", this);
}, function writeExtraHeaderForJSON() {
// If this is a *.json file, add some extra fluff
if(path.extname(filePath) === ".json")
fs.write(fd, "module.exports = ", null, "utf8", this);
else
this(null);
}, function readFile() {
// Read file
fs.readFile(filePath, {"encoding": "utf8"}, this);
}, function processFile(code) {
// Remove some line comments from code
code = code.replace(/(?:\r\n?|\n)\s*\/\/.*/g, "");
// Scan file for `require(...)`, `__dirname`, and `__filename`
/* Quick notes about the somewhat intense `requireRegex`:
- require('...') and require("...") is matched
- The single or double quote matched is group 1
- Whitespace can go anywhere
- The module path matched is group 2
- Backslashes are allowed as escape characters only if followed
by another backlash (to support Windows paths)
*/
var requireRegex = /require\s*\(\s*(["'])((?:(?:(?!\1)[^\\]|(?:\\\\)))*)\1\s*\)/g,
dirnameRegex = /__dirname/g,
filenameRegex = /__filename/g;
code = code.replace(requireRegex, function(match, quote, modulePath) {
// First thing is to replace "\\" with "\"
modulePath = modulePath.replace("\\\\", "\\");
// Check to see if this require path begins with "./" or "../" or "/"
if(modulePath.match(/^\.?\.?\//) !== null) {
try {
modulePath = require.resolve(path.resolve(
path.join(path.dirname(filePath), modulePath)
) );
// Include module in project at end of this function
} catch(e) {
// Ignore; do not replace
return match;
}
} else if(opts.includeNodeModules) {
var oldPaths = module.paths;
/* Temporarily overwrite `module.paths` to make
`require.resolve` work properly */
module.paths = _nodeModulePaths(path.dirname(filePath) );
try {
var modulePath = require.resolve(modulePath);
} catch(err) {
// Module not found; do not replace
return match;
} finally {
// Restore old `module.paths`
module.paths = oldPaths;
}
// Detect core module
if(modulePath.match(/^[a-z_]+$/) ) {
// Core module; do not replace
return match;
}
// Include module in project at end of this function
} else {
// Ignore; do not replace
return match;
}
/* If we reached this point, we need to include `modulePath`
in our project */
// If this is a native module, abort
if(path.extname(modulePath).toLowerCase() === ".node") {
// This is a native module; do not replace
nativeModules.push(modulePath);
return match;
}
// Lookup this module's ID
var index = files.indexOf(modulePath);
if(index < 0) {
// Not found; add this module to the project
if(!opts.excludeFiles ||
opts.excludeFiles.indexOf(modulePath) < 0)
{
index = files.push(modulePath) - 1;
}
else {
// Ignore; do not replace
return match;
}
}
// Replace the `require` statement with `__require`
var parentIndex = files.indexOf(filePath);
return "__require(" + index + "," + parentIndex + ")";
})
// Replace `__dirname` with `__getDirname(...)`
.replace(dirnameRegex, "__getDirname(" +
JSON.stringify(getPathRelativeToOutput(filePath) ) + ")")
// Replace `__filename` with `__getFilename(...)`
.replace(filenameRegex, "__getFilename(" +
JSON.stringify(getPathRelativeToOutput(filePath) ) + ")");
// Write the modified code
fs.write(fd, code, null, "utf8", this);
}, function writeFooter() {
// Write module footer
index = files.indexOf(filePath);
fs.write(fd, fileFooter
.replace(/\$\{id\}/g, index)
.replace(/\$\{path\}/g, filePath), null, "utf8", this);
}, function addPendingFiles() {
// Process any pending files that were required by this file
var lengthAfter = files.length;
var args = [];
for(var i = lengthBefore; i < lengthAfter; i++) {
// Create new context for filename
(function(filename) {
args.push(function() {
addToProject(fd, filename, this);
});
})(files[i]);
}
// Pass the `args` Array to stride, and kick off the processing
stride.apply(null, args).once("done", this);
}).once("done", cb);
}
};
// If this module is invoked directly, behave like a cli
// Expose `modConcat` function and `ModuleConcatStream`
modConcat.ModuleConcatStream = ModuleConcatStream;
module.exports = modConcat;
// If this module is invoked directly, behave like a CLI
if(require.main === module) {
if(process.argv.length !== 4) {
console.log("Usage: node concat.js [entryModule] [outputFile]");
process.exit(1);
console.log("Usage: node concat.js [entryModule] [outputPath]");
return process.exit(1);
}
module.exports(process.argv[2], process.argv[3], function(err, files) {
modConcat(process.argv[2], process.argv[3], function(err, stats) {
if(err) {
console.error("Error", err.stack);
process.exit(1);
}
else {
console.log(files.length + " files written to " + process.argv[3] + ".");
} else {
console.log(stats.files.length + " files written to " +
process.argv[3] + ".");
console.log("Completed successfully.");

@@ -291,0 +64,0 @@ }

@@ -20,3 +20,3 @@ /* This header is placed at the beginning of the output file and defines the

__moduleIsCached[uid] = true;
if(uid === 0 && typeof require === "object") {
if(uid === 0 && typeof require === "function") {
require.main = __modulesCache[0];

@@ -23,0 +23,0 @@ } else {

{
"name": "node-module-concat",
"version": "1.5.0",
"description": "Node.js module concatenation library",
"version": "2.0.0-beta",
"description": "CommonJS module concatenation library",
"main": "index.js",
"dependencies": {
"stride": ">=0.1.4 <2"
"resolve": ">=1.2 <2"
},

@@ -20,2 +20,3 @@ "devDependencies": {},

"module",
"commonjs",
"concatenate",

@@ -26,2 +27,3 @@ "obfuscation",

],
"engines": {"node" : ">=4"},
"author": "Blake Miner <miner.blake@gmail.com>",

@@ -28,0 +30,0 @@ "license": "MIT",

# node-module-concat
Node.js module concatenation library
CommonJS module concatenation library
## What is it?
This library exposes a single function that concatenates Node.js modules
This library exposes a single function that concatenates CommonJS modules
within a project. This can be used to obfuscate an entire project into a

@@ -29,4 +29,44 @@ single file. It can also be used to write client-side JavaScript code where

**`modConcat(entryModule, outputFile, [options,] cb)`**
**`var stream = new modConcat.ModuleConcatStream(entryModulePath [, options])`**
Constructs a [Readable Stream](https://nodejs.org/api/stream.html#stream_class_stream_readable)
of the concatenated project.
- `entryModulePath` - the path to the entry point of the project to be
concatenated. This might be an `index.js` file, for example.
- `options` - object to specify any of the following options
- `outputPath` - the path where the concatenated project file will be
written. Provide this whenever possible to ensure that instances
of `__dirname` and `__filename` are replaced properly. If
`__dirname` and `__filename` are not used in your project or your
project dependencies, it is not necessary to provide this path. This
has no effect when `browser` option is set.
- `excludeFiles` - An Array of files that should be excluded from the
project even if they were referenced by a `require(...)`.
Note: These `require` statements should probably be wrapped with a
conditional or a try/catch block to prevent uncaught exceptions.
- `excludeNodeModules` - Set to `true` if modules loaded from
`node_modules` folders should be excluded from the project.
- `browser` - Set to `true` when concatenating this project for the
browser. In this case, whenever a required library is loaded from
`node_modules`, the `browser` field in the `package.json` file (if
found) is used to determine which file to actually include in the
project.
- Any [option supported by the Readable class]
(https://nodejs.org/api/stream.html#stream_new_stream_readable_options)
**`stream.getStats()`**
Returns an Object containing statistics about the files included in the
project. This object is available after the 'end' event is fired and there
is no more data to consume. Properties include:
- `files` - An Array of files included in the project
- `addonsExcluded` - An Array of files excluded from the project because
they are native C/C++ add-ons.
**`modConcat(entryModule, outputPath, [options, cb])`**
Helper function that constructs a new `ModuleConcatStream` (see above) with
the following options and pipes the concatenated project to the `outputPath`.
- `entryModule` - the path to the entry point of the project to be

@@ -36,17 +76,7 @@ concatenated. This might be an `index.js` file, for example.

written.
- `options` - Optional. An Object containing any of the following:
- `outputStreamOptions` - Options passed to `fs.createWriteStream` call
when the `outputFile` is opened for writing.
- `excludeFiles` - An Array of files that should be excluded from the
project even if they were referenced by a `require(...)`.
- `options` - See `options` for `ModuleConcatStream` above.
- `cb` - Callback of the form `cb(err, stats)`. If no callback is provided,
a Promise is returned instead, which resolves to the `stats` Object returned
by `stream.getStats()` (see above).
Note: These `require` statements should probably be wrapped in a
try, catch block to prevent uncaught exceptions.
- `includeNodeModules` - Set to `true` if node_modules should also be
included in the project.
- `cb` - Callback of the form `cb(err, files, nativeModules)` where `files` is
an Array of files that have been included in the project and
`nativeModules` is an Array of native modules that were found (but not
included) when scanning the project.
## Known limitations

@@ -53,0 +83,0 @@ - Dynamic `require()` statements don't work

var fs = require("fs")
, hello = require("./lib/hello")
, world = require("./lib/world")
, func = require("./lib/func");
, func = require("./lib/func")
, cool = require("cool");
try {
let foo = require("notfound");
} catch(e) {}
console.log(hello.hello + ", " + world.world);

@@ -18,1 +23,2 @@ console.log(fs.readFileSync(__filename).toString() );

}
console.log(cool);

Sorry, the diff of this file is not supported yet

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