backpack 0.4.2 → 0.4.4

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: 44c0421edb61a91faa198193efbcffd57558eaf057464f1ced1311d2488634d8
4
- data.tar.gz: 7fa25d424958ab6b2c746e675160736dd5bdac34f96b325019377d2022669f1b
3
+ metadata.gz: 5734904d7d161fa9097deef8000ec5f21b7cf8ed6f425b11072980287b890cb0
4
+ data.tar.gz: 3e8f346c3815541434a3894c8445ea283094a2a89565ba3fff84317030c8e43e
5
5
  SHA512:
6
- metadata.gz: 03e451b6c6fee7247f0723de93e2065d2ff1a690a12bf7576285e872c038ff6c38fd3f641aa2c92c040a45d4afacd523c7ef31ad194f63c45dcbe3a2adebd5ba
7
- data.tar.gz: 6e741f04048aec8d997c4edfe10592e42664c97ceb9d00ac4c409453f10ff04b42cdd218b5ce055dbd359c53878171bf695229d34c625a9379086ec0c3f08571
6
+ metadata.gz: 9f60ec9fa255a439fd864f8b63ff5f3ead905cd25bc83fc5765fb55beac0c61a81f01dc1061003d3841e2d6d6a939f55787d84af7d8d55e8434974dd5f29079d
7
+ data.tar.gz: 2a18e3f53810b4f0b477e76e8f9b349566c340282570c5f8171e6b8a10b3bf42af79d7d47b3e4ec70d46ac3be73093c04b0a0ec69c0f1f9cdf6a3d75a086b5f5
data/README.md CHANGED
@@ -2,23 +2,26 @@
2
2
 
3
3
  ## Getting started
4
4
 
5
+ Install Backpack globally, then import it in your Rails project.
6
+
5
7
  ```bash
6
8
  gem install backpack
9
+
10
+ # At your Rails project root:
7
11
  backpack import
8
- gem uninstall backpack
9
12
  ```
10
13
 
11
14
  ## Usage
12
15
 
13
16
  ### Configure colors tokens
14
17
 
15
- Open `lib/backpack/stylesheets/tokens/_colors.scss` and follow the instructions.
18
+ Open `app/assets/stylesheets/tokens/_colors.scss` and follow the instructions.
16
19
 
17
20
  ### Sync tokens with Figma
18
21
 
19
22
  1. Use [Export/Import variables](https://www.figma.com/community/plugin/1256972111705530093/export-import-variables) plugin to export the Backpack group.
20
23
  2. Save `Backpack.json` in the `figma/` directory.
21
- 3. Run `bin/sync`
24
+ 3. Run `backpack sync`
22
25
 
23
26
  ## Development
24
27
 
data/bin/backpack ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "backpack/cli"
5
+
6
+ Backpack::CLI.start
data/demo/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- backpack (0.4.2)
4
+ backpack (0.4.4)
5
5
  literal (~> 1.8)
6
6
  phlex-rails (~> 2.3)
7
7
  rails
data/lib/backpack/cli.rb CHANGED
@@ -1,17 +1,20 @@
1
1
  require "thor"
2
2
  require "backpack"
3
+ require "json"
4
+ require_relative "commands/sync"
3
5
 
4
6
  module Backpack
5
7
  class CLI < Thor
6
8
  include Thor::Actions
7
9
 
8
- desc "import", "Import Backpack in the current project"
9
-
10
10
  source_root File.expand_path(__dir__)
11
11
 
12
+ desc "import", "Import Backpack in the current project"
12
13
  def import
13
14
  say ascii_art, :green
14
15
 
16
+ add_radix_colors_dependency if File.exist?("package.json")
17
+
15
18
  directory "stylesheets", "app/assets/stylesheets", recursive: true
16
19
  directory "components", "app/components", recursive: true
17
20
 
@@ -23,10 +26,44 @@ module Backpack
23
26
  directory "../../spec/components", "spec/components", recursive: true do |content|
24
27
  content.gsub("require 'spec_helper'", "require 'rails_helper'")
25
28
  end
29
+ directory "../../spec/fixtures/icons", "spec/fixtures/icons", recursive: true
30
+
31
+ copy_file "../../vendor/normalize.css", "vendor/normalize.css"
32
+ end
33
+
34
+ desc "sync", "Sync design tokens from Figma to CSS files"
35
+ def sync
36
+ Backpack::Commands::Sync.new(shell).sync
37
+ end
38
+
39
+ def self.exit_on_failure?
40
+ true
26
41
  end
27
42
 
28
43
  private
29
44
 
45
+ def add_radix_colors_dependency
46
+ package_json_path = "package.json"
47
+
48
+ # Read existing package.json or create minimal structure
49
+ if File.exist?(package_json_path) && !File.zero?(package_json_path)
50
+ begin
51
+ package_data = JSON.parse(File.read(package_json_path))
52
+ rescue JSON::ParserError
53
+ package_data = {}
54
+ end
55
+ else
56
+ package_data = {}
57
+ end
58
+
59
+ # Add the dependency
60
+ package_data["dependencies"] ||= {}
61
+ package_data["dependencies"]["@radix-ui/colors"] = "^3.0.0"
62
+
63
+ # Write back to file
64
+ File.write(package_json_path, "#{JSON.pretty_generate(package_data)}\n")
65
+ end
66
+
30
67
  def ascii_art
31
68
  <<~ASCII
32
69
  ┌┐ ┌─┐┌─┐┬┌─┌─┐┌─┐┌─┐┬┌─
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'date'
5
+ require 'thor'
6
+
7
+ module Backpack
8
+ module Commands
9
+ class Sync
10
+ include Thor::Actions
11
+
12
+ def initialize(shell = Thor::Base.shell.new)
13
+ @shell = shell
14
+ end
15
+
16
+ def sync
17
+ @shell.say "Syncing design tokens from Figma...", :green
18
+
19
+ layout_json = File.read(layout_tokens_file)
20
+ layout = JSON.parse(layout_json, symbolize_names: true)
21
+
22
+ raise "Wrong name" if layout[:name] != NAME
23
+
24
+ modes = layout[:modes]
25
+ tokens = extract_tokens(layout[:variables], modes)
26
+
27
+ # Read all CSS files
28
+ tokens_css_files = Dir.glob("#{tokens_folder}/*.css")
29
+
30
+ rewritten_lines_count = 0
31
+ remaining_tokens = tokens.keys
32
+
33
+ tokens_css_files.each do |file|
34
+ lines_rewritten = process_css_file(file, tokens, remaining_tokens)
35
+ rewritten_lines_count += lines_rewritten
36
+
37
+ if lines_rewritten > 0
38
+ file_name = File.basename(file)
39
+ @shell.say "#{file_name}: #{lines_rewritten} lines rewritten", :yellow
40
+ end
41
+ end
42
+
43
+ if remaining_tokens.any?
44
+ @shell.say ""
45
+ @shell.say "#{remaining_tokens.size} tokens have not been found:", :red
46
+ remaining_tokens.each do |token|
47
+ @shell.say token
48
+ end
49
+ @shell.say "Please check that these tokens are correctly named."
50
+ else
51
+ @shell.say "All tokens synced successfully!", :green
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ NAME = "Backpack".freeze
58
+ CSS_VARIABLE_REGEXP = /var\(--([^:]+)\)/
59
+ CSS_LINE_REGEXP = /--([^:]+): ([^;]+);(.*)/
60
+ APPEND_PX = proc { |value| "#{value}px" }
61
+ APPEND_REM = proc { |value| "#{value}rem" }
62
+ CONVERT_TO_REM = proc { |value| "#{value.to_f / 10.0}rem".gsub(".0rem", "rem") }
63
+ CONVERSION_RULES = {
64
+ /(font|height|radius|space)-(\d+|base|full)/ => CONVERT_TO_REM,
65
+ /(breakpoint)-(.+)/ => APPEND_PX,
66
+ /container-width/ => APPEND_REM
67
+ }
68
+
69
+ def layout_tokens_file
70
+ ENV['BACKPACK_TEST_FIGMA_PATH'] || File.expand_path("../../../figma/#{NAME}.json", __dir__)
71
+ end
72
+
73
+ def tokens_folder
74
+ ENV['BACKPACK_TEST_TOKENS_PATH'] || File.expand_path("../stylesheets/tokens", __dir__)
75
+ end
76
+
77
+ def normalized_figma_variable(name)
78
+ name.split('/').last.downcase.gsub(' ', '-')
79
+ end
80
+
81
+ def extract_tokens(variables, modes)
82
+ tokens = {}
83
+
84
+ variables.each do |variable|
85
+ name = if (css_name = variable.dig(:codeSyntax, :WEB))
86
+ CSS_VARIABLE_REGEXP.match(css_name)[1]
87
+ else
88
+ normalized_figma_variable(variable[:name])
89
+ end
90
+
91
+ value = variable[:resolvedValuesByMode][modes.keys[0]]
92
+
93
+ final_value = if value[:aliasName]
94
+ "var(--#{normalized_figma_variable(value[:aliasName])})"
95
+ else
96
+ apply_conversion_rules(name, value[:resolvedValue])
97
+ end
98
+
99
+ tokens[name] = final_value
100
+ end
101
+
102
+ tokens
103
+ end
104
+
105
+ def apply_conversion_rules(name, value)
106
+ CONVERSION_RULES.each do |pattern, conversion|
107
+ if pattern.match?(name)
108
+ return conversion.call(value)
109
+ end
110
+ end
111
+ value
112
+ end
113
+
114
+ def process_css_file(file, tokens, remaining_tokens)
115
+ lines = []
116
+ lines_rewritten = 0
117
+
118
+ File.open(file, 'r') do |css_file|
119
+ css_file.each_line do |line|
120
+ variable = line.match(CSS_LINE_REGEXP)
121
+
122
+ unless variable
123
+ lines << line
124
+ next
125
+ end
126
+
127
+ token_name = variable[1]
128
+ token_value = tokens[token_name]
129
+
130
+ unless token_value
131
+ lines << line
132
+ next
133
+ end
134
+
135
+ rewritten_line = line.gsub(CSS_LINE_REGEXP) do |match|
136
+ "--#{variable[1]}: #{token_value}; /* imported from Figma on #{Date.today} */"
137
+ end
138
+
139
+ lines_rewritten += 1
140
+ remaining_tokens.delete(token_name)
141
+ lines << rewritten_line
142
+ end
143
+ end
144
+
145
+ if lines_rewritten > 0
146
+ File.write(file, lines.join)
147
+ end
148
+
149
+ lines_rewritten
150
+ end
151
+ end
152
+ end
153
+ end
@@ -133,8 +133,8 @@
133
133
 
134
134
  /* ghost */
135
135
  .variant-ghost {
136
- color: var(--accent-9);
137
136
  background-color: transparent;
137
+ color: var(--accent-9);
138
138
  }
139
139
 
140
140
  @media (hover: hover) {
@@ -1,19 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rails-html-sanitizer'
3
+ require "rails-html-sanitizer"
4
4
 
5
5
  class Components::IconSprite < Components::Base
6
6
  ICON_PATHS = [
7
7
  Rails.root&.join("app/assets/icons"),
8
- Rails.root&.join("node_modules/lucide-static/icons"),
9
- File.expand_path("../../../spec/fixtures/icons", __dir__)
8
+ Rails.root&.join("node_modules/lucide-static/icons")
10
9
  ].compact.freeze
11
10
 
12
11
  def view_template
13
12
  svg(**mix({ class: root_classes }, @attributes, hidden: true)) do |s|
14
13
  s.defs do
15
14
  Current.icons.sort.map do |key|
15
+ # rubocop:disable Rails/OutputSafety
16
16
  raw Icon.new(key).as_svg_symbol
17
+ # rubocop:enable Rails/OutputSafety
17
18
  end
18
19
  end
19
20
  end
@@ -28,7 +29,7 @@ class Components::IconSprite < Components::Base
28
29
  prop :key, String, :positional
29
30
 
30
31
  def as_svg_symbol
31
- icon_files = Dir.glob("{#{ICON_PATHS.join(',')}}/#{@key}.svg")
32
+ icon_files = Dir.glob("{#{ICON_PATHS.join(",")}}/#{@key}.svg")
32
33
 
33
34
  return if icon_files.empty?
34
35
 
@@ -39,6 +40,7 @@ class Components::IconSprite < Components::Base
39
40
 
40
41
  private
41
42
 
43
+ # rubocop:disable Rails/OutputSafety
42
44
  def sanitized_svg(svg_string)
43
45
  Rails::Html::SafeListSanitizer
44
46
  .new
@@ -48,6 +50,7 @@ class Components::IconSprite < Components::Base
48
50
  .strip
49
51
  .html_safe
50
52
  end
53
+ # rubocop:enable Rails/OutputSafety
51
54
 
52
55
  def allowed_tags
53
56
  %w[svg path circle rect line polyline polygon ellipse g defs use]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Backpack
4
- VERSION = "0.4.2"
4
+ VERSION = "0.4.4"
5
5
  end
@@ -30,6 +30,7 @@ describe Backpack::CLI, type: :cli do
30
30
  expect(output).to include("create spec/components/button_spec.rb")
31
31
  expect(output).to include("create spec/components/previews/button_preview.rb")
32
32
  expect(output).to include("create spec/components/previews/button_preview/overview.html.erb")
33
+ expect(output).to include("create vendor/normalize.css")
33
34
  end
34
35
 
35
36
  it "copies the folders" do
@@ -38,14 +39,27 @@ describe Backpack::CLI, type: :cli do
38
39
  expect(File.directory?("app/components")).to be true
39
40
  expect(File.directory?("spec/components")).to be true
40
41
  expect(File.directory?("spec/components/previews")).to be true
42
+ expect(File.directory?("spec/fixtures/icons")).to be true
41
43
  end
42
44
 
43
- it "copies the lib files" do
45
+ it "copies the lib & vendor files" do
44
46
  command
45
47
  expect(File.exist?("lib/backpack/attributes.rb")).to be true
46
48
  expect(File.exist?("lib/backpack/classes.rb")).to be true
47
49
  expect(File.exist?("lib/backpack/identifier.rb")).to be true
48
50
  expect(File.exist?("lib/backpack/tokens.rb")).to be true
51
+ expect(File.exist?("vendor/normalize.css")).to be true
52
+ end
53
+
54
+ context "when there is a package.json" do
55
+ before do
56
+ FileUtils.touch("package.json")
57
+ end
58
+
59
+ it "installs radix colors" do
60
+ command
61
+ expect(File.read("package.json")).to include('"@radix-ui/colors": "^3.0.0"')
62
+ end
49
63
  end
50
64
  end
51
65
  end
@@ -0,0 +1,251 @@
1
+ require 'spec_helper'
2
+
3
+ describe Backpack::CLI, type: :cli do
4
+ sandbox_dir = "sandbox"
5
+
6
+ around do |example|
7
+ FileUtils.mkdir(sandbox_dir) unless File.exist?(sandbox_dir)
8
+ within_dir(sandbox_dir) do
9
+ example.run
10
+ end
11
+ ensure
12
+ FileUtils.rm_rf(sandbox_dir)
13
+ end
14
+
15
+ describe "sync" do
16
+ let(:figma_fixture_path) { File.expand_path("../fixtures/figma/Backpack.json", __dir__) }
17
+ let(:css_fixture_path) { File.expand_path("../fixtures/tokens/_test.css", __dir__) }
18
+ let(:figma_dir) { "figma" }
19
+ let(:tokens_dir) { "lib/backpack/stylesheets/tokens" }
20
+ let(:figma_file) { "#{figma_dir}/Backpack.json" }
21
+ let(:css_file) { "#{tokens_dir}/_test.css" }
22
+
23
+ before do
24
+ # Create directory structure
25
+ FileUtils.mkdir_p(figma_dir)
26
+ FileUtils.mkdir_p(tokens_dir)
27
+
28
+ # Copy fixtures to test environment
29
+ FileUtils.cp(figma_fixture_path, figma_file)
30
+ FileUtils.cp(css_fixture_path, css_file)
31
+
32
+ # Set environment variables to override paths for testing
33
+ ENV['BACKPACK_TEST_FIGMA_PATH'] = File.expand_path(figma_file)
34
+ ENV['BACKPACK_TEST_TOKENS_PATH'] = File.expand_path(tokens_dir)
35
+ end
36
+
37
+ after do
38
+ # Clean up environment variables
39
+ ENV.delete('BACKPACK_TEST_FIGMA_PATH')
40
+ ENV.delete('BACKPACK_TEST_TOKENS_PATH')
41
+ end
42
+
43
+ context "when figma file exists and tokens are found" do
44
+ let(:command) { `bundle exec ruby ../bin/backpack sync 2>&1` }
45
+ let(:output) { command.chomp }
46
+
47
+ it "successfully syncs tokens from Figma to CSS files" do
48
+ expect(output).to include("Syncing design tokens from Figma...")
49
+ expect(output).to include("_test.css: 6 lines rewritten")
50
+ end
51
+
52
+ it "updates CSS variables with converted values" do
53
+ command
54
+ css_content = File.read(css_file)
55
+
56
+ # Font sizes should be converted to rem
57
+ expect(css_content).to include("--font-1: 1.2rem; /* imported from Figma on #{Date.today} */")
58
+ expect(css_content).to include("--font-2: 1.4rem; /* imported from Figma on #{Date.today} */")
59
+
60
+ # Radius should be converted to rem
61
+ expect(css_content).to include("--radius-1: 0.2rem; /* imported from Figma on #{Date.today} */")
62
+
63
+ # Space should be converted to rem
64
+ expect(css_content).to include("--space-1: var(--space-base); /* imported from Figma on #{Date.today} */")
65
+ expect(css_content).to include("--space-base: 0.2rem; /* imported from Figma on #{Date.today} */")
66
+
67
+ # Container width should be converted to rem
68
+ expect(css_content).to include("--container-width: 128rem; /* imported from Figma on #{Date.today} */")
69
+ end
70
+
71
+ it "preserves unchanged CSS variables" do
72
+ command
73
+ css_content = File.read(css_file)
74
+
75
+ # These should remain unchanged
76
+ expect(css_content).to include("--font-unchanged: 20px;")
77
+ expect(css_content).to include("--unknown-token: 999px;")
78
+ end
79
+
80
+ it "handles alias references correctly" do
81
+ command
82
+ css_content = File.read(css_file)
83
+
84
+ # space-1 should reference space-base as an alias
85
+ expect(css_content).to include("--space-1: var(--space-base);")
86
+ end
87
+
88
+ it "reports remaining unmatched tokens" do
89
+ # Create a CSS file that doesn't have all tokens
90
+ limited_css = ":root {\n --font-1: 10px;\n}"
91
+ File.write(css_file, limited_css)
92
+
93
+ output = `bundle exec ruby ../bin/backpack sync 2>&1`.chomp
94
+
95
+ expect(output).to include("5 tokens have not been found:")
96
+ expect(output).to include("font-2")
97
+ expect(output).to include("radius-1")
98
+ expect(output).to include("space-1")
99
+ expect(output).to include("space-base")
100
+ expect(output).to include("container-width")
101
+ expect(output).to include("Please check that these tokens are correctly named.")
102
+ end
103
+
104
+ it "reports success when all tokens are matched" do
105
+ expect(output).to include("All tokens synced successfully!")
106
+ end
107
+ end
108
+
109
+ context "when figma file is missing" do
110
+ before do
111
+ FileUtils.rm(figma_file)
112
+ end
113
+
114
+ let(:command) { `bundle exec ruby ../bin/backpack sync 2>&1` }
115
+
116
+ it "raises an error" do
117
+ expect { command }.not_to raise_error
118
+ expect(command).to include("No such file or directory")
119
+ end
120
+ end
121
+
122
+ context "when figma file has wrong name" do
123
+ before do
124
+ wrong_figma_data = File.read(figma_file).gsub('"name": "Backpack"', '"name": "WrongName"')
125
+ File.write(figma_file, wrong_figma_data)
126
+ end
127
+
128
+ let(:command) { `bundle exec ruby ../bin/backpack sync 2>&1` }
129
+
130
+ it "raises an error for wrong name" do
131
+ expect { command }.not_to raise_error
132
+ expect(command).to include("Wrong name")
133
+ end
134
+ end
135
+
136
+ context "when no CSS files exist in tokens directory" do
137
+ before do
138
+ FileUtils.rm_rf(tokens_dir)
139
+ FileUtils.mkdir_p(tokens_dir)
140
+ end
141
+
142
+ let(:command) { `bundle exec ruby ../bin/backpack sync 2>&1` }
143
+ let(:output) { command.chomp }
144
+
145
+ it "completes without errors but reports unmatched tokens" do
146
+ expect(output).to include("Syncing design tokens from Figma...")
147
+ expect(output).to include("6 tokens have not been found:")
148
+ expect(output).to include("Please check that these tokens are correctly named.")
149
+ end
150
+ end
151
+
152
+ context "when CSS files exist but have no matching tokens" do
153
+ before do
154
+ empty_css = ":root {\n /* no matching tokens */\n --other-token: 123px;\n}"
155
+ File.write(css_file, empty_css)
156
+ end
157
+
158
+ let(:command) { `bundle exec ruby ../bin/backpack sync 2>&1` }
159
+ let(:output) { command.chomp }
160
+
161
+ it "reports all tokens as unmatched" do
162
+ expect(output).to include("6 tokens have not been found:")
163
+ expect(output).to include("font-1")
164
+ expect(output).to include("font-2")
165
+ expect(output).to include("radius-1")
166
+ expect(output).to include("space-1")
167
+ expect(output).to include("space-base")
168
+ expect(output).to include("container-width")
169
+ end
170
+ end
171
+
172
+ describe "conversion rules" do
173
+ before do
174
+ # Create specific test cases for conversion rules
175
+ test_css = <<~CSS
176
+ :root {
177
+ --font-1: 10px;
178
+ --height-1: 10px;
179
+ --radius-1: 1px;
180
+ --space-1: 1px;
181
+ --breakpoint-xs: 100px;
182
+ --container-width: 100px;
183
+ }
184
+ CSS
185
+ File.write(css_file, test_css)
186
+
187
+ # Create figma data with breakpoint token
188
+ figma_data = JSON.parse(File.read(figma_file))
189
+ figma_data["variables"] << {
190
+ "id" => "VariableID:test:1",
191
+ "name" => "Layout/Media queries/Breakpoint XS",
192
+ "type" => "FLOAT",
193
+ "valuesByMode" => { "1:0" => 576 },
194
+ "resolvedValuesByMode" => { "1:0" => { "resolvedValue" => 576, "alias" => nil } },
195
+ "codeSyntax" => {}
196
+ }
197
+ figma_data["variables"] << {
198
+ "id" => "VariableID:test:2",
199
+ "name" => "Typography/Line height/height-1",
200
+ "type" => "FLOAT",
201
+ "valuesByMode" => { "1:0" => 16 },
202
+ "resolvedValuesByMode" => { "1:0" => { "resolvedValue" => 16, "alias" => nil } },
203
+ "codeSyntax" => {}
204
+ }
205
+ File.write(figma_file, JSON.generate(figma_data))
206
+ end
207
+
208
+ let(:command) { `bundle exec ruby ../bin/backpack sync 2>&1` }
209
+
210
+ it "applies correct conversion rules" do
211
+ command
212
+ css_content = File.read(css_file)
213
+
214
+ # Font and height should convert to rem (value / 10)
215
+ expect(css_content).to include("--font-1: 1.2rem;")
216
+ expect(css_content).to include("--height-1: 1.6rem;")
217
+
218
+ # Radius should convert to rem (value / 10)
219
+ expect(css_content).to include("--radius-1: 0.2rem;")
220
+
221
+ # Space should convert to rem (value / 10)
222
+ expect(css_content).to include("--space-1: var(--space-base);")
223
+
224
+ # Breakpoint should append px
225
+ expect(css_content).to include("--breakpoint-xs: 576px;")
226
+
227
+ # Container width should append rem
228
+ expect(css_content).to include("--container-width: 128rem;")
229
+ end
230
+ end
231
+
232
+ describe "help command" do
233
+ let(:help_output) { `bundle exec ruby ../bin/backpack help sync 2>&1`.chomp }
234
+
235
+ it "shows sync command in help" do
236
+ expect(help_output).to include("Usage:")
237
+ expect(help_output).to include("backpack sync")
238
+ expect(help_output).to include("Sync design tokens from Figma to CSS files")
239
+ end
240
+ end
241
+
242
+ describe "command availability" do
243
+ let(:commands_output) { `bundle exec ruby ../bin/backpack help 2>&1`.chomp }
244
+
245
+ it "lists sync as an available command" do
246
+ expect(commands_output).to include("backpack sync")
247
+ expect(commands_output).to include("Sync design tokens from Figma to CSS files")
248
+ end
249
+ end
250
+ end
251
+ end
@@ -115,7 +115,11 @@ describe Components::Icon, type: :component do
115
115
  end
116
116
 
117
117
  before do
118
- stub_const('Current', current_class)
118
+ if defined?(Current)
119
+ Current.reset
120
+ else
121
+ stub_const('Current', current_class)
122
+ end
119
123
  end
120
124
 
121
125
  after do
@@ -21,6 +21,9 @@ describe Components::IconSprite, type: :component do
21
21
 
22
22
  before do
23
23
  stub_const('Current', current_class)
24
+ stub_const('Components::IconSprite::ICON_PATHS', [
25
+ File.expand_path("../fixtures/icons", __dir__)
26
+ ])
24
27
  end
25
28
 
26
29
  after do
@@ -0,0 +1,133 @@
1
+ {
2
+ "id": "VariableCollectionId:1:26",
3
+ "name": "Backpack",
4
+ "modes": {
5
+ "1:0": "Desktop"
6
+ },
7
+ "variableIds": [
8
+ "VariableID:1:27",
9
+ "VariableID:5:3",
10
+ "VariableID:79:12949",
11
+ "VariableID:79:13034",
12
+ "VariableID:79:13053",
13
+ "VariableID:5:24"
14
+ ],
15
+ "variables": [
16
+ {
17
+ "id": "VariableID:1:27",
18
+ "name": "Typography/Sizes/font-1",
19
+ "description": "",
20
+ "type": "FLOAT",
21
+ "valuesByMode": {
22
+ "1:0": 12
23
+ },
24
+ "resolvedValuesByMode": {
25
+ "1:0": {
26
+ "resolvedValue": 12,
27
+ "alias": null
28
+ }
29
+ },
30
+ "scopes": ["FONT_SIZE"],
31
+ "hiddenFromPublishing": false,
32
+ "codeSyntax": {
33
+ "WEB": "var(--font-1)"
34
+ }
35
+ },
36
+ {
37
+ "id": "VariableID:5:3",
38
+ "name": "Typography/Sizes/font-2",
39
+ "description": "",
40
+ "type": "FLOAT",
41
+ "valuesByMode": {
42
+ "1:0": 14
43
+ },
44
+ "resolvedValuesByMode": {
45
+ "1:0": {
46
+ "resolvedValue": 14,
47
+ "alias": null
48
+ }
49
+ },
50
+ "scopes": ["FONT_SIZE"],
51
+ "hiddenFromPublishing": false,
52
+ "codeSyntax": {
53
+ "WEB": "var(--font-2)"
54
+ }
55
+ },
56
+ {
57
+ "id": "VariableID:79:12949",
58
+ "name": "Radius/radius-1",
59
+ "description": "",
60
+ "type": "FLOAT",
61
+ "valuesByMode": {
62
+ "1:0": 2
63
+ },
64
+ "resolvedValuesByMode": {
65
+ "1:0": {
66
+ "resolvedValue": 2,
67
+ "alias": null
68
+ }
69
+ },
70
+ "scopes": ["CORNER_RADIUS"],
71
+ "hiddenFromPublishing": false,
72
+ "codeSyntax": {}
73
+ },
74
+ {
75
+ "id": "VariableID:79:13034",
76
+ "name": "Spacing/space-1",
77
+ "description": "",
78
+ "type": "FLOAT",
79
+ "valuesByMode": {
80
+ "1:0": {
81
+ "type": "VARIABLE_ALIAS",
82
+ "id": "VariableID:79:13053"
83
+ }
84
+ },
85
+ "resolvedValuesByMode": {
86
+ "1:0": {
87
+ "resolvedValue": 2,
88
+ "alias": "VariableID:79:13053",
89
+ "aliasName": "Spacing/space-base"
90
+ }
91
+ },
92
+ "scopes": ["GAP", "LETTER_SPACING"],
93
+ "hiddenFromPublishing": false,
94
+ "codeSyntax": {}
95
+ },
96
+ {
97
+ "id": "VariableID:79:13053",
98
+ "name": "Spacing/space-base",
99
+ "description": "",
100
+ "type": "FLOAT",
101
+ "valuesByMode": {
102
+ "1:0": 2
103
+ },
104
+ "resolvedValuesByMode": {
105
+ "1:0": {
106
+ "resolvedValue": 2,
107
+ "alias": null
108
+ }
109
+ },
110
+ "scopes": ["GAP", "LETTER_SPACING"],
111
+ "hiddenFromPublishing": false,
112
+ "codeSyntax": {}
113
+ },
114
+ {
115
+ "id": "VariableID:5:24",
116
+ "name": "Layout/Container/Container Width",
117
+ "description": "",
118
+ "type": "FLOAT",
119
+ "valuesByMode": {
120
+ "1:0": 128
121
+ },
122
+ "resolvedValuesByMode": {
123
+ "1:0": {
124
+ "resolvedValue": 128,
125
+ "alias": null
126
+ }
127
+ },
128
+ "scopes": [],
129
+ "hiddenFromPublishing": false,
130
+ "codeSyntax": {}
131
+ }
132
+ ]
133
+ }
@@ -0,0 +1,19 @@
1
+ :root {
2
+ /* font-size */
3
+ --font-1: 10px; /* old value to be updated */
4
+ --font-2: 12px; /* old value to be updated */
5
+ --font-unchanged: 20px; /* this should remain unchanged */
6
+
7
+ /* radius */
8
+ --radius-1: 1px; /* old value to be updated */
9
+
10
+ /* spacing */
11
+ --space-1: 1px; /* old value to be updated */
12
+ --space-base: 1px; /* old value to be updated */
13
+
14
+ /* container */
15
+ --container-width: 100rem; /* old value to be updated */
16
+
17
+ /* unmatched token */
18
+ --unknown-token: 999px; /* this should remain unchanged */
19
+ }
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backpack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Heymès
8
8
  - Hans Lemuet
9
9
  - Hugo Chantelauze
10
- bindir: exe
10
+ bindir: bin
11
11
  cert_chain: []
12
12
  date: 1980-01-02 00:00:00.000000000 Z
13
13
  dependencies:
@@ -97,7 +97,8 @@ dependencies:
97
97
  version: '0'
98
98
  email:
99
99
  - team@etamin.studio
100
- executables: []
100
+ executables:
101
+ - backpack
101
102
  extensions: []
102
103
  extra_rdoc_files: []
103
104
  files:
@@ -108,6 +109,7 @@ files:
108
109
  - LICENSE
109
110
  - README.md
110
111
  - Rakefile
112
+ - bin/backpack
111
113
  - demo/.gitignore
112
114
  - demo/.overmind.env
113
115
  - demo/.yarn/cache/@babel-code-frame-npm-7.27.1-4dbcabb137-5874edc5d3.zip
@@ -602,6 +604,7 @@ files:
602
604
  - lib/backpack/attributes.rb
603
605
  - lib/backpack/classes.rb
604
606
  - lib/backpack/cli.rb
607
+ - lib/backpack/commands/sync.rb
605
608
  - lib/backpack/components.rb
606
609
  - lib/backpack/components/badge.rb
607
610
  - lib/backpack/components/badge/badge.css
@@ -647,6 +650,7 @@ files:
647
650
  - lib/backpack/tokens.rb
648
651
  - lib/backpack/version.rb
649
652
  - spec/cli/import_spec.rb
653
+ - spec/cli/sync_spec.rb
650
654
  - spec/components/badge_spec.rb
651
655
  - spec/components/breadcrumb_spec.rb
652
656
  - spec/components/button_spec.rb
@@ -671,9 +675,11 @@ files:
671
675
  - spec/components/quotation_spec.rb
672
676
  - spec/components/rich_text_spec.rb
673
677
  - spec/components/skip_links_spec.rb
678
+ - spec/fixtures/figma/Backpack.json
674
679
  - spec/fixtures/icons/arrow-right.svg
675
680
  - spec/fixtures/icons/close.svg
676
681
  - spec/fixtures/icons/user.svg
682
+ - spec/fixtures/tokens/_test.css
677
683
  - spec/spec_helper.rb
678
684
  - spec/support/cli_helpers.rb
679
685
  - spec/support/components.rb
@@ -704,6 +710,7 @@ specification_version: 4
704
710
  summary: When you go on a trek, don't forget your backpack.
705
711
  test_files:
706
712
  - spec/cli/import_spec.rb
713
+ - spec/cli/sync_spec.rb
707
714
  - spec/components/badge_spec.rb
708
715
  - spec/components/breadcrumb_spec.rb
709
716
  - spec/components/button_spec.rb
@@ -728,9 +735,11 @@ test_files:
728
735
  - spec/components/quotation_spec.rb
729
736
  - spec/components/rich_text_spec.rb
730
737
  - spec/components/skip_links_spec.rb
738
+ - spec/fixtures/figma/Backpack.json
731
739
  - spec/fixtures/icons/arrow-right.svg
732
740
  - spec/fixtures/icons/close.svg
733
741
  - spec/fixtures/icons/user.svg
742
+ - spec/fixtures/tokens/_test.css
734
743
  - spec/spec_helper.rb
735
744
  - spec/support/cli_helpers.rb
736
745
  - spec/support/components.rb