ariadne_view_components 0.0.44-arm64-darwin → 0.0.46-arm64-darwin

Sign up to get free protection for your applications and to get access to all the features.
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