trmnl_preview 0.14.1 → 0.14.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fbe879abcadc7c6f7efceb9e54c88606aafd090a9538c9942f8cd3afef9596da
4
- data.tar.gz: d9a123366e021a55b8bdc5b55016e38f1f1b4107163bc570a5112d89c8084270
3
+ metadata.gz: 2dabcf8ddd9b11d8ce8fc76e389820c7cd4f2d3b48db7aa7848896f9dfb6ec59
4
+ data.tar.gz: 467322fc910fdfed1ed5c37bb9773a7256d3d8f1f44683e2c9e4f3882b4ab7df
5
5
  SHA512:
6
- metadata.gz: 0cd3eb90f4a2231a28dfe8e08097a90bc0d70d93861f9decd1946708b77c0ddf4b5ba3e3dd1acbae0b1daac7238cb88ea74d9f504880adeb1ee7c6ac6402c774
7
- data.tar.gz: fbd799614df7d3c978ca2a67fb1b7916b446994bfbd15ce268f764ad0dbd62fed7a34f6c8e63621926490a3eabece74d44b089f3175bd0ad305de7958cf622a1
6
+ metadata.gz: 75747b93b8aa45ac964b1ae87ef0dd50134c836bf5bc4bef19e47f166132b1dfca6edf8744f967702be6ffdc4c80c7b9b725646443053708f2d74df37a8537e9
7
+ data.tar.gz: b6fa8f755ab31f3dae9a3c2773ced039f646c7363288ae27512d0e3a95e72143093ca2869cb26a402084dd1b66c6c2dfc29a022ef8fed84bbb6e18fa5ffed347
data/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
 
2
2
  # Changelog
3
3
 
4
+ ## 0.14.2
5
+
6
+ - A failed transform prints its stderr once. The `transform failed:` line repeated the whole stack trace that the `transform stderr:` line had just printed; it now names only the exit code. The preview page still shows the full error. (#148)
7
+ - When a polling URL answers 401 and an OAuth account is connected, trmnlp refreshes the token and polls once more, as the hosted service does. Before, it refreshed only once the stored expiry had passed, so a token the provider revoked early kept failing until you reconnected. (#150)
8
+ - A polling response that is not a 200 still reaches the markup and the transform, as on the hosted service, with a warning for a status outside 2xx. trmnlp replaced it with `{}`, so a transform that follows a 202 "pending job" body (`job_id`, `poll`) got nothing locally. (#146)
9
+ - `trmnlp lint` reports bracketed Framework classes the Framework never generates, such as `w--[192px]` (sizes stop at 128px), `gap--[60px]` and `rounded--[51px]` (both stop at 50px), `h--[6cqw]` (height takes `cqh`) and, from 3.2.0, a screen-prefixed `md:gap--[20px]`. They match no CSS rule, so they do nothing: a flag sized `lg:w--[192px]` stayed at its smaller width on the X with no warning. The ranges are checked against every 3.x `plugins.css`, and plugins on Framework 2 or earlier are skipped, since v2 generated wider sizes. (#149)
10
+
4
11
  ## 0.14.1
5
12
 
6
13
  - `trmnlp lint` reports a `recipe_overview` under 100 words. Below that, TRMNL serves the public recipe page with `robots: noindex`, so the recipe never appears in Google or other search engines, and nothing said so: the page looked fine, and only its HTML gave it away. A blank overview still passes, since private plugins have none. (#144)
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../check'
4
+
5
+ module TRMNLP
6
+ module Lint
7
+ module Checks
8
+ # Reports bracketed Framework classes the Framework never generates, one
9
+ # finding per class. They match no rule, so they do nothing, and nothing
10
+ # says so: `lg:w--[192px]` leaves the element at its smaller width.
11
+ #
12
+ # The ranges mirror the loops in the Framework's utilities/_size.scss,
13
+ # _gap.scss and _rounded.scss, and hold for every 3.x release's
14
+ # plugins.css. v2 generated wider sizes, so earlier versions are skipped;
15
+ # prefixed gaps (`md:gap--[20px]`) existed until 3.2.0. There is no
16
+ # upstream file to sync from, so update this by hand when the Framework
17
+ # changes, the same way db/data/form_fields.yml is kept current.
18
+ class ArbitraryValuesInRange < Check
19
+ # Each family as written before the bracket: `w--[Npx]`, `w--max-[Npx]`.
20
+ SIZE_FAMILIES = %w[w-- h-- w--min- w--max- h--min- h--max-].freeze
21
+
22
+ # family => { unit => max }. Every range starts at 0 and steps by 1.
23
+ RANGES = SIZE_FAMILIES.to_h do |family|
24
+ [family, { 'px' => 128, (family.start_with?('w') ? 'cqw' : 'cqh') => 100 }]
25
+ end.merge('gap--' => { 'px' => 50 }, 'rounded--' => { 'px' => 50 }).freeze
26
+
27
+ # `gap--[Npx]` and `rounded--[Npx]` exist only without a screen prefix.
28
+ PREFIXABLE = SIZE_FAMILIES
29
+
30
+ FIRST_VERSION = Gem::Version.new('3.0.0')
31
+ UNPREFIXED_GAP_VERSION = Gem::Version.new('3.2.0')
32
+
33
+ PATTERN = /
34
+ (?<![\w:-])
35
+ (?<prefix>(?:[a-z0-9-]+:)*)
36
+ (?<family>w--min-|w--max-|h--min-|h--max-|w--|h--|gap--|rounded--)
37
+ \[(?<value>[\d.]+)(?<unit>[a-z]+)\]
38
+ /x
39
+
40
+ def issues
41
+ return [] if version < FIRST_VERSION
42
+
43
+ source.all_markup.scan(PATTERN).uniq.filter_map do |prefix, family, value, unit|
44
+ css_class = "#{prefix}#{family}[#{value}#{unit}]"
45
+ reason = problem(prefix, family, value, unit)
46
+ { message: "'#{css_class}' has no effect: #{reason}." } if reason
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def version = @version ||= Gem::Version.new(source.framework_version.number)
53
+
54
+ def problem(prefix, family, value, unit)
55
+ max = RANGES.dig(family, unit)
56
+ return "the Framework has no '#{unit}' values for '#{family}'" unless max
57
+ return "'#{family}[Npx]' takes no screen prefix" if prefix_unsupported?(prefix, family)
58
+ return nil if value.match?(/\A\d+\z/) && value.to_i <= max
59
+
60
+ "the Framework generates '#{family}[N#{unit}]' from 0 to #{max} in whole numbers"
61
+ end
62
+
63
+ def prefix_unsupported?(prefix, family)
64
+ !prefix.empty? && !PREFIXABLE.include?(family) &&
65
+ (family == 'rounded--' || version >= UNPREFIXED_GAP_VERSION)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -18,6 +18,7 @@ module TRMNLP
18
18
  def plugin_name = settings['name'].to_s
19
19
  def plugin_description = settings['description'].to_s
20
20
  def recipe_overview = settings['recipe_overview'].to_s
21
+ def framework_version = config.plugin.framework_version
21
22
  def settings = config.plugin.settings
22
23
  def custom_field_values = config.project.custom_fields
23
24
  def custom_field_definitions = config.plugin.custom_field_definitions
data/lib/trmnlp/lint.rb CHANGED
@@ -11,6 +11,7 @@ require_relative 'lint/checks/no_async_functions'
11
11
  require_relative 'lint/checks/waits_for_dom_load'
12
12
  require_relative 'lint/checks/limited_inline_styles'
13
13
  require_relative 'lint/checks/no_size_classes'
14
+ require_relative 'lint/checks/arbitrary_values_in_range'
14
15
  require_relative 'lint/checks/no_opacity'
15
16
  require_relative 'lint/checks/highcharts_animations_disabled'
16
17
  require_relative 'lint/checks/highcharts_elements_unique'
@@ -32,6 +33,7 @@ module TRMNLP
32
33
  Checks::WaitsForDomLoad,
33
34
  Checks::LimitedInlineStyles,
34
35
  Checks::NoSizeClasses,
36
+ Checks::ArbitraryValuesInRange,
35
37
  Checks::NoOpacity,
36
38
  Checks::HighchartsAnimationsDisabled,
37
39
  Checks::HighchartsElementsUnique,
@@ -45,6 +45,12 @@ module TRMNLP
45
45
 
46
46
  def disconnect = token_store.clear
47
47
 
48
+ # For a token the provider rejected before its expiry. nil when there is nothing to refresh.
49
+ def force_refresh!
50
+ stored = token_store.read
51
+ refreshed(stored) if stored&.access_token && stored.refresh_token
52
+ end
53
+
48
54
  private
49
55
 
50
56
  attr_reader :provider, :token_store, :client
data/lib/trmnlp/poller.rb CHANGED
@@ -43,21 +43,28 @@ module TRMNLP
43
43
  attr_reader :config, :paths, :oauth_session, :reporter
44
44
 
45
45
  def aggregate_responses
46
- # Resolve once per poll (it refreshes as a side effect), then share across
47
- # every URL, header, and body render.
48
- oauth_variables = oauth_session.liquid_variables
49
- urls = config.plugin.polling_urls(extra_variables: oauth_variables)
50
- responses = urls.map { |url| fetch_one(url, oauth_variables) }
46
+ fetched = fetch_all
47
+ # Like the hosted service: a token rejected before its expiry gets one refresh and retry.
48
+ fetched = fetch_all if fetched.any? { |_url, response| response.status == 401 } && oauth_session.force_refresh!
49
+ responses = fetched.map { |url, response| parse_response(response, url) }
51
50
  return responses.first if responses.size == 1
52
51
 
53
52
  responses.each_with_index.with_object({}) { |(r, i), h| h["IDX_#{i}"] = r }
54
53
  end
55
54
 
55
+ def fetch_all
56
+ # Resolve once per attempt (it refreshes as a side effect), then share across
57
+ # every URL, header, and body render.
58
+ oauth_variables = oauth_session.liquid_variables
59
+ urls = config.plugin.polling_urls(extra_variables: oauth_variables)
60
+ urls.map { |url| [url, fetch_one(url, oauth_variables)] }
61
+ end
62
+
56
63
  def fetch_one(url, oauth_variables)
57
64
  verb = config.plugin.polling_verb.upcase
58
65
  response = perform_request(url, verb, oauth_variables)
59
66
  reporter.info("#{verb} #{url} — received #{response.body.length} bytes (#{response.status} status)")
60
- parse_response(response)
67
+ response
61
68
  end
62
69
 
63
70
  def perform_request(url, verb, oauth_variables)
@@ -69,17 +76,13 @@ module TRMNLP
69
76
  end
70
77
  end
71
78
 
72
- def parse_response(response)
73
- return parse_failure(response.body) unless response.status == 200
74
-
79
+ # Like the hosted service: an error status is reported, but its body still reaches the data.
80
+ def parse_response(response, url)
81
+ success = (200..299).cover?(response.status)
82
+ reporter.info(reporter.yellow("warning: HTTP #{response.status} from #{url}")) unless success
75
83
  parse_body(response.body, response.headers['content-type'])
76
84
  end
77
85
 
78
- def parse_failure(body)
79
- reporter.info(body)
80
- {}
81
- end
82
-
83
86
  def parse_body(body, content_type_header)
84
87
  content_type = content_type_header&.split(';')&.first&.strip
85
88
  case content_type
@@ -55,7 +55,7 @@ module TRMNLP
55
55
 
56
56
  def record_failure(result, fallback)
57
57
  @error = result.error || "transform exited #{result.exit_code}: #{result.stderr.strip}"
58
- reporter.info("transform failed: #{@error}")
58
+ reporter.info("transform failed: #{result.error || "exited #{result.exit_code}"}")
59
59
  fallback
60
60
  end
61
61
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TRMNLP
4
- VERSION = '0.14.1'
4
+ VERSION = '0.14.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trmnl_preview
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.1
4
+ version: 0.14.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rockwell Schrock
@@ -304,6 +304,7 @@ files:
304
304
  - lib/trmnlp/image_quantizer.rb
305
305
  - lib/trmnlp/lint.rb
306
306
  - lib/trmnlp/lint/check.rb
307
+ - lib/trmnlp/lint/checks/arbitrary_values_in_range.rb
307
308
  - lib/trmnlp/lint/checks/custom_fields_used.rb
308
309
  - lib/trmnlp/lint/checks/description_length.rb
309
310
  - lib/trmnlp/lint/checks/form_fields_valid.rb