Skip to content

Instantly share code, notes, and snippets.

@sstephenson
Created December 23, 2010 23:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sstephenson/753684 to your computer and use it in GitHub Desktop.
Save sstephenson/753684 to your computer and use it in GitHub Desktop.
Sandboxed CommonJS module contexts for Node
{Script} = process.binding 'evals'
natives = process.binding 'natives'
Path = require 'path'
FS = require 'fs'
globals = {
Buffer, process, console,
setTimeout, clearTimeout,
setInterval, clearInterval
}
modularize = (source) -> """
(function(module, exports, require, __filename, __dirname) {
#{source}
})
"""
find = (base, path, types) ->
for candidate in candidatesFor base, path, types
if Path.existsSync candidate
return candidate if FS.statSync(candidate).isFile()
candidatesFor = (base, path, types) ->
candidates = [Path.join base, path]
for extension of types
candidates.push Path.join base, path + extension
for extension of types
candidates.push Path.join base, path, 'index' + extension
candidates
id = 0
compilerID = (compiler) ->
compiler.id ||= ++id
moduleCacheKeyFor = (path, compiler) ->
"#{compilerID compiler}/#{path}"
exports.Module = class Module
@nativeCache: []
@sourceCache: []
@loadNative: (path) ->
@nativeCache[path] ||= new Module natives[path], path + '.js'
@load: (path, compiler) ->
@sourceCache[moduleCacheKeyFor path, compiler] ||= (
source = compiler FS.readFileSync path, 'utf8'
new Module source, path
)
constructor: (@source, @path) ->
@script = new Script modularize(@source), @path
compile: (context) ->
closure = context.run @script
module = exports: {}, parent: {filename: @path}
require = @createRequire context
closure module, module.exports, require, @path, Path.dirname @path
module.exports
createRequire: (context) ->
base = @path
require = (path) -> context.require path, base
require.paths = context.paths
require
exports.Context = class Context
constructor: ->
@global = Script.createContext globals
@cache = {}
@paths = []
@types = Object.create exports.defaultTypes
run: (script) ->
script.runInContext @global
require: (path, base) ->
if path of natives
@cache[path] ||= (
module = exports.Module.loadNative path
module.compile this
)
else if actualPath = @resolve path, base
@cache[actualPath] ||= (
type = Path.extname actualPath
module = exports.Module.load actualPath, @types[type]
module.compile this
)
else
throw "can't find module #{path}"
resolve: (path, base) ->
if /^\.\.?\//.test path
return match if match = find Path.dirname(base), path, @types
else
for dirname in @paths
return match if match = find dirname, path, @types
exports.defaultTypes =
'.js': (source) ->
source.replace /^\#!.*?\n/, ''
try
CoffeeScript = require 'coffee-script'
exports.defaultTypes['.coffee'] = (source) ->
CoffeeScript.compile source, bare: true
try
eco = require 'eco'
exports.defaultTypes['.eco'] = (source) ->
eco.compile source
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment