maglevcms 1.1.7 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/components/maglev/content/image.rb +2 -0
  3. data/app/controllers/maglev/sitemap_controller.rb +31 -0
  4. data/app/frontend/editor/assets/remixicons/ri-palette-line.svg +1 -0
  5. data/app/frontend/editor/components/dynamic-form/dynamic-input.vue +1 -1
  6. data/app/frontend/editor/components/header-nav/publish-button.vue +1 -1
  7. data/app/frontend/editor/components/kit/collection-item-input.vue +1 -1
  8. data/app/frontend/editor/components/kit/color-input/core-input.vue +85 -0
  9. data/app/frontend/editor/components/kit/color-input/preset-button.vue +57 -0
  10. data/app/frontend/editor/components/kit/color-input/preset-dropdown.vue +46 -0
  11. data/app/frontend/editor/components/kit/color-input.vue +43 -0
  12. data/app/frontend/editor/components/kit/dropdown.vue +1 -0
  13. data/app/frontend/editor/components/kit/index.js +2 -2
  14. data/app/frontend/editor/components/kit/submit-button.vue +3 -3
  15. data/app/frontend/editor/components/kit/tabs.vue +2 -1
  16. data/app/frontend/editor/components/kit/text-input.vue +2 -2
  17. data/app/frontend/editor/misc/utils.js +15 -2
  18. data/app/frontend/editor/services/api.js +2 -2
  19. data/app/frontend/editor/spec/__mocks__/page.js +36 -36
  20. data/app/frontend/editor/spec/__mocks__/section.js +29 -29
  21. data/app/helpers/maglev/editor_helper.rb +1 -1
  22. data/app/helpers/maglev/page_preview_helper.rb +5 -1
  23. data/app/helpers/maglev/sitemap_helper.rb +13 -0
  24. data/app/models/concerns/maglev/sections_concern.rb +10 -0
  25. data/app/models/maglev/page/path_concern.rb +8 -1
  26. data/app/models/maglev/site/locales_concern.rb +8 -0
  27. data/app/services/concerns/maglev/get_page_sections/transform_collection_item_concern.rb +7 -0
  28. data/app/services/maglev/app_container.rb +2 -0
  29. data/app/services/maglev/persist_page.rb +23 -4
  30. data/app/services/maglev/setup_pages.rb +41 -5
  31. data/app/views/maglev/api/pages/_show.json.jbuilder +1 -2
  32. data/app/views/maglev/sitemap/index.xml.builder +21 -0
  33. data/config/routes.rb +1 -0
  34. data/lib/generators/maglev/install_generator.rb +3 -2
  35. data/lib/generators/maglev/section_generator.rb +1 -1
  36. data/lib/generators/maglev/templates/section/app/theme/sections/%category%/%file_name%.yml.tt +9 -9
  37. data/lib/maglev/engine.rb +8 -2
  38. data/lib/maglev/version.rb +1 -1
  39. data/package.json +3 -3
  40. data/yarn.lock +799 -565
  41. metadata +14 -7
  42. data/app/frontend/editor/components/kit/color-picker.vue +0 -81
  43. /data/app/frontend/editor/assets/remixicons/{check-line.svg → ri-check-line.svg} +0 -0
@@ -18,7 +18,7 @@ export const simpleContentSection = {
18
18
  width: 516,
19
19
  height: 320,
20
20
  filename: 'img-91-3.jpg',
21
- byte_size: 41683,
21
+ byteSize: 41683,
22
22
  },
23
23
  },
24
24
  { id: 'button', value: { url: '#', text: 'Click here' } },
@@ -48,7 +48,7 @@ export const normalizedSimpleContentSection = {
48
48
  width: 516,
49
49
  height: 320,
50
50
  filename: 'img-91-3.jpg',
51
- byte_size: 41683,
51
+ byteSize: 41683,
52
52
  },
53
53
  },
54
54
  { id: 'button', value: { url: '#', text: 'Click here' } },
@@ -69,7 +69,7 @@ export const navContentSection = {
69
69
  settings: [
70
70
  {
71
71
  id: 'link',
72
- value: { href: '#', text: 'Home', link_type: 'url' },
72
+ value: { href: '#', text: 'Home', linkType: 'url' },
73
73
  },
74
74
  ],
75
75
  },
@@ -79,7 +79,7 @@ export const navContentSection = {
79
79
  settings: [
80
80
  {
81
81
  id: 'link',
82
- value: { href: '#', text: 'About us', link_type: 'url' },
82
+ value: { href: '#', text: 'About us', linkType: 'url' },
83
83
  },
84
84
  ],
85
85
  },
@@ -93,11 +93,11 @@ export const navContentSection = {
93
93
  href: '//contact',
94
94
  text: 'Contact',
95
95
  email: null,
96
- link_id: 9,
97
- link_type: 'page',
98
- link_label: 'Contact us',
99
- section_id: null,
100
- open_new_window: false,
96
+ linkId: 9,
97
+ linkType: 'page',
98
+ linkLabel: 'Contact us',
99
+ sectionId: null,
100
+ openNewWindow: false,
101
101
  },
102
102
  },
103
103
  ],
@@ -112,11 +112,11 @@ export const navContentSection = {
112
112
  href: '/products',
113
113
  text: 'Products #1',
114
114
  email: null,
115
- link_id: 'd870133f9a075477a96a58e7639d40c5',
116
- link_type: 'page',
117
- link_label: 'Products',
118
- section_id: null,
119
- open_new_window: true,
115
+ linkId: 'd870133f9a075477a96a58e7639d40c5',
116
+ linkType: 'page',
117
+ linkLabel: 'Products',
118
+ sectionId: null,
119
+ openNewWindow: true,
120
120
  },
121
121
  },
122
122
  ],
@@ -131,7 +131,7 @@ export const navContentSection = {
131
131
  width: 572,
132
132
  height: 290,
133
133
  filename: 'Screen Shot 2021-06-30 at 3.44.04 PM.png',
134
- byte_size: 35070,
134
+ byteSize: 35070,
135
135
  },
136
136
  },
137
137
  ],
@@ -144,7 +144,7 @@ export const normalizedNavContentSection = {
144
144
  id: 'RiEo8C3f',
145
145
  type: 'navbar_item',
146
146
  settings: [
147
- { id: 'link', value: { href: '#', text: 'Home', link_type: 'url' } },
147
+ { id: 'link', value: { href: '#', text: 'Home', linkType: 'url' } },
148
148
  ],
149
149
  },
150
150
  P1fGieWs: {
@@ -153,7 +153,7 @@ export const normalizedNavContentSection = {
153
153
  settings: [
154
154
  {
155
155
  id: 'link',
156
- value: { href: '#', text: 'About us', link_type: 'url' },
156
+ value: { href: '#', text: 'About us', linkType: 'url' },
157
157
  },
158
158
  ],
159
159
  },
@@ -167,11 +167,11 @@ export const normalizedNavContentSection = {
167
167
  href: '//contact',
168
168
  text: 'Contact',
169
169
  email: null,
170
- link_id: 9,
171
- link_type: 'page',
172
- link_label: 'Contact us',
173
- section_id: null,
174
- open_new_window: false,
170
+ linkId: 9,
171
+ linkType: 'page',
172
+ linkLabel: 'Contact us',
173
+ sectionId: null,
174
+ openNewWindow: false,
175
175
  },
176
176
  },
177
177
  ],
@@ -186,11 +186,11 @@ export const normalizedNavContentSection = {
186
186
  href: '/products',
187
187
  text: 'Products #1',
188
188
  email: null,
189
- link_id: 'd870133f9a075477a96a58e7639d40c5',
190
- link_type: 'page',
191
- link_label: 'Products',
192
- section_id: null,
193
- open_new_window: true,
189
+ linkId: 'd870133f9a075477a96a58e7639d40c5',
190
+ linkType: 'page',
191
+ linkLabel: 'Products',
192
+ sectionId: null,
193
+ openNewWindow: true,
194
194
  },
195
195
  },
196
196
  ],
@@ -210,7 +210,7 @@ export const normalizedNavContentSection = {
210
210
  width: 572,
211
211
  height: 290,
212
212
  filename: 'Screen Shot 2021-06-30 at 3.44.04 PM.png',
213
- byte_size: 35070,
213
+ byteSize: 35070,
214
214
  },
215
215
  },
216
216
  ],
@@ -226,7 +226,7 @@ export const navContentSectionBlock = {
226
226
  settings: [
227
227
  {
228
228
  id: 'link',
229
- value: { href: '#', text: 'Foo', link_type: 'url' },
229
+ value: { href: '#', text: 'Foo', linkType: 'url' },
230
230
  },
231
231
  ],
232
232
  }
@@ -16,7 +16,7 @@ module Maglev
16
16
  end
17
17
 
18
18
  def api_base_path
19
- "#{root_path}api"
19
+ api_root_path
20
20
  end
21
21
 
22
22
  def editor_window_title
@@ -16,11 +16,15 @@ module Maglev
16
16
  # rubocop:enable Rails/OutputSafety
17
17
 
18
18
  def render_maglev_section(type, site: nil, theme: nil, page: nil, page_sections: nil)
19
+ sections = (page_sections || maglev_page_sections).find_all do |section|
20
+ (section['type'] || section[:type]).start_with?(type.to_s)
21
+ end
22
+
19
23
  render_maglev_sections(
20
24
  site: site,
21
25
  theme: theme,
22
26
  page: page,
23
- page_sections: (page_sections || maglev_page_sections).find_all { |section| section['type'] == type.to_s }
27
+ page_sections: sections
24
28
  )
25
29
  end
26
30
 
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maglev
4
+ module SitemapHelper
5
+ def sitemap_url(host, page, locale = nil)
6
+ path = maglev_services.get_page_fullpath.call(page: page, locale: locale)
7
+
8
+ return path if path =~ %r{^https?://}
9
+
10
+ [host, path].join
11
+ end
12
+ end
13
+ end
@@ -11,6 +11,16 @@ module Maglev::SectionsConcern
11
11
  end
12
12
  end
13
13
 
14
+ def prepare_sections_translations
15
+ return if sections_translations.blank?
16
+
17
+ sections_translations.each_key do |locale|
18
+ Maglev::I18n.with_locale(locale) do
19
+ prepare_sections
20
+ end
21
+ end
22
+ end
23
+
14
24
  private
15
25
 
16
26
  def prepare_section(section)
@@ -27,7 +27,14 @@ module Maglev::Page::PathConcern
27
27
  end
28
28
 
29
29
  def path=(value)
30
- current_path.value = value
30
+ unless value.respond_to?(:each_pair)
31
+ current_path.value = value
32
+ return
33
+ end
34
+
35
+ value.each_pair do |locale, new_path|
36
+ Maglev::I18n.with_locale(locale) { self.path = new_path }
37
+ end
31
38
  end
32
39
 
33
40
  def current_path
@@ -24,6 +24,14 @@ module Maglev::Site::LocalesConcern
24
24
  locales.map { |locale| locale.prefix.to_sym }
25
25
  end
26
26
 
27
+ def each_locale
28
+ locale_prefixes.each do |locale|
29
+ Maglev::I18n.with_locale(locale) do
30
+ yield(locale)
31
+ end
32
+ end
33
+ end
34
+
27
35
  class LocalesSerializer
28
36
  def self.dump(array)
29
37
  (array || []).map(&:as_json)
@@ -3,6 +3,11 @@
3
3
  # rubocop:disable Style/ClassAndModuleChildren
4
4
  module Maglev::GetPageSections::TransformCollectionItemConcern
5
5
  def transform_collection_item_content_setting(content, setting)
6
+ # the content might come from the default value of the collection_item setting
7
+ # rubocop:disable Style/StringHashKeys
8
+ content['value'] = { 'id' => 'any' } if content&.dig('value') == 'any'
9
+ # rubocop:enable Style/StringHashKeys
10
+
6
11
  item_id = content&.dig('value', 'id')
7
12
  return if item_id.blank?
8
13
 
@@ -11,6 +16,8 @@ module Maglev::GetPageSections::TransformCollectionItemConcern
11
16
  id: item_id
12
17
  )
13
18
 
19
+ return unless item
20
+
14
21
  content['value']['label'] = item.label
15
22
  content['value']['item'] = item.source
16
23
  end
@@ -31,6 +31,8 @@ module Maglev
31
31
  dependency :persist_section_screenshot, class: Maglev::PersistSectionScreenshot,
32
32
  depends_on: %i[fetch_theme fetch_section_screenshot_path]
33
33
 
34
+ dependency :change_site_locales, class: Maglev::ChangeSiteLocales
35
+
34
36
  dependency :fetch_page, class: Maglev::FetchPage, depends_on: :fetch_site
35
37
  dependency :get_page_fullpath, class: Maglev::GetPageFullpath, depends_on: %i[fetch_site get_base_url]
36
38
  dependency :get_page_sections, class: Maglev::GetPageSections,
@@ -25,15 +25,31 @@ module Maglev
25
25
 
26
26
  def persist_page!
27
27
  page.attributes = page_attributes
28
- page.prepare_sections
28
+
29
+ # the sections_translations attribute is put by the SetupPages service
30
+ # when we generate a brand new site
31
+ if page_attributes.key?(:sections_translations)
32
+ page.prepare_sections_translations
33
+ else
34
+ page.prepare_sections
35
+ end
36
+
29
37
  page.save!
30
38
  end
31
39
 
32
40
  def persist_site!
33
41
  return unless can_persist_site?
34
42
 
35
- site.attributes = site_attributes_with_consistent_sections
36
- site.prepare_sections
43
+ # the sections_translations attribute is put by the SetupPages service
44
+ # when we generate a brand new site
45
+ if site_attributes.key?(:sections_translations)
46
+ site.attributes = site_attributes
47
+ site.prepare_sections_translations
48
+ else
49
+ site.attributes = site_attributes_with_consistent_sections
50
+ site.prepare_sections
51
+ end
52
+
37
53
  site.save!
38
54
  end
39
55
 
@@ -68,7 +84,10 @@ module Maglev
68
84
  end
69
85
 
70
86
  def can_persist_site?
71
- site_attributes.present? && site_attributes[:sections].present?
87
+ site_attributes.present? && (
88
+ site_attributes[:sections].present? ||
89
+ site_attributes[:sections_translations].present?
90
+ )
72
91
  end
73
92
  end
74
93
  end
@@ -13,7 +13,9 @@ module Maglev
13
13
 
14
14
  def call
15
15
  pages&.map do |page_attributes|
16
- create_page(page_attributes)
16
+ create_page(
17
+ attributes_in_all_locales(page_attributes.with_indifferent_access)
18
+ )
17
19
  end
18
20
  end
19
21
 
@@ -34,12 +36,46 @@ module Maglev
34
36
  end
35
37
 
36
38
  def site_attributes_from(page_attributes)
39
+ if page_attributes.include?(:sections_translations)
40
+ { sections_translations: site_sections_translations(page_attributes) }
41
+ else
42
+ { sections: select_site_scoped_sections(page_attributes[:sections]) }
43
+ end.compact || {}
44
+ end
45
+
46
+ def site_sections_translations(page_attributes)
47
+ sections_translations = page_attributes[:sections_translations].transform_values do |sections|
48
+ select_site_scoped_sections(sections)
49
+ end
50
+ sections_translations.any? { |_, sections| sections.blank? } ? nil : sections_translations
51
+ end
52
+
53
+ def attributes_in_all_locales(attributes)
37
54
  {
38
- sections: (page_attributes[:sections] || []).find_all do |section|
39
- definition = theme.sections.find(section['type'])
40
- definition.site_scoped?
41
- end
55
+ title_translations: value_in_all_locales(attributes[:title]),
56
+ sections_translations: value_in_all_locales(attributes[:sections]),
57
+ path: value_in_all_locales(attributes[:path])
42
58
  }
43
59
  end
60
+
61
+ def value_in_all_locales(value)
62
+ if value.respond_to?(:each_pair)
63
+ default_value = value[site.default_locale_prefix]
64
+ fill_translations(default_value).merge(value)
65
+ else
66
+ fill_translations(value)
67
+ end
68
+ end
69
+
70
+ def fill_translations(value)
71
+ site.locale_prefixes.index_with { |_locale| value }
72
+ end
73
+
74
+ def select_site_scoped_sections(sections)
75
+ (sections || []).find_all do |section|
76
+ definition = theme.sections.find(section['type'])
77
+ definition.site_scoped?
78
+ end
79
+ end
44
80
  end
45
81
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- json.sections services.get_page_sections.call(page: page)
4
-
5
3
  json.key_format! camelize: :lower
6
4
  json.deep_format_keys!
7
5
 
@@ -20,5 +18,6 @@ json.og_image_url page.og_image_url
20
18
  json.preview_url services.get_page_fullpath.call(page: page, preview_mode: true, locale: content_locale)
21
19
  json.live_url services.get_page_fullpath.call(page: page, preview_mode: false, locale: content_locale)
22
20
  json.section_names services.get_page_section_names.call(page: page)
21
+ json.sections services.get_page_sections.call(page: page)
23
22
  json.lock_version page.lock_version
24
23
  json.translated page.path.present?
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ xml.instruct!
4
+
5
+ xml.urlset xmlns: 'http://www.google.com/schemas/sitemap/0.9', "xmlns:xhtml": 'http://www.w3.org/1999/xhtml' do
6
+ @pages.each do |page|
7
+ xml.url do
8
+ xml.loc sitemap_url(@host, page, maglev_site.default_locale_prefix)
9
+ xml.lastmod page.updated_at.strftime('%Y-%m-%d')
10
+
11
+ if maglev_site.locales.size > 1
12
+ maglev_site.locales.each do |locale|
13
+ xml.xhtml :link,
14
+ rel: 'alternate',
15
+ hreflang: locale.prefix,
16
+ href: sitemap_url(@host, page, locale.prefix)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
data/config/routes.rb CHANGED
@@ -5,6 +5,7 @@ Maglev::Engine.routes.draw do
5
5
  # API
6
6
  constraints format: :json do
7
7
  namespace :api do
8
+ root to: proc { [200, {}, ['{}']] }
8
9
  resource :site, only: :show
9
10
  resources :pages do
10
11
  resources :clones, controller: :page_clones, only: :create
@@ -29,6 +29,7 @@ module Maglev
29
29
  inject_into_file 'config/routes.rb', before: /^end/ do
30
30
  <<-CODE
31
31
  mount Maglev::Engine => '/maglev'
32
+ get '/sitemap', to: 'maglev/sitemap#index', defaults: { format: 'xml' }
32
33
  get '(*path)', to: 'maglev/page_preview#index', defaults: { path: 'index' }, constraints: Maglev::PreviewConstraint.new
33
34
  CODE
34
35
  end
@@ -38,8 +39,8 @@ module Maglev
38
39
  $stdout.puts <<~INFO
39
40
  Done! 🎉
40
41
 
41
- You can now tweak /config/initializers/maglev.rb.
42
- You can also modify your theme (in /app/theme and /app/views/theme)
42
+ You can now tweak config/initializers/maglev.rb
43
+ You can also modify your theme (in app/theme and app/views/theme)
43
44
  and generate new sections with rails g maglev:section.
44
45
 
45
46
  👉 The next step is to create a site using `rails maglev:create_site`.
@@ -109,7 +109,7 @@ module Maglev
109
109
  when 'text' then label
110
110
  when 'image' then '"/theme/image-placeholder.jpg"'
111
111
  when 'checkbox' then true
112
- when 'link' then '{ text: "Link", url: "#" }'
112
+ when 'link' then '{ text: "Link", href: "#" }'
113
113
  when 'color' then '#E5E7EB'
114
114
  when 'radio', 'select' then 'option_1'
115
115
  when 'icon' then 'default-icon-class'
@@ -12,7 +12,7 @@ category: <%= category %>
12
12
  # insert_button: false
13
13
 
14
14
  # Insert this section at the top or the bottom of the page (default: nil)
15
- # insert_at: top
15
+ # insert_at: top
16
16
 
17
17
  # One single instance per page?
18
18
  # singleton: true
@@ -33,7 +33,7 @@ settings:
33
33
  <%- when 'link' -%>
34
34
  with_text: true
35
35
  <%- when 'select', 'radio' -%>
36
- options:
36
+ select_options:
37
37
  - label: Option 1
38
38
  value: option_1
39
39
  - label: Option 2
@@ -48,7 +48,7 @@ settings:
48
48
  <%- end -%>
49
49
  # Definition of the blocks.
50
50
  # You can define as many types of blocks as you want.
51
- blocks: <%- if blocks.blank? -%>[]
51
+ blocks:<%- if blocks.blank? -%> []
52
52
  <% end %>
53
53
  <%- blocks.each do |(type, settings)| -%>
54
54
  - name: "<%= type.humanize %>"
@@ -66,7 +66,7 @@ blocks: <%- if blocks.blank? -%>[]
66
66
  <%- when 'link' -%>
67
67
  with_text: true
68
68
  <%- when 'select', 'radio' -%>
69
- options:
69
+ select_options:
70
70
  - label: Option 1
71
71
  value: option_1
72
72
  - label: Option 2
@@ -84,12 +84,12 @@ blocks: <%- if blocks.blank? -%>[]
84
84
  # The title can be changed with the following property:
85
85
  # blocks_label: "My list of items"
86
86
 
87
- # By default, blocks are presented as a list in the editor UI.
88
- # If you like to use blocks to build a menu with sub menu items,
87
+ # By default, blocks are presented as a list in the editor UI.
88
+ # If you like to use blocks to build a menu with sub menu items,
89
89
  # consider using the tree presentation
90
90
  # blocks_presentation: "tree"
91
91
 
92
- # In order to get a section with "meaning" content for the editors,
92
+ # In order to get a section with "meaning" content for the editors,
93
93
  # you can set the default content of a new instance of this section.
94
94
  # (it's also be useful when taking a screenshot of the section).
95
95
  # sample:
@@ -98,13 +98,13 @@ blocks: <%- if blocks.blank? -%>[]
98
98
  # <% if setting.value? -%><%= setting.id %>: <%= setting.default %><% end -%>
99
99
 
100
100
  <%- end -%>
101
- # blocks: <%- if blocks.blank? -%>[]
101
+ # blocks:<%- if blocks.blank? -%> []
102
102
  <% end %>
103
103
  <%- blocks.each do |(type, settings)| -%>
104
104
  # - type: <%= type %>
105
105
  # settings:
106
106
  <%- settings.each do |setting| -%>
107
107
  # <% if setting.value? -%><%= setting.id %>: <%= setting.default %><% end -%>
108
-
108
+
109
109
  <%- end -%>
110
110
  <%- end -%>
data/lib/maglev/engine.rb CHANGED
@@ -52,13 +52,19 @@ module Maglev
52
52
  app.middleware.insert_after ActionDispatch::Static,
53
53
  Rack::Static,
54
54
  urls: ["/#{vite_ruby.config.public_output_dir}"],
55
- root: root.join(vite_ruby.config.public_dir)
55
+ root: root.join(vite_ruby.config.public_dir),
56
+ header_rules: [
57
+ [:all, { "Access-Control-Allow-Origin": '*' }]
58
+ ]
56
59
  else
57
60
  # mostly when running the application in production behind NGINX or APACHE
58
61
  app.middleware.insert_before 0,
59
62
  Rack::Static,
60
63
  urls: ["/#{vite_ruby.config.public_output_dir}"],
61
- root: root.join(vite_ruby.config.public_dir)
64
+ root: root.join(vite_ruby.config.public_dir),
65
+ header_rules: [
66
+ [:all, { "Access-Control-Allow-Origin": '*' }]
67
+ ]
62
68
  end
63
69
  end
64
70
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Maglev
4
- VERSION = '1.1.7'
4
+ VERSION = '1.2.0'
5
5
  end
data/package.json CHANGED
@@ -32,11 +32,11 @@
32
32
  "jsdom": "^20.0.2",
33
33
  "postcss": "^8.4.18",
34
34
  "prettier": "^2.3.2",
35
- "sass": "^1.55.0",
35
+ "sass": "^1.55.0",
36
36
  "tailwindcss": "^3.2.1",
37
- "vite": "^3.2.0",
37
+ "vite": "^4.3.0",
38
38
  "vite-plugin-eslint": "^1.8.1",
39
- "vite-plugin-ruby": "^3.1.2",
39
+ "vite-plugin-ruby": "^3.2.0",
40
40
  "vite-plugin-vue2-svg": "^0.3.0",
41
41
  "vitest": "^0.24.4",
42
42
  "vue-template-compiler": "^2.7.13"