asciidoctor-reducer 1.0.0.alpha.5 → 1.0.0.alpha.8

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: 8e5bfad3f3b343a1fd2741938d31812feac3218e8b6998d43b3614b6800874b0
4
- data.tar.gz: 443bcb83342bb51cf3537b9b5cba497b7a1f5340eb7e7fb0956e85746344f917
3
+ metadata.gz: 7df897a17f1fc52a4240d6c4bfe476e9072014dae43042063485634bda01eb13
4
+ data.tar.gz: 5be6330e865889c0a93cf83eb6a884e5785d644d8421d962b5f33950a0da67bc
5
5
  SHA512:
6
- metadata.gz: b52ca3132349b059d672bab08dd1b1c5a2586d88e8f4f31eb8d2ae7f0f9891917f0ee0cd46bd63826e7127cb8a64fda650bc5a13815760d299c06c6e5181e02b
7
- data.tar.gz: 6bdb49618d5adb7b1ce702d266ee8d6a229545b6ca8676931083df9362b21aaedabf539c7d1c4ea5c2fabecbb25426a9bcc140d80ffce93cd9b6da52bdc0e4cd
6
+ metadata.gz: f4a788b3926a6f9bf925ec5607cc1756688de3ade791ea2178b3e893b9ad35bf0f2912cb0e23b5fc16a8834d2f28be4cc6c0bf6173a10858942522107991091b
7
+ data.tar.gz: 04afd98f0789e686f0328eb721c56f2c2150261209a66c90025d2dd91c10d9397a969b6a45c0060d77559df549a80cf3839f2940030e47105ca11bcd9ba02e5c
data/CHANGELOG.adoc CHANGED
@@ -4,6 +4,74 @@
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.8 (2022-02-23) - @mojavelinux
8
+
9
+ === Added
10
+
11
+ * Add secure mode as value of `-S` CLI option (#31)
12
+ * Add `--trace` option to CLI to trace cause of application errors (#29)
13
+
14
+ === Changed
15
+
16
+ * Replace include directive with link macro if safe mode is secure (#31)
17
+ * Track line numbers in include replacements using 1-based index
18
+ * Only mix in preprocessor conditional tracker if `:preserve_conditionals` option is not set (#36)
19
+
20
+ === Fixed
21
+
22
+ * Handle signals gracefully (#33)
23
+
24
+ === Details
25
+
26
+ {url-repo}/releases/tag/v1.0.0.alpha.8[git tag]
27
+
28
+ == 1.0.0.alpha.7 (2022-02-14) - @mojavelinux
29
+
30
+ === Added
31
+
32
+ * Add asciidoctor/reducer/api to require main API (#3)
33
+ * Add `Asciidoctor::Reducer.reduce` and `Asciidoctor::Reducer.reduce_file` API methods (#3)
34
+ * Add asciidoctor/reducer/extensions to require extensions API (#3)
35
+ * Add `Asciidoctor::Reducer::Extensions` API (#3)
36
+
37
+ === Changed
38
+
39
+ * Scope extensions to single call instead of registering them globally (#3)
40
+ * Use `:safe` as the default safe mode when using the API
41
+ * Make `CurrentPosition` module private to the `PreprocessorDirectiveTracker` module
42
+
43
+ === Fixed
44
+
45
+ * Require asciidoctor/reducer/version automatically when `Asciidoctor::Reducer::VERSION` is accessed
46
+
47
+ === Details
48
+
49
+ {url-repo}/releases/tag/v1.0.0.alpha.7[git tag]
50
+
51
+ == 1.0.0.alpha.6 (2022-02-10) - @mojavelinux
52
+
53
+ === Added
54
+
55
+ * Add `-S`, `--safe-mode` option to CLI to set safe mode (#13)
56
+ * Add `-r`, `--require` option to CLI to specify additional libraries to require before running (#17)
57
+
58
+ === Changed
59
+
60
+ * Sort CLI options in help text, except for the `-h`, `--help` option
61
+ * Update CLI to always use a new logger instance
62
+ * Defer initializing logger until run method is called
63
+
64
+ === Fixed
65
+
66
+ * Replace include directives that follow an unresolved include (#19)
67
+ * Don't activate reducer extensions on reduced document
68
+ * Prevent custom extension registry from activating extensions twice during reload (#21)
69
+ * Retain includes table in document catalog when reloading document (#23)
70
+
71
+ === Details
72
+
73
+ {url-repo}/releases/tag/v1.0.0.alpha.6[git tag]
74
+
7
75
  == 1.0.0.alpha.5 (2022-02-06) - @mojavelinux
8
76
 
9
77
  === Changed
@@ -17,12 +85,20 @@ For a detailed view of what has changed, refer to the {url-repo}/commits/main[co
17
85
 
18
86
  * Suppress log messages when reloading document (#14)
19
87
 
88
+ === Details
89
+
90
+ {url-repo}/releases/tag/v1.0.0.alpha.5[git tag]
91
+
20
92
  == 1.0.0.alpha.4 (2022-02-03) - @mojavelinux
21
93
 
22
94
  === Fixed
23
95
 
24
96
  * Fix replacement of nested empty and unresolved includes
25
97
 
98
+ === Details
99
+
100
+ {url-repo}/releases/tag/v1.0.0.alpha.4[git tag]
101
+
26
102
  == 1.0.0.alpha.3 (2022-02-02) - @mojavelinux
27
103
 
28
104
  === Changed
@@ -32,45 +108,37 @@ For a detailed view of what has changed, refer to the {url-repo}/commits/main[co
32
108
  * Only reload document if source lines have changed; otherwise, update source lines on reader directly
33
109
  * Change default safe mode for CLI to :unsafe
34
110
 
111
+ === Details
112
+
113
+ {url-repo}/releases/tag/v1.0.0.alpha.3[git tag]
114
+
35
115
  == 1.0.0.alpha.2 (2022-01-27) - @mojavelinux
36
116
 
37
117
  === Added
38
118
 
39
- * Add `-a` / `--attribute` option to CLI for setting an AsciiDoc document attribute at runtime (#6)
119
+ * Add `-a`, `--attribute` option to CLI for setting an AsciiDoc document attribute at runtime (#6)
40
120
 
41
121
  === Changed
42
122
 
43
- * Reduce preprocessor conditionals by default; add option (`--preserve-conditionals` / `:preserve_conditionals`) to preserve them (#8)
123
+ * Reduce preprocessor conditionals by default; add option (`--preserve-conditionals`, `:preserve_conditionals`) to preserve them (#8)
44
124
  * Don't enable sourcemap automatically (#4)
45
125
  * Don't override logger by default; instead, rely on `:logger` API option to change logger
46
126
  * Add `--log-level` option to CLI to set severity level on logger (#9)
47
- * Add `-q` / `--quiet` option to CLI to suppress log messages (#9)
127
+ * Add `-q`, `--quiet` option to CLI to suppress log messages (#9)
48
128
  * Reserve zero index in include replacements for top-level document
49
129
 
50
130
  === Fixed
51
131
 
52
132
  * Preserve return value when overridding `preprocess_include_directive` method
53
133
 
54
- == 1.0.0.alpha.1 (2022-01-12) - @mojavelinux
55
-
56
- Initial release.
57
-
58
- === Details
59
-
60
- {url-repo}/releases/tag/v1.0.0.alpha.1[git tag]
61
-
62
134
  === Details
63
135
 
64
136
  {url-repo}/releases/tag/v1.0.0.alpha.2[git tag]
65
137
 
66
- === Details
67
-
68
- {url-repo}/releases/tag/v1.0.0.alpha.3[git tag]
69
-
70
- === Details
138
+ == 1.0.0.alpha.1 (2022-01-12) - @mojavelinux
71
139
 
72
- {url-repo}/releases/tag/v1.0.0.alpha.4[git tag]
140
+ Initial release.
73
141
 
74
142
  === Details
75
143
 
76
- {url-repo}/releases/tag/v1.0.0.alpha.5[git tag]
144
+ {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.5, 2022-02-06
3
+ v1.0.0.alpha.8, 2022-02-23
4
4
  :idprefix:
5
5
  :idseparator: -
6
6
  ifndef::env-github[:icons: font]
@@ -22,15 +22,15 @@ If the document does not contain any preprocessor directives, the tool returns t
22
22
 
23
23
  == Prerequisites
24
24
 
25
- {project-name} is a Ruby program that you install using Ruby packaging.
26
- Therefore, to install and run {project-name}, you need Ruby 2.5 or better installed.
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.
27
27
 
28
- To check whether you have Ruby installed, and which version, run the following command:
28
+ Run the following command to check which version of Ruby you have installed, if any:
29
29
 
30
30
  $ ruby -v
31
31
 
32
32
  If Ruby is not installed, you can install it using {url-rvm}[RVM] (or, if you prefer, the package manager for your system).
33
- We generally recommend using RVM because it allows you to install gems without requiring elevated privileges or messing with system libraries.
33
+ We generally recommend using RVM as it allows you to install gems without requiring elevated privileges or messing with system libraries.
34
34
 
35
35
  == Installation
36
36
 
@@ -41,7 +41,9 @@ You can install the latest version of the gem using the following command:
41
41
  $ gem install asciidoctor-reducer --pre
42
42
 
43
43
  Installing this gem makes the `asciidoctor-reducer` command available on your $PATH.
44
- You can also require the gem into the Ruby runtime to use it as an Asciidoctor extension.
44
+ You can also require the gem into the Ruby runtime to use it as a library or Asciidoctor extension.
45
+
46
+ === Project-scoped
45
47
 
46
48
  If you prefer to manage the application as a project-scoped dependency, you can declare the gem in the project's [.path]_Gemfile_:
47
49
 
@@ -61,7 +63,7 @@ Installing the gem this way makes the `bundle exec asciidoctor-reducer` command
61
63
 
62
64
  == Usage
63
65
 
64
- === As command
66
+ === Command
65
67
 
66
68
  You can run this tool using the provided command (i.e., CLI), named `asciidoctor-reducer`.
67
69
  To learn how to use the command, and to verify it's available, run the command with the `-h` option:
@@ -75,7 +77,9 @@ asciidoctor-reducer [OPTION]... FILE
75
77
  ....
76
78
 
77
79
  The argument `FILE` is the AsciiDoc file you want to reduce.
78
- To use the command, pass the AsciiDoc file as the sole argument:
80
+ The options, represented by `+[OPTION]...+`, are optional, as the name suggestions.
81
+
82
+ Thus, to use the command, pass the AsciiDoc file as the sole argument:
79
83
 
80
84
  $ asciidoctor-reducer input.adoc
81
85
 
@@ -89,27 +93,26 @@ To use the command in this way, pass `-` as the first argument:
89
93
 
90
94
  $ cat input.adoc | asciidoctor-reducer -
91
95
 
92
- To write the output to a file instead of stdout, also specify an output file using the `-o` option:
96
+ To write the output to a file in this case, specify an output file using the `-o` option:
93
97
 
94
98
  $ cat input.adoc | asciidoctor-reducer -o output.adoc -
95
99
 
96
- Note that top-level include files in the input AsciiDoc document are resolved relative to current working directory.
97
-
98
- === As extension
100
+ === API
99
101
 
100
- You can use this tool as an Asciidoctor extension when using the Asciidoctor API.
101
- To do so, require it before calling the `Asciidoctor.load` method.
102
+ You can also use this tool from a Ruby application using the provided API.
103
+ To begin, require the API for this library.
102
104
 
103
105
  [,ruby]
104
106
  ----
105
- require 'asciidoctor/reducer'
107
+ require 'asciidoctor/reducer/api'
106
108
  ----
107
109
 
108
- Next, load a parent document that contains includes.
110
+ Next, reduce a parent document that contains includes.
111
+ This works without having to specify the safe mode since the default safe mode when using the API is `:safe`.
109
112
 
110
113
  [,ruby]
111
114
  ----
112
- doc = Asciidoctor.load_file 'sample.adoc', safe: :safe
115
+ doc = Asciidoctor::Reducer.reduce_file 'sample.adoc'
113
116
  ----
114
117
 
115
118
  Finally, you can retrieve the reduced source from the returned document.
@@ -119,11 +122,60 @@ Finally, you can retrieve the reduced source from the returned document.
119
122
  puts doc.source
120
123
  ----
121
124
 
122
- You can write this source to a file to save the reduced document.
125
+ The benefit of this approach is that you can access the reduced source and the parsed document that corresponds to it.
126
+
127
+ If you don't need the parsed document, you can retrieve the reduced source directly by passing the `String` type to the `:to` option:
128
+
129
+ [,ruby]
130
+ ----
131
+ puts Asciidoctor::Reducer.reduce_file 'sample.adoc', to: String
132
+ ----
133
+
134
+ You can write the reduced source directly to a file by passing a file path to the `:to` option:
135
+
136
+ [,ruby]
137
+ ----
138
+ Asciidoctor::Reducer.reduce_file 'sample.adoc', to: 'sample-reduced.adoc'
139
+ ----
140
+
141
+ == Extension
142
+
143
+ Instead of using the API for this library, you can use the load API provided by Asciidoctor.
144
+ If you want to register the extension globally, require the library as follows:
145
+
146
+ [,ruby]
147
+ ----
148
+ require 'asciidoctor/reducer'
149
+ ----
150
+
151
+ When you use the Asciidoctor load API, the document will automatically be reduced.
152
+
153
+ [,ruby]
154
+ ----
155
+ puts (Asciidoctor.load_file 'sample.adoc', safe: :safe).source
156
+ ----
157
+
158
+ If you want to keep the extension scoped to the call, require the library as follows:
159
+
160
+ [,ruby]
161
+ ----
162
+ require 'asciidoctor/reducer/extensions'
163
+ ----
164
+
165
+ Next, use the extensions API to prepare an extension registry and pass it to the Asciidoctor load API:
166
+
167
+ [,ruby]
168
+ ----
169
+ puts (Asciidoctor.load_file 'sample.adoc', safe: :safe, extension_registry: Asciidoctor::Reducer.prepare_registry).source
170
+ ----
171
+
172
+ Working with the extension directly is intended for low-level operations.
173
+ Most of the time, you should use the API provided by this library.
123
174
 
124
175
  == How it Works
125
176
 
126
177
  {project-name} uses a collection of Asciidoctor extensions to rebuild the AsciiDoc source as a single document.
178
+ Top-level include files in the input AsciiDoc document are resolved relative to current working directory.
127
179
 
128
180
  It starts by using a preprocessor extension to enhance the PreprocessorReader class to be notified each time an include is entered (pushed) or exited (popped).
129
181
  When an include directive is encountered, the enhanced reader stores the resolved lines and location of the include directive, thus keeping track of where those lines should be inserted in the original source.
@@ -189,7 +241,7 @@ For more fine-grained control, you can also run the tests directly using RSpec:
189
241
 
190
242
  To run all tests in a single spec, point RSpec at the spec file:
191
243
 
192
- $ bundle exec rspec spec/asciidoctor_reducer_spec.rb
244
+ $ bundle exec rspec spec/reducer_spec.rb
193
245
 
194
246
  ==== Run specific tests
195
247
 
@@ -234,7 +286,7 @@ When running the `asciidoctor-reducer` command from source, you must prefix the
234
286
  [subs=attributes+]
235
287
  $ bundle exec asciidoctor-reducer sample.adoc
236
288
 
237
- To avoid having to do this, or make the `asciidoctor-reducer` command available from anywhere, you need to build the development gem and install it.
289
+ To avoid having to do this, or to make the `asciidoctor-reducer` command available from anywhere, you need to build the development gem and install it.
238
290
 
239
291
  == Copyright and License
240
292
 
@@ -35,5 +35,5 @@ Gem::Specification.new do |s|
35
35
  s.add_runtime_dependency 'asciidoctor', '~> 2.0'
36
36
 
37
37
  s.add_development_dependency 'rake', '~> 13.0.0'
38
- s.add_development_dependency 'rspec', '~> 3.10.0'
38
+ s.add_development_dependency 'rspec', '~> 3.11.0'
39
39
  end
@@ -1,13 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- asciidoctor_reducer = File.absolute_path '../lib/asciidoctor/reducer', __dir__
5
- if File.exist? asciidoctor_reducer
6
- require asciidoctor_reducer
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
7
  else
8
- require 'asciidoctor/reducer'
8
+ require 'asciidoctor/reducer/cli'
9
9
  end
10
10
 
11
- require 'asciidoctor/reducer/cli'
12
-
13
11
  exit Asciidoctor::Reducer::Cli.run
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require_relative 'extensions'
5
+
6
+ module Asciidoctor::Reducer
7
+ autoload :VERSION, (::File.join __dir__, 'version.rb')
8
+
9
+ class << self
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
13
+ case input
14
+ when ::File
15
+ doc = ::Asciidoctor.load_file input, opts
16
+ when ::Pathname
17
+ doc = ::Asciidoctor.load_file input.to_path, opts
18
+ else
19
+ doc = ::Asciidoctor.load input, opts
20
+ end
21
+ write doc, opts[:to]
22
+ end
23
+
24
+ def reduce_file input_file, opts = {}
25
+ reduce (::Pathname.new input_file), opts
26
+ end
27
+
28
+ private
29
+
30
+ 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
41
+ end
42
+ doc
43
+ end
44
+ end
45
+
46
+ LF = ?\n
47
+ UTF_8 = ::Encoding::UTF_8
48
+
49
+ private_constant :LF, :UTF_8
50
+ end
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'api'
3
4
  autoload :OptionParser, 'optparse'
4
- autoload :Pathname, 'pathname'
5
5
 
6
6
  module Asciidoctor::Reducer
7
- autoload :VERSION, (::File.join __dir__, 'version.rb')
8
-
9
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
11
+
10
12
  def parse args
11
- options = { attributes: {}, safe: :unsafe }
13
+ options = { attributes: {}, log_level: LOG_LEVELS['warn'], safe: :unsafe }
12
14
 
13
15
  opt_parser = ::OptionParser.new do |opts|
14
16
  opts.program_name = 'asciidoctor-reducer'
@@ -19,10 +21,6 @@ module Asciidoctor::Reducer
19
21
 
20
22
  EOS
21
23
 
22
- opts.on '-o FILE', '--output=FILE', 'set the output filename or stream' do |file|
23
- options[:output_file] = file
24
- end
25
-
26
24
  opts.on '-a KEY[=VALUE]', '--attribute=KEY[=VALUE]',
27
25
  'set a document attribute in the AsciiDoc document: [key, key!, key=value]' do |attr|
28
26
  key, val = attr.split '=', 2
@@ -30,26 +28,43 @@ module Asciidoctor::Reducer
30
28
  options[:attributes][key] = val
31
29
  end
32
30
 
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
35
+
36
+ opts.on '-o FILE', '--output=FILE', 'set the output filename or stream' do |file|
37
+ options[:output_file] = file
38
+ end
39
+
33
40
  opts.on '--preserve-conditionals', 'preserve preprocessor conditional directives in the reduced source' do
34
41
  options[:preserve_conditionals] = true
35
42
  end
36
43
 
37
- opts.on '--log-level LEVEL', %w(debug info warn error fatal),
38
- 'set the minimum level of messages to log: [debug, info, warn, error, fatal] (default: warn)' do |level|
39
- (options[:logger] = ::Asciidoctor::Logger.new $stderr).level = level unless level == 'warn'
44
+ opts.on '-q', '--quiet', 'suppress all application log messages' do
45
+ options[:log_level] = nil
40
46
  end
41
47
 
42
- opts.on '-q', '--quiet', 'suppress all application log messages' do
43
- options[:logger] = ::Asciidoctor::NullLogger.new
48
+ opts.on '-rLIBRARY', '--require LIBRARY', 'require the specified library or libraries before running' do |path|
49
+ (options[:requires] ||= []).concat path.split ','
44
50
  end
45
51
 
46
- opts.on '-h', '--help', 'display this help text and exit' do
47
- $stdout.write opts.help
48
- return 0
52
+ opts.on '-S', '--safe-mode SAFE_MODE', ['unsafe', 'safe', 'server', 'secure'],
53
+ 'set safe mode level: [unsafe, safe, server, secure] (default: unsafe)' do |name|
54
+ options[:safe] = ::Asciidoctor::SafeMode.value_for_name name
55
+ end
56
+
57
+ opts.on '--trace', 'trace the cause of application errors (default: false)' do
58
+ options[:trace] = true
49
59
  end
50
60
 
51
61
  opts.on '-v', '--version', 'display the version information and exit' do
52
- $stdout.write %(#{opts.program_name} #{VERSION}\n)
62
+ print_version opts
63
+ return 0
64
+ end
65
+
66
+ opts.on '-h', '--help', 'display this help text and exit' do
67
+ print_help opts
53
68
  return 0
54
69
  end
55
70
  end
@@ -58,44 +73,65 @@ module Asciidoctor::Reducer
58
73
 
59
74
  if args.empty?
60
75
  opt_parser.warn 'Please specify an AsciiDoc file to reduce.'
61
- $stdout.write opt_parser.help
76
+ print_help opt_parser
62
77
  1
63
78
  elsif args.size == 1
79
+ if (requires = options.delete :requires)
80
+ requires.uniq.each do |path|
81
+ require path
82
+ rescue ::LoadError
83
+ $stderr.puts %(#{opt_parser.program_name}: '#{path}' could not be required (reason: #{$!.message}))
84
+ return 1
85
+ end
86
+ end
64
87
  options[:input_file] = args[0]
65
88
  options[:output_file] = '-' unless options[:output_file]
66
89
  [0, options]
67
90
  else
68
91
  opt_parser.warn %(extra arguments detected (unparsed arguments: #{(args.drop 1).join ' '}))
69
- $stdout.write opt_parser.help
70
- [1, options]
92
+ print_help opt_parser
93
+ 1
71
94
  end
72
95
  rescue ::OptionParser::InvalidOption
73
- $stderr.write %(#{opt_parser.program_name}: #{$!.message}\n)
74
- $stdout.write opt_parser.help
96
+ $stderr.puts %(#{opt_parser.program_name}: #{$!.message})
97
+ print_help opt_parser
75
98
  1
76
99
  end
77
100
 
78
101
  def self.run args = ARGV
79
- old_logger = ::Asciidoctor::LoggerManager.logger
80
102
  code, options = new.parse (Array args)
81
103
  return code unless code == 0 && options
82
- if (output_file = options.delete :output_file) == '-'
83
- to = $stdout
84
- else
85
- (to = ::Pathname.new output_file).dirname.mkpath
86
- end
87
- if (input_file = options.delete :input_file) == '-'
88
- reduced = (::Asciidoctor.load $stdin, options).source + ?\n
104
+ trace = options.delete :trace
105
+ old_logger = ::Asciidoctor::LoggerManager.logger
106
+ if (log_level = options.delete :log_level)
107
+ (options[:logger] = ::Asciidoctor::Logger.new $stderr).level = log_level
89
108
  else
90
- reduced = (::Asciidoctor.load_file input_file, (options.merge to_file: false)).source + ?\n
109
+ options[:logger] = nil
91
110
  end
92
- ::Pathname === to ? (to.write reduced, encoding: ::Encoding::UTF_8) : (to.write reduced)
111
+ options[:to] = (output_file = options.delete :output_file) == '-' ? $stdout : (::Pathname.new output_file)
112
+ input = (input_file = options.delete :input_file) == '-' ? $stdin : (::Pathname.new input_file)
113
+ ::Asciidoctor::Reducer.reduce input, options
93
114
  0
115
+ rescue ::SignalException
116
+ $stderr.puts if ::Interrupt === $!
117
+ $!.signo
94
118
  rescue
95
- $stderr.write %(asciidoctor-reducer: #{$!.message}\n)
119
+ raise $! if trace
120
+ $stderr.puts %(asciidoctor-reducer: #{$!.message.delete_prefix 'asciidoctor: '})
121
+ $stderr.puts ' Use --trace to show backtrace'
96
122
  1
97
123
  ensure
98
- ::Asciidoctor::LoggerManager.logger = old_logger
124
+ ::Asciidoctor::LoggerManager.logger = old_logger if old_logger
125
+ end
126
+
127
+ private
128
+
129
+ def print_help opt_parser
130
+ $stdout.puts opt_parser.help.chomp
131
+ end
132
+
133
+ def print_version opt_parser
134
+ $stdout.puts %(#{opt_parser.program_name} #{VERSION})
99
135
  end
100
136
  end
101
137
  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 = @x_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
@@ -1,4 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'asciidoctor' unless defined? Asciidoctor.load
3
4
  require_relative 'preprocessor'
4
5
  require_relative 'tree_processor'
6
+
7
+ module Asciidoctor::Reducer
8
+ module Extensions
9
+ module_function
10
+
11
+ def group
12
+ proc do
13
+ next if document.options[:reduced]
14
+ preprocessor Preprocessor
15
+ tree_processor TreeProcessor
16
+ end
17
+ end
18
+
19
+ def prepare_registry registry = nil
20
+ 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
27
+ end
28
+ registry
29
+ end
30
+
31
+ def register
32
+ ::Asciidoctor::Extensions.register :reducer, &group
33
+ end
34
+
35
+ def unregister
36
+ ::Asciidoctor::Extensions.unregister :reducer
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Asciidoctor::Reducer
4
+ module IncludeDirectiveTracker
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, ([{}].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_include_directive target, attrlist
15
+ @x_include_directive_line = %(include::#{target}[#{attrlist}])
16
+ @x_include_pushed = false
17
+ inc_lineno = @lineno # we're currently on the include line, which is 1-based
18
+ result = super
19
+ unless @x_include_pushed
20
+ if (ln = peek_line true) && (ln.end_with? ']') && !(unresolved = ln.start_with? 'Unresolved directive in ')
21
+ if @document.safe >= ::Asciidoctor::SafeMode::SECURE && inc_lineno == @lineno && (ln.start_with? 'link:')
22
+ unresolved = !(ln = %(#{ln.slice 0, (ln.length - 1)}role=include])).nil?
23
+ end
24
+ end
25
+ push_include_replacement inc_lineno, (unresolved ? [ln] : []), unresolved
26
+ end
27
+ @x_include_directive_line = @x_include_pushed = nil
28
+ result
29
+ end
30
+
31
+ def push_include data, file, path, lineno, attrs
32
+ @x_include_pushed = true
33
+ inc_lineno = @lineno - 1 # we're below the include line, which is 1-based
34
+ prev_inc_depth = @include_stack.size
35
+ result = super
36
+ push_include_replacement inc_lineno, (@include_stack.size > prev_inc_depth ? lines : [])
37
+ result
38
+ end
39
+
40
+ def pop_include
41
+ @x_include_replacements.pos = @x_include_replacements.current[:into] unless @x_include_pushed
42
+ super
43
+ end
44
+
45
+ private
46
+
47
+ def push_include_replacement lineno, lines, unresolved = false
48
+ (inc_replacements = @x_include_replacements) << {
49
+ into: inc_replacements.pos,
50
+ lineno: lineno,
51
+ line: @x_include_directive_line,
52
+ lines: lines,
53
+ }
54
+ inc_replacements.pos = inc_replacements.size - 1 unless unresolved || lines.empty?
55
+ nil
56
+ end
57
+ end
58
+
59
+ module CurrentPosition
60
+ attr_accessor :pos
61
+
62
+ def self.extended instance
63
+ instance.pos = instance.size - 1
64
+ end
65
+
66
+ def current
67
+ self[@pos]
68
+ end
69
+ end
70
+
71
+ private_constant :CurrentPosition
72
+ 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
8
  def process doc, reader
8
- doc.options[:reduced] ? reader : (reader.extend PreprocessorDirectiveTracker)
9
+ reader.extend IncludeDirectiveTracker
10
+ reader.extend ConditionalDirectiveTracker unless doc.options[:preserve_conditionals]
9
11
  end
10
12
  end
11
13
  end
@@ -3,28 +3,36 @@
3
3
  module Asciidoctor::Reducer
4
4
  class TreeProcessor < ::Asciidoctor::Extensions::TreeProcessor
5
5
  def process doc
6
- return if doc.options[:reduced]
7
- unless (inc_replacements = doc.reader.x_include_replacements).length == 1 && inc_replacements[0][:drop].empty?
6
+ if (inc_replacements = doc.reader.x_include_replacements).size > 1 || !(inc_replacements[0][:drop] || []).empty?
8
7
  inc_replacements[0][:lines] = doc.source_lines.dup
9
8
  inc_replacements.reverse_each do |it|
10
9
  if (into = it[:into])
11
10
  target_lines = inc_replacements[into][:lines]
12
11
  # adds extra bit of assurance that we're replacing the correct line
13
- next unless target_lines[(index = it[:lineno])] == it[:line]
12
+ next unless target_lines[(idx = it[:lineno] - 1)] == it[:line]
14
13
  end
15
14
  lines = it[:lines]
16
- unless (drop = it[:drop]).empty?
17
- 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
18
19
  end
19
- target_lines[index] = lines if target_lines
20
+ target_lines[idx] = lines if target_lines
20
21
  end
21
22
  source_lines = inc_replacements[0][:lines].flatten
22
23
  if doc.sourcemap
23
24
  logger = ::Asciidoctor::LoggerManager.logger
24
- doc = ::Asciidoctor.load source_lines, (doc.options.merge logger: false, reduced: true)
25
+ opts = doc.options.merge logger: nil, parse: false, reduced: true
26
+ if (ext_reg = opts[:extension_registry])
27
+ opts[:extension_registry] = ::Asciidoctor::Extensions::Registry.new ext_reg.groups
28
+ end
29
+ includes = doc.catalog[:includes]
30
+ doc = ::Asciidoctor.load source_lines, opts
31
+ doc.catalog[:includes] = includes
32
+ doc.parse
25
33
  ::Asciidoctor::LoggerManager.logger = logger
26
34
  else
27
- source_lines.pop while (last = source_lines[-1]) && last.empty?
35
+ source_lines.pop while (source_lines[-1] || :eof).empty?
28
36
  doc.reader.source_lines = source_lines
29
37
  end
30
38
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Asciidoctor
4
4
  module Reducer
5
- VERSION = '1.0.0.alpha.5'
5
+ VERSION = '1.0.0.alpha.8'
6
6
  end
7
7
  end
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'asciidoctor'
4
3
  require_relative 'reducer/extensions'
5
4
 
6
- Asciidoctor::Extensions.register :reducer do
7
- preprocessor Asciidoctor::Reducer::Preprocessor
8
- tree_processor Asciidoctor::Reducer::TreeProcessor
9
- end
5
+ Asciidoctor::Reducer::Extensions.register
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.5
4
+ version: 1.0.0.alpha.8
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-06 00:00:00.000000000 Z
11
+ date: 2022-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 3.10.0
47
+ version: 3.11.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 3.10.0
54
+ version: 3.11.0
55
55
  description: A tool that reduces a composite AsciiDoc document containing preprocessor
56
56
  directives (includes and conditionals) to a single AsciiDoc document by expanding
57
57
  the includes and applying the conditionals.
@@ -68,10 +68,12 @@ files:
68
68
  - bin/asciidoctor-reducer
69
69
  - lib/asciidoctor-reducer.rb
70
70
  - lib/asciidoctor/reducer.rb
71
+ - lib/asciidoctor/reducer/api.rb
71
72
  - lib/asciidoctor/reducer/cli.rb
73
+ - lib/asciidoctor/reducer/conditional_directive_tracker.rb
72
74
  - lib/asciidoctor/reducer/extensions.rb
75
+ - lib/asciidoctor/reducer/include_directive_tracker.rb
73
76
  - lib/asciidoctor/reducer/preprocessor.rb
74
- - lib/asciidoctor/reducer/preprocessor_directive_tracker.rb
75
77
  - lib/asciidoctor/reducer/tree_processor.rb
76
78
  - lib/asciidoctor/reducer/version.rb
77
79
  homepage: https://asciidoctor.org
@@ -1,92 +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 (opts = @document.options)[:preserve_conditionals] || opts[:reduced]
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
- if @x_include_pushed
42
- @x_include_directive_line = @x_include_pushed = nil
43
- return result
44
- end
45
- inc_lines = ((line = lines[0].to_s).start_with? 'Unresolved directive in ') && (line.end_with? ']') ? [line] : []
46
- push_include_replacement inc_lineno, inc_lines
47
- @x_include_directive_line = @x_include_pushed = nil
48
- result
49
- end
50
-
51
- def push_include data, file, path, lineno, attrs
52
- @x_include_pushed = true
53
- inc_lineno = @lineno - 2 # we're below the include line, which is 1-based
54
- prev_inc_depth = @include_stack.length
55
- result = super
56
- inc_lines = lines if @include_stack.length > prev_inc_depth
57
- push_include_replacement inc_lineno, inc_lines
58
- result
59
- end
60
-
61
- def pop_include
62
- @x_include_replacements.pos = @x_include_replacements.current[:into] unless @x_include_pushed
63
- super
64
- end
65
-
66
- private
67
-
68
- def push_include_replacement inc_lineno, inc_lines
69
- @x_include_replacements << {
70
- into: @x_include_replacements.pos,
71
- lineno: inc_lineno,
72
- line: @x_include_directive_line,
73
- lines: inc_lines || [],
74
- drop: [],
75
- }
76
- @x_include_replacements.pos = @x_include_replacements.length - 1 if inc_lines
77
- nil
78
- end
79
- end
80
-
81
- module CurrentPosition
82
- attr_accessor :pos
83
-
84
- def self.extended instance
85
- instance.pos = instance.length - 1
86
- end
87
-
88
- def current
89
- self[@pos]
90
- end
91
- end
92
- end