class Opal::CLI

Attributes

stdout[RW]
argv[R]
compiler_options[R]
debug[R]
evals[R]
exit_status[R]
file[R]
filename[R]
gems[R]
lib_only[R]
load_paths[R]
missing_require_severity[R]
no_cache[R]
no_exit[R]
options[R]
output[R]
preload[R]
rbrequires[R]
requires[R]
runner_options[R]
stdin[R]
stubs[R]
verbose[R]

Public Class Methods

new(options = nil) click to toggle source
# File lib/opal/cli.rb, line 25
def initialize(options = nil)
  options ||= {}

  # Runner
  @runner_type    = options.delete(:runner)         || :nodejs
  @runner_options = options.delete(:runner_options) || {}

  @options     = options
  @sexp        = options.delete(:sexp)
  @repl        = options.delete(:repl)
  @no_exit     = options.delete(:no_exit)
  @lib_only    = options.delete(:lib_only)
  @argv        = options.delete(:argv)       { [] }
  @evals       = options.delete(:evals)      { [] }
  @load_paths  = options.delete(:load_paths) { [] }
  @gems        = options.delete(:gems)       { [] }
  @stubs       = options.delete(:stubs)      { [] }
  @preload     = options.delete(:preload)    { [] }
  @output      = options.delete(:output)     { self.class.stdout || $stdout }
  @verbose     = options.delete(:verbose)    { false }
  @debug       = options.delete(:debug)      { false }
  @requires    = options.delete(:requires)   { [] }
  @rbrequires  = options.delete(:rbrequires) { [] }
  @no_cache    = options.delete(:no_cache)   { false }
  @stdin       = options.delete(:stdin)      { $stdin }

  @debug_source_map = options.delete(:debug_source_map) { false }

  @missing_require_severity = options.delete(:missing_require_severity) { Opal::Config.missing_require_severity }

  @requires.unshift('opal') unless options.delete(:skip_opal_require)

  @compiler_options = compiler_option_names.map do |option|
    key = option.to_sym
    next unless options.key? key
    value = options.delete(key)
    [key, value]
  end.compact.to_h

  if @lib_only
    raise ArgumentError, 'no libraries to compile' if @requires.empty?
    raise ArgumentError, "can't accept evals, file, or extra arguments in `library only` mode" if @argv.any? || @evals.any?
  elsif @evals.any?
    @filename = '-e'
    @file = Evals.new(@evals.join("\n"))
  elsif @argv.first && @argv.first != '-'
    @filename = @argv.shift
    @file = File.open(@filename)
  else
    @filename = @argv.shift || '-'
    @file = @stdin
  end

  raise ArgumentError, "unknown options: #{options.inspect}" unless @options.empty?
end

Public Instance Methods

compiler_option_names() click to toggle source
# File lib/opal/cli.rb, line 181
def compiler_option_names
  %w[
    method_missing
    arity_check
    dynamic_require_severity
    source_map_enabled
    irb_enabled
    inline_operators
    enable_source_location
    enable_file_source_embed
    use_strict
    parse_comments
    esm
  ]
end
create_builder() click to toggle source
# File lib/opal/cli.rb, line 118
def create_builder
  builder = Opal::Builder.new(
    stubs: stubs,
    compiler_options: compiler_options,
    missing_require_severity: missing_require_severity,
  )

  # --no-cache
  builder.cache = Opal::Cache::NullCache.new if no_cache

  # --include
  builder.append_paths(*load_paths)

  # --gem
  gems.each { |gem_name| builder.use_gem gem_name }

  # --require
  requires.each { |required| builder.build(required, requirable: true, load: true) }

  # --preload
  preload.each { |path| builder.build_require(path) }

  # --verbose
  builder.build_str '$VERBOSE = true', '(flags)', no_export: true if verbose

  # --debug
  builder.build_str '$DEBUG = true', '(flags)', no_export: true if debug

  # --eval / stdin / file
  source = evals_or_file_source
  builder.build_str(source, filename) if source

  # --no-exit
  builder.build_str '::Kernel.exit', '(exit)', no_export: true unless no_exit

  builder
end
debug_source_map() click to toggle source
# File lib/opal/cli.rb, line 165
def debug_source_map
  source = evals_or_file_source or return # rubocop:disable Style/AndOr

  compiler = Opal::Compiler.new(source, file: filename, **compiler_options)

  compiler.compile

  b64 = [
    compiler.result,
    compiler.source_map.to_json,
    evals_or_file_source,
  ].map { |i| Base64.strict_encode64(i) }.join(',')

  output.puts "https://sokra.github.io/source-map-visualization/#base64,#{b64}"
end
evals_or_file_source() click to toggle source

Internal: Yields a string of source code and the proper filename for either

evals, stdin or a filepath.
# File lib/opal/cli.rb, line 199
def evals_or_file_source
  return if lib_only # --library
  return @cached_content if @cached_content

  unless file.tty?
    begin
      file.rewind
      can_read_again = true
    rescue Errno::ESPIPE # rubocop:disable Lint/HandleExceptions
      # noop
    end
  end

  if @cached_content.nil? || can_read_again
    # On MacOS file.read is not enough to pick up changes, probably due to some
    # cache or buffer, unclear if coming from ruby or the OS.
    content = File.file?(file) ? File.read(file) : file.read
  end

  @cached_content ||= content unless can_read_again
  content
end
run() click to toggle source
# File lib/opal/cli.rb, line 81
def run
  return show_sexp if @sexp
  return debug_source_map if @debug_source_map
  return run_repl if @repl

  rbrequires.each { |file| require file }

  runner = self.runner

  # Some runners may need to use a dynamic builder, that is,
  # a builder that will try to build the entire package every time
  # a page is loaded - for example a Server runner that needs to
  # rerun if files are changed.
  builder = proc { create_builder }

  @exit_status = runner.call(
    options: runner_options,
    output: output,
    argv: argv,
    builder: builder,
  )
end
run_repl() click to toggle source
# File lib/opal/cli.rb, line 109
def run_repl
  require 'opal/repl'

  repl = REPL.new
  repl.run(argv)
end
runner() click to toggle source
# File lib/opal/cli.rb, line 104
def runner
  CliRunners[@runner_type] ||
    raise(ArgumentError, "unknown runner: #{@runner_type.inspect}")
end
show_sexp() click to toggle source
# File lib/opal/cli.rb, line 156
def show_sexp
  source = evals_or_file_source or return # rubocop:disable Style/AndOr

  buffer = ::Opal::Parser::SourceBuffer.new(filename)
  buffer.source = source
  sexp = Opal::Parser.default_parser.parse(buffer)
  output.puts sexp.inspect
end