docyard 1.0.2 → 1.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -1
- data/lib/docyard/build/asset_bundler.rb +7 -33
- data/lib/docyard/build/file_copier.rb +7 -15
- data/lib/docyard/build/llms_txt_generator.rb +0 -2
- data/lib/docyard/build/sitemap_generator.rb +1 -1
- data/lib/docyard/build/static_generator.rb +30 -32
- data/lib/docyard/build/step_runner.rb +88 -0
- data/lib/docyard/build/validator.rb +98 -0
- data/lib/docyard/builder.rb +82 -55
- data/lib/docyard/cli.rb +36 -4
- data/lib/docyard/components/aliases.rb +0 -4
- data/lib/docyard/components/processors/callout_processor.rb +1 -1
- data/lib/docyard/components/processors/code_block_diff_preprocessor.rb +1 -1
- data/lib/docyard/components/processors/code_block_focus_preprocessor.rb +1 -1
- data/lib/docyard/components/processors/code_block_options_preprocessor.rb +2 -2
- data/lib/docyard/components/processors/code_group_processor.rb +1 -1
- data/lib/docyard/components/processors/icon_processor.rb +2 -2
- data/lib/docyard/components/processors/tabs_processor.rb +1 -1
- data/lib/docyard/config/schema/definition.rb +29 -0
- data/lib/docyard/config/schema/sections.rb +63 -0
- data/lib/docyard/config/schema/simple_sections.rb +78 -0
- data/lib/docyard/config/schema.rb +28 -31
- data/lib/docyard/config/type_validators.rb +121 -0
- data/lib/docyard/config/validator.rb +136 -61
- data/lib/docyard/config.rb +1 -13
- data/lib/docyard/diagnostic.rb +89 -0
- data/lib/docyard/diagnostic_context.rb +48 -0
- data/lib/docyard/doctor/code_block_checker.rb +136 -0
- data/lib/docyard/doctor/component_checker.rb +49 -0
- data/lib/docyard/doctor/component_checkers/abbreviation_checker.rb +74 -0
- data/lib/docyard/doctor/component_checkers/badge_checker.rb +71 -0
- data/lib/docyard/doctor/component_checkers/base.rb +111 -0
- data/lib/docyard/doctor/component_checkers/callout_checker.rb +34 -0
- data/lib/docyard/doctor/component_checkers/cards_checker.rb +57 -0
- data/lib/docyard/doctor/component_checkers/code_group_checker.rb +47 -0
- data/lib/docyard/doctor/component_checkers/details_checker.rb +51 -0
- data/lib/docyard/doctor/component_checkers/icon_checker.rb +36 -0
- data/lib/docyard/doctor/component_checkers/image_attrs_checker.rb +46 -0
- data/lib/docyard/doctor/component_checkers/space_after_colons_checker.rb +45 -0
- data/lib/docyard/doctor/component_checkers/steps_checker.rb +35 -0
- data/lib/docyard/doctor/component_checkers/tabs_checker.rb +35 -0
- data/lib/docyard/doctor/component_checkers/tooltip_checker.rb +67 -0
- data/lib/docyard/doctor/component_checkers/unknown_type_checker.rb +34 -0
- data/lib/docyard/doctor/config_checker.rb +19 -0
- data/lib/docyard/doctor/config_fixer.rb +87 -0
- data/lib/docyard/doctor/content_checker.rb +164 -0
- data/lib/docyard/doctor/file_scanner.rb +113 -0
- data/lib/docyard/doctor/image_checker.rb +103 -0
- data/lib/docyard/doctor/link_checker.rb +91 -0
- data/lib/docyard/doctor/markdown_fixer.rb +62 -0
- data/lib/docyard/doctor/orphan_checker.rb +82 -0
- data/lib/docyard/doctor/reporter.rb +152 -0
- data/lib/docyard/doctor/sidebar_checker.rb +127 -0
- data/lib/docyard/doctor/sidebar_fixer.rb +47 -0
- data/lib/docyard/doctor.rb +178 -0
- data/lib/docyard/editor_launcher.rb +119 -0
- data/lib/docyard/errors.rb +0 -49
- data/lib/docyard/initializer.rb +32 -39
- data/lib/docyard/navigation/sidebar/local_config_loader.rb +44 -21
- data/lib/docyard/rendering/icon_helpers.rb +1 -3
- data/lib/docyard/search/build_indexer.rb +39 -24
- data/lib/docyard/search/dev_indexer.rb +9 -23
- data/lib/docyard/server/dev_server.rb +55 -13
- data/lib/docyard/server/error_overlay.rb +73 -0
- data/lib/docyard/server/file_watcher.rb +0 -1
- data/lib/docyard/server/page_diagnostics.rb +27 -0
- data/lib/docyard/server/preview_server.rb +17 -13
- data/lib/docyard/server/rack_application.rb +64 -3
- data/lib/docyard/server/resolution_result.rb +0 -4
- data/lib/docyard/templates/assets/css/error-overlay.css +669 -0
- data/lib/docyard/templates/assets/css/variables.css +1 -1
- data/lib/docyard/templates/assets/fonts/Inter-Variable.woff2 +0 -0
- data/lib/docyard/templates/assets/js/components/relative-time.js +42 -0
- data/lib/docyard/templates/assets/js/error-overlay.js +547 -0
- data/lib/docyard/templates/assets/js/hot-reload.js +35 -7
- data/lib/docyard/templates/errors/404.html.erb +1 -1
- data/lib/docyard/templates/errors/500.html.erb +1 -1
- data/lib/docyard/templates/partials/_head.html.erb +1 -1
- data/lib/docyard/templates/partials/_page_actions.html.erb +1 -1
- data/lib/docyard/ui.rb +80 -0
- data/lib/docyard/utils/logging.rb +5 -1
- data/lib/docyard/utils/text_formatter.rb +0 -6
- data/lib/docyard/version.rb +1 -1
- data/lib/docyard.rb +4 -0
- metadata +47 -25
- data/lib/docyard/config/key_validator.rb +0 -30
- data/lib/docyard/config/validation_helpers.rb +0 -83
- data/lib/docyard/config/validators/navigation.rb +0 -43
- data/lib/docyard/config/validators/section.rb +0 -114
- data/lib/docyard/templates/assets/fonts/Inter-Variable.ttf +0 -0
data/lib/docyard/builder.rb
CHANGED
|
@@ -1,84 +1,133 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
|
-
|
|
4
|
+
require_relative "build/step_runner"
|
|
5
5
|
|
|
6
6
|
module Docyard
|
|
7
7
|
class Builder
|
|
8
|
-
attr_reader :config, :clean, :verbose, :start_time
|
|
8
|
+
attr_reader :config, :clean, :verbose, :strict, :start_time
|
|
9
9
|
|
|
10
|
-
def initialize(clean: true, verbose: false)
|
|
10
|
+
def initialize(clean: true, verbose: false, strict: false)
|
|
11
11
|
@config = Config.new
|
|
12
12
|
@clean = clean
|
|
13
13
|
@verbose = verbose
|
|
14
|
+
@strict = strict || config.build.strict
|
|
14
15
|
@start_time = Time.now
|
|
16
|
+
@step_runner = Build::StepRunner.new(verbose: verbose)
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def build
|
|
18
|
-
|
|
19
|
-
log "Building static site..."
|
|
20
|
-
|
|
21
|
-
pages_built = generate_static_pages
|
|
22
|
-
bundles_created = bundle_assets
|
|
23
|
-
assets_copied = copy_static_files
|
|
24
|
-
generate_seo_files
|
|
25
|
-
pages_indexed = generate_search_index
|
|
20
|
+
return false unless passes_validation?
|
|
26
21
|
|
|
27
|
-
|
|
22
|
+
print_header
|
|
23
|
+
prepare_output_directory
|
|
24
|
+
execute_build_steps
|
|
25
|
+
print_summary
|
|
28
26
|
true
|
|
29
27
|
rescue StandardError => e
|
|
30
|
-
|
|
31
|
-
error e.backtrace.first if verbose
|
|
28
|
+
print_error(e)
|
|
32
29
|
false
|
|
33
30
|
end
|
|
34
31
|
|
|
35
32
|
private
|
|
36
33
|
|
|
37
|
-
def
|
|
38
|
-
|
|
34
|
+
def print_header
|
|
35
|
+
puts
|
|
36
|
+
puts " #{UI.bold('Docyard')} v#{VERSION}"
|
|
37
|
+
puts
|
|
38
|
+
puts " Building to #{UI.dim("#{config.build.output}/")}..."
|
|
39
|
+
puts
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def passes_validation?
|
|
43
|
+
require_relative "build/validator"
|
|
44
|
+
validator = Build::Validator.new(config, strict: strict)
|
|
45
|
+
return true if validator.valid?
|
|
46
|
+
|
|
47
|
+
validator.print_errors
|
|
48
|
+
false
|
|
49
|
+
end
|
|
39
50
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
def execute_build_steps
|
|
52
|
+
@step_runner.run("Generating pages") { generate_static_pages }
|
|
53
|
+
@step_runner.run("Bundling assets") { bundle_assets }
|
|
54
|
+
@step_runner.run("Copying files") { copy_static_files }
|
|
55
|
+
@step_runner.run("Generating SEO") { generate_seo_files }
|
|
56
|
+
@step_runner.run("Indexing search") { generate_search_index }
|
|
57
|
+
end
|
|
44
58
|
|
|
59
|
+
def print_error(error)
|
|
60
|
+
puts UI.error("failed")
|
|
61
|
+
puts
|
|
62
|
+
puts " #{UI.error('Error:')} #{error.message}"
|
|
63
|
+
puts " #{error.backtrace.first}" if verbose
|
|
64
|
+
puts
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def print_summary
|
|
68
|
+
puts
|
|
69
|
+
puts " #{UI.success('Build complete')} in #{format('%.2fs', build_duration)}"
|
|
70
|
+
puts " #{UI.dim(output_summary)}"
|
|
71
|
+
puts
|
|
72
|
+
@step_runner.print_timing_breakdown if verbose
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def build_duration
|
|
76
|
+
Time.now - start_time
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def output_summary
|
|
80
|
+
"Output: #{config.build.output}/ (#{format_size(calculate_output_size)})"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def format_size(bytes)
|
|
84
|
+
kb = bytes / 1024.0
|
|
85
|
+
kb >= 1000 ? format("%.1f MB", kb / 1024.0) : format("%.1f KB", kb)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def calculate_output_size
|
|
89
|
+
Dir.glob(File.join(config.build.output, "**", "*"))
|
|
90
|
+
.select { |f| File.file?(f) }
|
|
91
|
+
.sum { |f| File.size(f) }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def prepare_output_directory
|
|
95
|
+
output_dir = config.build.output
|
|
96
|
+
FileUtils.rm_rf(output_dir) if clean && Dir.exist?(output_dir)
|
|
45
97
|
FileUtils.mkdir_p(output_dir)
|
|
46
98
|
end
|
|
47
99
|
|
|
48
100
|
def generate_static_pages
|
|
49
101
|
require_relative "build/static_generator"
|
|
50
|
-
|
|
51
|
-
generator.generate
|
|
102
|
+
Build::StaticGenerator.new(config, verbose: verbose).generate
|
|
52
103
|
end
|
|
53
104
|
|
|
54
105
|
def bundle_assets
|
|
55
106
|
require_relative "build/asset_bundler"
|
|
56
|
-
|
|
57
|
-
bundler.bundle
|
|
107
|
+
[Build::AssetBundler.new(config, verbose: verbose).bundle, nil]
|
|
58
108
|
end
|
|
59
109
|
|
|
60
110
|
def copy_static_files
|
|
61
111
|
require_relative "build/file_copier"
|
|
62
|
-
|
|
63
|
-
copier.copy
|
|
112
|
+
Build::FileCopier.new(config, verbose: verbose).copy
|
|
64
113
|
end
|
|
65
114
|
|
|
66
115
|
def generate_seo_files
|
|
67
116
|
require_relative "build/sitemap_generator"
|
|
68
|
-
|
|
69
|
-
sitemap_gen.generate
|
|
117
|
+
sitemap_result = Build::SitemapGenerator.new(config).generate
|
|
70
118
|
|
|
71
119
|
require_relative "build/llms_txt_generator"
|
|
72
|
-
|
|
73
|
-
llms_gen.generate
|
|
120
|
+
Build::LlmsTxtGenerator.new(config).generate
|
|
74
121
|
|
|
75
122
|
File.write(File.join(config.build.output, "robots.txt"), robots_txt_content)
|
|
76
|
-
|
|
123
|
+
|
|
124
|
+
result = ["sitemap.xml", "robots.txt", "llms.txt"]
|
|
125
|
+
details = ["sitemap.xml (#{sitemap_result} URLs)", "robots.txt", "llms.txt", "llms-full.txt"]
|
|
126
|
+
[result, details]
|
|
77
127
|
end
|
|
78
128
|
|
|
79
129
|
def generate_search_index
|
|
80
|
-
|
|
81
|
-
indexer.index
|
|
130
|
+
Search::BuildIndexer.new(config, verbose: verbose).index
|
|
82
131
|
end
|
|
83
132
|
|
|
84
133
|
def robots_txt_content
|
|
@@ -92,27 +141,5 @@ module Docyard
|
|
|
92
141
|
Sitemap: #{base}sitemap.xml
|
|
93
142
|
ROBOTS
|
|
94
143
|
end
|
|
95
|
-
|
|
96
|
-
def display_summary(pages, bundles, assets, indexed = 0)
|
|
97
|
-
elapsed = Time.now - start_time
|
|
98
|
-
|
|
99
|
-
puts "\n#{'=' * 50}"
|
|
100
|
-
puts "Build complete in #{format('%.2f', elapsed)}s"
|
|
101
|
-
puts "Output: #{config.build.output}/"
|
|
102
|
-
|
|
103
|
-
summary = "#{pages} pages, #{bundles} bundles, #{assets} static files"
|
|
104
|
-
summary += ", #{indexed} pages indexed" if indexed.positive?
|
|
105
|
-
puts summary
|
|
106
|
-
|
|
107
|
-
puts "=" * 50
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def log(message)
|
|
111
|
-
Docyard.logger.info(message)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def error(message)
|
|
115
|
-
Docyard.logger.error(message)
|
|
116
|
-
end
|
|
117
144
|
end
|
|
118
145
|
end
|
data/lib/docyard/cli.rb
CHANGED
|
@@ -8,6 +8,8 @@ module Docyard
|
|
|
8
8
|
true
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
class_option :no_color, type: :boolean, default: false, desc: "Disable colored output"
|
|
12
|
+
|
|
11
13
|
desc "version", "Show docyard version"
|
|
12
14
|
def version
|
|
13
15
|
puts "docyard #{Docyard::VERSION}"
|
|
@@ -17,6 +19,7 @@ module Docyard
|
|
|
17
19
|
method_option :force, type: :boolean, default: false, aliases: "-f",
|
|
18
20
|
desc: "Overwrite existing files"
|
|
19
21
|
def init(project_name = nil)
|
|
22
|
+
apply_global_options
|
|
20
23
|
initializer = Docyard::Initializer.new(project_name, force: options[:force])
|
|
21
24
|
exit(1) unless initializer.run
|
|
22
25
|
end
|
|
@@ -24,23 +27,28 @@ module Docyard
|
|
|
24
27
|
desc "build", "Build static site for production"
|
|
25
28
|
method_option :clean, type: :boolean, default: true, desc: "Clean output directory before building"
|
|
26
29
|
method_option :verbose, type: :boolean, default: false, aliases: "-v", desc: "Show verbose output"
|
|
30
|
+
method_option :strict, type: :boolean, default: false, desc: "Fail on any validation errors"
|
|
27
31
|
def build
|
|
32
|
+
apply_global_options
|
|
28
33
|
require_relative "builder"
|
|
29
34
|
builder = Docyard::Builder.new(
|
|
30
35
|
clean: options[:clean],
|
|
31
|
-
verbose: options[:verbose]
|
|
36
|
+
verbose: options[:verbose],
|
|
37
|
+
strict: options[:strict]
|
|
32
38
|
)
|
|
33
39
|
exit(1) unless builder.build
|
|
34
40
|
rescue ConfigError => e
|
|
35
|
-
|
|
36
|
-
exit(1)
|
|
41
|
+
print_config_error(e)
|
|
37
42
|
end
|
|
38
43
|
|
|
39
44
|
desc "preview", "Preview the built site locally"
|
|
40
45
|
method_option :port, type: :numeric, default: 4000, aliases: "-p", desc: "Port to run preview server on"
|
|
41
46
|
def preview
|
|
47
|
+
apply_global_options
|
|
42
48
|
require_relative "server/preview_server"
|
|
43
49
|
Docyard::PreviewServer.new(port: options[:port]).start
|
|
50
|
+
rescue ConfigError => e
|
|
51
|
+
print_config_error(e)
|
|
44
52
|
end
|
|
45
53
|
|
|
46
54
|
desc "serve", "Start the development server"
|
|
@@ -49,6 +57,7 @@ module Docyard
|
|
|
49
57
|
method_option :search, type: :boolean, default: false, aliases: "-s",
|
|
50
58
|
desc: "Enable search indexing (slower startup)"
|
|
51
59
|
def serve
|
|
60
|
+
apply_global_options
|
|
52
61
|
require_relative "server/dev_server"
|
|
53
62
|
config = Docyard::Config.load
|
|
54
63
|
server = Docyard::DevServer.new(
|
|
@@ -59,7 +68,30 @@ module Docyard
|
|
|
59
68
|
)
|
|
60
69
|
server.start
|
|
61
70
|
rescue ConfigError => e
|
|
62
|
-
|
|
71
|
+
print_config_error(e)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
desc "doctor", "Check documentation for issues"
|
|
75
|
+
method_option :fix, type: :boolean, default: false, desc: "Auto-fix fixable issues"
|
|
76
|
+
def doctor
|
|
77
|
+
apply_global_options
|
|
78
|
+
require_relative "doctor"
|
|
79
|
+
doctor = Docyard::Doctor.new(fix: options[:fix])
|
|
80
|
+
exit(doctor.run)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def apply_global_options
|
|
86
|
+
UI.enabled = false if options[:no_color]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def print_config_error(error)
|
|
90
|
+
puts
|
|
91
|
+
puts " #{UI.bold('Docyard')} v#{VERSION}"
|
|
92
|
+
puts
|
|
93
|
+
puts " #{UI.red(error.message)}"
|
|
94
|
+
puts
|
|
63
95
|
exit(1)
|
|
64
96
|
end
|
|
65
97
|
end
|
|
@@ -26,11 +26,7 @@ module Docyard
|
|
|
26
26
|
TabsProcessor = Processors::TabsProcessor
|
|
27
27
|
TooltipProcessor = Processors::TooltipProcessor
|
|
28
28
|
|
|
29
|
-
CodeDetector = Support::CodeDetector
|
|
30
|
-
IconDetector = Support::Tabs::IconDetector
|
|
31
|
-
|
|
32
29
|
CodeBlockFeatureExtractor = Support::CodeBlock::FeatureExtractor
|
|
33
|
-
CodeBlockIconDetector = Support::CodeBlock::IconDetector
|
|
34
30
|
CodeBlockLineWrapper = Support::CodeBlock::LineWrapper
|
|
35
31
|
CodeBlockPatterns = Support::CodeBlock::Patterns
|
|
36
32
|
CodeLineParser = Support::CodeBlock::LineParser
|
|
@@ -43,7 +43,7 @@ module Docyard
|
|
|
43
43
|
private
|
|
44
44
|
|
|
45
45
|
def process_container_syntax(markdown)
|
|
46
|
-
markdown.gsub(/^:::
|
|
46
|
+
markdown.gsub(/^:::(\w+)(?:[ \t]+([^\n]+?))?[ \t]*\n(.*?)^:::[ \t]*$/m) do
|
|
47
47
|
match = Regexp.last_match
|
|
48
48
|
next match[0] if inside_code_block?(match.begin(0), @code_block_ranges)
|
|
49
49
|
|
|
@@ -12,7 +12,7 @@ module Docyard
|
|
|
12
12
|
self.priority = 6
|
|
13
13
|
|
|
14
14
|
CODE_BLOCK_REGEX = /^```(\w*).*?\n(.*?)^```/m
|
|
15
|
-
TABS_BLOCK_REGEX = /^:::
|
|
15
|
+
TABS_BLOCK_REGEX = /^:::tabs[ \t]*\n.*?^:::[ \t]*$/m
|
|
16
16
|
|
|
17
17
|
def preprocess(content)
|
|
18
18
|
context[:code_block_diff_lines] ||= []
|
|
@@ -20,7 +20,7 @@ module Docyard
|
|
|
20
20
|
}x
|
|
21
21
|
|
|
22
22
|
CODE_BLOCK_REGEX = /^```(\w*).*?\n(.*?)^```/m
|
|
23
|
-
TABS_BLOCK_REGEX = /^:::
|
|
23
|
+
TABS_BLOCK_REGEX = /^:::tabs[ \t]*\n.*?^:::[ \t]*$/m
|
|
24
24
|
|
|
25
25
|
def preprocess(content)
|
|
26
26
|
context[:code_block_focus_lines] ||= []
|
|
@@ -10,8 +10,8 @@ module Docyard
|
|
|
10
10
|
self.priority = 5
|
|
11
11
|
|
|
12
12
|
CODE_FENCE_REGEX = /^```(\w+)(?:\s*\[([^\]]+)\])?(:\S+)?(?:\s*\{([^}\n]+)\})?/
|
|
13
|
-
TABS_BLOCK_REGEX = /^:::
|
|
14
|
-
CODE_GROUP_BLOCK_REGEX = /^:::
|
|
13
|
+
TABS_BLOCK_REGEX = /^:::tabs[ \t]*\n.*?^:::[ \t]*$/m
|
|
14
|
+
CODE_GROUP_BLOCK_REGEX = /^:::code-group[ \t]*\n.*?^:::[ \t]*$/m
|
|
15
15
|
EXCLUDED_LANGUAGES = %w[filetree].freeze
|
|
16
16
|
|
|
17
17
|
def preprocess(content)
|
|
@@ -22,7 +22,7 @@ module Docyard
|
|
|
22
22
|
|
|
23
23
|
self.priority = 12
|
|
24
24
|
|
|
25
|
-
CODE_GROUP_PATTERN = /^:::
|
|
25
|
+
CODE_GROUP_PATTERN = /^:::code-group[ \t]*\n(.*?)^:::[ \t]*$/m
|
|
26
26
|
CODE_BLOCK_PATTERN = /```(\w*)\s*\[([^\]]+)\]([^\n]*)\n(.*?)```/m
|
|
27
27
|
|
|
28
28
|
CodeBlockFeatureExtractor = Support::CodeBlock::FeatureExtractor
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../base_processor"
|
|
4
|
+
require_relative "../../rendering/icons"
|
|
4
5
|
|
|
5
6
|
module Docyard
|
|
6
7
|
module Components
|
|
@@ -9,7 +10,6 @@ module Docyard
|
|
|
9
10
|
self.priority = 20
|
|
10
11
|
|
|
11
12
|
ICON_PATTERN = /:([a-z][a-z0-9-]*):(?:([a-z]+):)?/i
|
|
12
|
-
VALID_WEIGHTS = %w[regular bold fill light thin duotone].freeze
|
|
13
13
|
|
|
14
14
|
def postprocess(html)
|
|
15
15
|
segments = split_preserving_code_blocks(html)
|
|
@@ -49,7 +49,7 @@ module Docyard
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def render_icon(name, weight)
|
|
52
|
-
weight = "regular" unless VALID_WEIGHTS.include?(weight)
|
|
52
|
+
weight = "regular" unless Icons::VALID_WEIGHTS.include?(weight)
|
|
53
53
|
weight_class = weight == "regular" ? "ph" : "ph-#{weight}"
|
|
54
54
|
%(<i class="#{weight_class} ph-#{name}" aria-hidden="true"></i>)
|
|
55
55
|
end
|
|
@@ -21,7 +21,7 @@ module Docyard
|
|
|
21
21
|
|
|
22
22
|
@code_block_ranges = find_code_block_ranges(content)
|
|
23
23
|
|
|
24
|
-
content.gsub(/^:::
|
|
24
|
+
content.gsub(/^:::tabs[ \t]*\n(.*?)^:::[ \t]*$/m) do
|
|
25
25
|
match = Regexp.last_match
|
|
26
26
|
next match[0] if inside_code_block?(match.begin(0), @code_block_ranges)
|
|
27
27
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "sections"
|
|
4
|
+
|
|
5
|
+
module Docyard
|
|
6
|
+
class Config
|
|
7
|
+
module Schema
|
|
8
|
+
DEFINITION = {
|
|
9
|
+
title: { type: :string },
|
|
10
|
+
description: { type: :string },
|
|
11
|
+
url: { type: :url },
|
|
12
|
+
og_image: { type: :string },
|
|
13
|
+
twitter: { type: :string },
|
|
14
|
+
source: { type: :string },
|
|
15
|
+
sidebar: { type: :enum, values: SIDEBAR_MODES },
|
|
16
|
+
branding: BRANDING_SCHEMA,
|
|
17
|
+
socials: SOCIALS_SCHEMA,
|
|
18
|
+
tabs: TABS_SCHEMA,
|
|
19
|
+
build: BUILD_SCHEMA,
|
|
20
|
+
search: SEARCH_SCHEMA,
|
|
21
|
+
navigation: NAVIGATION_SCHEMA,
|
|
22
|
+
announcement: ANNOUNCEMENT_SCHEMA,
|
|
23
|
+
repo: REPO_SCHEMA,
|
|
24
|
+
analytics: ANALYTICS_SCHEMA,
|
|
25
|
+
feedback: FEEDBACK_SCHEMA
|
|
26
|
+
}.freeze
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "simple_sections"
|
|
4
|
+
|
|
5
|
+
module Docyard
|
|
6
|
+
class Config
|
|
7
|
+
module Schema
|
|
8
|
+
SOCIALS_SCHEMA = {
|
|
9
|
+
type: :hash,
|
|
10
|
+
allow_extra_keys: true,
|
|
11
|
+
keys: {
|
|
12
|
+
github: { type: :url },
|
|
13
|
+
twitter: { type: :url },
|
|
14
|
+
discord: { type: :url },
|
|
15
|
+
slack: { type: :url },
|
|
16
|
+
linkedin: { type: :url },
|
|
17
|
+
youtube: { type: :url },
|
|
18
|
+
bluesky: { type: :url },
|
|
19
|
+
custom: {
|
|
20
|
+
type: :array,
|
|
21
|
+
items: {
|
|
22
|
+
type: :hash,
|
|
23
|
+
keys: { icon: { type: :string }, href: { type: :url } }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
TABS_SCHEMA = {
|
|
30
|
+
type: :array,
|
|
31
|
+
items: {
|
|
32
|
+
type: :hash,
|
|
33
|
+
keys: {
|
|
34
|
+
text: { type: :string },
|
|
35
|
+
href: { type: :string },
|
|
36
|
+
icon: { type: :string },
|
|
37
|
+
external: { type: :boolean }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}.freeze
|
|
41
|
+
|
|
42
|
+
NAVIGATION_SCHEMA = {
|
|
43
|
+
type: :hash,
|
|
44
|
+
keys: {
|
|
45
|
+
breadcrumbs: { type: :boolean },
|
|
46
|
+
cta: {
|
|
47
|
+
type: :array,
|
|
48
|
+
max_items: 2,
|
|
49
|
+
items: {
|
|
50
|
+
type: :hash,
|
|
51
|
+
keys: {
|
|
52
|
+
text: { type: :string },
|
|
53
|
+
href: { type: :string },
|
|
54
|
+
variant: { type: :enum, values: CTA_VARIANTS },
|
|
55
|
+
external: { type: :boolean }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}.freeze
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Docyard
|
|
4
|
+
class Config
|
|
5
|
+
module Schema
|
|
6
|
+
BRANDING_SCHEMA = {
|
|
7
|
+
type: :hash,
|
|
8
|
+
keys: {
|
|
9
|
+
logo: { type: :file_or_url },
|
|
10
|
+
favicon: { type: :file_or_url },
|
|
11
|
+
credits: { type: :boolean },
|
|
12
|
+
copyright: { type: :string },
|
|
13
|
+
color: { type: :color }
|
|
14
|
+
}
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
BUILD_SCHEMA = {
|
|
18
|
+
type: :hash,
|
|
19
|
+
keys: {
|
|
20
|
+
output: { type: :string, format: :no_slashes },
|
|
21
|
+
base: { type: :string, format: :starts_with_slash },
|
|
22
|
+
strict: { type: :boolean }
|
|
23
|
+
}
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
SEARCH_SCHEMA = {
|
|
27
|
+
type: :hash,
|
|
28
|
+
keys: {
|
|
29
|
+
enabled: { type: :boolean },
|
|
30
|
+
placeholder: { type: :string },
|
|
31
|
+
exclude: { type: :array, items: { type: :string } }
|
|
32
|
+
}
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
ANNOUNCEMENT_SCHEMA = {
|
|
36
|
+
type: :hash,
|
|
37
|
+
keys: {
|
|
38
|
+
text: { type: :string },
|
|
39
|
+
link: { type: :string },
|
|
40
|
+
dismissible: { type: :boolean },
|
|
41
|
+
button: {
|
|
42
|
+
type: :hash,
|
|
43
|
+
keys: { text: { type: :string }, link: { type: :string } }
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
REPO_SCHEMA = {
|
|
49
|
+
type: :hash,
|
|
50
|
+
keys: {
|
|
51
|
+
url: { type: :url },
|
|
52
|
+
branch: { type: :string },
|
|
53
|
+
edit_path: { type: :string },
|
|
54
|
+
edit_link: { type: :boolean },
|
|
55
|
+
last_updated: { type: :boolean }
|
|
56
|
+
}
|
|
57
|
+
}.freeze
|
|
58
|
+
|
|
59
|
+
ANALYTICS_SCHEMA = {
|
|
60
|
+
type: :hash,
|
|
61
|
+
keys: {
|
|
62
|
+
google: { type: :string },
|
|
63
|
+
plausible: { type: :string },
|
|
64
|
+
fathom: { type: :string },
|
|
65
|
+
script: { type: :string }
|
|
66
|
+
}
|
|
67
|
+
}.freeze
|
|
68
|
+
|
|
69
|
+
FEEDBACK_SCHEMA = {
|
|
70
|
+
type: :hash,
|
|
71
|
+
keys: {
|
|
72
|
+
enabled: { type: :boolean },
|
|
73
|
+
question: { type: :string }
|
|
74
|
+
}
|
|
75
|
+
}.freeze
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -3,37 +3,34 @@
|
|
|
3
3
|
module Docyard
|
|
4
4
|
class Config
|
|
5
5
|
module Schema
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
SIDEBAR_EXTERNAL_LINK = %w[link text icon target].freeze
|
|
33
|
-
|
|
34
|
-
SOCIALS_BUILTIN = %w[github twitter discord slack linkedin youtube bluesky custom].freeze
|
|
35
|
-
|
|
36
|
-
CUSTOM_SOCIAL = %w[icon href].freeze
|
|
6
|
+
SIDEBAR_MODES = %w[config auto distributed].freeze
|
|
7
|
+
CTA_VARIANTS = %w[primary secondary].freeze
|
|
8
|
+
SIDEBAR_ITEM_KEYS = %w[text icon badge badge_type items collapsed index group collapsible].freeze
|
|
9
|
+
SIDEBAR_EXTERNAL_LINK_KEYS = %w[link text icon target].freeze
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def validate_keys(hash, valid_keys, context:)
|
|
13
|
+
return [] unless hash.is_a?(Hash)
|
|
14
|
+
|
|
15
|
+
unknown = hash.keys.map(&:to_s) - valid_keys
|
|
16
|
+
unknown.map { |key| build_key_error(key, valid_keys, context) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def build_key_error(key, valid_keys, context)
|
|
20
|
+
suggestion = find_key_suggestion(key, valid_keys)
|
|
21
|
+
msg = "unknown key '#{key}'"
|
|
22
|
+
msg += ". Did you mean '#{suggestion}'?" if suggestion
|
|
23
|
+
fix = suggestion ? { type: :rename, from: key, to: suggestion } : nil
|
|
24
|
+
{ context: context, message: msg, key: key, fix: fix }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def find_key_suggestion(key, valid_keys)
|
|
28
|
+
checker = DidYouMean::SpellChecker.new(dictionary: valid_keys)
|
|
29
|
+
checker.correct(key).first
|
|
30
|
+
end
|
|
31
|
+
end
|
|
37
32
|
end
|
|
38
33
|
end
|
|
39
34
|
end
|
|
35
|
+
|
|
36
|
+
require_relative "schema/definition"
|