easol-canvas 1.1.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5fe5e6dba572cf93a26d99560c7f36cd3f7f2188c019f5c5bd2429c6b897dd20
4
- data.tar.gz: df34c9f374a73b17f47e4486345d6f6c21f4f184640d72976f664e3bf873e4ee
3
+ metadata.gz: 5cc9522b87ab434f7121dfd557aa987b820c9934486da6c96e8c7706ac4f92b4
4
+ data.tar.gz: e922ef25a0d8a916a82a7b9c4d8b4faf342088f6da65077518d72b68bf64eeab
5
5
  SHA512:
6
- metadata.gz: d42d0a2cecd8040521956b2bb2a7380c08471e1aebfaf162a652b61a532bda67de5426ffba517b75bbc50449ac65eccbc6df78e009103c6ccc5e86874184c732
7
- data.tar.gz: b4590073ce09eef3e258f924b66bfb0f996b88dacfbfbc72c1d0120f8fe96fafc30911015ae31df76836d665efea529db0af9bf3184cd6212747993f8c3a5d1e
6
+ metadata.gz: c33da855f7ad39a4dad8172fc0942c9c60d4af81acf6c677bddb9c55dcacd9759392fa9c24e65910d76228a760bad31c244b865e8f83e52623ffdf26282f99b6
7
+ data.tar.gz: e4d2f3ebae85a1ed14716a1f5514bd3211ae130b973475bbcaf3b52c946cb25db90cdcfbd75143db9984600e3604071d153eb1766e79388e172a2307e09abcfb
@@ -14,11 +14,18 @@ module Canvas
14
14
 
15
15
  def run
16
16
  REQUIRED_FILES.each do |filename|
17
- next unless Dir.glob(filename).empty?
17
+ file_paths = Dir.glob(filename)
18
18
 
19
- @offenses << Offense.new(
20
- message: "Missing file: #{filename}"
21
- )
19
+
20
+ if file_paths.empty?
21
+ @offenses << Offense.new(
22
+ message: "Missing file: #{filename}"
23
+ )
24
+ elsif File.zero?(file_paths.first)
25
+ @offenses << Offense.new(
26
+ message: "Empty file: #{file_paths.first}"
27
+ )
28
+ end
22
29
  end
23
30
  end
24
31
  end
@@ -37,7 +37,8 @@ module Canvas
37
37
 
38
38
  def validate_format(filename, front_matter)
39
39
  return true if front_matter.is_a?(Hash) &&
40
- front_matter.values.all? { |attr| attr.is_a?(Hash) }
40
+ (front_matter.key?("attributes") ? front_matter["attributes"] : front_matter).values.all? { |attr| attr.is_a?(Hash) }
41
+
41
42
 
42
43
  @offenses << Offense.new(
43
44
  message: "Invalid Block Schema: #{filename} - \nSchema is not in a valid format"
@@ -45,8 +46,7 @@ module Canvas
45
46
  false
46
47
  end
47
48
 
48
- def validate_schema(filename, front_matter, custom_types)
49
- schema = extract_schema(front_matter)
49
+ def validate_schema(filename, schema, custom_types)
50
50
  validator = Validator::BlockSchema.new(schema: schema, custom_types: custom_types)
51
51
  return if validator.validate
52
52
 
@@ -62,11 +62,5 @@ module Canvas
62
62
  front_matter = extractor.front_matter
63
63
  front_matter.nil? ? {} : YAML.safe_load(front_matter)
64
64
  end
65
-
66
- def extract_schema(front_matter)
67
- {
68
- "attributes" => Canvas::ExpandAttributes.call(front_matter)
69
- }
70
- end
71
65
  end
72
66
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Canvas
4
+ # :documented:
5
+ #
6
+ # This check will find all files ending in .css, .scss or .sass
7
+ # and run them through the sass validator - {Canvas::Validator::Sass}.
8
+ class ValidSassCheck < Check
9
+ def run
10
+ sass_files.each do |filename|
11
+ file = File.read(filename)
12
+ validator = Validator::Sass.new(file)
13
+
14
+ next if validator.validate
15
+
16
+ validator.errors.each do |message|
17
+ @offenses << Offense.new(
18
+ message: "Invalid Sass: #{filename} - \n#{message}"
19
+ )
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def sass_files
27
+ Dir.glob("**/*.{css,scss,sass}")
28
+ end
29
+ end
30
+ end
@@ -30,6 +30,7 @@ module Canvas
30
30
  # @return [Array<Hash>] array of hashes that represent each attribute
31
31
  def call(attributes_hash)
32
32
  return [] if attributes_hash.nil?
33
+ return attributes_hash if attributes_hash.is_a?(Array)
33
34
 
34
35
  attributes_hash.each_with_object([]) do |(name, attribute_hash), attrs|
35
36
  attrs << attribute_hash.merge("name" => name)
@@ -23,21 +23,24 @@ module Canvas
23
23
  # ]
24
24
  # }
25
25
  class BlockSchema
26
- PERMITTED_KEYS = %w[attributes].freeze
26
+ PERMITTED_KEYS = %w[attributes layout].freeze
27
27
 
28
28
  attr_reader :errors, :schema
29
29
 
30
30
  # @param schema [Hash] the schema to be validated
31
31
  # @param custom_types [Array<Hash>] a list of custom types
32
32
  def initialize(schema:, custom_types: [])
33
- @schema = schema
33
+ @schema = normalize_schema(schema)
34
34
  @custom_types = custom_types
35
35
  @errors = []
36
36
  end
37
37
 
38
38
  def validate
39
+ @errors = []
40
+
39
41
  if ensure_valid_format
40
42
  ensure_no_unrecognized_keys
43
+ ensure_layout_is_valid
41
44
  ensure_attributes_are_valid
42
45
  end
43
46
 
@@ -54,6 +57,16 @@ module Canvas
54
57
  false
55
58
  end
56
59
 
60
+ def ensure_layout_is_valid
61
+ return true unless schema["layout"]
62
+
63
+ layout_validator = LayoutSchema.new(schema: @schema)
64
+ return true if layout_validator.validate
65
+
66
+ @errors += layout_validator.errors
67
+ false
68
+ end
69
+
57
70
  def ensure_no_unrecognized_keys
58
71
  unrecognized_keys = schema.keys - PERMITTED_KEYS
59
72
  return true if unrecognized_keys.empty?
@@ -81,6 +94,19 @@ module Canvas
81
94
  schema["attributes"].is_a?(Array) &&
82
95
  schema["attributes"].all? { |attr| attr.is_a?(Hash) }
83
96
  end
97
+
98
+ def normalize_schema(schema)
99
+ if schema.key?("attributes")
100
+ {
101
+ **schema,
102
+ "attributes" => Canvas::ExpandAttributes.call(schema["attributes"])
103
+ }
104
+ else
105
+ {
106
+ "attributes" => Canvas::ExpandAttributes.call(schema)
107
+ }
108
+ end
109
+ end
84
110
  end
85
111
  end
86
112
  end
@@ -38,6 +38,7 @@ module Canvas
38
38
  ensure_has_required_keys &&
39
39
  ensure_no_unrecognized_keys &&
40
40
  ensure_keys_are_correct_types &&
41
+ ensure_key_value_is_not_reserved &&
41
42
  ensure_no_duplicate_attributes &&
42
43
  ensure_attributes_are_valid &&
43
44
  ensure_first_attribute_not_array
@@ -91,6 +92,17 @@ module Canvas
91
92
  true
92
93
  end
93
94
 
95
+ # Ensuring the value for key doesn't clash with our primitive type keys.
96
+ # See {Canvas::Constants::PRIMITIVE_TYPES}
97
+ def ensure_key_value_is_not_reserved
98
+ if schema["key"] && Constants::PRIMITIVE_TYPES.include?(schema["key"].downcase)
99
+ @errors << "\"key\" can't be one of these reserved words: #{Constants::PRIMITIVE_TYPES.join(', ')}"
100
+ return false
101
+ end
102
+
103
+ true
104
+ end
105
+
94
106
  def ensure_no_duplicate_attributes
95
107
  attribute_names = schema["attributes"].map { |a| a["name"] }
96
108
  duplicated_names = attribute_names.select { |a| attribute_names.count(a) > 1 }.uniq
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "json-schema"
5
+
6
+ module Canvas
7
+ module Validator
8
+ # :documented:
9
+ # This class is used to validate a layout definition, part of block schema.
10
+ # Example of a valid layout definition:
11
+ # {
12
+ # "layout" => [
13
+ # {
14
+ # "label" => "Design",
15
+ # "type" => "tab",
16
+ # "elements" => [
17
+ # "heading",
18
+ # {
19
+ # "type" => "accordion",
20
+ # "label" => "Logo",
21
+ # "elements" => [
22
+ # "description",
23
+ # { "type" => "attribute", "name" => "logo_alt" },
24
+ # "title"
25
+ # ]
26
+ # }
27
+ # ]
28
+ # }]
29
+ # }
30
+ class LayoutSchema
31
+ attr_reader :errors
32
+
33
+ def initialize(schema:)
34
+ @schema = schema
35
+ @errors = []
36
+ end
37
+
38
+ def validate
39
+ @errors = []
40
+
41
+ if ensure_valid_format
42
+ ensure_no_unrecognized_keys
43
+ ensure_no_duplicate_keys
44
+ end
45
+
46
+ @errors.empty?
47
+ end
48
+
49
+ private
50
+
51
+ def ensure_no_duplicate_keys
52
+ attributes = gather_attributes_from_layout_schema
53
+ duplicates =
54
+ attributes
55
+ .group_by { |(key)| key }
56
+ .filter { |key, usage| usage.size > 1 }
57
+
58
+ unless duplicates.empty?
59
+ duplicates.each do |attribute, usage|
60
+ @errors << "Duplicated attribute key `#{attribute}` found. Location: #{usage.map { |(_, location)| location }.join(", ")}"
61
+ end
62
+ end
63
+ end
64
+
65
+ def ensure_no_unrecognized_keys
66
+ attributes = gather_attributes_from_layout_schema
67
+ defined_attributes = @schema["attributes"]&.map { |definition| normalize_attribute(definition["name"]) } || []
68
+
69
+ attributes.each do |attribute, location|
70
+ @errors << "Unrecognized attribute `#{attribute}`. Location: #{location}" unless defined_attributes.include?(attribute)
71
+ end
72
+ end
73
+
74
+ def gather_attributes_from_layout_schema
75
+ attribute_keys = []
76
+
77
+ fetch_attribute_type = ->(node, path) {
78
+ if node.is_a?(Hash) && node.key?("elements")
79
+ node["elements"].each_with_index do |element, i|
80
+ current_path = "#{path}/elements/#{i}"
81
+ fetch_attribute_type.call(element, current_path)
82
+ end
83
+ else
84
+ attribute_keys << [
85
+ normalize_attribute(node.is_a?(Hash) ? node["name"] : node),
86
+ path
87
+ ]
88
+ end
89
+ }
90
+
91
+ layout_schema.each_with_index do |tab, i|
92
+ current_path = "layout/#{i}"
93
+ fetch_attribute_type.call(tab, current_path)
94
+ end
95
+
96
+ attribute_keys
97
+ end
98
+
99
+ def ensure_valid_format
100
+ result = JSON::Validator.fully_validate(schema_definition, { "layout" => layout_schema }, strict: true, clear_cache: true)
101
+
102
+ return true if result.empty?
103
+
104
+ @errors += result
105
+ false
106
+ end
107
+
108
+ def layout_schema
109
+ @schema["layout"] || []
110
+ end
111
+
112
+ def schema_definition
113
+ File.read(
114
+ File.join(File.dirname(__FILE__), "../../../", "schema_definitions", "block_layout.json")
115
+ )
116
+ end
117
+
118
+ def normalize_attribute(name)
119
+ name.strip.downcase
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sassc"
4
+
5
+ module Canvas
6
+ module Validator
7
+ # :documented:
8
+ #
9
+ # This validator can be used to validate Sass.
10
+ class Sass
11
+ attr_reader :errors
12
+
13
+ def initialize(file)
14
+ @file = file
15
+ end
16
+
17
+ def validate
18
+ SassC::Engine.new(@file, style: :compressed).render
19
+ true
20
+ rescue SassC::SyntaxError => e
21
+ @errors = [e.message]
22
+ false
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Canvas
4
- VERSION = "1.1.0"
4
+ VERSION = "1.4.1"
5
5
  end
data/lib/canvas.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "canvas/version"
4
- require_relative "canvas/constants"
5
- require_relative "canvas/cli"
6
- require_relative "canvas/lint"
7
- require_relative "canvas/check"
8
- require_relative "canvas/offense"
9
- require_relative "canvas/checks"
3
+ module Canvas
4
+ autoload :Check, "canvas/check"
5
+ autoload :Checks, "canvas/checks"
6
+ autoload :Cli, "canvas/cli"
7
+ autoload :Constants, "canvas/constants"
8
+ autoload :Lint, "canvas/lint"
9
+ autoload :Offense, "canvas/offense"
10
+ autoload :Version, "canvas/version"
11
+ end
10
12
 
11
13
  # We need to ensure Canvas::Validator::SchemaAttribute::Base is required first
12
14
  require_relative "canvas/validators/schema_attributes/base"
@@ -26,10 +28,8 @@ Canvas::Checks.register(Canvas::RequiredFilesCheck)
26
28
  Canvas::Checks.register(Canvas::ValidHtmlCheck)
27
29
  Canvas::Checks.register(Canvas::ValidLiquidCheck)
28
30
  Canvas::Checks.register(Canvas::ValidJsonCheck)
31
+ Canvas::Checks.register(Canvas::ValidSassCheck)
29
32
  Canvas::Checks.register(Canvas::ValidBlockSchemasCheck)
30
33
  Canvas::Checks.register(Canvas::ValidMenuSchemaCheck)
31
34
  Canvas::Checks.register(Canvas::ValidFooterSchemaCheck)
32
35
  Canvas::Checks.register(Canvas::ValidCustomTypesCheck)
33
-
34
- module Canvas
35
- end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Enables using this gem "as is"
4
+ require_relative "../canvas"
@@ -0,0 +1,67 @@
1
+ {
2
+ "type": "object",
3
+ "properties": {
4
+ "layout": {
5
+ "type": "array",
6
+ "items": { "$ref": "#/$defs/tab" }
7
+ }
8
+ },
9
+ "$defs": {
10
+ "tab": {
11
+ "type": "object",
12
+ "required": ["type", "label"],
13
+ "properties": {
14
+ "type": {
15
+ "type": "string",
16
+ "const": "tab"
17
+ },
18
+ "label": {
19
+ "type": "string"
20
+ },
21
+ "elements": {
22
+ "type": "array",
23
+ "minItems": 1,
24
+ "items": {
25
+ "oneOf": [
26
+ { "type": "string" },
27
+ { "$ref": "#/$defs/component" },
28
+ { "$ref": "#/$defs/attribute" }
29
+ ]
30
+ }
31
+ }
32
+ }
33
+ },
34
+ "component": {
35
+ "type": "object",
36
+ "required": ["label", "type"],
37
+ "properties": {
38
+ "type": {
39
+ "type": "string",
40
+ "enum": ["accordion"]
41
+ },
42
+ "label": {
43
+ "type": "string"
44
+ },
45
+ "elements": {
46
+ "type": "array",
47
+ "items": {
48
+ "oneOf": [{ "type": "string" }, { "$ref": "#/$defs/attribute" }]
49
+ }
50
+ }
51
+ }
52
+ },
53
+ "attribute": {
54
+ "type": "object",
55
+ "required": ["name", "type"],
56
+ "properties": {
57
+ "type": {
58
+ "type": "string",
59
+ "const": "attribute"
60
+ },
61
+ "name": {
62
+ "type": "string"
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easol-canvas
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyle Byrne
8
8
  - Ian Mooney
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-06-01 00:00:00.000000000 Z
12
+ date: 2022-07-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thor
@@ -67,6 +67,34 @@ dependencies:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
69
  version: '5.3'
70
+ - !ruby/object:Gem::Dependency
71
+ name: sassc
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '2.4'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '2.4'
84
+ - !ruby/object:Gem::Dependency
85
+ name: json-schema
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '3'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '3'
70
98
  description: |
71
99
  Canvas is a command line tool to help with building themes for Easol.
72
100
  It provides tooling to check theme directories for errors and to make sure
@@ -90,6 +118,7 @@ files:
90
118
  - lib/canvas/checks/valid_json_check.rb
91
119
  - lib/canvas/checks/valid_liquid_check.rb
92
120
  - lib/canvas/checks/valid_menu_schema_check.rb
121
+ - lib/canvas/checks/valid_sass_check.rb
93
122
  - lib/canvas/cli.rb
94
123
  - lib/canvas/constants.rb
95
124
  - lib/canvas/lint.rb
@@ -102,8 +131,10 @@ files:
102
131
  - lib/canvas/validators/footer_schema.rb
103
132
  - lib/canvas/validators/html.rb
104
133
  - lib/canvas/validators/json.rb
134
+ - lib/canvas/validators/layout_schema.rb
105
135
  - lib/canvas/validators/liquid.rb
106
136
  - lib/canvas/validators/menu_schema.rb
137
+ - lib/canvas/validators/sass.rb
107
138
  - lib/canvas/validators/schema_attribute.rb
108
139
  - lib/canvas/validators/schema_attributes/base.rb
109
140
  - lib/canvas/validators/schema_attributes/color.rb
@@ -118,11 +149,13 @@ files:
118
149
  - lib/canvas/validators/schema_attributes/select.rb
119
150
  - lib/canvas/validators/schema_attributes/variant.rb
120
151
  - lib/canvas/version.rb
152
+ - lib/easol/canvas.rb
153
+ - schema_definitions/block_layout.json
121
154
  homepage: https://rubygems.org/gems/easol-canvas
122
155
  licenses:
123
156
  - MIT
124
157
  metadata: {}
125
- post_install_message:
158
+ post_install_message:
126
159
  rdoc_options: []
127
160
  require_paths:
128
161
  - lib
@@ -137,8 +170,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
170
  - !ruby/object:Gem::Version
138
171
  version: '0'
139
172
  requirements: []
140
- rubygems_version: 3.1.6
141
- signing_key:
173
+ rubygems_version: 3.2.33
174
+ signing_key:
142
175
  specification_version: 4
143
176
  summary: CLI to help with building themes for Easol
144
177
  test_files: []