Skip to content

Commit 460eb96

Browse files
committed
Add lazy-eval option.
1 parent ea93c70 commit 460eb96

File tree

7 files changed

+88
-26
lines changed

7 files changed

+88
-26
lines changed

README.markdown

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ Usage
2121
`modulr` accepts a singular file as input (the _program_) on which is does static
2222
analysis to recursively resolve its dependencies.
2323

24-
The program, its dependencies and a small, namespaced JavaScript library are concatenated into a single `js` file. This
25-
[improves load times by minimizing HTTP requests](http://developer.yahoo.com/performance/rules.html#num_http).
24+
The program, its dependencies and a small, namespaced JavaScript library are
25+
concatenated into a single `js` file. This improves load times by
26+
[minimizing HTTP requests](http://developer.yahoo.com/performance/rules.html#num_http).
27+
Further load time performance improvements are made possible by the built-in
28+
[lazy evaluation](http://googlecode.blogspot.com/2009/09/gmail-for-mobile-html5-series-reducing.html)
29+
option. Modules are delivered as JavaScript strings--instead of functions--and are
30+
evaluated only when required.
2631

2732
The bundled JavaScript library provides each module with the necessary `require`
2833
function and `exports` and `module` free variables.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.1
1+
0.2.0

assets/modulr.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ var modulr = (function(global) {
3030

3131
fn = _modules[id];
3232
if (!fn) { throw 'Can\'t find module "' + identifier + '".'; }
33+
if (typeof fn === 'string') {
34+
fn = eval('function(require, exports, module) {' + fn + '}');
35+
}
3336
fn(require, expts, modObj);
3437
}
3538
return expts;

bin/modulrize

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,24 @@ options = {
77
}
88

99
opts = OptionParser.new do |opts|
10-
opts.banner = 'Usage: modulrize [options] FILE'
10+
opts.banner = 'Usage: modulrize program.js [options] > output.js'
1111

12-
opts.on('-o', '--output=FILE', 'Write the output to FILE. Defaults to stdout') do |output|
12+
opts.on('-o', '--output=FILE', 'Write the output to FILE. Defaults to stdout.') do |output|
1313
options[:output] = File.open(output, 'w')
1414
end
1515

16-
opts.on('-r', '--root=DIR', 'Set DIR as root directory. Defaults to the directory containing FILE') do |root|
16+
opts.on('-r', '--root=DIR', 'Set DIR as root directory. Defaults to the directory containing FILE.') do |root|
1717
options[:root] = root
1818
end
1919

20-
opts.on_tail('-h', '--help', 'Show this message') do
20+
opts.on('--lazy-eval [MODULES]', Array,
21+
'Enable lazy evaluation of all JS modules or of those specified by MODULES.',
22+
'MODULES accepts a comma-separated list of identifiers.') do |modules|
23+
modules = true unless modules
24+
options[:lazy_eval] = modules
25+
end
26+
27+
opts.on_tail('-h', '--help', 'Show this message.') do
2128
puts opts
2229
exit
2330
end

lib/modulr.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class ModulrError < StandardError
1515
PATH_TO_MODULR_JS = File.join(LIB_DIR, '..', 'assets', 'modulr.js')
1616

1717
def self.ize(input_filename, options = {})
18-
collector = Collector.new(options[:root])
18+
collector = Collector.new(options)
1919
collector.parse_file(input_filename)
2020
collector.to_js
2121
end

lib/modulr/collector.rb

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,48 @@ module Modulr
22
class Collector
33
attr_reader :modules, :main
44

5-
def initialize(root = nil)
6-
@root = root
7-
@modules = {}
5+
def initialize(options = {})
6+
@root = options[:root]
7+
@lazy_eval = options[:lazy_eval]
8+
@modules = []
89
end
910

1011
def parse_file(path)
1112
@src = File.read(path)
1213
@root ||= File.dirname(path)
1314
@main = JSModule.new(File.basename(path, '.js'), @root, path)
14-
modules[main.id] = main
15+
modules << main
1516
collect_dependencies(main)
1617
end
17-
18-
def collect_dependencies(js_module)
19-
js_module.dependencies.each do |dependency|
20-
unless modules[dependency.id]
21-
modules[dependency.id] = dependency
22-
collect_dependencies(dependency)
23-
end
24-
end
25-
end
2618

2719
def to_js(buffer = '')
28-
buffer << File.read(PATH_TO_MODULR_JS);
29-
modules.each { |id, js_module| js_module.to_js(buffer) }
20+
buffer << File.read(PATH_TO_MODULR_JS)
21+
modules.each do |js_module|
22+
if lazy_eval_module?(js_module)
23+
js_module.to_js_string(buffer)
24+
else
25+
js_module.to_js(buffer)
26+
end
27+
end
3028
buffer << "\nmodulr.require('#{main.identifier}');\n"
3129
end
30+
31+
private
32+
def collect_dependencies(js_module)
33+
js_module.dependencies.each do |dependency|
34+
unless modules.include?(dependency)
35+
modules << dependency
36+
collect_dependencies(dependency)
37+
end
38+
end
39+
end
40+
41+
def lazy_eval_module?(js_module)
42+
return false unless @lazy_eval
43+
return true if @lazy_eval === true
44+
return true if @lazy_eval.include?(js_module.identifier)
45+
return true if @lazy_eval.include?(js_module.id)
46+
false
47+
end
3248
end
3349
end

lib/modulr/js_module.rb

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
module Modulr
22
class JSModule
3+
include Comparable
4+
5+
JS_ESCAPE_MAP = {
6+
'\\' => '\\\\',
7+
'</' => '<\/',
8+
"\r\n" => '\n',
9+
"\n" => '\n',
10+
"\r" => '\n',
11+
'"' => '\\"',
12+
"'" => "\\'"
13+
}
14+
315
def self.parser
416
@dependency_finder ||= Parser.new
517
end
@@ -21,6 +33,10 @@ def initialize(identifier, root, file=nil, line=nil)
2133
@terms = identifier.split('/').reject { |term| term == '' }
2234
raise ModuleIdentifierError.new(self) unless identifier_valid?
2335
end
36+
37+
def <=> (other_module)
38+
id <=> other_module.id
39+
end
2440

2541
def inspect
2642
"#<#{self.class.name} \"#{identifier}\">"
@@ -61,18 +77,27 @@ def src
6177
end
6278
end
6379

80+
def escaped_src
81+
@escaped_src ||= src.gsub(/(\\|<\/|\r\n|[\n\r"'])/) {
82+
JS_ESCAPE_MAP[$1]
83+
}
84+
end
85+
6486
def dependencies
6587
@dependencies ||= self.class.find_dependencies(self)
6688
end
6789

6890
def to_js(buffer = '')
69-
if relative?
70-
buffer << "\nmodulr.alias('#{identifier}', '#{id}');"
71-
end
91+
call_alias_js_function(buffer)
7292
fn = "function(require, exports, module) {\n#{src}\n}"
7393
buffer << "\nmodulr.cache('#{id}', #{fn});\n"
7494
end
7595

96+
def to_js_string(buffer = '')
97+
call_alias_js_function(buffer)
98+
buffer << "\nmodulr.cache('#{id}', '#{escaped_src}');\n"
99+
end
100+
76101
protected
77102
def partial_path
78103
File.join(*terms)
@@ -81,6 +106,12 @@ def partial_path
81106
def directory
82107
relative? ? File.dirname(file) : root
83108
end
109+
110+
def call_alias_js_function(buffer)
111+
if relative?
112+
buffer << "\nmodulr.alias('#{identifier}', '#{id}');"
113+
end
114+
end
84115
end
85116

86117
class ModuleIdentifierError < ModulrError

0 commit comments

Comments
 (0)