Skip to content

Commit

Permalink
Towards a more perfect module loader
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles Jolley committed Jan 17, 2010
1 parent b527d2d commit 839c50e
Show file tree
Hide file tree
Showing 12 changed files with 101 additions and 50 deletions.
4 changes: 3 additions & 1 deletion Buildfile
Expand Up @@ -83,7 +83,9 @@ mode :all do
:lazy_instantiation => false,

# indicates that you want to include bundle loading code. this is almost
# always a setting you want to leave enabled
# always a setting you want to leave enabled. The value of this property
# should be the name of the global loader object. It should also be the
# name of the framework that contains the loader.
:use_loader => true,

# Actives module loading. Currently off by default. If you enable the
Expand Down
4 changes: 2 additions & 2 deletions buildtasks/build.rake
Expand Up @@ -80,8 +80,8 @@ namespace :build do
end

desc "builds the bundle_info.js file for a required framework"
build_task :bundle_info do
SC::Builder::BundleInfo.build ENTRY, DST_PATH
build_task :package_info do
SC::Builder::PackageInfo.build ENTRY, DST_PATH
end

desc "builds a module entries entry"
Expand Down
23 changes: 15 additions & 8 deletions buildtasks/manifest.rake
Expand Up @@ -150,7 +150,11 @@ namespace :manifest do
# etc.
if entry.ext == 'js'
entry = MANIFEST.add_transform entry,
:build_task => 'build:javascript'
:build_task => 'build:javascript',
:module_name => entry.filename.ext,
:use_modules => CONFIG.use_modules,
:use_loader => CONFIG.use_loader
entry.discover_build_directives!(true)
end

# Add transform to build into test.
Expand Down Expand Up @@ -204,6 +208,7 @@ namespace :manifest do
:filename => ['source', entry.filename].join('/'),
:module_name => entry.filename.ext,
:use_modules => CONFIG.use_modules,
:use_loader => CONFIG.use_loader,
:build_path => File.join(MANIFEST.build_root, 'source', entry.filename),
:url => [MANIFEST.url_root, 'source', entry.filename].join("/"),
:build_task => 'build:javascript',
Expand Down Expand Up @@ -301,28 +306,30 @@ namespace :manifest do
ordered_entries = SC::Helpers::EntrySorter.sort(entries, pf)

# add a bundle_info.js if needed
if CONFIG.use_loader
bundle_info = MANIFEST.add_entry 'bundle_info.js',
:build_task => 'build:bundle_info',
if CONFIG.use_loader && ((ordered_entries.size == 0) || (ordered_entries.find { |e| e.use_loader }))
bundle_info = MANIFEST.add_entry 'package_info.js',
:build_task => 'build:package_info',
:resource => resource_name,
:entry_type => :javascript,
:source_entries => entries.dup
:source_entries => entries.dup,
:composite => true

entries << bundle_info
ordered_entries.unshift(bundle_info) # load first
end

# if we're using modules, then add a generated entries module as well
has_exports = !!entries.find { |e| e.module_name == 'package' }
if CONFIG.use_modules && !has_exports
if CONFIG.use_modules && !has_exports && ((ordered_entries.size == 0) || (ordered_entries.find { |e| e.use_modules }))
module_exports = MANIFEST.add_entry 'package_exports.js',
:build_task => 'build:package_exports',
:resource => resource_name,
:entry_type => :javascript,
:source_entries => entries.dup,
:module_name => 'package'
:module_name => 'package',
:composite => true
entries << module_exports
ordered_entries.unshift(module_exports)
ordered_entries.push(module_exports) # load last
end

MANIFEST.add_composite entry_name,
Expand Down
31 changes: 19 additions & 12 deletions lib/sproutcore/builders/bundle.rb
Expand Up @@ -11,7 +11,7 @@ module SC

# Builds a bundle_info.js file which MUST be run *before* the framework is
# loaded by the application or framework doing the loading.
class Builder::BundleInfo < Builder::Base
class Builder::PackageInfo < Builder::Base

def build(dst_path)
begin
Expand All @@ -22,11 +22,14 @@ def build(dst_path)

# emit a bundle definition for the current target
loader_name = entry.target.config.module_loader
bundle_name = entry.manifest.bundle_name
package_name = entry.manifest.package_name
desc = entry.manifest.bundle_info
lines = []
lines << ";#{loader_name}.bundle('#{bundle_name}', #{desc.to_json});\n"
lines << "#{loader_name}.script('#{entry.cacheable_url}');\n"
lines << ";#{loader_name}.register('#{package_name}', #{desc.to_json});\n"

if !entry.target.config.combine_javascript
lines << "#{loader_name}.script('#{entry.cacheable_url}');\n"
end

writelines dst_path, lines
end
Expand All @@ -41,25 +44,25 @@ def build(dst_path)

entries = entry.source_entries.reject { |e| e.exports.nil? }

bundle_name = entry.target.bundle_name
package_name = entry.target.package_name
loader_name = entry.target.config.module_loader

has_main = false

lines = []
lines << "#{loader_name}.module('#{bundle_name}', 'package', function(require, exports, module) {\n"
lines << "#{loader_name}.module('#{package_name}', 'package', function(require, exports, module) {\n"
lines << "var m;\n"
entries.each do |e|
next if e.package_exports.nil?

if e.package_exports && e.package_exports.size>0
lines << "m = require('#{bundle_name}', '#{e.module_name}');\n"
lines << "m = require('#{package_name}', '#{e.module_name}');\n"
e.package_exports.each do |exp|
lines << "exports.#{exp} = m.#{exp};\n"
has_main = true if exp == 'main'
lines << "exports.#{exp[1]} = m.#{exp[1]};\n"
has_main = true if exp[1] == 'main'
end
else
lines << "require('#{bundle_name}', '#{e.module_name}');\n"
lines << "require('#{package_name}', '#{e.module_name}');\n"
end

end
Expand All @@ -69,10 +72,14 @@ def build(dst_path)
# if this is a loadable target (i.e. an app), and a main() is defined,
# then try to call it automatically when the package becomes ready.
if entry.target.loadable?
lines << "\n#{loader_name}.load('#{bundle_name}').then(function() {\n #{loader_name}.require('#{bundle_name}','package').main();\n});\n\n"
lines << "\n#{loader_name}.load('#{package_name}').then(function() {\n #{loader_name}.require('#{package_name}','package').main();\n});\n\n"
end


if !entry.target.config.combine_javascript
lines << "#{loader_name}.script('#{entry.cacheable_url}');"
end

lines << "#{loader_name}.script('#{entry.cacheable_url}');"
writelines dst_path, lines
end

Expand Down
3 changes: 2 additions & 1 deletion lib/sproutcore/builders/combine.rb
Expand Up @@ -20,6 +20,7 @@ class Builder::Combine < Builder::Base
def build(dst_path)
lines = []
entries = entry.ordered_entries || entry.source_entries
loader_name = entry.target.config.module_loader

target_name = entry.target.target_name.to_s.sub(/^\//,'')
if entry.top_level_lazy_instantiation && entry.combined
Expand Down Expand Up @@ -54,7 +55,7 @@ def build(dst_path)
end

if entry.notify_onload && entry.entry_type == :javascript
lines << "; sc_loader.script('#{entry.cacheable_url}');"
lines << "; #{loader_name}.script('#{entry.cacheable_url}');"
end

writelines dst_path, lines
Expand Down
2 changes: 1 addition & 1 deletion lib/sproutcore/builders/html.rb
Expand Up @@ -45,7 +45,7 @@ class Builder::Html < Builder::Base
attr_reader :manifest

def target_name; target.target_name.to_s.sub(/^\//,''); end
alias_method :bundle_name, :target_name # backwards compat
alias_method :package_name, :target_name # backwards compat

def config; target.config; end

Expand Down
2 changes: 1 addition & 1 deletion lib/sproutcore/builders/javascript.rb
Expand Up @@ -41,7 +41,7 @@ def build(dst_path)
# Wrap in a module if enabled
if entry.use_modules
lines.unshift entry.module_preamble
lines.unshift "#{loader_name}.module('#{entry.manifest.bundle_name}', '#{entry.module_name}', function(require, exports, module) {"
lines.unshift "#{loader_name}.module('#{entry.manifest.package_name}', '#{entry.module_name}', function(require, exports, module) {"

lines << entry.module_postamble
lines << "\n});\n"
Expand Down
16 changes: 14 additions & 2 deletions lib/sproutcore/builders/test.rb
Expand Up @@ -49,8 +49,20 @@ def default_content_for_key; :body; end
def render_jstest(entry)
lines = readlines(entry.staging_path)
pathname = entry.staging_path.gsub(/^.+\/staging\//,'').gsub(/"/, '\"')
lines.unshift %[<script type="text/javascript">\nif (typeof SC !== "undefined") {\n SC.mode = "TEST_MODE";\n SC.filename = "#{pathname}"; \n}\n(function() {\n]
lines.push %[\n})();\n</script>\n]
lines.unshift %[<script type="text/javascript">\nif (typeof SC !== "undefined") {\n SC.mode = "TEST_MODE";\n SC.filename = "#{pathname}"; \n}\n]

if entry.use_modules
loader_name = entry.target.config.module_loader
package_name = entry.manifest.package_name
lines.push %[#{loader_name}.load('#{package_name}').then(function() {\n #{loader_name}.require('#{package_name}', '#{entry.module_name}'); \n});]

else
lines.unshift %[(function() {\n]
lines.push %[\n})();]
end

lines.push %[\n</script>\n]

@content_for_final = (@content_for_final || '') + lines.join("")
end

Expand Down
2 changes: 1 addition & 1 deletion lib/sproutcore/helpers/static_helper.rb
Expand Up @@ -18,7 +18,7 @@ module StaticHelper
# required by the named bundle. If you pass no options, the current
# client will be used.
#
# bundle_name = the name of the bundle to render or nil to use the
# package_name = the name of the bundle to render or nil to use the
# current :language => the language to render. defaults to current
# language
#
Expand Down
8 changes: 4 additions & 4 deletions lib/sproutcore/models/manifest.rb
Expand Up @@ -352,8 +352,8 @@ def find_entry(fragment, opts = {}, seen=nil)
# BUNDLE INFO
#

def bundle_name
target.bundle_name
def package_name
target.package_name
end

# Returns a HashStruct with a valid bundle_info for the bundle that you
Expand Down Expand Up @@ -400,7 +400,7 @@ def bundle_info(opts ={})

# get all required target names. these go in the depends hash
targets = target.required_targets(t_opts) || []
depends = targets.map { |t| t.bundle_name }.compact
depends = targets.map { |t| t.package_name }.compact
bundle_info['depends'] = depends if depends.size>0

# expand to include dynamic required target and build bundle info
Expand All @@ -410,7 +410,7 @@ def bundle_info(opts ={})
if targets.size > 0
bundles = {}
targets.each do |t|
bundles[t.bundle_name] =
bundles[t.package_name] =
t.manifest_for(self.variation).bundle_info(:depends => false)
end
bundle_info['packages'] = bundles
Expand Down
54 changes: 38 additions & 16 deletions lib/sproutcore/models/manifest_entry.rb
Expand Up @@ -256,19 +256,19 @@ def scan_module(&block)
# representing CSS or JavaScript resources. It will yield undefined
# results on all other file types.
#
def discover_build_directives!
def discover_build_directives!(force = false)

target.begin_attr_changes

self.required = []
entry = self.transform? ? self.source_entry : self

# use new module parser
if self.use_modules && self.entry_type == :javascript
if self.use_modules && (force || (self.entry_type == :javascript))

self.imports = []
self.exports = []
bundle_name = self.target.bundle_name
package_name = self.target.package_name

entry.scan_module do |directive, args|
directive = directive.to_s.downcase.gsub(/^\s+/,'').gsub(/\s+$/,'')
Expand All @@ -280,29 +280,51 @@ def discover_build_directives!
# handle import foo as bar
if args.size == 3 && args[1] == 'as'
a = args[0]
a = (a =~ /:/ ? a : [bundle_name,a].join(':'))
a = (a =~ /:/ ? a : [package_name,a].join(':'))
self.imports << [a, args[2]]

else
# normalize. if no explicit target, assume local
args.each do |a|
a = (a =~ /:/ ? a : [bundle_name,a].join(':'))
a = (a =~ /:/ ? a : [package_name,a].join(':'))
self.imports << [a, '*']
end
end

when 'export'
if args.first == 'package'
args.shift # remove package directive

is_global = args.first == 'global'
is_package = is_global || (args.first == 'package')
args.shift if is_global || is_package

# convert to tuples; import/export names
if args.size == 3 && args[1] == 'as'
args = [[args[0], args[2]]]
else
args = args.map { |a| [a, a] }
end

# save in global exports if needed
if is_global
self.global_exports = [] if self.global_exports.nil?
self.global_exports += args
end

# save in package exports if needed
if is_package
self.package_exports = [] if self.package_exports.nil?
self.package_exports += args
end


# save regular exports always
self.exports += args

when 'use'
self.use_modules = (args[1] != 'false') if args[0] == 'modules'
self.use_loader = (args[1] != 'false') if args[0] == 'loader'

when 'require'
self.required += args
end
end

Expand Down Expand Up @@ -353,8 +375,8 @@ def module_preamble
# import symbols from other modules
self.imports.each do |import|
import, as_symbol = import # split array
bundle_name, module_name = import.split(':')
bundle_target = self.target.target_for(bundle_name)
package_name, module_name = import.split(':')
bundle_target = self.target.target_for(package_name)

puts "IMPORT: import=#{import} as_symbol=#{as_symbol}"
if bundle_target
Expand All @@ -367,13 +389,13 @@ def module_preamble

if module_entry
if as_symbol != '*'
lines << "var #{as_symbol} = require('#{bundle_name}','#{module_name}');"
lines << "var #{as_symbol} = require('#{package_name}','#{module_name}');"

elsif (module_exports = module_entry.exports).size>0
lines << "var $m__ = require('#{bundle_name}','#{module_name}'), #{module_exports.map { |s| "#{s}=$m__.#{s}" }.join(',')};"
lines << "var $m__ = require('#{package_name}','#{module_name}'), #{module_exports.map { |s| "#{s[1]}=$m__.#{s[1]}" }.join(',')};"

else
lines << "require('#{bundle_name}', '#{module_name}');"
lines << "require('#{package_name}', '#{module_name}');"
end

else
Expand All @@ -388,7 +410,7 @@ def module_preamble

# setup export variables
if self.exports.size>0
lines << "var #{self.exports.join(',')};"
lines << "var #{self.exports.map { |x| x[0].split('.').first }*','};"
end

return lines * ''
Expand All @@ -399,7 +421,7 @@ def module_postamble
return '' if !self.use_modules
lines = [';'] # always add semicolon in case module code is missing one
self.exports.each do |export|
lines << "exports.#{export} = #{export};\n"
lines << "exports.#{export[1]} = #{export[0]};\n"
end
return lines * ''
end
Expand Down
2 changes: 1 addition & 1 deletion lib/sproutcore/models/target.rb
Expand Up @@ -455,7 +455,7 @@ def manifest_for(variation={})

# Returns the bundle name. this is the target name without the leading
# slash
def bundle_name
def package_name
target_name.to_s[1..-1]
end

Expand Down

0 comments on commit 839c50e

Please sign in to comment.