Module: Jade::CLI::Check

Defined in:
lib/jade/cli/check.rb

Overview

Type-checks without generating anything.

Class Method Summary collapse

Class Method Details

.collect(registry) ⇒ Object



47
48
49
50
51
52
53
54
# File 'lib/jade/cli/check.rb', line 47

def collect(registry)
  registry
    .modules
    .each_value
    .reject { Stdlib.is_stdlib?(it) }
    .reject { it.source.nil? }
    .flat_map { it.diagnostics.items }
end

.compile(project, entry) ⇒ Object

Tolerant, so a module yields every diagnostic rather than the first.



39
40
41
42
43
44
45
# File 'lib/jade/cli/check.rb', line 39

def compile(project, entry)
  ModuleLoader
    .load(project.source_root, entry, cache_dir: project.cache_path, tolerant: true)
    .then { collect(it) }
rescue CompilationError => e
  e.diagnostics.items
end

.count(items, noun) ⇒ Object



77
78
79
80
81
# File 'lib/jade/cli/check.rb', line 77

def count(items, noun)
  return nil if items.empty?

  "#{items.size} #{noun}#{'s' unless items.size == 1}"
end

.diagnose(project, files) ⇒ Object



19
20
21
22
23
# File 'lib/jade/cli/check.rb', line 19

def diagnose(project, files)
  entries(project, files)
    .flat_map { compile(project, it) }
    .uniq { |d| [d.primary&.source&.uri, d.message, d.primary&.span] }
end

.entries(project, files) ⇒ Object



25
26
27
28
29
# File 'lib/jade/cli/check.rb', line 25

def entries(project, files)
  return sources(project) if files.empty?

  files.map { project.source_relative(it) }
end

.report(items, project) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/jade/cli/check.rb', line 56

def report(items, project)
  return if items.empty?

  Diagnostics::Renderer
    .new(colors: $stdout.tty?)
    .render_all(Diagnostics::List.new(items:))
    .then { warn(it) }

  warn(summary(items, project))
  exit 1 if items.any?(&:error?)
end

.run(argv) ⇒ Object



11
12
13
14
15
16
17
# File 'lib/jade/cli/check.rb', line 11

def run(argv)
  usage if argv.any? { it == '-h' || it == '--help' }

  Project
    .find!
    .then { report(diagnose(it, argv), it) }
end

.sources(project) ⇒ Object



31
32
33
34
35
36
# File 'lib/jade/cli/check.rb', line 31

def sources(project)
  Dir
    .glob(File.join(project.source_root, '**', '*.jd'))
    .map { Pathname.new(it).relative_path_from(project.source_root).to_s }
    .sort
end

.summary(items, project) ⇒ Object



68
69
70
71
72
73
74
75
# File 'lib/jade/cli/check.rb', line 68

def summary(items, project)
  items
    .partition(&:error?)
    .then { |(errors, rest)| [count(errors, 'error'), count(rest, 'warning')] }
    .compact
    .join(', ')
    .then { "\n#{it} in #{project.source_roots.first}" }
end

.usageObject



83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/jade/cli/check.rb', line 83

def usage
  warn <<~USAGE
    Usage: jade check [FILE...]

    Type-checks FILEs and everything they import, writing diagnostics
    to stderr. Checks every .jd file under the source root when given
    no arguments. Exits 1 if there were errors, 0 otherwise.

    FILE is relative to the project root or the source root.
  USAGE
  exit 1
end