nanoc-cli 4.11.13

Sign up to get free protection for your applications and to get access to all the features.
@@ -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