admiral-tools-figma 0.0.1
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 +7 -0
- data/README.md +1 -0
- data/admiral-tools-figma.gemspec +9 -0
- data/admiraltools/actions/figma_download_images.rb +116 -0
- data/admiraltools/actions/figma_generate_styles.rb +77 -0
- data/admiraltools/actions/figma_get_image_links.rb +139 -0
- data/admiraltools/actions/figma_get_styles.rb +90 -0
- data/admiraltools/helper/admiraltools_helper.rb +18 -0
- data/admiraltools/helper/figma/figma_client/figma_client.rb +193 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_color.rb +35 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_component.rb +58 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_components_styles_meta.rb +30 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_frame_info.rb +46 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_node.rb +43 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_paint.rb +32 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_style.rb +48 -0
- data/admiraltools/helper/figma/figma_client/models/api/primitives/figma_type_style.rb +56 -0
- data/admiraltools/helper/figma/figma_client/models/api/results/figma_components_styles_result.rb +36 -0
- data/admiraltools/helper/figma/figma_client/models/api/results/figma_images_result.rb +25 -0
- data/admiraltools/helper/figma/figma_client/models/api/results/figma_nodes_result.rb +52 -0
- data/admiraltools/helper/figma/figma_client/models/domain/converters/figma_to_domain_converter.rb +98 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/color.rb +44 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/component.rb +86 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/components_list.rb +29 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/font.rb +56 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/frame_info.rb +46 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/image_link.rb +33 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/style.rb +113 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/styles_group.rb +22 -0
- data/admiraltools/helper/figma/figma_client/models/domain/primitives/styles_list.rb +29 -0
- data/admiraltools/helper/figma/figma_client/models/models.rb +25 -0
- data/admiraltools/helper/figma/figma_image_downloader/bin/svg2vectordrawable.jar +0 -0
- data/admiraltools/helper/figma/figma_image_downloader/figma_image_downloader.rb +65 -0
- data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator.rb +47 -0
- data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator_raw.rb +32 -0
- data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator_res.rb +38 -0
- data/admiraltools/helper/figma/figma_image_downloader/generators/folder_generator_xcassets.rb +180 -0
- data/admiraltools/helper/figma/figma_image_downloader/generators/generators.rb +6 -0
- data/admiraltools/helper/figma/figma_image_downloader/helpers/image_name_formatter.rb +72 -0
- data/admiraltools/helper/figma/figma_image_downloader/helpers/progress_tasks_logger.rb +22 -0
- data/admiraltools/helper/figma/figma_image_downloader/helpers/scale_formatter.rb +96 -0
- data/admiraltools/helper/figma/figma_image_downloader/helpers/svg_to_vector_converter.rb +12 -0
- data/admiraltools/helper/figma/figma_image_downloader/models/download_image_params.rb +31 -0
- data/admiraltools/helper/figma/figma_image_downloader/models/image_name_components.rb +10 -0
- data/admiraltools/helper/figma/figma_image_downloader/models/image_ref.rb +12 -0
- data/admiraltools/helper/figma/figma_image_downloader/models/image_set_ref.rb +14 -0
- data/admiraltools/helper/figma/figma_image_downloader/models/models.rb +5 -0
- data/admiraltools/helper/figma/figma_template_generator/figma_template_generator.rb +22 -0
- data/admiraltools/helper/figma/figma_template_generator/models/styles_template.rb +34 -0
- data/admiraltools/helper/figma/helpers/file_folder.rb +30 -0
- data/admiraltools/helper/figma/helpers/hash_access.rb +18 -0
- data/admiraltools/helper/figma/helpers/string_filters.rb +63 -0
- data/admiraltools/helper/figma/helpers/string_format.rb +60 -0
- data/admiraltools/helper/system/system.rb +17 -0
- data/admiraltools/version.rb +7 -0
- data/admiraltools.rb +18 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: db6a98e25fe4de8274065f26dfc4db1808034dfe4b60b80d40c9f99d383729f7
|
4
|
+
data.tar.gz: 8b204394e797d86d8ac6de7ace055ece02631b879b30457cf1ed43aa0e11fe72
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 34fc5807ebeb31cc95e1797e535039dd3aafa58c051b3c628f70a12faf43cbcf869888f3afcad60746a4fd746f9cbd36f377ee2d91a89d6ebc8863a7bbed050b
|
7
|
+
data.tar.gz: 389fc58b3afa3584df804e769e3ccee55823a594300522fe1dc0e1c3f001f2e91d70343bb81da95066dac45e78a823d98af879876611bb926894ddf35049ae64
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# admiral-tools-figma
|
@@ -0,0 +1,9 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "admiral-tools-figma"
|
3
|
+
s.authors = ["Admiral team"]
|
4
|
+
s.version = "0.0.1"
|
5
|
+
s.homepage = "https://github.com/admiral-team"
|
6
|
+
s.description = "The tool to download figma icons"
|
7
|
+
s.files = Dir['**/*']
|
8
|
+
s.summary = "The tool to download figma icons"
|
9
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fastlane/action'
|
4
|
+
require_relative '../helper/admiraltools_helper'
|
5
|
+
require_relative '../helper/figma/figma_image_downloader/figma_image_downloader'
|
6
|
+
|
7
|
+
module Fastlane
|
8
|
+
module Actions
|
9
|
+
class FigmaDownloadImagesAction < Action
|
10
|
+
def self.run(params)
|
11
|
+
image_components_json = params[:image_components]
|
12
|
+
output_folder = params[:output_folder]
|
13
|
+
output_format = params[:output_format]
|
14
|
+
folder_depth = params[:folder_depth]
|
15
|
+
naming_style = params[:naming_style]
|
16
|
+
xcassets_params = params[:xcassets_params]
|
17
|
+
convert_svg_to_vector = params[:convert_svg_to_vector]
|
18
|
+
convert_scales_to_dpi = params[:convert_scales_to_dpi]
|
19
|
+
include_page_name = params[:include_page_name]
|
20
|
+
include_frame_name = params[:include_frame_name]
|
21
|
+
|
22
|
+
image_components = ComponentsList.from_json(image_components_json).components || []
|
23
|
+
downloader = FigmaImageDownloader.new
|
24
|
+
|
25
|
+
image_params = DownloadImageParams.new(
|
26
|
+
image_components: image_components,
|
27
|
+
output_folder: output_folder,
|
28
|
+
folder_depth: folder_depth,
|
29
|
+
output_format: output_format,
|
30
|
+
naming_style: naming_style,
|
31
|
+
xcassets_params: xcassets_params,
|
32
|
+
convert_svg_to_vector: convert_svg_to_vector,
|
33
|
+
convert_scales_to_dpi: convert_scales_to_dpi,
|
34
|
+
include_page_name: include_page_name,
|
35
|
+
include_frame_name: include_frame_name
|
36
|
+
)
|
37
|
+
|
38
|
+
downloader.download_images(params: image_params)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.description
|
42
|
+
'Figma plugin'
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.authors
|
46
|
+
['ton252']
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.return_value
|
50
|
+
# If your method provides a return value, you can describe here what it does
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.details
|
54
|
+
'Figma plugin'
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.available_options
|
58
|
+
[
|
59
|
+
FastlaneCore::ConfigItem.new(key: :image_components,
|
60
|
+
description: 'Image components json',
|
61
|
+
optional: false,
|
62
|
+
type: String),
|
63
|
+
FastlaneCore::ConfigItem.new(key: :output_folder,
|
64
|
+
description: 'Output folder path',
|
65
|
+
optional: false,
|
66
|
+
type: String),
|
67
|
+
FastlaneCore::ConfigItem.new(key: :output_format,
|
68
|
+
description: "Output type: #{FigmaImageDownloader::OUTPUT_FORMAT_XCASSETS}, #{FigmaImageDownloader::OUTPUT_FORMAT_RES}, #{FigmaImageDownloader::OUTPUT_FORMAT_RAW}",
|
69
|
+
optional: false,
|
70
|
+
type: String),
|
71
|
+
FastlaneCore::ConfigItem.new(key: :folder_depth,
|
72
|
+
description: 'Folder depth',
|
73
|
+
optional: true,
|
74
|
+
default_value: -1,
|
75
|
+
type: Integer),
|
76
|
+
FastlaneCore::ConfigItem.new(key: :naming_style,
|
77
|
+
description: "Image naming style: #{String::FORMAT_STYLE_CAMELCASE}, #{String::FORMAT_STYLE_SNAKECASE}, #{String::FORMAT_STYLE_KEBABCASE}",
|
78
|
+
optional: true,
|
79
|
+
type: String),
|
80
|
+
FastlaneCore::ConfigItem.new(key: :xcassets_params,
|
81
|
+
description: 'Xcassets parameters (template_rendering_intent, provides-namespace, preserves_vector_representation)',
|
82
|
+
optional: true,
|
83
|
+
type: Hash),
|
84
|
+
FastlaneCore::ConfigItem.new(key: :convert_svg_to_vector,
|
85
|
+
description: 'Convert svg images to Android vector',
|
86
|
+
optional: true,
|
87
|
+
default_value: false,
|
88
|
+
is_string: false),
|
89
|
+
FastlaneCore::ConfigItem.new(key: :convert_scales_to_dpi,
|
90
|
+
description: 'Convert scales to dpi',
|
91
|
+
optional: true,
|
92
|
+
default_value: false,
|
93
|
+
is_string: false),
|
94
|
+
FastlaneCore::ConfigItem.new(key: :include_page_name,
|
95
|
+
description: 'Include page name',
|
96
|
+
optional: true,
|
97
|
+
default_value: false,
|
98
|
+
is_string: false),
|
99
|
+
FastlaneCore::ConfigItem.new(key: :include_frame_name,
|
100
|
+
description: 'Include page name',
|
101
|
+
optional: true,
|
102
|
+
default_value: false,
|
103
|
+
is_string: false)
|
104
|
+
]
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.is_supported?(_platform)
|
108
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
109
|
+
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
|
110
|
+
#
|
111
|
+
# [:ios, :mac, :android].include?(platform)
|
112
|
+
true
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'fastlane/action'
|
5
|
+
require_relative '../helper/admiraltools_helper'
|
6
|
+
require_relative '../helper/figma/figma_template_generator/figma_template_generator'
|
7
|
+
|
8
|
+
module Fastlane
|
9
|
+
module Actions
|
10
|
+
class FigmaGenerateStylesAction < Action
|
11
|
+
def self.run(params)
|
12
|
+
Actions.verify_gem!('liquid')
|
13
|
+
|
14
|
+
styles_json = params[:styles]
|
15
|
+
style_types = params[:style_types]
|
16
|
+
template_file = params[:template_file]
|
17
|
+
output_file = params[:output_file]
|
18
|
+
|
19
|
+
generator = FigmaTemplateGenerator.new
|
20
|
+
styles = StylesList.from_json(styles_json).styles
|
21
|
+
|
22
|
+
generator.generate(
|
23
|
+
styles: styles,
|
24
|
+
style_types: style_types,
|
25
|
+
template_path: template_file,
|
26
|
+
output_path: output_file
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.description
|
31
|
+
'Figma plugin'
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.authors
|
35
|
+
['ton252']
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.return_value
|
39
|
+
# If your method provides a return value, you can describe here what it does
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.details
|
43
|
+
'Figma plugin'
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.available_options
|
47
|
+
[
|
48
|
+
FastlaneCore::ConfigItem.new(key: :styles,
|
49
|
+
description: 'Figma styles list json',
|
50
|
+
optional: false,
|
51
|
+
type: String),
|
52
|
+
FastlaneCore::ConfigItem.new(key: :template_file,
|
53
|
+
description: 'Figma template liquid file path',
|
54
|
+
optional: false,
|
55
|
+
type: String),
|
56
|
+
FastlaneCore::ConfigItem.new(key: :output_file,
|
57
|
+
description: 'Figma output styles file path',
|
58
|
+
optional: false,
|
59
|
+
type: String),
|
60
|
+
FastlaneCore::ConfigItem.new(key: :style_types,
|
61
|
+
description: 'Figma style type filter [FILL, TEXT, EFFECT, GRID]',
|
62
|
+
optional: true,
|
63
|
+
type: Array)
|
64
|
+
|
65
|
+
]
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.is_supported?(_platform)
|
69
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
70
|
+
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
|
71
|
+
#
|
72
|
+
# [:ios, :mac, :android].include?(platform)
|
73
|
+
true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fastlane/action'
|
4
|
+
require_relative '../helper/admiraltools_helper'
|
5
|
+
require_relative '../helper/figma/figma_client/figma_client'
|
6
|
+
|
7
|
+
module Fastlane
|
8
|
+
module Actions
|
9
|
+
class FigmaGetImageLinksAction < Action
|
10
|
+
def self.run(params)
|
11
|
+
access_token = params[:access_token]
|
12
|
+
file_key = params[:file_key]
|
13
|
+
name_filter_regex = params[:name_filter_regex]
|
14
|
+
description_filter_regex = params[:description_filter_regex]
|
15
|
+
frame_filter_regex = params[:frame_filter_regex]
|
16
|
+
page_filter_regex = params[:page_filter_regex]
|
17
|
+
components_output_file = params[:output_file]
|
18
|
+
image_format = params[:format]
|
19
|
+
image_scales = params[:scales]
|
20
|
+
svg_include_id = params[:svg_include_id]
|
21
|
+
svg_simplify_stroke = params[:svg_simplify_stroke]
|
22
|
+
use_absolute_bounds = params[:use_absolute_bounds]
|
23
|
+
request_batch = params[:request_batch]
|
24
|
+
scales = ScaleFormatter.new.any_scales_to_scales(image_scales)
|
25
|
+
|
26
|
+
client = FigmaClient.new(access_token: access_token)
|
27
|
+
|
28
|
+
components_list = client.get_full_image_components(
|
29
|
+
file_key: file_key,
|
30
|
+
image_format: image_format,
|
31
|
+
scales: scales,
|
32
|
+
name_filter_regex: name_filter_regex,
|
33
|
+
desc_filter_regex: description_filter_regex,
|
34
|
+
frame_filter_regex: frame_filter_regex,
|
35
|
+
page_filter_regex: page_filter_regex,
|
36
|
+
svg_include_id: svg_include_id,
|
37
|
+
svg_simplify_stroke: svg_simplify_stroke,
|
38
|
+
use_absolute_bounds: use_absolute_bounds,
|
39
|
+
request_batch: request_batch
|
40
|
+
)
|
41
|
+
|
42
|
+
unless components_output_file.nil?
|
43
|
+
File.write_file_json(
|
44
|
+
hash: components_list.to_hash,
|
45
|
+
path: components_output_file,
|
46
|
+
create_directories: true
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
components_list
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.description
|
54
|
+
'Figma plugin'
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.authors
|
58
|
+
['ton252']
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.return_value
|
62
|
+
# If your method provides a return value, you can describe here what it does
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.details
|
66
|
+
'Figma plugin'
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.available_options
|
70
|
+
[
|
71
|
+
FastlaneCore::ConfigItem.new(key: :access_token,
|
72
|
+
description: 'Figma access token',
|
73
|
+
env_name: 'FIGMA_ACCESS_TOKEN',
|
74
|
+
optional: false,
|
75
|
+
type: String),
|
76
|
+
FastlaneCore::ConfigItem.new(key: :file_key,
|
77
|
+
env_name: 'FIGMA_FILE_KEY',
|
78
|
+
description: 'Figma file key',
|
79
|
+
optional: false,
|
80
|
+
type: String),
|
81
|
+
FastlaneCore::ConfigItem.new(key: :name_filter_regex,
|
82
|
+
description: 'Name filter regex',
|
83
|
+
optional: true,
|
84
|
+
type: String),
|
85
|
+
FastlaneCore::ConfigItem.new(key: :description_filter_regex,
|
86
|
+
description: 'Description filter regex',
|
87
|
+
optional: true,
|
88
|
+
type: String),
|
89
|
+
FastlaneCore::ConfigItem.new(key: :frame_filter_regex,
|
90
|
+
description: 'Frame filter regex',
|
91
|
+
optional: true,
|
92
|
+
type: String),
|
93
|
+
FastlaneCore::ConfigItem.new(key: :page_filter_regex,
|
94
|
+
description: 'Page filter regex',
|
95
|
+
optional: true,
|
96
|
+
type: String),
|
97
|
+
FastlaneCore::ConfigItem.new(key: :format,
|
98
|
+
description: 'Image output format, can be [jpg png svg pdf]',
|
99
|
+
optional: true,
|
100
|
+
type: String),
|
101
|
+
FastlaneCore::ConfigItem.new(key: :scales,
|
102
|
+
description: 'An array of number between 0.01 and 4, the image scaling factor. Example: ["0.5", "1", "2", "3"]',
|
103
|
+
optional: true,
|
104
|
+
type: Array),
|
105
|
+
FastlaneCore::ConfigItem.new(key: :request_batch,
|
106
|
+
description: 'Figma max request components batch',
|
107
|
+
default_value: 1_000_000,
|
108
|
+
optional: true,
|
109
|
+
type: Integer),
|
110
|
+
FastlaneCore::ConfigItem.new(key: :output_file,
|
111
|
+
description: 'Figma output component file path',
|
112
|
+
optional: true,
|
113
|
+
type: String),
|
114
|
+
FastlaneCore::ConfigItem.new(key: :svg_include_id,
|
115
|
+
description: 'Whether to include id attributes for all SVG elements. Default: false',
|
116
|
+
optional: true,
|
117
|
+
type: Boolean),
|
118
|
+
FastlaneCore::ConfigItem.new(key: :svg_simplify_stroke,
|
119
|
+
description: 'Whether to simplify inside/outside strokes and use stroke attribute if possible instead of <mask>. Default: true',
|
120
|
+
optional: true,
|
121
|
+
type: Boolean),
|
122
|
+
FastlaneCore::ConfigItem.new(key: :use_absolute_bounds,
|
123
|
+
description: 'If image use its bounds box',
|
124
|
+
default_value: true,
|
125
|
+
optional: true,
|
126
|
+
type: Boolean)
|
127
|
+
]
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.is_supported?(_platform)
|
131
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
132
|
+
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
|
133
|
+
#
|
134
|
+
# [:ios, :mac, :android].include?(platform)
|
135
|
+
true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'fastlane/action'
|
5
|
+
require_relative '../helper/admiraltools_helper'
|
6
|
+
require_relative '../helper/figma/figma_client/figma_client'
|
7
|
+
|
8
|
+
module Fastlane
|
9
|
+
module Actions
|
10
|
+
class FigmaGetStylesAction < Action
|
11
|
+
def self.run(params)
|
12
|
+
Actions.verify_gem!('liquid')
|
13
|
+
|
14
|
+
access_token = params[:access_token]
|
15
|
+
file_key = params[:file_key]
|
16
|
+
styles_output_file = params[:output_file]
|
17
|
+
style_types = params[:style_types]
|
18
|
+
request_batch = params[:request_batch]
|
19
|
+
|
20
|
+
client = FigmaClient.new(access_token: access_token)
|
21
|
+
styles = client.get_full_styles(file_key: file_key, request_batch: request_batch) || []
|
22
|
+
styles = styles.select { |s| (style_types.nil? || style_types.include?(s.style_type)) }
|
23
|
+
styles_list = StylesList.new(styles: styles)
|
24
|
+
|
25
|
+
unless styles_output_file.nil?
|
26
|
+
File.write_file_json(
|
27
|
+
hash: styles_list.to_hash,
|
28
|
+
path: styles_output_file,
|
29
|
+
create_directories: true
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
styles_list
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.description
|
37
|
+
'Figma plugin'
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.authors
|
41
|
+
['ton252']
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.return_value
|
45
|
+
# If your method provides a return value, you can describe here what it does
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.details
|
49
|
+
'Figma plugin'
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.available_options
|
53
|
+
[
|
54
|
+
FastlaneCore::ConfigItem.new(key: :access_token,
|
55
|
+
description: 'Figma access token',
|
56
|
+
env_name: 'FIGMA_ACCESS_TOKEN',
|
57
|
+
optional: false,
|
58
|
+
type: String),
|
59
|
+
FastlaneCore::ConfigItem.new(key: :file_key,
|
60
|
+
env_name: 'FIGMA_FILE_KEY',
|
61
|
+
description: 'Figma file key',
|
62
|
+
optional: false,
|
63
|
+
type: String),
|
64
|
+
FastlaneCore::ConfigItem.new(key: :style_types,
|
65
|
+
description: 'Figma style type filter [FILL, TEXT, EFFECT, GRID]',
|
66
|
+
optional: true,
|
67
|
+
type: Array),
|
68
|
+
FastlaneCore::ConfigItem.new(key: :request_batch,
|
69
|
+
description: 'Figma max request components batch',
|
70
|
+
default_value: 1_000_000,
|
71
|
+
optional: true,
|
72
|
+
type: Integer),
|
73
|
+
FastlaneCore::ConfigItem.new(key: :output_file,
|
74
|
+
description: 'Figma output styles file path',
|
75
|
+
optional: true,
|
76
|
+
type: String)
|
77
|
+
|
78
|
+
]
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.is_supported?(_platform)
|
82
|
+
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
83
|
+
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
|
84
|
+
#
|
85
|
+
# [:ios, :mac, :android].include?(platform)
|
86
|
+
true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fastlane_core/ui/ui'
|
4
|
+
|
5
|
+
module Fastlane
|
6
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?('UI')
|
7
|
+
|
8
|
+
module Helper
|
9
|
+
class AdmiraltoolsHelper
|
10
|
+
# class methods that you define here become available in your action
|
11
|
+
# as `Helper::AdmiraltoolsHelper.your_method`
|
12
|
+
#
|
13
|
+
def self.show_message
|
14
|
+
UI.message('Hello from the admiraltools plugin helpers!')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
require 'net/http'
|
6
|
+
require_relative 'models/models'
|
7
|
+
|
8
|
+
class FigmaClient
|
9
|
+
TOKEN_HEADER_NAME = 'X-FIGMA-TOKEN'
|
10
|
+
CONTENT_TYPE_HEADER_NAME = 'Content-Type'
|
11
|
+
CONTENT_TYPE_HEADER_VALUE = 'application/json'
|
12
|
+
BASE_URL_DEFAULT = 'https://api.figma.com/v1'
|
13
|
+
|
14
|
+
def initialize(access_token:, base_url: nil)
|
15
|
+
@access_token = access_token
|
16
|
+
@base_url = base_url || BASE_URL_DEFAULT
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_full_image_components(
|
20
|
+
file_key:,
|
21
|
+
image_format:,
|
22
|
+
scales:,
|
23
|
+
name_filter_regex:,
|
24
|
+
desc_filter_regex: nil,
|
25
|
+
frame_filter_regex: nil,
|
26
|
+
page_filter_regex: nil,
|
27
|
+
request_batch: nil,
|
28
|
+
svg_include_id: nil,
|
29
|
+
svg_simplify_stroke: nil,
|
30
|
+
use_absolute_bounds: nil
|
31
|
+
)
|
32
|
+
converter = FigmaToDomainConverter.new
|
33
|
+
components_result = get_components(file_key: file_key)
|
34
|
+
figma_components = components_result&.meta&.components || []
|
35
|
+
scales = [1.0] if scales.nil? || scales.empty?
|
36
|
+
|
37
|
+
unless name_filter_regex.nil?
|
38
|
+
figma_components = figma_components.select { |c| c.name[Regexp.new(name_filter_regex)] }
|
39
|
+
end
|
40
|
+
|
41
|
+
unless desc_filter_regex.nil?
|
42
|
+
figma_components = figma_components.select { |c| c.description[Regexp.new(desc_filter_regex)] }
|
43
|
+
end
|
44
|
+
|
45
|
+
unless frame_filter_regex.nil?
|
46
|
+
figma_components = figma_components.select { |c| c.containing_frame&.name[Regexp.new(frame_filter_regex)] }
|
47
|
+
end
|
48
|
+
|
49
|
+
unless page_filter_regex.nil?
|
50
|
+
figma_components = figma_components.select { |c| c.containing_frame&.page_name[Regexp.new(page_filter_regex)] }
|
51
|
+
end
|
52
|
+
|
53
|
+
node_ids = figma_components.map(&:node_id)
|
54
|
+
node_id_batches = node_ids.each_slice(request_batch || 1_000_000).to_a
|
55
|
+
image_links_hash = {}
|
56
|
+
|
57
|
+
scales.each do |scale|
|
58
|
+
images_all = {}
|
59
|
+
node_id_batches.each do |batch_node_ids|
|
60
|
+
images = get_images(
|
61
|
+
file_key: file_key,
|
62
|
+
node_ids: batch_node_ids,
|
63
|
+
image_format: image_format,
|
64
|
+
scale: scale,
|
65
|
+
svg_include_id: svg_include_id,
|
66
|
+
svg_simplify_stroke: svg_simplify_stroke,
|
67
|
+
use_absolute_bounds: use_absolute_bounds
|
68
|
+
)&.images || {}
|
69
|
+
images_all.merge!(images)
|
70
|
+
end
|
71
|
+
image_links_hash[scale] = images_all
|
72
|
+
end
|
73
|
+
|
74
|
+
components = converter.fulfill_components(
|
75
|
+
figma_components: figma_components,
|
76
|
+
image_links_hash: image_links_hash,
|
77
|
+
image_format: image_format
|
78
|
+
)
|
79
|
+
|
80
|
+
ComponentsList.new(components: components)
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_components(file_key:)
|
84
|
+
uri = URI.parse("#{@base_url}/files/#{file_key}/components")
|
85
|
+
|
86
|
+
req = create_request(uri)
|
87
|
+
http = create_http_client(uri)
|
88
|
+
|
89
|
+
res = http.start do |h|
|
90
|
+
h.request(req)
|
91
|
+
end
|
92
|
+
|
93
|
+
raise "HTTP Status Code: #{res.code}\n#{res.body}" unless res.is_a?(Net::HTTPSuccess)
|
94
|
+
|
95
|
+
json_data = JSON.parse(res.body)
|
96
|
+
FigmaComponentsStylesResult.from_hash(json_data)
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_images(file_key:, node_ids:, image_format:, scale:, use_absolute_bounds:, svg_include_id:, svg_simplify_stroke:)
|
100
|
+
uri = URI.parse("#{@base_url}/images/#{file_key}")
|
101
|
+
params = {}
|
102
|
+
params['ids'] = node_ids.join(',')
|
103
|
+
params['format'] = image_format unless image_format.nil?
|
104
|
+
params['scale'] = scale unless scale.nil?
|
105
|
+
params['use_absolute_bounds'] = use_absolute_bounds unless use_absolute_bounds.nil?
|
106
|
+
params['svg_include_id'] = svg_include_id unless svg_include_id.nil?
|
107
|
+
params['svg_simplify_stroke'] = svg_simplify_stroke unless svg_simplify_stroke.nil?
|
108
|
+
add_query_parameters(uri: uri, params: params)
|
109
|
+
|
110
|
+
req = create_request(uri)
|
111
|
+
http = create_http_client(uri)
|
112
|
+
|
113
|
+
res = http.start do |h|
|
114
|
+
h.request(req)
|
115
|
+
end
|
116
|
+
|
117
|
+
raise "HTTP Status Code: #{res.code}\n#{res.body}" unless res.is_a?(Net::HTTPSuccess)
|
118
|
+
|
119
|
+
json_data = JSON.parse(res.body)
|
120
|
+
FigmaImagesResult.from_hash(json_data)
|
121
|
+
end
|
122
|
+
|
123
|
+
def get_full_styles(file_key:, request_batch:)
|
124
|
+
converter = FigmaToDomainConverter.new
|
125
|
+
styles_result = get_styles(file_key: file_key)
|
126
|
+
node_ids = (styles_result&.meta&.styles || []).map(&:node_id)
|
127
|
+
node_id_batches = node_ids.each_slice(request_batch || 1_000_000).to_a
|
128
|
+
|
129
|
+
nodes_result = FigmaNodesResult.new(name: nil, nodes: {})
|
130
|
+
|
131
|
+
node_id_batches.each do |batch_node_ids|
|
132
|
+
result = get_nodes(file_key: file_key, node_ids: batch_node_ids, depth: 1)
|
133
|
+
nodes_result.name = result.name
|
134
|
+
nodes_result.nodes.merge!(result.nodes)
|
135
|
+
end
|
136
|
+
|
137
|
+
converter.fulfill_styles(figma_nodes_result: nodes_result, figma_styles_result: styles_result)
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_styles(file_key:)
|
141
|
+
uri = URI.parse("#{@base_url}/files/#{file_key}/styles")
|
142
|
+
|
143
|
+
req = create_request(uri)
|
144
|
+
http = create_http_client(uri)
|
145
|
+
|
146
|
+
res = http.start do |h|
|
147
|
+
h.request(req)
|
148
|
+
end
|
149
|
+
|
150
|
+
raise "HTTP Status Code: #{res.code}\n#{res.body}" unless res.is_a?(Net::HTTPSuccess)
|
151
|
+
|
152
|
+
json_data = JSON.parse(res.body)
|
153
|
+
FigmaComponentsStylesResult.from_hash(json_data)
|
154
|
+
end
|
155
|
+
|
156
|
+
def get_nodes(file_key:, node_ids:, depth: 1)
|
157
|
+
uri = URI.parse("#{@base_url}/files/#{file_key}/nodes")
|
158
|
+
node_ids_string = node_ids.join(',')
|
159
|
+
params = { "ids": node_ids_string, "depth": depth }
|
160
|
+
add_query_parameters(uri: uri, params: params)
|
161
|
+
|
162
|
+
req = create_request(uri)
|
163
|
+
http = create_http_client(uri)
|
164
|
+
|
165
|
+
res = http.start do |h|
|
166
|
+
h.request(req)
|
167
|
+
end
|
168
|
+
|
169
|
+
raise "HTTP Status Code: #{res.code}\n#{res.body}" unless res.is_a?(Net::HTTPSuccess)
|
170
|
+
|
171
|
+
json_data = JSON.parse(res.body)
|
172
|
+
FigmaNodesResult.from_hash(json_data)
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def add_query_parameters(uri:, params:)
|
178
|
+
uri.query = URI.encode_www_form(URI.decode_www_form(uri.query || '').concat(params.to_a))
|
179
|
+
end
|
180
|
+
|
181
|
+
def create_request(uri)
|
182
|
+
req = Net::HTTP::Get.new(uri.request_uri.to_s)
|
183
|
+
req[CONTENT_TYPE_HEADER_NAME] = CONTENT_TYPE_HEADER_VALUE
|
184
|
+
req[TOKEN_HEADER_NAME] = @access_token
|
185
|
+
req
|
186
|
+
end
|
187
|
+
|
188
|
+
def create_http_client(uri)
|
189
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
190
|
+
http.use_ssl = true
|
191
|
+
http
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class FigmaColor
|
4
|
+
attr_accessor :r, :g, :b, :a
|
5
|
+
|
6
|
+
def initialize(r:, g:, b:, a:)
|
7
|
+
@r = r
|
8
|
+
@g = g
|
9
|
+
@b = b
|
10
|
+
@a = a
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.from_hash(hash)
|
14
|
+
return nil if hash.nil?
|
15
|
+
|
16
|
+
r = hash['r']
|
17
|
+
g = hash['g']
|
18
|
+
b = hash['b']
|
19
|
+
a = hash['a']
|
20
|
+
|
21
|
+
return FigmaColor.new(r: r, g: g, b: b, a: a) if !r.nil? && !g.nil? && !b.nil? && !a.nil?
|
22
|
+
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_hash
|
27
|
+
hash = {}
|
28
|
+
hash['r'] = r
|
29
|
+
hash['g'] = g
|
30
|
+
hash['b'] = b
|
31
|
+
hash['a'] = a
|
32
|
+
|
33
|
+
hash
|
34
|
+
end
|
35
|
+
end
|