rill 0.0.1

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.
Files changed (3) hide show
  1. data/bin/rill +50 -0
  2. data/lib/rill.rb +166 -0
  3. metadata +47 -0
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rill'
4
+ require 'optparse'
5
+
6
+ options = {}
7
+ parser = OptionParser.new do |opts|
8
+ opts.banner = "Usage: rill --files foo,bar [options]"
9
+
10
+ opts.on('-f', '--files FILES', Array, 'Files to be polished') do |files|
11
+ options[:files] = files
12
+ end
13
+ opts.on('-b', '--base [BASE]', 'Base directory') do |path|
14
+ options[:base] = path
15
+ end
16
+ opts.on('-p', '--preloads [PRELOADS]', Array, 'Modules to be preloaded') do |preloads|
17
+ options[:preloads] = preloads.nil? ? [] : preloads
18
+ end
19
+ end
20
+
21
+ begin
22
+ parser.parse!
23
+ raise OptionParser::MissingArgument if options[:files].nil?
24
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument
25
+ puts $!
26
+ puts parser
27
+ exit
28
+ end
29
+
30
+ rill = Rill.new(:base => options[:base],
31
+ :preloads => options[:preloads])
32
+
33
+ options[:files].each do |f|
34
+ base = options[:base] || '.'
35
+ base = File.expand_path(base, Dir.pwd)
36
+ abort("Cann't find base directory #{path}") unless File.directory?(base)
37
+ pattern = File.expand_path(f, base)
38
+
39
+ files = Dir.glob(pattern)
40
+ if files.length == 0
41
+ puts "No files matches #{f}"
42
+ next
43
+ else
44
+ files.each do |f|
45
+ mod = f.sub(base, '').sub(/^\//, '')
46
+ rill.polish(mod)
47
+ puts "Polished #{mod}"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,166 @@
1
+ class Rill
2
+
3
+ DEFAULT_CONFIG = {
4
+ :base => Dir.pwd,
5
+ :preloads => []
6
+ }
7
+
8
+ def initialize(attrs = nil)
9
+ attrs ||= DEFAULT_CONFIG.dup
10
+
11
+ @base = attrs[:base]
12
+ @preloads = attrs[:preloads] || []
13
+ end
14
+
15
+ def resolve(mods)
16
+ @modules = []
17
+ @codes = []
18
+
19
+ if mods.is_a?(String)
20
+ mods = [mods]
21
+ end
22
+ mods.each do |mod|
23
+ resolve_mod(mod)
24
+ end
25
+ end
26
+
27
+ def resolve_mod(mod)
28
+ return if @preloads.include?(mod) || @modules.include?(mod)
29
+
30
+ mod = parse_mod(mod)
31
+ path = File.join(@base, "#{mod}.js")
32
+ code = File.open(path).read
33
+
34
+ unless code =~ /^define\((['"])[^'"]+\1/
35
+ code = polish(mod, code)
36
+ # fio = File.open(path, 'w')
37
+ # fio.write(code)
38
+ end
39
+ @codes.unshift(code)
40
+ @modules.unshift(mod)
41
+
42
+ deps = parse_deps_from_define(code)
43
+ deps.each do |dep|
44
+ dep = expand_path(mod, dep)
45
+ resolve_mod(dep)
46
+ end
47
+ end
48
+
49
+ def append(mod, code)
50
+ @modules << mod
51
+ @codes << polish(mod, code)
52
+ end
53
+
54
+ def bundle
55
+ @preloads.each do |file|
56
+ code = File.open(File.join(@base, "#{file}.js")).read
57
+ @codes.unshift(code)
58
+ end
59
+ @codes.join("\n")
60
+ end
61
+
62
+ # mark the module id
63
+ # parse and set the module dependencies if not present
64
+ def polish(mod, code = nil)
65
+ mod = parse_mod(mod)
66
+
67
+ return polish_code(mod, code) unless code.nil? || code == ''
68
+
69
+ path = File.join(@base, "#{mod}.js")
70
+ code = File.open(path).read
71
+
72
+ unless code =~ /^define\((['"])[^'"]+\1/
73
+ code = polish_code(mod, code)
74
+ fio = File.open(path, 'w')
75
+ fio.write(code)
76
+ fio.close
77
+ end
78
+
79
+ code
80
+ end
81
+
82
+ def polish_code(mod, code)
83
+ mod = parse_mod(mod)
84
+
85
+ if code =~ /^define\(function/
86
+ deps = parse_deps(code)
87
+ deps -= @preloads
88
+ deps_str = deps.length > 0 ? "['#{deps.join("', '")}']" : '[]'
89
+
90
+ code.sub!(/^(define\()/, "\\1'#{mod}', #{deps_str}, ")
91
+ elsif code =~ /^define\([\[\{]/
92
+ code.sub!(/^(define\()/, "\\1'#{mod}', ")
93
+ end
94
+
95
+ code
96
+ end
97
+
98
+ def parse_mod(mod)
99
+ start = 0
100
+ fini = mod.rindex('.js') || mod.length
101
+
102
+ mod.slice(start, fini - start)
103
+ end
104
+
105
+ def expand_path(mod, dep)
106
+ context = mod.include?('/') ? mod.slice(0, mod.rindex('/')) : ''
107
+ dep_bare = dep.slice(dep.index('/') || 0, dep.length)
108
+ ret = ''
109
+
110
+ if dep.starts_with?('./')
111
+ ret = context + dep_bare
112
+ elsif dep.starts_with?('../')
113
+ ret = context.slice(0, context.rindex('/')) + dep_bare
114
+ else
115
+ ret = dep
116
+ end
117
+
118
+ ret
119
+ end
120
+
121
+ def parse_deps_from_define(code)
122
+ pattern = /^define\((['"])[^'"]+\1,\s*(\[[^\]]+\])/
123
+ match = pattern.match(code)
124
+ deps = []
125
+
126
+ if match
127
+ deps = match[2]
128
+ deps = JSON.parse(deps.gsub("'", '"'))
129
+ deps.delete_if do |d|
130
+ d.blank?
131
+ end
132
+ else
133
+ pattern = /^define\((['"])[^'"]+\1,\s*(['"])([^\1]+)\1\.split/
134
+ match = pattern.match(code)
135
+ if match
136
+ deps = match[3].split(/,\s*/)
137
+ end
138
+ end
139
+
140
+ deps
141
+ end
142
+
143
+ def parse_deps(code)
144
+ # Parse these `requires`:
145
+ # var a = require('a');
146
+ # someMethod(require('b'));
147
+ # require('c');
148
+ # ...
149
+ # Doesn't parse:
150
+ # someInstance.require(...);
151
+ pattern = /(?:^|[^.])\brequire\s*\(\s*(["'])([^"'\s\)]+)\1\s*\)/
152
+ code = sans_comments(code)
153
+ matches = code.scan(pattern)
154
+
155
+ matches.map! do |m|
156
+ m[1]
157
+ end
158
+
159
+ matches.uniq.compact
160
+ end
161
+
162
+ # http://lifesinger.github.com/lab/2011/remove-comments-safely/
163
+ def sans_comments(code)
164
+ code.gsub(/(?:^|\n|\r)\s*\/\*[\s\S]*?\*\/\s*(?:\r|\n|$)/, "\n").gsub(/(?:^|\n|\r)\s*\/\/.*(?:\r|\n|$)/, "\n")
165
+ end
166
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rill
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jake Chen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-20 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: A simple CMD module bundler
15
+ email: jakeplus@gmail.com
16
+ executables:
17
+ - rill
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/rill.rb
22
+ - bin/rill
23
+ homepage: http://rubygems.org/gems/rill
24
+ licenses: []
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - lib
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ required_rubygems_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 1.8.15
44
+ signing_key:
45
+ specification_version: 3
46
+ summary: SeaJS Bundler in Ruby
47
+ test_files: []