ariadne_view_components 0.0.44-x86_64-darwin → 0.0.46-x86_64-darwin

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.
data/lib/tasks/docs.rake DELETED
@@ -1,466 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "yard"
4
- require "yard/renders_one_handler"
5
- require "yard/renders_many_handler"
6
-
7
- require "active_support/inflector"
8
- require "fileutils"
9
-
10
- namespace :docs do
11
- desc "Rebuilds docs on change; run via the Procfile"
12
- task :watch do
13
- require "listen"
14
-
15
- Rake::Task["docs:build"].execute
16
-
17
- puts "Listening for changes to documentation..."
18
-
19
- listener = Listen.to("app") do |modified, added, removed|
20
- puts "modified absolute path: #{modified}"
21
- puts "added absolute path: #{added}"
22
- puts "removed absolute path: #{removed}"
23
-
24
- if modified.length.nonzero?
25
- changed = modified.dup.uniq
26
- while (path = changed.shift)
27
- puts "Reloading #{path}"
28
- # reload constants (in case they changed)
29
- load(path)
30
- end
31
- end
32
-
33
- Rake::Task["docs:build"].execute
34
- end
35
- listener.start # not blocking
36
- sleep
37
- end
38
-
39
- # for classes in hashes
40
- CLASS_MAPPINGS = '(?:\\S+|:\\"[\\S-]+\\"): \\"([\\S -]+)\\"'
41
- # for classes in constants
42
- CLASS_CONSTANT = '[A-Z_]+ = \\"([\\S -]+)\\"'
43
- CLASS_REGEX = Regexp.new("(?:#{CLASS_MAPPINGS}|#{CLASS_CONSTANT})")
44
-
45
- desc "Generate the documentation."
46
- task :build do
47
- registry = generate_yard_registry
48
-
49
- puts "Converting YARD documentation to Markdown files."
50
-
51
- # Rails controller for rendering arbitrary ERB
52
- view_context = ApplicationController.new.tap { |c| c.request = ActionDispatch::TestRequest.create }.view_context
53
- components = [
54
- Ariadne::AvatarComponent,
55
- Ariadne::AvatarStackComponent,
56
- Ariadne::CommentComponent,
57
- Ariadne::BodyComponent,
58
- Ariadne::BlankslateComponent,
59
- Ariadne::BaseButton,
60
- Ariadne::ButtonComponent,
61
- Ariadne::ContainerComponent,
62
- Ariadne::ClipboardCopyComponent,
63
- Ariadne::CounterComponent,
64
- Ariadne::DetailsComponent,
65
- Ariadne::DropdownComponent,
66
- Ariadne::GridComponent,
67
- Ariadne::FlashComponent,
68
- Ariadne::FlexComponent,
69
- Ariadne::FooterComponent,
70
- Ariadne::HeaderComponent,
71
- Ariadne::HeadingComponent,
72
- Ariadne::HeroiconComponent,
73
- Ariadne::ImageComponent,
74
- Ariadne::InlineFlexComponent,
75
- Ariadne::LinkComponent,
76
- Ariadne::ListComponent,
77
- Ariadne::NarrowContainerComponent,
78
- Ariadne::PanelBarComponent,
79
- Ariadne::PillComponent,
80
- Ariadne::RichTextAreaComponent,
81
- Ariadne::SlideoverComponent,
82
- Ariadne::TabComponent,
83
- Ariadne::TabContainerComponent,
84
- Ariadne::TableNavComponent,
85
- Ariadne::TabNavComponent,
86
- Ariadne::Text,
87
- Ariadne::TimeAgoComponent,
88
- Ariadne::TimelineComponent,
89
- Ariadne::TooltipComponent,
90
- ]
91
-
92
- # TODO: Form is not in documentation
93
- js_components = [
94
- Ariadne::ClipboardCopyComponent,
95
- Ariadne::RichTextAreaComponent,
96
- Ariadne::SlideoverComponent,
97
- Ariadne::TabContainerComponent,
98
- Ariadne::TabNavComponent,
99
- Ariadne::TimeAgoComponent,
100
- Ariadne::TooltipComponent,
101
- ]
102
-
103
- all_components = Ariadne::Component.descendants - [Ariadne::BaseComponent, Ariadne::Content] # TODO: why is `Ariadne::Content` not picked up?
104
- components_needing_docs = all_components - components
105
-
106
- args_for_components = []
107
- classes_found_in_examples = []
108
-
109
- errors = []
110
-
111
- # Deletes docs before regenerating them, guaranteeing that we don't keep stale docs.
112
- components_content_glob = File.join("docs", "content", "components", "**", "*.md")
113
- FileUtils.rm_rf(components_content_glob)
114
-
115
- components.sort_by(&:name).each do |component|
116
- documentation = registry.get(component.name)
117
-
118
- data = docs_metadata(component)
119
-
120
- path = Pathname.new(data[:path])
121
- path.dirname.mkpath unless path.dirname.exist?
122
- File.open(path, "w") do |f|
123
- f.puts("---")
124
- f.puts("title: #{data[:title]}")
125
- f.puts("componentId: #{data[:component_id]}")
126
- f.puts("status: #{data[:status]}")
127
- f.puts("source: #{data[:source]}")
128
- f.puts("lookbook: #{data[:lookbook]}")
129
- f.puts("---")
130
- f.puts
131
- f.puts("import Example from '#{data[:example_path]}'")
132
-
133
- initialize_method = documentation.meths.find(&:constructor?)
134
-
135
- if js_components.include?(component)
136
- f.puts("import RequiresJSFlash from '#{data[:require_js_path]}'")
137
- f.puts
138
- f.puts("<RequiresJSFlash />")
139
- end
140
-
141
- f.puts
142
- f.puts("<!-- Warning: AUTO-GENERATED file, do not edit. Add code comments to your Ruby instead <3 -->")
143
- f.puts
144
- f.puts(view_context.render(inline: documentation.base_docstring))
145
-
146
- if documentation.tags(:deprecated).any?
147
- f.puts
148
- f.puts("## Deprecation")
149
- documentation.tags(:deprecated).each do |tag|
150
- f.puts
151
- f.puts view_context.render(inline: tag.text)
152
- end
153
- end
154
-
155
- if documentation.tags(:accessibility).any?
156
- f.puts
157
- f.puts("## Accessibility")
158
- documentation.tags(:accessibility).each do |tag|
159
- f.puts
160
- f.puts view_context.render(inline: tag.text)
161
- end
162
- end
163
-
164
- params = initialize_method.tags(:param)
165
-
166
- errors << { component.name => { arguments: "No argument documentation found" } } if params.none?
167
-
168
- f.puts
169
- f.puts("## Arguments")
170
- f.puts
171
- f.puts("| Name | Type | Default | Description |")
172
- f.puts("| :- | :- | :- | :- |")
173
-
174
- documented_params = params.map(&:name)
175
- component_params = component.instance_method(:initialize).parameters.map { |p| p.last.to_s }
176
-
177
- if (documented_params & component_params).size != component_params.size
178
- err = { arguments: {} }
179
- (component_params - documented_params).each do |arg|
180
- err[:arguments][arg] = "Not documented"
181
- end
182
-
183
- errors << { component.name => err }
184
- end
185
-
186
- args = []
187
- params.each do |tag|
188
- default_value = pretty_default_value(tag, component)
189
-
190
- args << {
191
- "name" => tag.name,
192
- "type" => tag.types.join(", "),
193
- "default" => default_value,
194
- "description" => view_context.render(inline: tag.text.squish),
195
- }
196
-
197
- f.puts("| `#{tag.name}` | `#{tag.types.join(", ")}` | #{default_value} | #{view_context.render(inline: tag.text.squish)} |")
198
- end
199
-
200
- component_args = {
201
- "component" => data[:title],
202
- "source" => data[:source],
203
- "parameters" => args,
204
- }
205
-
206
- args_for_components << component_args
207
-
208
- # Slots V2 docs
209
- slot_v2_methods = documentation.meths.select { |x| x[:renders_one] || x[:renders_many] }
210
-
211
- if slot_v2_methods.any?
212
- f.puts
213
- f.puts("## Slots")
214
-
215
- slot_v2_methods.each do |slot_documentation|
216
- f.puts
217
- f.puts("### `#{slot_documentation.name.to_s.capitalize}`")
218
-
219
- if slot_documentation.base_docstring.to_s.present?
220
- f.puts
221
- f.puts(view_context.render(inline: slot_documentation.base_docstring))
222
- end
223
-
224
- param_tags = slot_documentation.tags(:param)
225
- if param_tags.any?
226
- f.puts
227
- f.puts("| Name | Type | Default | Description |")
228
- f.puts("| :- | :- | :- | :- |")
229
- end
230
-
231
- param_tags.each do |tag|
232
- f.puts("| `#{tag.name}` | `#{tag.types.join(", ")}` | #{pretty_default_value(tag, component)} | #{view_context.render(inline: tag.text)} |")
233
- end
234
- end
235
- end
236
-
237
- example_tags = initialize_method.tags(:example)
238
-
239
- if example_tags.any?
240
- f.puts
241
- f.puts("## Examples")
242
-
243
- example_tags.each do |tag|
244
- name, description, code = parse_example_tag(tag)
245
- f.puts
246
- f.puts("### #{name}")
247
- if description
248
- f.puts
249
- f.puts(view_context.render(inline: description.squish))
250
- end
251
- f.puts
252
- html = begin
253
- view_context.render(inline: code)
254
- rescue StandardError => e
255
- raise StandardError, "Unexpected error with #{data[:title]}: #{e.message}"
256
- end
257
- html.scan(/class="([^"]*)"/) do |classnames|
258
- classes_found_in_examples.concat(classnames[0].split.reject { |c| c.starts_with?("heroicon", "js") }.map { ".#{_1}" })
259
- end
260
- f.puts("<Example src=\"#{html.tr('"', "'").delete("\n")}\" />")
261
- f.puts
262
- f.puts("```erb")
263
- f.puts(code.to_s)
264
- f.puts("```")
265
- end
266
- else
267
- errors << { component.name => { example: "No examples found" } } unless components_without_examples.include?(component)
268
- end
269
- end
270
- end
271
-
272
- unless errors.empty?
273
- puts "==============================================="
274
- puts "===================== ERRORS =================="
275
- puts "===============================================\n\n"
276
- puts JSON.pretty_generate(errors)
277
- puts "\n\n==============================================="
278
- puts "==============================================="
279
- puts "==============================================="
280
-
281
- raise
282
- end
283
-
284
- File.open("static/classes.yml", "w") do |f|
285
- non_ariadne_classes = classes_found_in_examples.reject { |c| c =~ /(?:ariadne|tiptap)/ }.uniq
286
- if non_ariadne_classes.length.nonzero?
287
- puts "==============================================="
288
- puts "===================== ERRORS =================="
289
- puts "===============================================\n\n"
290
- puts "The following non-Ariadne classes were found: #{non_ariadne_classes.join(", ")}"
291
- puts "\n\n==============================================="
292
- puts "==============================================="
293
- puts "==============================================="
294
-
295
- raise
296
- end
297
-
298
- f.puts YAML.dump(classes_found_in_examples.uniq)
299
- end
300
-
301
- File.open("static/arguments.yml", "w") do |f|
302
- f.puts YAML.dump(args_for_components)
303
- end
304
-
305
- puts "Markdown compiled."
306
-
307
- if components_needing_docs.any?
308
- puts "\nThe following components need documentation. Could you add it to them? \n* #{components_needing_docs.map(&:name).join("\n* ")}"
309
- end
310
- end
311
-
312
- desc "Generate previews from documentation examples"
313
- task :preview do
314
- registry = generate_yard_registry
315
-
316
- FileUtils.rm_rf("lookbook/test/components/previews/ariadne/docs/")
317
-
318
- components = Ariadne::Component.descendants
319
-
320
- components.each do |component|
321
- documentation = registry.get(component.name)
322
- short_name = component.name.gsub(/Ariadne|::/, "")
323
- initialize_method = documentation.meths.find(&:constructor?)
324
-
325
- next unless initialize_method&.tags(:example)&.any?
326
-
327
- yard_example_tags = initialize_method.tags(:example)
328
-
329
- path = Pathname.new("lookbook/test/components/previews/ariadne/docs/#{short_name.underscore}_preview.rb")
330
- FileUtils.mkdir_p("lookbook/test/components/previews/ariadne/docs") unless path.dirname.exist?
331
-
332
- File.open(path, "w") do |f|
333
- f.puts("module Ariadne")
334
- f.puts(" module Docs")
335
- f.puts(" class #{short_name}Preview < ViewComponent::Preview")
336
-
337
- yard_example_tags.each_with_index do |tag, index|
338
- name, _, code = parse_example_tag(tag)
339
- method_name = name.split("|").first.downcase.parameterize.underscore
340
- f.puts(" def #{method_name}; end")
341
- f.puts unless index == yard_example_tags.size - 1
342
- path = Pathname.new("lookbook/test/components/previews/ariadne/docs/#{short_name.underscore}_preview/#{method_name}.html.erb")
343
- FileUtils.mkdir_p("lookbook/test/components/previews/ariadne/docs/#{short_name.underscore}_preview") unless path.dirname.exist?
344
- File.open(path, "w") do |view_file|
345
- view_file.puts(code.to_s)
346
- end
347
- end
348
-
349
- f.puts(" end")
350
- f.puts(" end")
351
- f.puts("end")
352
- end
353
- end
354
- end
355
- end
356
-
357
- def generate_yard_registry
358
- require "action_dispatch"
359
- require_relative "../../app/lib/ariadne/view_helper"
360
- require File.expand_path("./../../lookbook/config/environment.rb", __dir__)
361
-
362
- YARD::Registry.yardoc_file = ".yardoc"
363
-
364
- require "./app/components/ariadne/component.rb"
365
- require "ariadne/view_components"
366
- require "yard/docs_helper"
367
- require "view_component/base"
368
- require "view_component/test_helpers"
369
- include(ViewComponent::TestHelpers)
370
- include(Ariadne::ViewHelper)
371
- include(YARD::DocsHelper)
372
-
373
- Dir["./app/components/ariadne/**/*.rb"].sort.each do |file|
374
- puts file
375
- require file
376
- end
377
-
378
- YARD::Rake::YardocTask.new
379
-
380
- # Custom tags for yard
381
- YARD::Tags::Library.define_tag("Accessibility", :accessibility)
382
- YARD::Tags::Library.define_tag("Deprecation", :deprecation)
383
- YARD::Tags::Library.define_tag("Parameter", :param, :with_types_name_and_default)
384
-
385
- puts "Building YARD documentation."
386
- Rake::Task["yard"].execute
387
-
388
- registry = YARD::RegistryStore.new
389
- registry.load!(".yardoc")
390
- registry
391
- end
392
-
393
- def parse_example_tag(tag)
394
- name = tag.name
395
- description = nil
396
- code = nil
397
-
398
- if tag.text.include?("@description")
399
- splitted = tag.text.split(/@description|@code/)
400
- description = splitted.second.gsub(/^[ \t]{2}/, "").strip
401
- code = splitted.last.gsub(/^[ \t]{2}/, "").strip
402
- else
403
- code = tag.text
404
- end
405
-
406
- [name, description, code]
407
- end
408
-
409
- def pretty_default_value(tag, component)
410
- params = tag.object.parameters.find { |param| [tag.name.to_s, "#{tag.name}:"].include?(param[0]) }
411
- default = tag.defaults&.first || params&.second
412
-
413
- return "N/A" unless default
414
-
415
- constant_name = "#{component.name}::#{default}"
416
- constant_value = default.safe_constantize || constant_name.safe_constantize
417
-
418
- return pretty_value(default) if constant_value.nil?
419
-
420
- pretty_value(constant_value)
421
- end
422
-
423
- def docs_metadata(component)
424
- (status_module, component_name) = status_module_and_component_name(component)
425
- status_path = status_module.nil? ? "" : "/"
426
- status = component.status.to_s
427
-
428
- {
429
- title: component_name,
430
- component_id: component_name.underscore,
431
- status: status.capitalize,
432
- source: source_url(component),
433
- lookbook: lookbook_url(component),
434
- path: "docs/content/components/#{status_path}#{component_name.underscore}.md",
435
- example_path: example_path(component),
436
- require_js_path: require_js_path(component),
437
- }
438
- end
439
-
440
- def source_url(component)
441
- path = component.name.split("::").map(&:underscore).join("/")
442
-
443
- "https://github.com/yettoapp/ariadne/ruby/view_components/tree/main/app/components/#{path}.rb"
444
- end
445
-
446
- def lookbook_url(component)
447
- path = component.name.split("::").map { |n| n.underscore.dasherize }.join("-")
448
-
449
- "https://ariadne.style/view-components/lookbook/?path=/component/#{path}"
450
- end
451
-
452
- def example_path(component)
453
- example_path = "../../src/@primer/gatsby-theme-doctocat/components/example"
454
- example_path = "../#{example_path}" if status_module?(component)
455
- example_path
456
- end
457
-
458
- def require_js_path(component)
459
- require_js_path = "../../src/@primer/gatsby-theme-doctocat/components/requires-js-flash"
460
- require_js_path = "../#{require_js_path}" if status_module?(component)
461
- require_js_path
462
- end
463
-
464
- def status_module?(component)
465
- ["Alpha", "Beta"].intersect?(component.name.split("::"))
466
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "ast_traverser"
4
-
5
- # :nodoc:
6
- class AstProcessor
7
- class << self
8
- def increment(stats, component, arg_name, value)
9
- stats[component][:arguments][arg_name][value] = 0 unless stats[component][:arguments][arg_name][value]
10
- stats[component][:arguments][arg_name][value] += 1
11
- end
12
-
13
- def process_ast(ast, stats)
14
- traverser = AstTraverser.new
15
- traverser.walk(ast)
16
-
17
- return if traverser.stats.empty?
18
-
19
- traverser.stats.each do |component, component_info|
20
- stats[component] ||= {
21
- paths: [],
22
- }
23
-
24
- stats[component][:paths] << component_info[:path]
25
- stats[component][:paths].uniq!
26
- stats[component][:arguments] ||= {}
27
-
28
- component_info[:arguments]&.each do |arg, value|
29
- arg_name = arg.to_s
30
- stats[component][:arguments][arg_name] ||= {}
31
-
32
- # we want to count each class separately
33
- if arg_name == "classes"
34
- value.split.each do |val|
35
- increment(stats, component, arg_name, val)
36
- end
37
- else
38
- increment(stats, component, arg_name, value)
39
- end
40
- end
41
- end
42
- end
43
- end
44
- end
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "ariadne/view_components/statuses"
4
- require_relative "../../../app/lib/ariadne/view_helper"
5
-
6
- # :nodoc:
7
- class AstTraverser
8
- include RuboCop::AST::Traversal
9
-
10
- attr_reader :stats
11
-
12
- def initialize
13
- @stats = {}
14
- end
15
-
16
- def on_send(node)
17
- return super(node) unless component_node?(node)
18
-
19
- name = component_name(node)
20
- args = extract_arguments(node, name)
21
-
22
- @stats[name] = { path: node.loc.expression.source_buffer.name }
23
- @stats[name][:arguments] = args unless args.empty?
24
-
25
- super(node) # recursively iterate over children
26
- end
27
-
28
- def view_helpers
29
- @view_helpers ||= ::Ariadne::ViewHelper::HELPERS.keys.map { |key| "ariadne_#{key}".to_sym }
30
- end
31
-
32
- def component_node?(node)
33
- view_helpers.include?(node.method_name) || (node.method_name == :new && !node.receiver.nil? && ::Ariadne::ViewComponents::STATUSES.key?(node.receiver.const_name))
34
- end
35
-
36
- def component_name(node)
37
- return node.receiver.const_name if node.method_name == :new
38
-
39
- helper_key = node.method_name.to_s.gsub("ariadne_", "").to_sym
40
- Ariadne::ViewHelper::HELPERS[helper_key]
41
- end
42
-
43
- def extract_arguments(node, name)
44
- args = node.arguments
45
- res = {}
46
-
47
- return res if args.empty?
48
-
49
- kwargs = args.last
50
- if kwargs.respond_to?(:pairs)
51
- res = kwargs.pairs.each_with_object({}) do |pair, h|
52
- h.merge!(extract_values(pair))
53
- end
54
- end
55
-
56
- # Heroicon is the only component that accepts positional arguments.
57
- res[:icon] = args.first.source if name == "Ariadne::HeroiconComponent" && args.size > 1
58
-
59
- res
60
- end
61
-
62
- def extract_values(pair)
63
- return { pair.key.value => pair.value.source } unless pair.value.type == :hash
64
-
65
- flatten_pairs(pair, prefix: "#{pair.key.value}-")
66
- end
67
-
68
- def flatten_pairs(pair, prefix: "")
69
- pair.value.pairs.each_with_object({}) do |value_pair, h|
70
- if value_pair.value.type == :hash
71
- h.merge!(flatten_pairs(value_pair, prefix: "#{prefix}#{value_pair.key.value}-"))
72
- else
73
- h["#{prefix}#{value_pair.key.value}"] = value_pair.value.source
74
- end
75
- end
76
- end
77
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :static do
4
- desc "Generate static JSON mappings of components for easier loading"
5
- task :dump do
6
- require File.expand_path("./../../lookbook/config/environment.rb", __dir__)
7
- require "ariadne/view_components"
8
- # Loads all components for `.descendants` to work properly
9
- Dir["./app/components/ariadne/**/*.rb"].sort.each { |file| require file }
10
-
11
- Ariadne::ViewComponents.dump(:statuses)
12
- Ariadne::ViewComponents.dump(:constants)
13
- Ariadne::ViewComponents.dump(:audited_at)
14
- end
15
- end
data/lib/tasks/test.rake DELETED
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rake/testtask"
4
-
5
- Rake::TestTask.new(:test) do |t|
6
- ENV["TZ"] = "Asia/Yerevan"
7
-
8
- t.libs << "test"
9
- t.libs << "lib"
10
- t.warning = false
11
- t.test_files = FileList[ENV["TESTS"] || "test/**/*_test.rb"]
12
- end
13
-
14
- Rake::TestTask.new(:bench) do |t|
15
- t.libs << "test"
16
- t.test_files = FileList["test/benchmarks/**/bench_*.rb"]
17
- t.verbose = true
18
- t.warning = false
19
- end