nanoc-cli 4.11.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6614fdc62f969231b662ed63c693f5c2e28185ab2e139cab3fb1e88b46f4707c
4
+ data.tar.gz: 3e55bbea5197f43f0e09afcb908fc438f283a2cd5fd4a23a52ef8020a31f1d37
5
+ SHA512:
6
+ metadata.gz: 0f57b9258cc0639792f33863e56785778bc6c1750b86aeab3d1131547e912799304ce8875a1bb0fa6069eab8a1bb80a2ff2377220c780d4f95a7b44f103cd71b
7
+ data.tar.gz: 6778aefc198872092095f5b9da16454a333f1f516919eb53349671339d84ad280a95e9ee460ed2505f55c6a6d84b6297d6b3fe6d5ffb903de8ae680c8d3c9083
data/NEWS.md ADDED
@@ -0,0 +1,3 @@
1
+ # nanoc-cli news
2
+
3
+ TODO
@@ -0,0 +1,3 @@
1
+ # nanoc-cli
2
+
3
+ TODO
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'nanoc/cli'
@@ -0,0 +1,237 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nanoc/core'
4
+ require 'diff/lcs'
5
+ require 'diff/lcs/hunk'
6
+ require 'logger'
7
+
8
+ begin
9
+ require 'cri'
10
+ rescue LoadError => e
11
+ $stderr.puts e
12
+ $stderr.puts "If you are using a Gemfile, make sure that the Gemfile contains Nanoc ('gem \"nanoc\"')."
13
+ exit 1
14
+ end
15
+
16
+ module Nanoc
17
+ # @api private
18
+ module CLI
19
+ # @return [Boolean] true if debug output is enabled, false if not
20
+ def self.debug?
21
+ @debug || false
22
+ end
23
+
24
+ # @param [Boolean] boolean true if debug output should be enabled,
25
+ # false if it should not
26
+ #
27
+ # @return [void]
28
+ def self.debug=(boolean)
29
+ @debug = boolean
30
+ end
31
+
32
+ def self.verbosity
33
+ @verbosity || 0
34
+ end
35
+
36
+ def self.verbosity=(val)
37
+ @verbosity = val
38
+ end
39
+
40
+ # Wraps `$stdout` and `$stderr` in appropriate cleaning streams.
41
+ #
42
+ # @return [void]
43
+ def self.setup_cleaning_streams
44
+ $stdout = wrap_in_cleaning_stream($stdout)
45
+ $stderr = wrap_in_cleaning_stream($stderr)
46
+ end
47
+
48
+ # Wraps the given stream in a cleaning stream. The cleaning streams will
49
+ # have the proper stream cleaners configured.
50
+ #
51
+ # @param [IO] io The stream to wrap
52
+ #
53
+ # @return [::Nanoc::CLI::CleaningStream]
54
+ def self.wrap_in_cleaning_stream(io)
55
+ cio = ::Nanoc::CLI::CleaningStream.new(io)
56
+
57
+ unless enable_utf8?(io)
58
+ cio.add_stream_cleaner(Nanoc::CLI::StreamCleaners::UTF8)
59
+ end
60
+
61
+ unless enable_ansi_colors?(io)
62
+ cio.add_stream_cleaner(Nanoc::CLI::StreamCleaners::ANSIColors)
63
+ end
64
+
65
+ cio
66
+ end
67
+
68
+ # @return [Boolean] true if UTF-8 support is present, false if not
69
+ def self.enable_utf8?(io)
70
+ return true unless io.tty?
71
+
72
+ %w[LC_ALL LC_CTYPE LANG].any? { |e| ENV[e] =~ /UTF/i }
73
+ end
74
+
75
+ # @return [Boolean] true if color support is present, false if not
76
+ def self.enable_ansi_colors?(io)
77
+ io.tty? && !ENV.key?('NO_COLOR')
78
+ end
79
+
80
+ # Invokes the Nanoc command-line tool with the given arguments.
81
+ #
82
+ # @param [Array<String>] args An array of command-line arguments
83
+ #
84
+ # @return [void]
85
+ def self.run(args)
86
+ Nanoc::CLI::ErrorHandler.handle_while do
87
+ setup
88
+ root_command.run(args)
89
+ end
90
+ end
91
+
92
+ # @return [Cri::Command] The root command, i.e. the command-line tool itself
93
+ def self.root_command
94
+ @root_command
95
+ end
96
+
97
+ # Adds the given command to the collection of available commands.
98
+ #
99
+ # @param [Cri::Command] cmd The command to add
100
+ #
101
+ # @return [void]
102
+ def self.add_command(cmd)
103
+ root_command.add_command(cmd)
104
+ end
105
+
106
+ # Schedules the given block to be executed after the CLI has been set up.
107
+ #
108
+ # @return [void]
109
+ def self.after_setup(&block)
110
+ # TODO: decide what should happen if the CLI is already set up
111
+ add_after_setup_proc(block)
112
+ end
113
+
114
+ # Makes the command-line interface ready for use.
115
+ #
116
+ # @return [void]
117
+ def self.setup
118
+ Nanoc::CLI.setup_cleaning_streams
119
+ setup_commands
120
+ load_custom_commands
121
+ after_setup_procs.each(&:call)
122
+ end
123
+
124
+ # Sets up the root command and base subcommands.
125
+ #
126
+ # @return [void]
127
+ def self.setup_commands
128
+ # Reinit
129
+ @root_command = nil
130
+
131
+ # Add root command
132
+ filename = __dir__ + '/cli/commands/nanoc.rb'
133
+ @root_command = Cri::Command.load_file(filename, infer_name: true)
134
+
135
+ # Add help command
136
+ help_cmd = Cri::Command.new_basic_help
137
+ add_command(help_cmd)
138
+
139
+ # Add other commands
140
+ cmd_filenames = Dir[__dir__ + '/cli/commands/*.rb']
141
+ cmd_filenames.each do |cmd_filename|
142
+ basename = File.basename(cmd_filename, '.rb')
143
+
144
+ next if basename == 'nanoc'
145
+
146
+ cmd = Cri::Command.load_file(cmd_filename, infer_name: true)
147
+ add_command(cmd)
148
+ end
149
+
150
+ if defined?(Bundler)
151
+ # Discover external commands through Bundler
152
+ begin
153
+ Bundler.require(:nanoc)
154
+ rescue Bundler::GemfileNotFound
155
+ # When running Nanoc with Bundler being defined but
156
+ # no gemfile being present (rubygems automatically loads
157
+ # Bundler when executing from command line), don't crash.
158
+ end
159
+ end
160
+ end
161
+
162
+ # Loads site-specific commands.
163
+ #
164
+ # @return [void]
165
+ def self.load_custom_commands
166
+ if Nanoc::Core::SiteLoader.cwd_is_nanoc_site?
167
+ config = Nanoc::Core::ConfigLoader.new.new_from_cwd
168
+ config[:commands_dirs].each do |path|
169
+ load_commands_at(path)
170
+ end
171
+ end
172
+ end
173
+
174
+ def self.load_commands_at(path)
175
+ recursive_contents_of(path).each do |filename|
176
+ # Create command
177
+ command = Cri::Command.load_file(filename, infer_name: true)
178
+
179
+ # Get supercommand
180
+ pieces = filename.gsub(/^#{path}\/|\.rb$/, '').split('/')
181
+ pieces = pieces[0, pieces.size - 1] || []
182
+ root = Nanoc::CLI.root_command
183
+ supercommand = pieces.reduce(root) do |cmd, piece|
184
+ cmd.nil? ? nil : cmd.command_named(piece)
185
+ end
186
+
187
+ # Add to supercommand
188
+ if supercommand.nil?
189
+ raise "Cannot load command at #{filename} because its supercommand cannot be found"
190
+ end
191
+
192
+ supercommand.add_command(command)
193
+ end
194
+ end
195
+
196
+ # @return [Array] The directory contents
197
+ def self.recursive_contents_of(path)
198
+ return [] unless File.directory?(path)
199
+
200
+ files, dirs = *Dir[path + '/*'].sort.partition { |e| File.file?(e) }
201
+ dirs.each { |d| files.concat recursive_contents_of(d) }
202
+ files
203
+ end
204
+
205
+ def self.after_setup_procs
206
+ @after_setup_procs || []
207
+ end
208
+
209
+ def self.add_after_setup_proc(proc)
210
+ @after_setup_procs ||= []
211
+ @after_setup_procs << proc
212
+ end
213
+ end
214
+ end
215
+
216
+ inflector_class = Class.new(Zeitwerk::Inflector) do
217
+ def camelize(basename, abspath)
218
+ case basename
219
+ when 'version', 'cli', 'utf8'
220
+ basename.upcase
221
+ when 'ansi_colors'
222
+ 'ANSIColors'
223
+ when 'ansi_string_colorizer'
224
+ 'ANSIStringColorizer'
225
+ else
226
+ super
227
+ end
228
+ end
229
+ end
230
+
231
+ loader = Zeitwerk::Loader.new
232
+ loader.inflector = inflector_class.new
233
+ loader.push_dir(__dir__ + '/..')
234
+ loader.ignore(__dir__ + '/../nanoc-cli.rb')
235
+ loader.ignore(__dir__ + '/cli/commands')
236
+ loader.setup
237
+ loader.eager_load
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module CLI
5
+ # A simple ANSI colorizer for strings. When given a string and a list of
6
+ # attributes, it returns a colorized string.
7
+ #
8
+ # @api private
9
+ module ANSIStringColorizer
10
+ # TODO: complete mapping
11
+ MAPPING = {
12
+ bold: "\e[1m",
13
+ red: "\e[31m",
14
+ green: "\e[32m",
15
+ yellow: "\e[33m",
16
+ blue: "\e[34m",
17
+ }.freeze
18
+
19
+ # @param [String] str The string to colorize
20
+ #
21
+ # @param [Array] attrs An array of attributes from `MAPPING` to colorize the
22
+ # string with
23
+ #
24
+ # @return [String] A string colorized using the given attributes
25
+ def self.c(str, *attrs)
26
+ attrs.map { |a| MAPPING[a] }.join('') + str + "\e[0m"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module CLI
5
+ # An output stream that passes output through stream cleaners. This can be
6
+ # used to strip ANSI color sequences, for instance.
7
+ class CleaningStream
8
+ # @param [IO, StringIO] stream The stream to wrap
9
+ def initialize(stream)
10
+ @stream = stream
11
+ @stream_cleaners = []
12
+ end
13
+
14
+ # Adds a stream cleaner for the given class to this cleaning stream. If the
15
+ # cleaning stream already has the given stream cleaner, nothing happens.
16
+ #
17
+ # @param [Nanoc::CLI::StreamCleaners::Abstract] klass The class of the
18
+ # stream cleaner to add
19
+ #
20
+ # @return [void]
21
+ def add_stream_cleaner(klass)
22
+ unless @stream_cleaners.map(&:class).include?(klass)
23
+ @stream_cleaners << klass.new
24
+ end
25
+ end
26
+
27
+ # Removes the stream cleaner for the given class from this cleaning stream.
28
+ # If the cleaning stream does not have the given stream cleaner, nothing
29
+ # happens.
30
+ #
31
+ # @param [Nanoc::CLI::StreamCleaners::Abstract] klass The class of the
32
+ # stream cleaner to add
33
+ #
34
+ # @return [void]
35
+ def remove_stream_cleaner(klass)
36
+ @stream_cleaners.delete_if { |c| c.class == klass }
37
+ end
38
+
39
+ # @group IO proxy methods
40
+
41
+ # @see IO#write
42
+ def write(str)
43
+ _nanoc_swallow_broken_pipe_errors_while do
44
+ @stream.write(_nanoc_clean(str))
45
+ end
46
+ end
47
+
48
+ # @see IO#<<
49
+ def <<(str)
50
+ _nanoc_swallow_broken_pipe_errors_while do
51
+ @stream.<<(_nanoc_clean(str))
52
+ end
53
+ end
54
+
55
+ # @see IO#tty?
56
+ def tty?
57
+ @cached_is_tty ||= @stream.tty?
58
+ end
59
+
60
+ # @see IO#isatty
61
+ def isatty
62
+ tty?
63
+ end
64
+
65
+ # @see IO#flush
66
+ def flush
67
+ _nanoc_swallow_broken_pipe_errors_while do
68
+ @stream.flush
69
+ end
70
+ end
71
+
72
+ # @see IO#tell
73
+ def tell
74
+ @stream.tell
75
+ end
76
+
77
+ # @see IO#print
78
+ def print(str)
79
+ _nanoc_swallow_broken_pipe_errors_while do
80
+ @stream.print(_nanoc_clean(str))
81
+ end
82
+ end
83
+
84
+ # @see IO#puts
85
+ def puts(*str)
86
+ _nanoc_swallow_broken_pipe_errors_while do
87
+ @stream.puts(*str.map { |ss| _nanoc_clean(ss) })
88
+ end
89
+ end
90
+
91
+ # @see StringIO#string
92
+ def string
93
+ @stream.string
94
+ end
95
+
96
+ # @see IO#reopen
97
+ def reopen(*args)
98
+ @stream.reopen(*args)
99
+ end
100
+
101
+ # @see IO#close
102
+ def close
103
+ @stream.close
104
+ end
105
+
106
+ # @see File#exist?
107
+ def exist?
108
+ @stream.exist?
109
+ end
110
+
111
+ # @see File.exists?
112
+ def exists?
113
+ @stream.exists?
114
+ end
115
+
116
+ # @see IO.winsize
117
+ def winsize
118
+ @stream.winsize
119
+ end
120
+
121
+ # @see IO.winsize=
122
+ def winsize=(arg)
123
+ @stream.winsize = arg
124
+ end
125
+
126
+ # @see IO.sync
127
+ def sync
128
+ @stream.sync
129
+ end
130
+
131
+ # @see IO.sync=
132
+ def sync=(arg)
133
+ @stream.sync = arg
134
+ end
135
+
136
+ # @see IO.sync=
137
+ def external_encoding
138
+ @stream.external_encoding
139
+ end
140
+
141
+ # @see ARGF.set_encoding
142
+ # rubocop:disable Naming/AccessorMethodName
143
+ def set_encoding(*args)
144
+ @stream.set_encoding(*args)
145
+ end
146
+ # rubocop:enable Naming/AccessorMethodName
147
+
148
+ protected
149
+
150
+ def _nanoc_clean(str)
151
+ @stream_cleaners.reduce(str.to_s.scrub) { |acc, elem| elem.clean(acc) }
152
+ end
153
+
154
+ def _nanoc_swallow_broken_pipe_errors_while
155
+ yield
156
+ rescue Errno::EPIPE
157
+ end
158
+ end
159
+ end
160
+ end