metanorma 2.0.0 → 2.5.5

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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +1 -0
  3. data/.gitignore +3 -0
  4. data/.rubocop.yml +23 -2
  5. data/Gemfile +17 -0
  6. data/Gemfile.devel +10 -0
  7. data/README.adoc +1 -1
  8. data/lib/metanorma/collection/artifact_store.rb +142 -0
  9. data/lib/metanorma/collection/collection.rb +160 -122
  10. data/lib/metanorma/collection/config/bibdata.rb +3 -3
  11. data/lib/metanorma/collection/config/compile_options.rb +4 -5
  12. data/lib/metanorma/collection/config/config.rb +81 -83
  13. data/lib/metanorma/collection/config/converters.rb +92 -8
  14. data/lib/metanorma/collection/config/directive.rb +9 -3
  15. data/lib/metanorma/collection/config/doc_container.rb +28 -0
  16. data/lib/metanorma/collection/config/manifest.rb +59 -29
  17. data/lib/metanorma/collection/config/namespaces.rb +12 -0
  18. data/lib/metanorma/collection/document/document.rb +56 -15
  19. data/lib/metanorma/collection/filelookup/filelookup.rb +185 -77
  20. data/lib/metanorma/collection/filelookup/filelookup_sectionsplit.rb +99 -27
  21. data/lib/metanorma/collection/filelookup/utils.rb +52 -0
  22. data/lib/metanorma/collection/helpers.rb +82 -0
  23. data/lib/metanorma/collection/log.rb +24 -0
  24. data/lib/metanorma/collection/manifest/manifest.rb +45 -14
  25. data/lib/metanorma/collection/multilingual/multilingual.rb +660 -0
  26. data/lib/metanorma/collection/renderer/filelocation.rb +166 -0
  27. data/lib/metanorma/collection/renderer/fileparse.rb +135 -115
  28. data/lib/metanorma/collection/renderer/fileprocess.rb +185 -37
  29. data/lib/metanorma/collection/renderer/navigation.rb +21 -7
  30. data/lib/metanorma/collection/renderer/render_word.rb +15 -10
  31. data/lib/metanorma/collection/renderer/renderer.rb +236 -32
  32. data/lib/metanorma/collection/renderer/svg.rb +107 -0
  33. data/lib/metanorma/collection/renderer/utils.rb +110 -39
  34. data/lib/metanorma/collection/sectionsplit/collection.rb +110 -0
  35. data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +198 -115
  36. data/lib/metanorma/collection/util/disambig_files.rb +4 -5
  37. data/lib/metanorma/collection/util/util.rb +155 -18
  38. data/lib/metanorma/collection/xrefprocess/xrefprocess.rb +47 -33
  39. data/lib/metanorma/compile/assets/icc-boilerplate.adoc +41 -0
  40. data/lib/metanorma/compile/compile.rb +181 -156
  41. data/lib/metanorma/compile/compile_options.rb +186 -79
  42. data/lib/metanorma/compile/extract.rb +75 -58
  43. data/lib/metanorma/compile/flavor.rb +28 -0
  44. data/lib/metanorma/compile/output_filename.rb +75 -0
  45. data/lib/metanorma/compile/output_filename_config.rb +27 -0
  46. data/lib/metanorma/compile/relaton_drop.rb +56 -0
  47. data/lib/metanorma/compile/render.rb +226 -0
  48. data/lib/metanorma/compile/validator.rb +26 -0
  49. data/lib/metanorma/compile/writeable.rb +12 -0
  50. data/lib/metanorma/util/fontist_helper.rb +35 -19
  51. data/lib/metanorma/version.rb +1 -1
  52. data/lib/metanorma.rb +1 -7
  53. data/metanorma.gemspec +15 -21
  54. data/plans/collection-rendering-architecture-review.md +516 -0
  55. metadata +65 -113
  56. data/.hound.yml +0 -5
  57. data/lib/metanorma/asciidoctor_extensions/glob_include_processor.rb +0 -22
  58. data/lib/metanorma/asciidoctor_extensions.rb +0 -1
  59. data/lib/metanorma/compile/compile_validate.rb +0 -68
  60. data/lib/metanorma/config/config.rb +0 -23
  61. data/lib/metanorma/input/asciidoc.rb +0 -107
  62. data/lib/metanorma/input/base.rb +0 -9
  63. data/lib/metanorma/input.rb +0 -7
  64. data/lib/metanorma/processor/processor.rb +0 -54
  65. data/lib/metanorma/processor.rb +0 -6
  66. data/lib/metanorma/registry/registry.rb +0 -63
  67. data/lib/metanorma/shale_monkeypatch.rb +0 -15
  68. data/lib/metanorma/util/util.rb +0 -45
@@ -0,0 +1,226 @@
1
+ module Metanorma
2
+ class Compile
3
+ NO_FONTIST_FORMATS = %i[xml presentation rxl mko].freeze
4
+
5
+ # Generate presentation XML from semantic XML
6
+ def generate_presentation_xml(source_file, xml, bibdata, output_paths, opt)
7
+ process_ext(:presentation, source_file, xml, bibdata, output_paths, opt)
8
+ end
9
+
10
+ # Generate multiple output formats with parallel processing
11
+ def generate_outputs_parallel(
12
+ source_file, semantic_xml, bibdata, extensions, output_paths, options
13
+ )
14
+ @queue = ::Metanorma::Util::WorkersPool.new(
15
+ ENV["METANORMA_PARALLEL"]&.to_i || DEFAULT_NUM_WORKERS,
16
+ )
17
+ # Install required fonts for all extensions
18
+ gather_and_install_fonts(source_file, options.dup, extensions)
19
+ # Process each extension in order
20
+ process_extensions_in_order(
21
+ source_file, semantic_xml, bibdata, extensions, output_paths, options
22
+ )
23
+ @queue.shutdown
24
+ end
25
+
26
+ def process_extensions_in_order(
27
+ source_file, semantic_xml, bibdata, extensions, output_paths, options
28
+ )
29
+ Util.sort_extensions_execution(extensions).each do |ext|
30
+ process_ext(
31
+ ext, source_file, semantic_xml, bibdata, output_paths, options
32
+ ) or break
33
+ end
34
+ end
35
+
36
+ # Export given bibliographic data to Relaton XML on disk
37
+ # @param bibdata [Nokogiri::XML::Element] the bibliographic data element
38
+ # @param options [Hash] compilation options
39
+ def export_relaton_from_bibdata(bibdata, options)
40
+ options[:relaton] or return
41
+ export_output(options[:relaton], bibdata.to_xml)
42
+ end
43
+
44
+ # @param xml [Nokogiri::XML::Document] the XML document
45
+ # @return [Nokogiri::XML::Element] the bibliographic data element
46
+ def extract_relaton_metadata(xml)
47
+ xml.at("//bibdata") || xml.at("//xmlns:bibdata")
48
+ end
49
+
50
+ def wrap_html(options, file_extension, outfilename)
51
+ if options[:wrapper] && /html$/.match(file_extension)
52
+ outfilename = outfilename.sub(/\.html$/, "")
53
+ FileUtils.mkdir_p outfilename
54
+ FileUtils.mv "#{outfilename}.html", outfilename
55
+ FileUtils.mv "#{outfilename}_images", outfilename, force: true
56
+ end
57
+ end
58
+
59
+ # isodoc is Raw Metanorma XML
60
+ def gather_and_install_fonts(source_file, options, extensions)
61
+ return if (extensions - NO_FONTIST_FORMATS).empty?
62
+
63
+ install_options = @processor.extract_options(source_file).merge(options)
64
+ font_install(install_options)
65
+ end
66
+
67
+ # Process a single extension (output format)
68
+ def process_ext(ext, source_file, semantic_xml, bibdata, output_paths,
69
+ options)
70
+ output_paths[:ext] = effective_output_formats(options)[ext]
71
+ output_paths[:out] = @output_filename.for_format(ext) ||
72
+ output_paths[:xml].sub(/\.[^.]+$/, ".#{output_paths[:ext]}")
73
+ isodoc_options = get_isodoc_options(source_file, options, ext)
74
+
75
+ # Handle special cases first
76
+ return true if process_ext_special(
77
+ ext, semantic_xml, bibdata, output_paths, options, isodoc_options
78
+ )
79
+
80
+ # Otherwise, determine if it uses presentation XML
81
+ if uses_presentation_xml?(ext, options)
82
+ # Format requires presentation XML first, then convert to final format
83
+ process_via_presentation_xml(ext, output_paths, options, isodoc_options)
84
+ else
85
+ # Format can be generated directly from semantic XML
86
+ process_from_semantic_xml(
87
+ ext, output_paths, semantic_xml, isodoc_options
88
+ )
89
+ end
90
+ end
91
+
92
+ # Process special extensions with custom handling
93
+ def process_ext_special(
94
+ ext, sem_xml, bibdata, output_paths, options, isodoc_options
95
+ )
96
+ if ext == :rxl
97
+ # Special case: Relaton export
98
+ export_relaton_from_bibdata(
99
+ bibdata,
100
+ options.merge(relaton: output_paths[:out]),
101
+ )
102
+ true
103
+ elsif ext == :presentation && options[:passthrough_presentation_xml]
104
+ # Special case: Pass through presentation XML
105
+ f = if File.exist?(output_paths[:orig_filename])
106
+ output_paths[:orig_filename]
107
+ else
108
+ output_paths[:xml]
109
+ end
110
+ FileUtils.cp f, output_paths[:presentationxml]
111
+ true
112
+ elsif ext == :mko
113
+ # Harness format: export the MKO bundle from the typed model
114
+ export_mko_bundle(output_paths)
115
+ true
116
+ elsif ext == :html && options[:sectionsplit]
117
+ # Special case: Split HTML into sections
118
+ sectionsplit_convert(
119
+ output_paths[:xml], sem_xml, output_paths[:out], isodoc_options
120
+ )
121
+ true
122
+ else
123
+ false
124
+ end
125
+ end
126
+
127
+ # Export the MKO machine serialization (metanorma-document). The
128
+ # semantic and presentation XML are already on disk by the time this
129
+ # runs; Mko resolves the flavor model via the Core::Flavors registry.
130
+ def export_mko_bundle(output_paths)
131
+ require "metanorma/document"
132
+ require "metanorma/mko"
133
+ # :xml is the freshly written semantic XML; orig_filename is the
134
+ # adoc SOURCE and is only right when an output_dir kept the
135
+ # semantic XML at the input location.
136
+ semantic_path = [output_paths[:xml], output_paths[:orig_filename]]
137
+ .find { |p1| p1 && File.exist?(p1) && File.extname(p1) != ".adoc" }
138
+ presentation = if File.exist?(output_paths[:presentationxml])
139
+ File.read(output_paths[:presentationxml])
140
+ end
141
+ Metanorma::Mko.export(File.read(semantic_path),
142
+ to: File.dirname(output_paths[:out]),
143
+ presentation_xml: presentation)
144
+ rescue LoadError => e
145
+ @log.add_msg("MKO_OUTPUT" => { category: "MKO Output",
146
+ error: "the mko output format requires " \
147
+ "the metanorma-document gem: %s",
148
+ severity: 0 })
149
+ @log.add("MKO_OUTPUT", nil, params: [e.message])
150
+ nil
151
+ end
152
+
153
+ # Process format that requires presentation XML
154
+ def process_via_presentation_xml(ext, output_paths, options, isodoc_options)
155
+ @queue.schedule(ext, output_paths.dup, options.dup,
156
+ isodoc_options.dup) do |a, b, c, d|
157
+ process_output_from_presentation_xml(a, b, c, d)
158
+ end
159
+ end
160
+
161
+ # Generate output format from presentation XML
162
+ def process_output_from_presentation_xml(ext, output_paths, options,
163
+ isodoc_options)
164
+ @processor.output(nil, output_paths[:presentationxml],
165
+ output_paths[:out], ext, isodoc_options)
166
+ wrap_html(options, output_paths[:ext], output_paths[:out])
167
+ rescue StandardError => e
168
+ isodoc_error_process(e, strict_error?(ext, isodoc_options), false)
169
+ end
170
+
171
+ # Process format directly from semantic XML
172
+ def process_from_semantic_xml(ext, output_paths, sem_xml, isodoc_options)
173
+ @processor.output(sem_xml, output_paths[:xml], output_paths[:out],
174
+ ext, isodoc_options)
175
+ true # Return as Thread equivalent
176
+ rescue StandardError => e
177
+ isodoc_error_process(e, strict_error?(ext, isodoc_options), true)
178
+ ext != :presentation
179
+ end
180
+
181
+ # assume we pass in Presentation XML, but we want to recover Semantic XML
182
+ def sectionsplit_convert(input_filename, file, output_filename = nil,
183
+ opts = {})
184
+ @isodoc ||= IsoDoc::PresentationXMLConvert.new({})
185
+ input_filename += ".xml" unless input_filename.match?(/\.xml$/)
186
+ File.exist?(input_filename) or export_output(input_filename, file)
187
+ presxml = File.read(input_filename, encoding: "utf-8")
188
+ _xml, filename, dir = @isodoc.convert_init(presxml, input_filename, false)
189
+ ::Metanorma::Collection::Sectionsplit.new(
190
+ input: input_filename, isodoc: @isodoc, xml: presxml,
191
+ base: File.basename(output_filename || filename),
192
+ sectionsplit_filename: opts[:sectionsplit_filename],
193
+ output: output_filename || filename, dir: dir, compile_opts: opts
194
+ ).build_collection
195
+ end
196
+
197
+ private
198
+
199
+ # A presentation failure is always strict: every downstream format
200
+ # needs the presentation XML. The :strict option arrives as a
201
+ # boolean from the CLI but as a string from document attributes, so
202
+ # accept both (the two rescue sites previously each checked only
203
+ # one form).
204
+ def strict_error?(ext, isodoc_options)
205
+ ext == :presentation ||
206
+ [true, "true"].include?(isodoc_options[:strict])
207
+ end
208
+
209
+ # On the strict path the message is pooled into @errors for the
210
+ # caller to surface (the standalone CLI reports the pool and
211
+ # aborts; the collection renderer attributes it to the failed
212
+ # member, #586). The class+message line is also printed here in all
213
+ # cases, so it sits adjacent to its backtrace in the build log: a
214
+ # pooled message surfaces only at end of run, and a bare backtrace
215
+ # mid-log is undiagnosable. The exception class is retained because
216
+ # for errors like Encoding::UndefinedConversionError it carries
217
+ # half the diagnosis.
218
+ def isodoc_error_process(err, strict, must_abort)
219
+ msg = "#{err.class}: #{err.message}"
220
+ strict || err.message.include?("Fatal:") and @errors << msg
221
+ puts msg
222
+ puts err.backtrace.join("\n")
223
+ must_abort and 1
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ module Validator
6
+ def validate_options!(options)
7
+ validate_type!(options)
8
+ validate_format!(options)
9
+ end
10
+
11
+ def validate_type!(options)
12
+ options[:type] or
13
+ Util.log("[metanorma] Error: Please specify a standard type: "\
14
+ "#{@registry.supported_backends}.", :fatal)
15
+ stdtype = options[:type].to_sym
16
+ load_flavor(stdtype)
17
+ end
18
+
19
+ def validate_format!(options)
20
+ options[:format] == :asciidoc or
21
+ Util.log("[metanorma] Error: Only source file format currently "\
22
+ "supported is 'asciidoc'.", :fatal)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Metanorma
4
+ class Compile
5
+ module Writeable
6
+ def export_output(fname, content, **options)
7
+ mode = options[:binary] ? "wb" : "w:UTF-8"
8
+ File.open(fname, mode) { |f| f.write content }
9
+ end
10
+ end
11
+ end
12
+ end
@@ -15,12 +15,12 @@ module Metanorma
15
15
 
16
16
  def validate_install_fonts(processor, options)
17
17
  unless install_fonts?(options)
18
- Util.log("[fontist] Skip font installation because" \
19
- " --no-install-fonts argument passed", :debug)
18
+ Util.log("[fontist] Skip font installation because " \
19
+ "--no-install-fonts argument passed", :debug)
20
20
  return false
21
21
  end
22
22
  unless has_custom_fonts?(processor, options, options)
23
- Util.log("[fontist] Skip font installation because "\
23
+ Util.log("[fontist] Skip font installation because " \
24
24
  "fonts_manifest is missing", :debug)
25
25
  return false
26
26
  end
@@ -34,24 +34,23 @@ module Metanorma
34
34
  rescue Fontist::Errors::FontError => e
35
35
  log_level = continue ? :warning : :fatal
36
36
  Util.log("[fontist] '#{e.font}' font is not supported. " \
37
- "Please report this issue at github.com/metanorma/metanorma" \
38
- "/issues to report this issue.", log_level)
37
+ "Please report this issue at " \
38
+ "https://github.com/metanorma/metanorma/issues", log_level)
39
39
  rescue Fontist::Errors::FormulaIndexNotFoundError
40
40
  fontist_update_repo(manifest, agree, continue, no_progress)
41
41
  end
42
42
 
43
43
  def fontist_install(manifest, agree, no_progress)
44
- if agree
45
- no_license_log
44
+ if agree then no_license_log
46
45
  else
47
- Fontist.log_level = :info
46
+ # Fontist.log_level = :info
47
+ # Shutting up Fontist 2.0's verbose logging of installation locations
48
+ Fontist.log_level = :fatal
48
49
  end
49
-
50
- Fontist::Manifest::Install.from_hash(
51
- manifest,
50
+ Fontist::Manifest.from_hash(manifest).install(
52
51
  confirmation: agree ? "yes" : "no",
53
52
  no_progress: no_progress,
54
- )
53
+ ).to_hash
55
54
  end
56
55
 
57
56
  def license_error_log(continue)
@@ -61,8 +60,8 @@ module Metanorma
61
60
  :debug,
62
61
  )
63
62
  else
64
- Util.log("[fontist] Aborting without proper fonts installed," \
65
- " make sure that you have set option --agree-to-terms",
63
+ Util.log("[fontist] Aborting without proper fonts installed, " \
64
+ "make sure that you have set option --agree-to-terms",
66
65
  :fatal)
67
66
  end
68
67
  end
@@ -92,7 +91,12 @@ module Metanorma
92
91
  return unless validate_install_fonts(processor, options)
93
92
 
94
93
  @@updated_formulas_repo = false
95
- manifest = processor.fonts_manifest.dup
94
+ manifest = if processor.respond_to?(:fonts_manifest) &&
95
+ !processor.fonts_manifest.nil?
96
+ processor.fonts_manifest.dup
97
+ else
98
+ {}
99
+ end
96
100
  append_source_fonts(manifest, options)
97
101
  agree_to_terms, can_without_fonts, no_progress = validate_options(options)
98
102
 
@@ -105,16 +109,28 @@ module Metanorma
105
109
  end
106
110
 
107
111
  def self.has_custom_fonts?(processor, options, source_attributes)
108
- install_fonts?(options) \
112
+ (install_fonts?(options) \
109
113
  && processor.respond_to?(:fonts_manifest) \
110
- && !processor.fonts_manifest.nil? \
114
+ && !processor.fonts_manifest.nil?) \
111
115
  || source_attributes[:fonts]
112
116
  end
113
117
 
114
118
  def self.location_manifest(processor, source_attributes)
115
- Fontist::Manifest::Locations.from_hash(
116
- append_source_fonts(processor.fonts_manifest.dup, source_attributes),
119
+ base = if processor.respond_to?(:fonts_manifest) &&
120
+ !processor.fonts_manifest.nil?
121
+ processor.fonts_manifest.dup
122
+ else
123
+ {}
124
+ end
125
+
126
+ return nil if base.empty? && !source_attributes[:fonts]
127
+
128
+ instance = Fontist::Manifest.from_hash(
129
+ append_source_fonts(base, source_attributes),
130
+ locations: true,
117
131
  )
132
+
133
+ instance.to_hash unless instance.nil?
118
134
  end
119
135
 
120
136
  def self.append_source_fonts(manifest, source_attributes)
@@ -1,3 +1,3 @@
1
1
  module Metanorma
2
- VERSION = "2.0.0".freeze
2
+ VERSION = "2.5.5".freeze
3
3
  end
data/lib/metanorma.rb CHANGED
@@ -5,13 +5,7 @@ class Array
5
5
  end
6
6
 
7
7
  require "metanorma/version"
8
- require "asciidoctor"
9
- require "metanorma/util/util"
10
- require "metanorma/config/config"
11
- require "metanorma/input"
12
- require "metanorma/registry/registry"
13
- require "metanorma/processor/processor"
14
- require "metanorma/asciidoctor_extensions"
8
+ require "metanorma-core"
15
9
  require "metanorma/compile/compile"
16
10
  require "metanorma/collection/collection"
17
11
  require "metanorma/collection/manifest/manifest"
data/metanorma.gemspec CHANGED
@@ -15,35 +15,29 @@ Gem::Specification.new do |spec|
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
17
  f.match(%r{^(test|spec|features|bin|.github)/}) \
18
- || f.match(%r{Rakefile|bin/rspec})
18
+ || f.match(%r{Rakefile|bin/rspec}) \
19
+ || f.match(%r{flake|\.(?:direnv|pryrc|irbrc|nix)})
19
20
  end
20
21
  spec.extra_rdoc_files = %w[README.adoc CHANGELOG.adoc LICENSE.txt]
21
22
  spec.bindir = "bin"
22
23
  # spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
24
  spec.require_paths = ["lib"]
24
- spec.required_ruby_version = ">= 2.7.0"
25
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.3.0")
25
26
 
26
27
  spec.add_runtime_dependency "asciidoctor"
27
- spec.add_runtime_dependency "fontist", ">= 1.14.3"
28
+ spec.add_runtime_dependency "concurrent-ruby"
29
+ spec.add_runtime_dependency "fontist", ">= 2.1.2"
28
30
  spec.add_runtime_dependency "htmlentities"
29
- spec.add_runtime_dependency "isodoc", ">= 2.6.3"
30
- spec.add_runtime_dependency "mn2pdf", "~> 1"
31
+ spec.add_runtime_dependency "isodoc", ">= 3.5.0"
32
+ spec.add_runtime_dependency "marcel"
33
+ spec.add_runtime_dependency "metanorma-core"
34
+ spec.add_runtime_dependency "metanorma-standoc"
35
+ spec.add_runtime_dependency "mn2pdf", "~> 2"
31
36
  spec.add_runtime_dependency "nokogiri"
32
- spec.add_runtime_dependency "shale"
33
-
34
- # get relaton-cli to avoid circular reference with metanorma-standoc
35
- # spec.add_dependency "relaton-cli"
36
- # spec.add_dependency "metanorma-standoc"
37
-
38
- spec.add_development_dependency "debug"
39
- spec.add_development_dependency "equivalent-xml", "~> 0.6"
37
+ spec.add_development_dependency "canon" #, "= 0.2.3"
38
+ spec.add_development_dependency "metanorma-iho"
40
39
  spec.add_development_dependency "metanorma-iso"
41
- spec.add_development_dependency "mnconvert"
42
- spec.add_development_dependency "pry"
43
- spec.add_development_dependency "rake", "~> 13.0"
44
- spec.add_development_dependency "rspec", "~> 3.0"
45
- spec.add_development_dependency "rspec-command", "~> 1.0"
46
- spec.add_development_dependency "rubocop", "~> 1.5.2"
47
- spec.add_development_dependency "sassc", "~> 2.4.0"
48
- spec.add_development_dependency "simplecov", "~> 0.15"
40
+
41
+ # relaton-cli is required by Metanorma::Collection
42
+ spec.add_dependency "relaton-cli"
49
43
  end