modulr 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +7 -2
- data/Rakefile +3 -3
- data/VERSION +1 -1
- data/assets/modulr.js +11 -8
- data/bin/modulrize +11 -4
- data/lib/modulr/collector.rb +30 -41
- data/lib/modulr/js_module.rb +54 -10
- data/lib/modulr/parser.rb +35 -0
- data/lib/modulr.rb +2 -1
- metadata +2 -1
data/README.markdown
CHANGED
@@ -21,8 +21,13 @@ Usage
|
|
21
21
|
`modulr` accepts a singular file as input (the _program_) on which is does static
|
22
22
|
analysis to recursively resolve its dependencies.
|
23
23
|
|
24
|
-
The program, its dependencies and a small, namespaced JavaScript library are
|
25
|
-
|
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.
|
26
31
|
|
27
32
|
The bundled JavaScript library provides each module with the necessary `require`
|
28
33
|
function and `exports` and `module` free variables.
|
data/Rakefile
CHANGED
@@ -23,7 +23,7 @@ task :spec do
|
|
23
23
|
begin
|
24
24
|
puts File.basename(dir).center(80, "_")
|
25
25
|
File.open(output, 'w') do |f|
|
26
|
-
f << Modulr.ize(spec)
|
26
|
+
f << Modulr.ize(spec, {:lazy_eval => ['a']})
|
27
27
|
end
|
28
28
|
system("js -f #{output}")
|
29
29
|
rescue => e
|
@@ -31,8 +31,8 @@ task :spec do
|
|
31
31
|
puts "ERROR while #{phase} (#{e.class}):"
|
32
32
|
puts e.message
|
33
33
|
ensure
|
34
|
-
FileUtils.rm(output)
|
35
|
-
FileUtils.rm(system)
|
34
|
+
#FileUtils.rm(output)
|
35
|
+
#FileUtils.rm(system)
|
36
36
|
puts
|
37
37
|
puts
|
38
38
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/assets/modulr.js
CHANGED
@@ -16,7 +16,8 @@ var modulr = (function(global) {
|
|
16
16
|
|
17
17
|
function require(identifier) {
|
18
18
|
var fn, modObj,
|
19
|
-
|
19
|
+
key = PREFIX + identifier,
|
20
|
+
id = _references[key] || key,
|
20
21
|
expts = _exports[id];
|
21
22
|
|
22
23
|
log('Required module "' + identifier + '".');
|
@@ -29,20 +30,22 @@ var modulr = (function(global) {
|
|
29
30
|
|
30
31
|
fn = _modules[id];
|
31
32
|
if (!fn) { throw 'Can\'t find module "' + identifier + '".'; }
|
33
|
+
if (typeof fn === 'string') {
|
34
|
+
fn = eval('function(require, exports, module) {' + fn + '}');
|
35
|
+
}
|
32
36
|
fn(require, expts, modObj);
|
33
37
|
}
|
34
38
|
return expts;
|
35
39
|
}
|
36
40
|
|
37
|
-
function cache(
|
38
|
-
|
39
|
-
id = PREFIX + id;
|
41
|
+
function cache(id, fn) {
|
42
|
+
var key = PREFIX + id;
|
40
43
|
|
41
|
-
|
42
|
-
|
44
|
+
log('Cached module "' + id + '".');
|
45
|
+
if (_modules[key]) {
|
46
|
+
throw 'Can\'t overwrite module "' + id + '".';
|
43
47
|
}
|
44
|
-
_modules[
|
45
|
-
_references[PREFIX + identifier] = id;
|
48
|
+
_modules[key] = fn;
|
46
49
|
}
|
47
50
|
|
48
51
|
function alias(identifier, id) {
|
data/bin/modulrize
CHANGED
@@ -7,17 +7,24 @@ options = {
|
|
7
7
|
}
|
8
8
|
|
9
9
|
opts = OptionParser.new do |opts|
|
10
|
-
opts.banner = 'Usage: modulrize [options]
|
10
|
+
opts.banner = 'Usage: modulrize program.js [options] > output.js'
|
11
11
|
|
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|
|
13
13
|
options[:output] = File.open(output, 'w')
|
14
14
|
end
|
15
15
|
|
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|
|
17
17
|
options[:root] = root
|
18
18
|
end
|
19
19
|
|
20
|
-
opts.
|
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
|
21
28
|
puts opts
|
22
29
|
exit
|
23
30
|
end
|
data/lib/modulr/collector.rb
CHANGED
@@ -1,60 +1,49 @@
|
|
1
|
-
require 'rkelly'
|
2
|
-
|
3
1
|
module Modulr
|
4
2
|
class Collector
|
5
|
-
attr_reader :modules, :
|
3
|
+
attr_reader :modules, :main
|
6
4
|
|
7
|
-
def initialize(
|
8
|
-
@root = root
|
9
|
-
@
|
10
|
-
@
|
5
|
+
def initialize(options = {})
|
6
|
+
@root = options[:root]
|
7
|
+
@lazy_eval = options[:lazy_eval]
|
8
|
+
@modules = []
|
11
9
|
end
|
12
10
|
|
13
11
|
def parse_file(path)
|
14
12
|
@src = File.read(path)
|
15
13
|
@root ||= File.dirname(path)
|
16
14
|
@main = JSModule.new(File.basename(path, '.js'), @root, path)
|
17
|
-
modules
|
18
|
-
|
15
|
+
modules << main
|
16
|
+
collect_dependencies(main)
|
19
17
|
end
|
20
18
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
def 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
|
28
|
+
buffer << "\nmodulr.require('#{main.identifier}');\n"
|
27
29
|
end
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
aliases[js_module.identifier] = js_module.id
|
36
|
-
end
|
37
|
-
else
|
38
|
-
modules[js_module.id] = js_module
|
39
|
-
find_dependencies(js_module.src, js_module.path)
|
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)
|
40
37
|
end
|
41
38
|
end
|
42
39
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
def to_js(buffer = '')
|
52
|
-
buffer << File.read(PATH_TO_MODULR_JS);
|
53
|
-
modules.each { |id, js_module| js_module.to_js(buffer) }
|
54
|
-
aliases.each do |identifier, id|
|
55
|
-
buffer << "modulr.alias('#{identifier}', '#{id}');\n"
|
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
|
56
47
|
end
|
57
|
-
buffer << "\nmodulr.require('#{main.identifier}');\n"
|
58
|
-
end
|
59
48
|
end
|
60
49
|
end
|
data/lib/modulr/js_module.rb
CHANGED
@@ -1,10 +1,28 @@
|
|
1
1
|
module Modulr
|
2
2
|
class JSModule
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
JS_ESCAPE_MAP = {
|
6
|
+
'\\' => '\\\\',
|
7
|
+
'</' => '<\/',
|
8
|
+
"\r\n" => '\n',
|
9
|
+
"\n" => '\n',
|
10
|
+
"\r" => '\n',
|
11
|
+
'"' => '\\"',
|
12
|
+
"'" => "\\'"
|
13
|
+
}
|
14
|
+
|
15
|
+
def self.parser
|
16
|
+
@dependency_finder ||= Parser.new
|
6
17
|
end
|
7
|
-
|
18
|
+
|
19
|
+
def self.find_dependencies(js_module)
|
20
|
+
expressions = parser.get_require_expressions(js_module.src)
|
21
|
+
expressions.map do |exp|
|
22
|
+
new(exp[:identifier], js_module.root, js_module.path, exp[:line])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
8
26
|
attr_reader :identifier, :root, :terms, :file, :line
|
9
27
|
|
10
28
|
def initialize(identifier, root, file=nil, line=nil)
|
@@ -15,6 +33,10 @@ module Modulr
|
|
15
33
|
@terms = identifier.split('/').reject { |term| term == '' }
|
16
34
|
raise ModuleIdentifierError.new(self) unless identifier_valid?
|
17
35
|
end
|
36
|
+
|
37
|
+
def <=> (other_module)
|
38
|
+
id <=> other_module.id
|
39
|
+
end
|
18
40
|
|
19
41
|
def inspect
|
20
42
|
"#<#{self.class.name} \"#{identifier}\">"
|
@@ -26,8 +48,12 @@ module Modulr
|
|
26
48
|
|
27
49
|
def id
|
28
50
|
return @id if @id
|
29
|
-
|
30
|
-
|
51
|
+
if top_level?
|
52
|
+
@id = identifier
|
53
|
+
else
|
54
|
+
@id = File.expand_path(partial_path, directory)
|
55
|
+
@id.sub!("#{File.expand_path(root)}/", '')
|
56
|
+
end
|
31
57
|
end
|
32
58
|
|
33
59
|
def relative?
|
@@ -51,9 +77,25 @@ module Modulr
|
|
51
77
|
end
|
52
78
|
end
|
53
79
|
|
80
|
+
def escaped_src
|
81
|
+
@escaped_src ||= src.gsub(/(\\|<\/|\r\n|[\n\r"'])/) {
|
82
|
+
JS_ESCAPE_MAP[$1]
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def dependencies
|
87
|
+
@dependencies ||= self.class.find_dependencies(self)
|
88
|
+
end
|
89
|
+
|
54
90
|
def to_js(buffer = '')
|
91
|
+
call_alias_js_function(buffer)
|
55
92
|
fn = "function(require, exports, module) {\n#{src}\n}"
|
56
|
-
buffer << "
|
93
|
+
buffer << "\nmodulr.cache('#{id}', #{fn});\n"
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_js_string(buffer = '')
|
97
|
+
call_alias_js_function(buffer)
|
98
|
+
buffer << "\nmodulr.cache('#{id}', '#{escaped_src}');\n"
|
57
99
|
end
|
58
100
|
|
59
101
|
protected
|
@@ -62,10 +104,12 @@ module Modulr
|
|
62
104
|
end
|
63
105
|
|
64
106
|
def directory
|
107
|
+
relative? ? File.dirname(file) : root
|
108
|
+
end
|
109
|
+
|
110
|
+
def call_alias_js_function(buffer)
|
65
111
|
if relative?
|
66
|
-
|
67
|
-
else
|
68
|
-
root
|
112
|
+
buffer << "\nmodulr.alias('#{identifier}', '#{id}');"
|
69
113
|
end
|
70
114
|
end
|
71
115
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rkelly'
|
2
|
+
|
3
|
+
module Modulr
|
4
|
+
class Parser
|
5
|
+
|
6
|
+
def parse(src)
|
7
|
+
parser.parse(src)
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_require_expressions(src)
|
11
|
+
nodes = parse(src)
|
12
|
+
nodes = nodes.select { |node| is_a_require_expression?(node) }
|
13
|
+
nodes.map { |node| normalize(node) }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def parser
|
18
|
+
@parser ||= RKelly::Parser.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_a_require_expression?(node)
|
22
|
+
node.is_a?(RKelly::Nodes::FunctionCallNode) &&
|
23
|
+
node.value.is_a?(RKelly::Nodes::ResolveNode) &&
|
24
|
+
node.value.value == 'require'
|
25
|
+
end
|
26
|
+
|
27
|
+
def normalize(node)
|
28
|
+
str = node.arguments.first.value.first
|
29
|
+
{
|
30
|
+
:identifier => str.value[1...-1],
|
31
|
+
:line => str.line.to_i
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/modulr.rb
CHANGED
@@ -8,13 +8,14 @@ module Modulr
|
|
8
8
|
end
|
9
9
|
|
10
10
|
require 'modulr/js_module'
|
11
|
+
require 'modulr/parser'
|
11
12
|
require 'modulr/collector'
|
12
13
|
require 'modulr/version'
|
13
14
|
|
14
15
|
PATH_TO_MODULR_JS = File.join(LIB_DIR, '..', 'assets', 'modulr.js')
|
15
16
|
|
16
17
|
def self.ize(input_filename, options = {})
|
17
|
-
collector = Collector.new(options
|
18
|
+
collector = Collector.new(options)
|
18
19
|
collector.parse_file(input_filename)
|
19
20
|
collector.to_js
|
20
21
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: modulr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobie Langel
|
@@ -38,6 +38,7 @@ files:
|
|
38
38
|
- lib/modulr.rb
|
39
39
|
- lib/modulr/collector.rb
|
40
40
|
- lib/modulr/js_module.rb
|
41
|
+
- lib/modulr/parser.rb
|
41
42
|
- lib/modulr/version.rb
|
42
43
|
- vendor/rkelly/CHANGELOG.rdoc
|
43
44
|
- vendor/rkelly/Manifest.txt
|