lib/closure/goog.rb in closure-1.2.701 vs lib/closure/goog.rb in closure-1.3.0

- old
+ new

@@ -16,11 +16,11 @@ class Closure # Scripts render with an instance named goog in the context. class Goog - + def initialize(env, sources, render_stack) @sources = sources @env = env @render_stack = render_stack @dependencies = [] @@ -29,93 +29,140 @@ # You can add additional files to have their mtimes scanned. # Perhaps you want to use a .yml file to define build options. # Closure::Script calls this for every render so you don't need # to define compiler arguments in the same script that calls compile. def add_dependency(dependency) - dependency = File.expand_path dependency, @render_stack.last + dependency = File.expand_path dependency, File.dirname(@render_stack.last) @dependencies << dependency unless @dependencies.include? dependency end + + # If you change any javascript sources then you need to tell Script. + # This is a lazy refresh, you may call it repeatedly. + def refresh + @sources.invalidate @env + end + + # Convert soy templates to javascript. Accepts all arguments that + # SoyToJsSrcCompiler.jar support plus it expands filename globs. + # All source filenames are relative to the script calling #soy_to_js. + # @param [Array<String>] args + def soy_to_js(args) + Templates::compile(args, File.dirname(@render_stack.last)) + refresh + end - # Run a compiler job. Accepts every argument that compiler.jar supports. - # Accepts new `--ns namespace` option which literally expands into - # `--js filename` arguments in place to satisfy the namespace. - # If you specify a --js_output_file then the compiler will check File.mtime - # on every source file plus all the closure-scripts and skip the compilation - # if the js_output_file is newest. - # Paths are relative to the script calling #compile. + # Compile javascript. Accepts every argument that compiler.jar supports. + # This method supports all compiler augmentations added by Closure Script. + # Path options are expanded relative to the script calling #compile. + # - `--ns namespace` expands in place to `--js filename` arguments which satisfy the namespace. + # - `--module name:*:dep` File count will be filled in automatically. The * is replaced with the + # count of files up to next --module or the end. + # - `--js_output_file file` is compared against sources modification times to determine + # if compilation is to be performed. + # - `--compilation_level` when not supplied, the scripts are loaded raw. # @example myapp.js.erb # <% @response = goog.compile(%w{ # --js_output_file ../public/myapp.js # --ns myapp.HelloWorld # --compilation_level ADVANCED_OPTIMIZATIONS - # }).to_response_with_console %> + # }).to_response %> # @param [Array<String>] args # @return [Compilation] def compile(args) - args = Array.new args - files = [] - files_index = 0 - args_index = 0 - temp_deps_js = nil - compilation_level = nil + args = Array.new args # work on a copy + pre_js_tempfile = nil begin - while args_index < args.length - option, value = args[args_index, 2] - compilation_level = value if option == '--compilation_level' - if option == '--ns' - files_for(value, files) - replacement = [] - while files_index < files.length - if files[files_index] =~ /\.externs$/ - require 'tempfile' - temp_deps_js ||= Tempfile.new 'closure_deps_js' - replacement.push '--externs' - else - replacement.push '--js' - end - replacement.push files[files_index] - files_index = files_index + 1 + Compiler::Util.expand_paths args, File.dirname(@render_stack.last) + orig_externs = Compiler::Util.arg_values args, '--externs' + Compiler::Util.namespace_augment args, @sources, @env + mods = Compiler::Util.module_augment args + if Compiler::Util.arg_values(args, '--compilation_level').empty? + # Raw mode + comp = Compiler::Compilation.new @env + unless mods.empty? + comp << Compiler::Util.module_info(mods) + comp << Compiler::Util.module_uris_raw(mods, @sources) + end + js_counter = 0 + args_index = 0 + while args_index < args.length + option, value = args[args_index, 2] + if option == '--js' + value = File.expand_path value, File.dirname(@render_stack.last) + script_tag = "<script src=#{src_for(value).dump}></script>" + comp << "document.write(#{script_tag.dump});\n" + js_counter += 1 + # For modules, just the files for the first module + break if !mods.empty? and js_counter >= mods[0][:files].length end - args[args_index, 2] = replacement - else args_index = args_index + 2 end - end - if compilation_level - if temp_deps_js - # EXPERIMENTAL: support for goog.provide and require in externs. - # This is ugly but I hope it will no longer be necessary - # once compiler.jar is made aware of goog.provide in externs. - temp_deps_js.open + else + # Compiled mode + module_output_path_prefix = Compiler::Util.arg_values(args, '--module_output_path_prefix').last + if !mods.empty? and !module_output_path_prefix + # raise this before compilation so we don't write to a weird place + raise "--module_output_path_prefix is required when using --module" + end + # If the externs were changed by namespace_augment then we need to include + # a temp file containing the goog.provide statements that satisfy compiler.jar. + if orig_externs != Compiler::Util.arg_values(args, '--externs') + pre_js_tempfile = Tempfile.new 'closure_pre_js' + # Insert before the first --js (in case of modules) + args_index = 0 + while args_index < args.length + if args[args_index] == '--js' + args.insert args_index, '--js', pre_js_tempfile.path + break + end + args_index = args_index + 2 + end + pre_js_tempfile.open @sources.deps_response(File.dirname(base_js), @env).each do |s| - temp_deps_js.write s + next unless s =~ /^goog\.provide/ + pre_js_tempfile.write s end - temp_deps_js.close + pre_js_tempfile.close # File mtime is rolled back to not trigger compilation. - File.utime(Time.now, Time.at(0), temp_deps_js.path) - args.unshift temp_deps_js.path - args.unshift '--js' + File.utime(Time.now, Time.at(0), pre_js_tempfile.path) end - Compiler.new args, @dependencies, File.dirname(@render_stack.last), @env - else - comp = Compiler.new [] - comp.stdout = '' - args_index = 0 - while args_index < args.length - option, value = args[args_index, 2] - if option == '--js' - script_tag = "<script src=#{path_for(value).dump}></script>" - comp.stdout += "document.write(#{script_tag.dump});\n" + comp = Compiler.compile args, @dependencies, @env + unless mods.empty? + refresh # compilation may add new files, module_uris_compiled uses src_for + prefix = File.expand_path module_output_path_prefix, File.dirname(@render_stack.last) + if comp.js_output_file + File.open comp.js_output_file, 'w' do |f| + f.write Compiler::Util.module_info mods + f.write Compiler::Util.module_uris_compiled mods, @sources, prefix + end + else + comp << Compiler::Util.module_info(mods) + comp << Compiler::Util.module_uris_compiled(mods, @sources, prefix) end - args_index = args_index + 2 + # Load the first module + first_module_file = module_output_path_prefix + mods[0][:name] + '.js' + first_module_file = File.expand_path first_module_file, File.dirname(@render_stack.last) + script_tag = "<script src=#{src_for(first_module_file).dump}></script>" + comp << "document.write(#{script_tag.dump});\n" end - comp end + comp ensure - temp_deps_js.unlink if temp_deps_js + if pre_js_tempfile + pre_js_tempfile.close + pre_js_tempfile.unlink + end end end + + # Calculate the deps src for a filename. + # @param (String) filename + # @return (String) http path info with forward caching query. + def src_for(filename) + filename = File.expand_path filename + @sources.src_for(filename, @env) + end # Calculate files needed to satisfy a namespace. # This will be especially useful for module generation. # If you pass the filenames returned from last run, # additional files (if any) will be appended to satisfy @@ -125,30 +172,26 @@ # @return (Array) def files_for(namespace, filenames=nil) @sources.files_for(namespace, filenames, @env) end - # Calculate the file server path for a filename. - # @param (String) filename - # @return (String) - def path_for(filename) - @sources.path_for(filename, @env) - end - # The Google Closure base.js script. # If you use this instead of a static link, you are free to relocate relative # to the Google Closure library without updating every html fixture page. - # Unfortunately, the better caching can't be used because of the way - # base.js explores the DOM looking for where to load deps.js. # @example view_test.erb # <script src="<%= goog.base_js %>"></script> + # @return [String] def base_js @sources.base_js(@env) end # This is where base.js looks to find deps.js by default. You will always # be served a Closure Script generated deps.js from this location. + # Very old Library versions may get confused by the forward caching query + # string; either update your base.js, install a deps_response Script where + # it's looking, or manually set CLOSURE_BASE_PATH. + # @return [String] def deps_js @sources.deps_js(@env) end # You can serve a deps.js from anywhere you want to drop a script. @@ -156,9 +199,20 @@ # <% @response = goog.deps_response %> # @return (Rack::Response) def deps_response @sources.deps_response(File.dirname(Rack::Utils.unescape(@env["PATH_INFO"])), @env) end + + # Advanced Scripts may need to know where all the sources are. + # This has potential for a source browser, editor, and more. + # @example + # goog.each {|directory, path| ... } + def each + @sources.each do |directory, path| + yield directory, path + end + end + include Enumerable end end