asciidoctor-reducer 1.0.0.alpha.7 → 1.0.0.alpha.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a685ae408de9db32a7ec96055b70120ea6f7476436dca071cc37155cc680d56
4
- data.tar.gz: 39d783c66481423417d64a232ee01bdaa88bef517474d22e8ec9fdeb670d09d9
3
+ metadata.gz: 425f7379b4e69cb9487a2c2affcd77bd7cc548bd7818b073334f45a64f853f9c
4
+ data.tar.gz: b3a85d94ee64d2ecee967a3a88a7ce4295af83eda84c76f852df24fcb312b9ee
5
5
  SHA512:
6
- metadata.gz: a3376645764a9d608f3e3aad5f29f2080748d9ecd20d90a06e2335263cdd7c88c0450a72ea061f8f36c17fa19af0f985a89002ee70e12ce950f4320c483b8140
7
- data.tar.gz: a7899259cf2ff2daf619d13b97d88dd460a013b0bc85d2c8402e1283f1b3d6fff3780bc3c112e4c281463ad06e0bcb035385998771528a01e3aac487dd7c6e09
6
+ metadata.gz: f48fffea4f5dd09c1cec0d375a1fe1050144c5e723802413e2de3191c57e221f0482f210addb533d4b7ddac23a7142ee5b75721e9d5b1269e001d17da97d5726
7
+ data.tar.gz: a4538dc754a1f4b82aa0fd4bb8b89ed848da9b3932430dbb2c51c3b443d08c0d5a4c345925be75cbbee0c0fa1cf8dce08e60d42d9a27d3b2ddb2b5ad9c30f5c6
data/CHANGELOG.adoc CHANGED
@@ -4,6 +4,63 @@
4
4
  This document provides a high-level view of the changes to the Asciidoctor Reducer by release.
5
5
  For a detailed view of what has changed, refer to the {url-repo}/commits/main[commit history] on GitHub.
6
6
 
7
+ == 1.0.0.alpha.10 (2022-04-21) - @mojavelinux
8
+
9
+ === Changed
10
+
11
+ * Set Ruby 2.7 as minimum Ruby version
12
+
13
+ === Details
14
+
15
+ {url-repo}/releases/tag/v1.0.0.alpha.10[git tag] | {url-repo}/compare/v1.0.0.alpha.9\...v1.0.0.alpha.10[full diff]
16
+
17
+ == 1.0.0.alpha.9 (2022-04-21) - @mojavelinux
18
+
19
+ === Added
20
+
21
+ * Add `Asciidoctor::Reducer::IncludeMapper` auxiliary extension, required by `asciidoctor/reducer/include_mapper/extension` (#26)
22
+ * Register `Asciidoctor::Reducer::IncludeMapper` extension when `asciidoctor/reducer/include_mapper` is required (#26)
23
+ * Add `Asciidoctor::Reducer::Extensions.key` method that returns key for registering extension group
24
+ * Update help text to note that the `-a` and `-r` CLI options may be specified multiple times
25
+ * Automate the release process
26
+
27
+ === Changed
28
+
29
+ * Rename x_include_replacements attr on reader to include_replacements since it's public
30
+ * Don't pass `:to` option to `Asciidoctor.load_file`
31
+ * Make `Asciidoctor::Reducer::Cli` a module instead of a class
32
+
33
+ === Fixed
34
+
35
+ * Replace remote include with link if `allow-uri-read` attribute is not set
36
+ * Don't raise error if `Asciidoctor::Reducer::Extensions.unregister` is called when extensions are not registered globally
37
+ * Ensure output is written to file with universal newlines (\n) on Windows
38
+
39
+ === Details
40
+
41
+ {url-repo}/releases/tag/v1.0.0.alpha.9[git tag] | {url-repo}/compare/v1.0.0.alpha.8\...v1.0.0.alpha.9[full diff]
42
+
43
+ == 1.0.0.alpha.8 (2022-02-23) - @mojavelinux
44
+
45
+ === Added
46
+
47
+ * Add secure mode as value of `-S` CLI option (#31)
48
+ * Add `--trace` option to CLI to trace cause of application errors (#29)
49
+
50
+ === Changed
51
+
52
+ * Replace include directive with link macro if safe mode is secure (#31)
53
+ * Track line numbers in include replacements using 1-based index
54
+ * Only mix in preprocessor conditional tracker if `:preserve_conditionals` option is not set (#36)
55
+
56
+ === Fixed
57
+
58
+ * Handle signals gracefully (#33)
59
+
60
+ === Details
61
+
62
+ {url-repo}/releases/tag/v1.0.0.alpha.8[git tag]
63
+
7
64
  == 1.0.0.alpha.7 (2022-02-14) - @mojavelinux
8
65
 
9
66
  === Added
@@ -23,6 +80,10 @@ For a detailed view of what has changed, refer to the {url-repo}/commits/main[co
23
80
 
24
81
  * Require asciidoctor/reducer/version automatically when `Asciidoctor::Reducer::VERSION` is accessed
25
82
 
83
+ === Details
84
+
85
+ {url-repo}/releases/tag/v1.0.0.alpha.7[git tag]
86
+
26
87
  == 1.0.0.alpha.6 (2022-02-10) - @mojavelinux
27
88
 
28
89
  === Added
@@ -43,6 +104,10 @@ For a detailed view of what has changed, refer to the {url-repo}/commits/main[co
43
104
  * Prevent custom extension registry from activating extensions twice during reload (#21)
44
105
  * Retain includes table in document catalog when reloading document (#23)
45
106
 
107
+ === Details
108
+
109
+ {url-repo}/releases/tag/v1.0.0.alpha.6[git tag]
110
+
46
111
  == 1.0.0.alpha.5 (2022-02-06) - @mojavelinux
47
112
 
48
113
  === Changed
@@ -56,12 +121,20 @@ For a detailed view of what has changed, refer to the {url-repo}/commits/main[co
56
121
 
57
122
  * Suppress log messages when reloading document (#14)
58
123
 
124
+ === Details
125
+
126
+ {url-repo}/releases/tag/v1.0.0.alpha.5[git tag]
127
+
59
128
  == 1.0.0.alpha.4 (2022-02-03) - @mojavelinux
60
129
 
61
130
  === Fixed
62
131
 
63
132
  * Fix replacement of nested empty and unresolved includes
64
133
 
134
+ === Details
135
+
136
+ {url-repo}/releases/tag/v1.0.0.alpha.4[git tag]
137
+
65
138
  == 1.0.0.alpha.3 (2022-02-02) - @mojavelinux
66
139
 
67
140
  === Changed
@@ -71,6 +144,10 @@ For a detailed view of what has changed, refer to the {url-repo}/commits/main[co
71
144
  * Only reload document if source lines have changed; otherwise, update source lines on reader directly
72
145
  * Change default safe mode for CLI to :unsafe
73
146
 
147
+ === Details
148
+
149
+ {url-repo}/releases/tag/v1.0.0.alpha.3[git tag]
150
+
74
151
  == 1.0.0.alpha.2 (2022-01-27) - @mojavelinux
75
152
 
76
153
  === Added
@@ -90,34 +167,14 @@ For a detailed view of what has changed, refer to the {url-repo}/commits/main[co
90
167
 
91
168
  * Preserve return value when overridding `preprocess_include_directive` method
92
169
 
93
- == 1.0.0.alpha.1 (2022-01-12) - @mojavelinux
94
-
95
- Initial release.
96
-
97
- === Details
98
-
99
- {url-repo}/releases/tag/v1.0.0.alpha.1[git tag]
100
-
101
170
  === Details
102
171
 
103
172
  {url-repo}/releases/tag/v1.0.0.alpha.2[git tag]
104
173
 
105
- === Details
106
-
107
- {url-repo}/releases/tag/v1.0.0.alpha.3[git tag]
108
-
109
- === Details
110
-
111
- {url-repo}/releases/tag/v1.0.0.alpha.4[git tag]
112
-
113
- === Details
114
-
115
- {url-repo}/releases/tag/v1.0.0.alpha.5[git tag]
116
-
117
- === Details
174
+ == 1.0.0.alpha.1 (2022-01-12) - @mojavelinux
118
175
 
119
- {url-repo}/releases/tag/v1.0.0.alpha.6[git tag]
176
+ Initial release.
120
177
 
121
178
  === Details
122
179
 
123
- {url-repo}/releases/tag/v1.0.0.alpha.7[git tag]
180
+ {url-repo}/releases/tag/v1.0.0.alpha.1[git tag]
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (C) 2021 Dan Allen
3
+ Copyright (C) 2021-present Dan Allen
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.adoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = {project-name}
2
2
  Dan Allen <https://github.com/mojavelinux[@mojavelinux]>
3
- v1.0.0.alpha.7, 2022-02-14
3
+ v1.0.0.alpha.10, 2022-04-21
4
4
  :idprefix:
5
5
  :idseparator: -
6
6
  ifndef::env-github[:icons: font]
@@ -16,14 +16,14 @@ endif::[]
16
16
  :url-rvm: https://rvm.io
17
17
  :url-repo: https://github.com/asciidoctor/{project-handle}
18
18
 
19
- {project-name} is a tool that reduces a composite AsciiDoc document containing includes to a single AsciiDoc document by expanding any includes reachable from the parent document.
20
- The tool also applies preprocessor conditionals (unless the option to preserve them is specified), leaving behind only the enabled lines.
19
+ {project-name} is a tool that reduces an AsciiDoc document that contains includes to a single AsciiDoc document by expanding any includes reachable from the parent document.
20
+ The tool also applies preprocessor conditionals (unless the option to preserve them is specified), leaving behind only the selected lines.
21
21
  If the document does not contain any preprocessor directives, the tool returns the original source.
22
22
 
23
23
  == Prerequisites
24
24
 
25
25
  {project-name} is a Ruby application that you install using Ruby packaging.
26
- To install and run {project-name}, you need Ruby 2.5 or better.
26
+ To install and run {project-name}, you need Ruby 2.7 or better.
27
27
 
28
28
  Run the following command to check which version of Ruby you have installed, if any:
29
29
 
@@ -197,6 +197,69 @@ This call will cause extensions that run during the load phase to be invoked aga
197
197
  An extension can check for this secondary load by checking for the `:reduced` option in the `Document#options` hash.
198
198
  If this option is set (the value of which will be `true`), then Asciidoctor is loading the reduced document.
199
199
 
200
+ == Include Mapper (Experimental)
201
+
202
+ One of the challenges of reducing a document is that interdocument xrefs that rely on the includes being registered in the document catalog no longer work.
203
+ That's because when the reduced document is converted, it has no includes and thus all interdocument xrefs are colocated in the same source file.
204
+ To work around this shortcoming, {project-name} provides a utility extension named the include mapper that will carry over the includes in the document catalog to the reduced document so they can be imported during conversion.
205
+
206
+ CAUTION: The include mapper is experimental and thus subject to change.
207
+
208
+ To use the include mapper when using the CLI to reduce the document, require it using the `-r` option as follows:
209
+
210
+ $ asciidoctor-reducer -r asciidoctor/reducer/include_mapper -o input-reduced.adoc input.adoc
211
+
212
+ To use the include mapper when converting the reduced document, again require it using the `-r` option as follows:
213
+
214
+ $ asciidoctor -r asciidoctor/reducer/include_mapper input-reduced.adoc
215
+
216
+ To use the include mapper when using the API, first require the extension:
217
+
218
+ [,ruby]
219
+ ----
220
+ require 'asciidocotor/reducer/include_mapper/extension'
221
+ ----
222
+
223
+ You then need to register the extension when reducing the document:
224
+
225
+ [,ruby]
226
+ ----
227
+ Asciidoctor::Reducer.reduce_file 'sample.adoc', to: 'sample-reduced.adoc', extensions: proc {
228
+ next if document.options[:reduced]
229
+ tree_processor Asciidoctor::Reducer::IncludeMapper
230
+ }
231
+ ----
232
+
233
+ Then register it again when converting the reduced document:
234
+
235
+ [,ruby]
236
+ ----
237
+ Asciidoctor.convert_file 'sample-reduced.adoc', safe: :safe, extensions: proc {
238
+ tree_processor Asciidoctor::Reducer::IncludeMapper
239
+ }
240
+ ----
241
+
242
+ You can also register the extension globally:
243
+
244
+ [,ruby]
245
+ ----
246
+ require 'asciidocotor/reducer/include_mapper'
247
+ ----
248
+
249
+ In this case, you don't have to pass it to the API explicitly.
250
+
251
+ === How it Works
252
+
253
+ The include mapper works by adding a magic comment to the bottom of the reduced file.
254
+ Here's an example of that comment:
255
+
256
+ [,asciidoc]
257
+ ----
258
+ //# includes=chapters/chapter-a,chapters/chapter-b
259
+ ----
260
+
261
+ When a document that contains the magic comment is converted, the include mapper reads the comma-separated paths in the value and loads them into the includes table of the document catalog.
262
+
200
263
  == Development
201
264
 
202
265
  Follow the instructions below to learn how to help develop the project or test-drive the development version.
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.homepage = 'https://asciidoctor.org'
15
15
  s.license = 'MIT'
16
16
  # NOTE required ruby version is informational only; it's not enforced since it can't be overridden and can cause builds to break
17
- #s.required_ruby_version = '>= 2.5.0'
17
+ #s.required_ruby_version = '>= 2.7.0'
18
18
  s.metadata = {
19
19
  'bug_tracker_uri' => 'https://github.com/asciidoctor/asciidoctor-reducer/issues',
20
20
  'changelog_uri' => 'https://github.com/asciidoctor/asciidoctor-reducer/blob/main/CHANGELOG.adoc',
@@ -1,11 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- asciidoctor_reducer_cli = File.absolute_path '../lib/asciidoctor/reducer/cli', __dir__
5
- if File.exist? asciidoctor_reducer_cli
6
- require asciidoctor_reducer_cli
7
- else
8
- require 'asciidoctor/reducer/cli'
9
- end
10
-
4
+ asciidoctor_reducer_cli = File.join (File.dirname __dir__), 'lib/asciidoctor/reducer/cli.rb'
5
+ require (File.file? asciidoctor_reducer_cli) ? asciidoctor_reducer_cli : 'asciidoctor/reducer/cli'
11
6
  exit Asciidoctor::Reducer::Cli.run
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pathname'
3
+ autoload :Pathname, 'pathname'
4
4
  require_relative 'extensions'
5
5
 
6
6
  module Asciidoctor::Reducer
@@ -8,8 +8,12 @@ module Asciidoctor::Reducer
8
8
 
9
9
  class << self
10
10
  def reduce input, opts = {}
11
- opts = opts.merge extension_registry: (Extensions.prepare_registry opts[:extension_registry] || opts[:extensions])
12
- opts[:safe] = ::Asciidoctor::SafeMode::SAFE unless opts.key? :safe
11
+ opts = opts&.merge || {}
12
+ if (extension_registry = Extensions.prepare_registry opts[:extension_registry] || opts[:extensions])
13
+ opts[:extension_registry] = extension_registry
14
+ end
15
+ opts[:safe] ||= :safe
16
+ to = opts.delete :to
13
17
  case input
14
18
  when ::File
15
19
  doc = ::Asciidoctor.load_file input, opts
@@ -18,7 +22,7 @@ module Asciidoctor::Reducer
18
22
  else
19
23
  doc = ::Asciidoctor.load input, opts
20
24
  end
21
- write doc, opts[:to]
25
+ write doc, to
22
26
  end
23
27
 
24
28
  def reduce_file input_file, opts = {}
@@ -28,16 +32,15 @@ module Asciidoctor::Reducer
28
32
  private
29
33
 
30
34
  def write doc, to
31
- if to && to != '/dev/null'
32
- output = doc.source
33
- return output if to == ::String
34
- output += LF unless output.empty?
35
- if ::Pathname === to || (!(to.respond_to? :write) && (to = ::Pathname.new to.to_s))
36
- to.dirname.mkpath
37
- to.write output, encoding: UTF_8
38
- else
39
- to.write output
40
- end
35
+ return doc unless to && to != '/dev/null'
36
+ output = doc.source
37
+ return output if to == ::String
38
+ output += LF unless output.empty?
39
+ if ::Pathname === to || (!(to.respond_to? :write) && (to = ::Pathname.new to.to_s))
40
+ to.dirname.mkpath
41
+ to.write output, encoding: UTF_8, newline: :universal
42
+ else
43
+ to.write output
41
44
  end
42
45
  doc
43
46
  end
@@ -4,114 +4,138 @@ require_relative 'api'
4
4
  autoload :OptionParser, 'optparse'
5
5
 
6
6
  module Asciidoctor::Reducer
7
- class Cli
8
- LOG_LEVELS = (::Logger::Severity.constants false).each_with_object({}) do |level, accum|
9
- accum[level.to_s.downcase] = (::Logger::Severity.const_get level) unless level == :UNKNOWN
10
- end
7
+ module Cli
8
+ class << self
9
+ def parse args
10
+ options = { attributes: {}, log_level: LOG_LEVELS['warn'], safe: :unsafe }
11
+
12
+ opt_parser = ::OptionParser.new do |opts|
13
+ opts.program_name = 'asciidoctor-reducer'
14
+ opts.banner = <<~END
15
+ Usage: #{opts.program_name} [OPTION]... FILE
16
+
17
+ Reduces a composite AsciiDoc document containing includes and conditionals to a single AsciiDoc document.
18
+
19
+ END
20
+
21
+ opts.on '-a KEY[=VALUE]', '--attribute=KEY[=VALUE]',
22
+ 'set a document attribute in the AsciiDoc document: [key, key!, key=value]',
23
+ 'may be specified multiple times' do |attr|
24
+ key, val = attr.split '=', 2
25
+ val ||= ''
26
+ options[:attributes][key] = val
27
+ end
11
28
 
12
- def parse args
13
- options = { attributes: {}, log_level: LOG_LEVELS['warn'], safe: :unsafe }
29
+ opts.on '--log-level LEVEL', %w(debug info warn error fatal),
30
+ 'set the minimum level of messages to log: [debug, info, warn, error, fatal] (default: warn)' do |level|
31
+ options[:log_level] = level
32
+ end
14
33
 
15
- opt_parser = ::OptionParser.new do |opts|
16
- opts.program_name = 'asciidoctor-reducer'
17
- opts.banner = <<~EOS
18
- Usage: #{opts.program_name} [OPTION]... FILE
34
+ opts.on '-o FILE', '--output=FILE', 'set the output filename or stream' do |file|
35
+ options[:output_file] = file
36
+ end
19
37
 
20
- Reduces a composite AsciiDoc document containing includes and conditionals to a single AsciiDoc document.
38
+ opts.on '--preserve-conditionals', 'preserve preprocessor conditional directives in the reduced source' do
39
+ options[:preserve_conditionals] = true
40
+ end
21
41
 
22
- EOS
42
+ opts.on '-q', '--quiet', 'suppress all application log messages' do
43
+ options[:log_level] = nil
44
+ end
23
45
 
24
- opts.on '-a KEY[=VALUE]', '--attribute=KEY[=VALUE]',
25
- 'set a document attribute in the AsciiDoc document: [key, key!, key=value]' do |attr|
26
- key, val = attr.split '=', 2
27
- val ||= ''
28
- options[:attributes][key] = val
29
- end
46
+ opts.on '-rLIBRARY', '--require LIBRARY', 'require the specified library or libraries before reducing',
47
+ 'may be specified multiple times' do |path|
48
+ (options[:requires] ||= []).concat path.split ','
49
+ end
30
50
 
31
- opts.on '--log-level LEVEL', %w(debug info warn error fatal),
32
- 'set the minimum level of messages to log: [debug, info, warn, error, fatal] (default: warn)' do |level|
33
- options[:log_level] = level
34
- end
51
+ opts.on '-S', '--safe-mode SAFE_MODE', ['unsafe', 'safe', 'server', 'secure'],
52
+ 'set safe mode level: [unsafe, safe, server, secure] (default: unsafe)' do |name|
53
+ options[:safe] = name.to_sym
54
+ end
35
55
 
36
- opts.on '-o FILE', '--output=FILE', 'set the output filename or stream' do |file|
37
- options[:output_file] = file
38
- end
56
+ opts.on '--trace', 'trace the cause of application errors (default: false)' do
57
+ options[:trace] = true
58
+ end
39
59
 
40
- opts.on '--preserve-conditionals', 'preserve preprocessor conditional directives in the reduced source' do
41
- options[:preserve_conditionals] = true
42
- end
60
+ opts.on '-v', '--version', 'display the version information and exit' do
61
+ print_version opts
62
+ return 0
63
+ end
43
64
 
44
- opts.on '-q', '--quiet', 'suppress all application log messages' do
45
- options[:log_level] = nil
65
+ opts.on '-h', '--help', 'display this help text and exit' do
66
+ print_help opts
67
+ return 0
68
+ end
46
69
  end
47
70
 
48
- opts.on '-rLIBRARY', '--require LIBRARY', 'require the specified library or libraries before running' do |path|
49
- (options[:requires] ||= []).concat path.split ','
71
+ if (args = opt_parser.parse args).empty?
72
+ opt_parser.warn 'Please specify an AsciiDoc file to reduce.'
73
+ print_help opt_parser
74
+ 1
75
+ elsif args.size == 1
76
+ if (requires = options.delete :requires)
77
+ requires.uniq.each do |path|
78
+ require path
79
+ rescue ::LoadError
80
+ $stderr.puts %(#{opt_parser.program_name}: '#{path}' could not be required (reason: #{$!.message}))
81
+ return 1
82
+ end
83
+ end
84
+ options[:input_file] = args[0]
85
+ options[:output_file] = '-' unless options[:output_file]
86
+ [0, options]
87
+ else
88
+ opt_parser.warn %(extra arguments detected (unparsed arguments: #{(args.drop 1).join ' '}))
89
+ print_help opt_parser
90
+ 1
50
91
  end
92
+ rescue ::OptionParser::InvalidOption
93
+ $stderr.puts %(#{opt_parser.program_name}: #{$!.message})
94
+ print_help opt_parser
95
+ 1
96
+ end
51
97
 
52
- opts.on '-S', '--safe-mode SAFE_MODE', ['unsafe', 'safe', 'server'],
53
- 'set supported safe mode level: [unsafe, safe, server] (default: unsafe)' do |name|
54
- options[:safe] = ::Asciidoctor::SafeMode.value_for_name name
98
+ def run args = ARGV
99
+ code, options = parse (Array args)
100
+ return code unless code == 0 && options
101
+ trace = options.delete :trace
102
+ old_logger = ::Asciidoctor::LoggerManager.logger
103
+ if (log_level = options.delete :log_level)
104
+ (options[:logger] = ::Asciidoctor::Logger.new $stderr).level = log_level
105
+ else
106
+ options[:logger] = nil
55
107
  end
108
+ options[:to] = (output_file = options.delete :output_file) == '-' ? $stdout : (::Pathname.new output_file)
109
+ input = (input_file = options.delete :input_file) == '-' ? $stdin : (::Pathname.new input_file)
110
+ ::Asciidoctor::Reducer.reduce input, options
111
+ 0
112
+ rescue ::SignalException
113
+ $stderr.puts if ::Interrupt === $!
114
+ $!.signo
115
+ rescue
116
+ raise $! if trace
117
+ $stderr.puts %(asciidoctor-reducer: #{$!.message.delete_prefix 'asciidoctor: '})
118
+ $stderr.puts ' Use --trace to show backtrace'
119
+ 1
120
+ ensure
121
+ ::Asciidoctor::LoggerManager.logger = old_logger if old_logger
122
+ end
56
123
 
57
- opts.on '-v', '--version', 'display the version information and exit' do
58
- $stdout.write %(#{opts.program_name} #{VERSION}\n)
59
- return 0
60
- end
124
+ private
61
125
 
62
- opts.on '-h', '--help', 'display this help text and exit' do
63
- $stdout.write opts.help
64
- return 0
65
- end
126
+ def print_help opt_parser
127
+ $stdout.puts opt_parser.help.chomp
66
128
  end
67
129
 
68
- args = opt_parser.parse args
69
-
70
- if args.empty?
71
- opt_parser.warn 'Please specify an AsciiDoc file to reduce.'
72
- $stdout.write opt_parser.help
73
- 1
74
- elsif args.size == 1
75
- if (requires = options.delete :requires)
76
- requires.uniq.each do |path|
77
- require path
78
- rescue ::LoadError
79
- $stderr.write %(#{opt_parser.program_name}: '#{path}' could not be required (reason: #{$!.message})\n)
80
- return 1
81
- end
82
- end
83
- options[:input_file] = args[0]
84
- options[:output_file] = '-' unless options[:output_file]
85
- [0, options]
86
- else
87
- opt_parser.warn %(extra arguments detected (unparsed arguments: #{(args.drop 1).join ' '}))
88
- $stdout.write opt_parser.help
89
- 1
130
+ def print_version opt_parser
131
+ $stdout.puts %(#{opt_parser.program_name} #{VERSION})
90
132
  end
91
- rescue ::OptionParser::InvalidOption
92
- $stderr.write %(#{opt_parser.program_name}: #{$!.message}\n)
93
- $stdout.write opt_parser.help
94
- 1
95
133
  end
96
134
 
97
- def self.run args = ARGV
98
- code, options = new.parse (Array args)
99
- return code unless code == 0 && options
100
- old_logger = ::Asciidoctor::LoggerManager.logger
101
- if (log_level = options.delete :log_level)
102
- (options[:logger] = ::Asciidoctor::Logger.new $stderr).level = log_level
103
- else
104
- options[:logger] = nil
105
- end
106
- options[:to] = (output_file = options.delete :output_file) == '-' ? $stdout : (::Pathname.new output_file)
107
- input = (input_file = options.delete :input_file) == '-' ? $stdin : (::Pathname.new input_file)
108
- ::Asciidoctor::Reducer.reduce input, options
109
- 0
110
- rescue
111
- $stderr.write %(asciidoctor-reducer: #{$!.message}\n)
112
- 1
113
- ensure
114
- ::Asciidoctor::LoggerManager.logger = old_logger if old_logger
135
+ LOG_LEVELS = (::Logger::Severity.constants false).each_with_object({}) do |level, accum|
136
+ accum[level.to_s.downcase] = (::Logger::Severity.const_get level) unless level == :UNKNOWN
115
137
  end
138
+
139
+ private_constant :LOG_LEVELS
116
140
  end
117
141
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor::Reducer
4
+ module ConditionalDirectiveTracker
5
+ def preprocess_conditional_directive keyword, target, delimiter, text
6
+ skip_active = @skipping
7
+ depth = @conditional_stack.size
8
+ cond_lineno = @lineno
9
+ result = super
10
+ return result if @skipping && skip_active
11
+ drop = @include_replacements.current[:drop] ||= []
12
+ if (depth_change = @conditional_stack.size - depth) < 0
13
+ if skip_active
14
+ drop.push(*(drop.pop..cond_lineno))
15
+ else
16
+ drop << cond_lineno
17
+ end
18
+ elsif depth_change > 0 || cond_lineno == @lineno
19
+ drop << cond_lineno
20
+ else
21
+ drop << [cond_lineno, text]
22
+ end
23
+ result
24
+ end
25
+ end
26
+ end
@@ -16,24 +16,27 @@ module Asciidoctor::Reducer
16
16
  end
17
17
  end
18
18
 
19
+ def key
20
+ :reducer
21
+ end
22
+
19
23
  def prepare_registry registry = nil
20
24
  registry = ::Asciidoctor::Extensions.create(&registry) if ::Proc === registry
21
- unless ::Asciidoctor::Extensions.groups[:reducer]
22
- if registry
23
- registry.groups[:reducer] = group
24
- else
25
- registry = ::Asciidoctor::Extensions.create :reducer, &group
26
- end
25
+ return registry if ::Asciidoctor::Extensions.groups[key]
26
+ if registry
27
+ registry.groups[key] = group
28
+ registry
29
+ else
30
+ ::Asciidoctor::Extensions.create key, &group
27
31
  end
28
- registry
29
32
  end
30
33
 
31
34
  def register
32
- ::Asciidoctor::Extensions.register :reducer, &group
35
+ ::Asciidoctor::Extensions.register key, &group
33
36
  end
34
37
 
35
38
  def unregister
36
- ::Asciidoctor::Extensions.unregister :reducer
39
+ ::Asciidoctor::Extensions.groups.delete key # NOTE `Extensions.unregister key` fails if groups is not initialized
37
40
  end
38
41
  end
39
42
  end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor::Reducer
4
+ module IncludeDirectiveTracker
5
+ attr_reader :include_replacements
6
+ attr_writer :source_lines
7
+
8
+ def self.extended instance
9
+ instance.instance_variable_set :@include_replacements, ([{}].extend CurrentPosition)
10
+ instance.instance_variable_set :@x_reducer, {}
11
+ end
12
+
13
+ def preprocess_include_directive target, attrlist
14
+ @x_reducer[:include_directive_line] = %(include::#{target}[#{attrlist}])
15
+ @x_reducer[:include_pushed] = false
16
+ inc_lineno = @lineno # we're currently on the include line, which is 1-based
17
+ result = super
18
+ unless @x_reducer[:include_pushed]
19
+ if ((ln = peek_line true)&.end_with? ']') && !(unresolved = ln.start_with? 'Unresolved directive in ') &&
20
+ inc_lineno == @lineno && (unresolved = ln.start_with? 'link:')
21
+ ln = %(#{ln.slice 0, (ln.length - 1)}role=include])
22
+ end
23
+ push_include_replacement inc_lineno, (unresolved ? [ln] : []), unresolved
24
+ end
25
+ @x_reducer.clear
26
+ result
27
+ end
28
+
29
+ def push_include data, file, path, lineno, attrs
30
+ @x_reducer[:include_pushed] = true
31
+ inc_lineno = @lineno - 1 # we're below the include line, which is 1-based
32
+ prev_inc_depth = @include_stack.size
33
+ result = super
34
+ push_include_replacement inc_lineno, (@include_stack.size > prev_inc_depth ? lines : [])
35
+ result
36
+ end
37
+
38
+ def pop_include
39
+ @include_replacements.up unless @x_reducer[:include_pushed]
40
+ super
41
+ end
42
+
43
+ private
44
+
45
+ def push_include_replacement lineno, lines, unresolved = false
46
+ (inc_replacements = @include_replacements) << {
47
+ into: inc_replacements.pointer,
48
+ lineno: lineno,
49
+ line: @x_reducer[:include_directive_line],
50
+ lines: lines,
51
+ }
52
+ inc_replacements.to_end unless unresolved || lines.empty?
53
+ nil
54
+ end
55
+ end
56
+
57
+ module CurrentPosition
58
+ attr_reader :pointer
59
+
60
+ def self.extended instance
61
+ instance.to_end
62
+ end
63
+
64
+ def current
65
+ self[@pointer]
66
+ end
67
+
68
+ def to_end
69
+ @pointer = size - 1
70
+ end
71
+
72
+ def up
73
+ @pointer = current[:into]
74
+ end
75
+ end
76
+
77
+ private_constant :CurrentPosition
78
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor::Reducer
4
+ class IncludeMapper < ::Asciidoctor::Extensions::TreeProcessor
5
+ def process doc
6
+ if doc.extensions.groups[:reducer]
7
+ unless (includes = doc.catalog[:includes].select {|_, v| v }.keys).empty?
8
+ doc.source_lines.concat ['', %(//# includes=#{includes.join ','})]
9
+ end
10
+ elsif (last_line = doc.source_lines[-1])&.start_with? '//# includes='
11
+ doc.catalog[:includes].update ((last_line.slice 13, last_line.length).split ',').map {|it| [it, true] }.to_h
12
+ end
13
+ doc
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'include_mapper/extension'
4
+
5
+ Asciidoctor::Extensions.register do
6
+ next if document.options[:reduced]
7
+ tree_processor Asciidoctor::Reducer::IncludeMapper
8
+ end
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'preprocessor_directive_tracker'
3
+ require_relative 'include_directive_tracker'
4
+ require_relative 'conditional_directive_tracker'
4
5
 
5
6
  module Asciidoctor::Reducer
6
7
  class Preprocessor < ::Asciidoctor::Extensions::Preprocessor
7
- def process _, reader
8
- reader.extend PreprocessorDirectiveTracker
8
+ def process doc, reader
9
+ doc.options[:preserve_conditionals] ?
10
+ (reader.extend IncludeDirectiveTracker) : (reader.extend ConditionalDirectiveTracker, IncludeDirectiveTracker)
9
11
  end
10
12
  end
11
13
  end
@@ -3,19 +3,21 @@
3
3
  module Asciidoctor::Reducer
4
4
  class TreeProcessor < ::Asciidoctor::Extensions::TreeProcessor
5
5
  def process doc
6
- unless (inc_replacements = doc.reader.x_include_replacements).length == 1 && inc_replacements[0][:drop].empty?
6
+ if (inc_replacements = doc.reader.include_replacements).size > 1 || !(inc_replacements[0][:drop] || []).empty?
7
7
  inc_replacements[0][:lines] = doc.source_lines.dup
8
8
  inc_replacements.reverse_each do |it|
9
9
  if (into = it[:into])
10
10
  target_lines = inc_replacements[into][:lines]
11
11
  # adds extra bit of assurance that we're replacing the correct line
12
- next unless target_lines[(index = it[:lineno])] == it[:line]
12
+ next unless target_lines[(idx = it[:lineno] - 1)] == it[:line]
13
13
  end
14
14
  lines = it[:lines]
15
- unless (drop = it[:drop]).empty?
16
- drop.reverse_each {|idx| ::Array === idx ? (lines[idx[0]] = idx[1]) : (lines.delete_at idx) }
15
+ unless (drop = it[:drop] || []).empty?
16
+ drop.reverse_each do |drop_it|
17
+ ::Array === drop_it ? (lines[drop_it[0] - 1] = drop_it[1]) : (lines.delete_at drop_it - 1)
18
+ end
17
19
  end
18
- target_lines[index] = lines if target_lines
20
+ target_lines[idx] = lines if target_lines
19
21
  end
20
22
  source_lines = inc_replacements[0][:lines].flatten
21
23
  if doc.sourcemap
@@ -30,7 +32,7 @@ module Asciidoctor::Reducer
30
32
  doc.parse
31
33
  ::Asciidoctor::LoggerManager.logger = logger
32
34
  else
33
- source_lines.pop while (last = source_lines[-1]) && last.empty?
35
+ source_lines.pop while (source_lines[-1] || :eof).empty?
34
36
  doc.reader.source_lines = source_lines
35
37
  end
36
38
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Asciidoctor
4
4
  module Reducer
5
- VERSION = '1.0.0.alpha.7'
5
+ VERSION = '1.0.0.alpha.10'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciidoctor-reducer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.alpha.7
4
+ version: 1.0.0.alpha.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Allen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-14 00:00:00.000000000 Z
11
+ date: 2022-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -70,9 +70,12 @@ files:
70
70
  - lib/asciidoctor/reducer.rb
71
71
  - lib/asciidoctor/reducer/api.rb
72
72
  - lib/asciidoctor/reducer/cli.rb
73
+ - lib/asciidoctor/reducer/conditional_directive_tracker.rb
73
74
  - lib/asciidoctor/reducer/extensions.rb
75
+ - lib/asciidoctor/reducer/include_directive_tracker.rb
76
+ - lib/asciidoctor/reducer/include_mapper.rb
77
+ - lib/asciidoctor/reducer/include_mapper/extension.rb
74
78
  - lib/asciidoctor/reducer/preprocessor.rb
75
- - lib/asciidoctor/reducer/preprocessor_directive_tracker.rb
76
79
  - lib/asciidoctor/reducer/tree_processor.rb
77
80
  - lib/asciidoctor/reducer/version.rb
78
81
  homepage: https://asciidoctor.org
@@ -98,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
101
  - !ruby/object:Gem::Version
99
102
  version: 1.3.1
100
103
  requirements: []
101
- rubygems_version: 3.2.32
104
+ rubygems_version: 3.3.7
102
105
  signing_key:
103
106
  specification_version: 4
104
107
  summary: Reduces a composite AsciiDoc document containing includes and conditionals
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Asciidoctor::Reducer
4
- module PreprocessorDirectiveTracker
5
- attr_writer :source_lines
6
- attr_reader :x_include_replacements
7
-
8
- def self.extended instance
9
- instance.instance_variable_set :@x_include_replacements, ([{ drop: [] }].extend CurrentPosition)
10
- instance.instance_variable_set :@x_include_directive_line, nil
11
- instance.instance_variable_set :@x_include_pushed, nil
12
- end
13
-
14
- def preprocess_conditional_directive keyword, target, delimiter, text
15
- return super if @document.options[:preserve_conditionals]
16
- skip_active = @skipping
17
- depth = @conditional_stack.length
18
- cond_lineno = @lineno - 1
19
- result = super
20
- return result if @skipping && skip_active
21
- drop = @x_include_replacements.current[:drop]
22
- if (depth_change = @conditional_stack.length - depth) < 0
23
- if skip_active
24
- drop.push(*(drop.pop..cond_lineno))
25
- else
26
- drop << cond_lineno
27
- end
28
- elsif depth_change > 0 || cond_lineno == @lineno - 1
29
- drop << cond_lineno
30
- else
31
- drop << [cond_lineno, text]
32
- end
33
- result
34
- end
35
-
36
- def preprocess_include_directive target, attrlist
37
- @x_include_directive_line = %(include::#{target}[#{attrlist}])
38
- @x_include_pushed = false
39
- inc_lineno = @lineno - 1 # we're currently on the include line, which is 1-based
40
- result = super
41
- unless @x_include_pushed
42
- unresolved = (l = peek_line true) && (l.start_with? 'Unresolved directive in ') && (l.end_with? ']') || nil
43
- push_include_replacement inc_lineno, (unresolved ? [l] : []), unresolved
44
- end
45
- @x_include_directive_line = @x_include_pushed = nil
46
- result
47
- end
48
-
49
- def push_include data, file, path, lineno, attrs
50
- @x_include_pushed = true
51
- inc_lineno = @lineno - 2 # we're below the include line, which is 1-based
52
- prev_inc_depth = @include_stack.length
53
- result = super
54
- push_include_replacement inc_lineno, (@include_stack.length > prev_inc_depth ? lines : [])
55
- result
56
- end
57
-
58
- def pop_include
59
- @x_include_replacements.pos = @x_include_replacements.current[:into] unless @x_include_pushed
60
- super
61
- end
62
-
63
- private
64
-
65
- def push_include_replacement inc_lineno, inc_lines, unresolved = false
66
- @x_include_replacements << {
67
- into: @x_include_replacements.pos,
68
- lineno: inc_lineno,
69
- line: @x_include_directive_line,
70
- lines: inc_lines,
71
- drop: [],
72
- }
73
- @x_include_replacements.pos = @x_include_replacements.length - 1 unless unresolved || inc_lines.empty?
74
- nil
75
- end
76
- end
77
-
78
- module CurrentPosition
79
- attr_accessor :pos
80
-
81
- def self.extended instance
82
- instance.pos = instance.length - 1
83
- end
84
-
85
- def current
86
- self[@pos]
87
- end
88
- end
89
-
90
- private_constant :CurrentPosition
91
- end