graphql-modernize 0.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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +40 -0
  3. data/CHANGELOG.md +5 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +106 -0
  6. data/Rakefile +10 -0
  7. data/docs/migration-guide.md +23 -0
  8. data/docs/research/changelog-survey.md +68 -0
  9. data/docs/rules/GQLM101.md +5 -0
  10. data/docs/rules/GQLM102.md +12 -0
  11. data/docs/rules/GQLM103.md +5 -0
  12. data/docs/rules/GQLM104.md +5 -0
  13. data/docs/rules/GQLM105.md +9 -0
  14. data/docs/rules/GQLM106.md +7 -0
  15. data/docs/rules/GQLM107.md +7 -0
  16. data/docs/rules/GQLM108.md +4 -0
  17. data/docs/rules/GQLM201.md +5 -0
  18. data/docs/rules/GQLM202.md +4 -0
  19. data/docs/rules/GQLM203.md +5 -0
  20. data/docs/rules/GQLM204.md +5 -0
  21. data/docs/rules/GQLM205.md +5 -0
  22. data/docs/rules/GQLM301.md +5 -0
  23. data/docs/rules/GQLM302.md +5 -0
  24. data/docs/rules/GQLM303.md +6 -0
  25. data/docs/rules/GQLM304.md +4 -0
  26. data/docs/rules/GQLM305.md +8 -0
  27. data/docs/rules/GQLM306.md +8 -0
  28. data/docs/rules/GQLM307.md +11 -0
  29. data/docs/rules/GQLM308.md +5 -0
  30. data/docs/rules/GQLM309.md +4 -0
  31. data/docs/rules/GQLM310.md +9 -0
  32. data/docs/rules/GQLM401.md +6 -0
  33. data/docs/rules/GQLM402.md +6 -0
  34. data/docs/rules/GQLM501.md +6 -0
  35. data/docs/rules/GQLM502.md +5 -0
  36. data/docs/rules/README.md +35 -0
  37. data/docs/writing-rules.md +29 -0
  38. data/exe/graphql-modernize +6 -0
  39. data/lib/graphql/modernize/application.rb +172 -0
  40. data/lib/graphql/modernize/class_hierarchy.rb +329 -0
  41. data/lib/graphql/modernize/cli.rb +175 -0
  42. data/lib/graphql/modernize/config.rb +163 -0
  43. data/lib/graphql/modernize/context_builder.rb +78 -0
  44. data/lib/graphql/modernize/file_finder.rb +45 -0
  45. data/lib/graphql/modernize/offense.rb +28 -0
  46. data/lib/graphql/modernize/reporter.rb +103 -0
  47. data/lib/graphql/modernize/rule_registry.rb +26 -0
  48. data/lib/graphql/modernize/rules/base.rb +132 -0
  49. data/lib/graphql/modernize/rules/deprecation.rb +219 -0
  50. data/lib/graphql/modernize/rules/legacy.rb +301 -0
  51. data/lib/graphql/modernize/rules/modernize.rb +69 -0
  52. data/lib/graphql/modernize/rules/relay.rb +74 -0
  53. data/lib/graphql/modernize/rules/schema_config.rb +38 -0
  54. data/lib/graphql/modernize/runner.rb +34 -0
  55. data/lib/graphql/modernize/suppression_index.rb +88 -0
  56. data/lib/graphql/modernize/version.rb +7 -0
  57. data/lib/graphql/modernize.rb +30 -0
  58. metadata +120 -0
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Modernize
5
+ module Rules
6
+ class ComplexityCostMode < Base
7
+ rule id: "GQLM501", safety: :manual, summary: "Choose an explicit complexity cost mode",
8
+ since: "2.5.3", docs: "docs/rules/GQLM501.md"
9
+ node_pattern :class_node, "(class_node)"
10
+
11
+ def on_match(node, _captures)
12
+ context = context_for(node)
13
+ return unless inside?(node, :schema)
14
+
15
+ configured = hierarchy.calls_for(context.class_name).any? do |call|
16
+ call.direct && call.name == :complexity_cost_calculation_mode
17
+ end
18
+ configured ||= hierarchy.class_method_defined?(context.class_name, :complexity_cost_calculation_mode_for)
19
+ return if configured
20
+
21
+ add_offense(node.location, message: "Choose a migration policy for `complexity_cost_calculation_mode`")
22
+ end
23
+ end
24
+
25
+ class DefaultMaxPageSize < Base
26
+ rule id: "GQLM502", safety: :unsafe, summary: "Migrate default_max_page_size to the new page size settings",
27
+ docs: "docs/rules/GQLM502.md"
28
+ node_pattern :call_node, "(call_node receiver: nil name: :default_max_page_size)"
29
+
30
+ def on_match(node, _captures)
31
+ return unless inside?(node, :schema)
32
+
33
+ add_offense(node.location, message: "Review the split into `default_page_size` and `max_page_size`")
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Modernize
5
+ class Runner
6
+ def initialize(hierarchy:, config: Config.new, explicit_rule_ids: [])
7
+ @hierarchy = hierarchy
8
+ @config = config
9
+ @explicit_rule_ids = explicit_rule_ids.map { |id| id.to_s.upcase }.freeze
10
+ end
11
+
12
+ def run(source_file, rules = RuleRegistry.all)
13
+ context = ContextBuilder.new(source_file, @hierarchy)
14
+ rewriter = Astel::Rewriter.new(source_file)
15
+ dispatcher = Astel::Dispatcher.new
16
+ suppression = SuppressionIndex.new(source_file, require_reason: @config.require_suppression_reason)
17
+ edit_rules = {}
18
+ instances = rules.select do |rule|
19
+ @explicit_rule_ids.include?(rule.metadata.id) || @config.rule_enabled?(rule.metadata)
20
+ end
21
+ .map do |rule|
22
+ rule.new(context: context, rewriter: rewriter, config: @config, suppression: suppression,
23
+ hierarchy: @hierarchy, edit_rules: edit_rules)
24
+ end
25
+
26
+ instances.each do |rule|
27
+ rule.class.node_types.each { |type| dispatcher.on(type) { |node| rule.visit(node) } }
28
+ end
29
+ dispatcher.run(source_file.ast)
30
+ [instances.flat_map(&:offenses) + suppression.unused_offenses, rewriter]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Modernize
5
+ class SuppressionIndex
6
+ Directive = Struct.new(:action, :line, :rules, :location, :inline, :valid, :used, keyword_init: true)
7
+ PATTERN = /graphql-modernize:(?<action>disable-file|disable|enable)\b(?<body>.*)/
8
+
9
+ def initialize(source_file, require_reason: false)
10
+ @source_file = source_file
11
+ first_statement = source_file.ast.statements.body.first
12
+ @directives = source_file.comments.filter_map do |comment|
13
+ match = comment.location.slice.match(PATTERN)
14
+ next unless match
15
+
16
+ reason = match[:body].split("--", 2)[1]
17
+ next if require_reason && match[:action] != "enable" && reason.to_s.strip.empty?
18
+
19
+ rules_text = match[:body].split("--", 2).first.strip
20
+ ids = rules_text.split(/[,\s]+/)
21
+ valid_rules = rules_text.empty? || ids.all? { |id| id.match?(/\AGQLM\d{3}\z/) }
22
+ line_start = source_file.line_start(comment.location.start_offset)
23
+ prefix = source_file.source.byteslice(line_start, comment.location.start_offset - line_start)
24
+ inline = !prefix.strip.empty?
25
+ valid_file_position = match[:action] != "disable-file" || (!inline &&
26
+ (!first_statement || comment.location.start_offset < first_statement.location.start_offset))
27
+ Directive.new(action: match[:action].tr("-", "_").to_sym, line: comment.location.start_line,
28
+ rules: rules_text.empty? ? nil : ids.freeze, location: comment.location,
29
+ inline: inline, valid: valid_rules && valid_file_position, used: false)
30
+ end
31
+ end
32
+
33
+ def suppressed?(rule_id, location)
34
+ directives = matching_directives(rule_id, location.start_line)
35
+ file_directive = directives.find { |directive| directive.action == :disable_file }
36
+ if file_directive
37
+ file_directive.used = true
38
+ return true
39
+ end
40
+
41
+ inline = directives.find do |directive|
42
+ directive.inline && directive.action == :disable &&
43
+ directive.line == location.start_line
44
+ end
45
+ if inline
46
+ inline.used = true
47
+ return true
48
+ end
49
+
50
+ active_directive = nil
51
+ directives.reject(&:inline).each do |directive|
52
+ active_directive = directive.action == :disable ? directive : nil
53
+ end
54
+ if active_directive
55
+ active_directive.used = true
56
+ closing = @directives.find do |directive|
57
+ directive.valid && !directive.inline && directive.action == :enable &&
58
+ directive.line > active_directive.line && matches_rule?(directive, rule_id)
59
+ end
60
+ closing.used = true if closing
61
+ end
62
+ !active_directive.nil?
63
+ end
64
+
65
+ def unused_offenses
66
+ @directives.reject(&:used).map do |directive|
67
+ Offense.new(rule_id: "GQLM000", location: directive.location,
68
+ message: "Unused suppression directive", safety: :manual,
69
+ path: @source_file.path, correctable: false)
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def matching_directives(rule_id, line)
76
+ @directives.select do |directive|
77
+ next false unless directive.valid && matches_rule?(directive, rule_id)
78
+
79
+ directive.action == :disable_file || directive.line <= line
80
+ end
81
+ end
82
+
83
+ def matches_rule?(directive, rule_id)
84
+ directive.rules.nil? || directive.rules.include?(rule_id)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ module Modernize
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "modernize/version"
4
+ require "astel"
5
+ require "astel/refactor"
6
+ require "astel/diff"
7
+
8
+ module GraphQL
9
+ module Modernize
10
+ class Error < StandardError; end
11
+ end
12
+ end
13
+
14
+ require_relative "modernize/config"
15
+ require_relative "modernize/file_finder"
16
+ require_relative "modernize/class_hierarchy"
17
+ require_relative "modernize/context_builder"
18
+ require_relative "modernize/offense"
19
+ require_relative "modernize/suppression_index"
20
+ require_relative "modernize/rule_registry"
21
+ require_relative "modernize/rules/base"
22
+ require_relative "modernize/runner"
23
+ require_relative "modernize/application"
24
+ require_relative "modernize/reporter"
25
+ require_relative "modernize/cli"
26
+ require_relative "modernize/rules/legacy"
27
+ require_relative "modernize/rules/relay"
28
+ require_relative "modernize/rules/deprecation"
29
+ require_relative "modernize/rules/modernize"
30
+ require_relative "modernize/rules/schema_config"
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: graphql-modernize
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yudai Takada
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: astel
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0.2'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '1.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0.2'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '1.0'
32
+ description: A Prism-based codemod for migrating deprecated graphql-ruby APIs.
33
+ email:
34
+ - t.yudai92@gmail.com
35
+ executables:
36
+ - graphql-modernize
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - ".rubocop.yml"
41
+ - CHANGELOG.md
42
+ - LICENSE.txt
43
+ - README.md
44
+ - Rakefile
45
+ - docs/migration-guide.md
46
+ - docs/research/changelog-survey.md
47
+ - docs/rules/GQLM101.md
48
+ - docs/rules/GQLM102.md
49
+ - docs/rules/GQLM103.md
50
+ - docs/rules/GQLM104.md
51
+ - docs/rules/GQLM105.md
52
+ - docs/rules/GQLM106.md
53
+ - docs/rules/GQLM107.md
54
+ - docs/rules/GQLM108.md
55
+ - docs/rules/GQLM201.md
56
+ - docs/rules/GQLM202.md
57
+ - docs/rules/GQLM203.md
58
+ - docs/rules/GQLM204.md
59
+ - docs/rules/GQLM205.md
60
+ - docs/rules/GQLM301.md
61
+ - docs/rules/GQLM302.md
62
+ - docs/rules/GQLM303.md
63
+ - docs/rules/GQLM304.md
64
+ - docs/rules/GQLM305.md
65
+ - docs/rules/GQLM306.md
66
+ - docs/rules/GQLM307.md
67
+ - docs/rules/GQLM308.md
68
+ - docs/rules/GQLM309.md
69
+ - docs/rules/GQLM310.md
70
+ - docs/rules/GQLM401.md
71
+ - docs/rules/GQLM402.md
72
+ - docs/rules/GQLM501.md
73
+ - docs/rules/GQLM502.md
74
+ - docs/rules/README.md
75
+ - docs/writing-rules.md
76
+ - exe/graphql-modernize
77
+ - lib/graphql/modernize.rb
78
+ - lib/graphql/modernize/application.rb
79
+ - lib/graphql/modernize/class_hierarchy.rb
80
+ - lib/graphql/modernize/cli.rb
81
+ - lib/graphql/modernize/config.rb
82
+ - lib/graphql/modernize/context_builder.rb
83
+ - lib/graphql/modernize/file_finder.rb
84
+ - lib/graphql/modernize/offense.rb
85
+ - lib/graphql/modernize/reporter.rb
86
+ - lib/graphql/modernize/rule_registry.rb
87
+ - lib/graphql/modernize/rules/base.rb
88
+ - lib/graphql/modernize/rules/deprecation.rb
89
+ - lib/graphql/modernize/rules/legacy.rb
90
+ - lib/graphql/modernize/rules/modernize.rb
91
+ - lib/graphql/modernize/rules/relay.rb
92
+ - lib/graphql/modernize/rules/schema_config.rb
93
+ - lib/graphql/modernize/runner.rb
94
+ - lib/graphql/modernize/suppression_index.rb
95
+ - lib/graphql/modernize/version.rb
96
+ homepage: https://github.com/ydah/graphql-modernize
97
+ licenses:
98
+ - MIT
99
+ metadata:
100
+ rubygems_mfa_required: 'true'
101
+ homepage_uri: https://github.com/ydah/graphql-modernize
102
+ source_code_uri: https://github.com/ydah/graphql-modernize
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: 3.3.0
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubygems_version: 4.0.19
118
+ specification_version: 4
119
+ summary: Modernize deprecated graphql-ruby APIs
120
+ test_files: []