Class: Opal::CLI

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

Defined Under Namespace

Classes: Evals

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)


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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'opal/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

  # directory is both a runner and compiler option
  @directory = @compiler_options[:directory]
  @runner_options[:directory] = @directory
  @output = File.open(@output, 'w') if @output.is_a?(String) && !@directory

  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

Class Attribute Details

.stdoutObject

Returns the value of attribute stdout.



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

def stdout
  @stdout
end

Instance Attribute Details

#argvObject (readonly)

Returns the value of attribute argv.



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

def argv
  @argv
end

#compiler_optionsObject (readonly)

Returns the value of attribute compiler_options.



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

def compiler_options
  @compiler_options
end

#debugObject (readonly)

Returns the value of attribute debug.



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

def debug
  @debug
end

#evalsObject (readonly)

Returns the value of attribute evals.



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

def evals
  @evals
end

#exit_statusObject (readonly)

Returns the value of attribute exit_status.



121
122
123
# File 'opal/lib/opal/cli.rb', line 121

def exit_status
  @exit_status
end

#fileObject (readonly)

Returns the value of attribute file.



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

def file
  @file
end

#filenameObject (readonly)

Returns the value of attribute filename.



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

def filename
  @filename
end

#gemsObject (readonly)

Returns the value of attribute gems.



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

def gems
  @gems
end

#lib_onlyObject (readonly)

Returns the value of attribute lib_only.



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

def lib_only
  @lib_only
end

#load_pathsObject (readonly)

Returns the value of attribute load_paths.



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

def load_paths
  @load_paths
end

#missing_require_severityObject (readonly)

Returns the value of attribute missing_require_severity.



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

def missing_require_severity
  @missing_require_severity
end

#no_cacheObject (readonly)

Returns the value of attribute no_cache.



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

def no_cache
  @no_cache
end

#no_exitObject (readonly)

Returns the value of attribute no_exit.



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

def no_exit
  @no_exit
end

#optionsObject (readonly)

Returns the value of attribute options.



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

def options
  @options
end

#outputObject (readonly)

Returns the value of attribute output.



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

def output
  @output
end

#preloadObject (readonly)

Returns the value of attribute preload.



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

def preload
  @preload
end

#rbrequiresObject (readonly)

Returns the value of attribute rbrequires.



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

def rbrequires
  @rbrequires
end

#requiresObject (readonly)

Returns the value of attribute requires.



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

def requires
  @requires
end

#runner_optionsObject (readonly)

Returns the value of attribute runner_options.



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

def runner_options
  @runner_options
end

#stdinObject (readonly)

Returns the value of attribute stdin.



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

def stdin
  @stdin
end

#stubsObject (readonly)

Returns the value of attribute stubs.



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

def stubs
  @stubs
end

#verboseObject (readonly)

Returns the value of attribute verbose.



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

def verbose
  @verbose
end

Instance Method Details

#compiler_option_namesObject



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'opal/lib/opal/cli.rb', line 186

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
    directory
  ]
end

#create_builderObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'opal/lib/opal/cli.rb', line 123

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_mapObject



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'opal/lib/opal/cli.rb', line 170

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_sourceObject

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



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'opal/lib/opal/cli.rb', line 205

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

#runObject



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'opal/lib/opal/cli.rb', line 86

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



114
115
116
117
118
119
# File 'opal/lib/opal/cli.rb', line 114

def run_repl
  require 'opal/repl'

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

#runnerObject



109
110
111
112
# File 'opal/lib/opal/cli.rb', line 109

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

#show_sexpObject



161
162
163
164
165
166
167
168
# File 'opal/lib/opal/cli.rb', line 161

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