# encoding: UTF-8 # frozen_string_literal: true require "memoist" require "method-not-implemented" require "json" module JadePug # # Abstraction layer for engine compiler. # class Compiler extend Memoist # # Returns the engine module. # # Used in such cases: # - used to compute the name for engine # - used to refer to the error classes # # @return [Jade, Pug] attr_reader :engine # # Returns the version of engine compiler. # # @return [String] attr_reader :version # # @param engine [Jade, Pug] # @param version [String] def initialize(engine, version) @engine = engine @version = version end # # Compiles template. # # By default does nothing. # # @abstract Derived compilers must implement it. # @param source [String, #read] # The template source code or any object which responds to #read and returns string. # @param options [Hash] # @return [String] def compile(source, = {}) method_not_implemented end # # Returns true if this compiler is a system compiler. # Otherwise returns false. # # @return [true, false] def system? SystemCompiler === self end # # Returns true if this compiler is a shipped compiler. # Otherwise returns false. # # @return [true, false] def shipped? ShippedCompiler === self end protected # # Reads the template source code. # Responds for pre-processing source code. # Actually, it just checks if source code responds to #read and if so # # @param source [String, #read] # @return [String] def prepare_source(source) source.respond_to?(:read) ? source.read : source end # # Responds for preparing compilation options. # # The next steps are executed: # - is merges options into the engine config # - it camelizes and symbolizes every option key # - it removes nil values from the options # # @param options [Hash] # @return [Hash] def () = engine.config.to_hash.merge() # rubocop:disable Performance/HashEachMethods .keys.each { |k| [k.to_s.gsub(/_([a-z])/) { $1.upcase }.to_sym] = [k] } .delete_if { |k, v| v.nil? } end # # Generates the JavaScript code that is the bridge # from the gem to the template engine compiler. # # The code responds for: # - invoking compiler # - rendering template function with given locals # - returning the result # # @param :method [String] # The name of engine method to call. # @param :arguments [Array<Object>] # The array of arguments to be passed to the method. # @param :locals [Hash] # The hash of template local variables to be used to render template. # @param :options [Hash] # The hash of options passed to {#compile}. # @return [String] def compilation_snippet(args) method = args.fetch(:method) arguments = args.fetch(:arguments) locals = args.fetch(:locals) = args.fetch(:options) <<-JAVASCRIPT (function() { var engine = #{ npm_package_require_snippet }; var template = engine[#{ JSON.dump(method) }].apply(engine, #{ JSON.dump(arguments) }); if (typeof template === 'function') { template = template(#{ JSON.dump(locals) }); } if (typeof console === 'object' && console !== null && typeof console.log === 'function') { console.log(template); } return template; })() JAVASCRIPT end # # Returns the JavaScript code used to access engine NPM module. # # @abstract Derived compilers must implement it. # @return [String] def npm_package_require_snippet method_not_implemented end # # Responds for post-processing compilation result. # # By default removes leading and trailing whitespace # by calling {String#strip} and returns the result. # Derived compilers may override it for it's own special behavior. # # @param source [String] The source code of template. # @param result [String] The compiled code of template. # @param options [Hash] The compilation options. # @return [String] def process_result(source, result, ) result.strip end end end