Class: Opal::CLI

Inherits:
Object
  • Object
show all
Defined in:
opal/lib/opal/cli.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = nil) ⇒ CLI

Returns a new instance of CLI.

Raises:

  • (ArgumentError)


18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'opal/lib/opal/cli.rb', line 18

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)
  @file        = options.delete(:file)
  @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 }
  @filename    = options.delete(:filename)   { @file && @file.path }
  @requires    = options.delete(:requires)   { [] }
  @rbrequires  = options.delete(:rbrequires) { [] }
  @no_cache    = options.delete(:no_cache)   { false }

  @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

  raise ArgumentError, 'no libraries to compile' if @lib_only && @requires.empty?
  raise ArgumentError, 'no runnable code provided (evals or file)' if @evals.empty? && @file.nil? && !@lib_only
  raise ArgumentError, "can't accept evals or file in `library only` mode" if (@evals.any? || @file) && @lib_only
  raise ArgumentError, "unknown options: #{options.inspect}" unless @options.empty?
end

Class Attribute Details

.stdoutObject

Returns the value of attribute stdout.



15
16
17
# File 'opal/lib/opal/cli.rb', line 15

def stdout
  @stdout
end

Instance Attribute Details

#argvObject (readonly)

Returns the value of attribute argv.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def argv
  @argv
end

#compiler_optionsObject (readonly)

Returns the value of attribute compiler_options.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def compiler_options
  @compiler_options
end

#debugObject (readonly)

Returns the value of attribute debug.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def debug
  @debug
end

#evalsObject (readonly)

Returns the value of attribute evals.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def evals
  @evals
end

#exit_statusObject (readonly)

Returns the value of attribute exit_status.



99
100
101
# File 'opal/lib/opal/cli.rb', line 99

def exit_status
  @exit_status
end

#fileObject (readonly)

Returns the value of attribute file.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def file
  @file
end

#filenameObject (readonly)

Returns the value of attribute filename.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def filename
  @filename
end

#gemsObject (readonly)

Returns the value of attribute gems.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def gems
  @gems
end

#lib_onlyObject (readonly)

Returns the value of attribute lib_only.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def lib_only
  @lib_only
end

#load_pathsObject (readonly)

Returns the value of attribute load_paths.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def load_paths
  @load_paths
end

#missing_require_severityObject (readonly)

Returns the value of attribute missing_require_severity.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def missing_require_severity
  @missing_require_severity
end

#no_cacheObject (readonly)

Returns the value of attribute no_cache.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def no_cache
  @no_cache
end

#no_exitObject (readonly)

Returns the value of attribute no_exit.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def no_exit
  @no_exit
end

#optionsObject (readonly)

Returns the value of attribute options.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def options
  @options
end

#outputObject (readonly)

Returns the value of attribute output.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def output
  @output
end

#preloadObject (readonly)

Returns the value of attribute preload.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def preload
  @preload
end

#rbrequiresObject (readonly)

Returns the value of attribute rbrequires.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def rbrequires
  @rbrequires
end

#requiresObject (readonly)

Returns the value of attribute requires.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def requires
  @requires
end

#runner_optionsObject (readonly)

Returns the value of attribute runner_options.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def runner_options
  @runner_options
end

#stubsObject (readonly)

Returns the value of attribute stubs.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def stubs
  @stubs
end

#verboseObject (readonly)

Returns the value of attribute verbose.



9
10
11
# File 'opal/lib/opal/cli.rb', line 9

def verbose
  @verbose
end

Instance Method Details

#compiler_option_namesObject



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'opal/lib/opal/cli.rb', line 162

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_builderObject



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'opal/lib/opal/cli.rb', line 101

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
  evals_or_file { |source, filename| builder.build_str(source, filename) }

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

  builder
end

#debug_source_mapObject



147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'opal/lib/opal/cli.rb', line 147

def debug_source_map
  evals_or_file do |contents, filename|
    compiler = Opal::Compiler.new(contents, file: filename, **compiler_options)

    compiler.compile

    result = compiler.result
    source_map = compiler.source_map.to_json

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

    output.puts "https://sokra.github.io/source-map-visualization/#base64,#{b64}"
  end
end

#evals_or_fileObject

Internal: Yields a string of source code and the proper filename for either evals, stdin or a filepath.



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'opal/lib/opal/cli.rb', line 180

def evals_or_file
  # --library
  return if lib_only

  if evals.any?
    yield evals.join("\n"), '-e'
  elsif file && (filename != '-' || evals.empty?)
    return @content if @content

    if file.tty?
      save = true
    else
      begin
        file.rewind
      rescue Errno::ESPIPE
        save = true
      end
    end

    content = yield(file.read, filename)
    @content = content if save
    content
  end
end

#runObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'opal/lib/opal/cli.rb', line 64

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_replObject



92
93
94
95
96
97
# File 'opal/lib/opal/cli.rb', line 92

def run_repl
  require 'opal/repl'

  repl = REPL.new
  repl.run(OriginalARGV)
end

#runnerObject



87
88
89
90
# File 'opal/lib/opal/cli.rb', line 87

def runner
  CliRunners[@runner_type] ||
    raise(ArgumentError, "unknown runner: #{@runner_type.inspect}")
end

#show_sexpObject



138
139
140
141
142
143
144
145
# File 'opal/lib/opal/cli.rb', line 138

def show_sexp
  evals_or_file do |contents, filename|
    buffer = ::Opal::Parser::SourceBuffer.new(filename)
    buffer.source = contents
    sexp = Opal::Parser.default_parser.parse(buffer)
    output.puts sexp.inspect
  end
end