xat_support 1.29.3

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.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +176 -0
  3. data/README.md +33 -0
  4. data/config/locales/en.yml +85 -0
  5. data/config/locales/translations/zendesk_apps_support.yml +210 -0
  6. data/lib/xat_support.rb +30 -0
  7. data/lib/zendesk_apps_support/app_file.rb +38 -0
  8. data/lib/zendesk_apps_support/app_requirement.rb +5 -0
  9. data/lib/zendesk_apps_support/app_version.rb +64 -0
  10. data/lib/zendesk_apps_support/assets/default_styles.scss +3 -0
  11. data/lib/zendesk_apps_support/assets/default_template.html.erb +10 -0
  12. data/lib/zendesk_apps_support/assets/installed.js.erb +17 -0
  13. data/lib/zendesk_apps_support/assets/src.js.erb +39 -0
  14. data/lib/zendesk_apps_support/build_translation.rb +50 -0
  15. data/lib/zendesk_apps_support/engine.rb +9 -0
  16. data/lib/zendesk_apps_support/i18n.rb +28 -0
  17. data/lib/zendesk_apps_support/installation.rb +20 -0
  18. data/lib/zendesk_apps_support/installed.rb +22 -0
  19. data/lib/zendesk_apps_support/location.rb +29 -0
  20. data/lib/zendesk_apps_support/package.rb +339 -0
  21. data/lib/zendesk_apps_support/product.rb +15 -0
  22. data/lib/zendesk_apps_support/sass_functions.rb +20 -0
  23. data/lib/zendesk_apps_support/stylesheet_compiler.rb +23 -0
  24. data/lib/zendesk_apps_support/validations/banner.rb +33 -0
  25. data/lib/zendesk_apps_support/validations/manifest.rb +214 -0
  26. data/lib/zendesk_apps_support/validations/marketplace.rb +20 -0
  27. data/lib/zendesk_apps_support/validations/requirements.rb +105 -0
  28. data/lib/zendesk_apps_support/validations/source.rb +95 -0
  29. data/lib/zendesk_apps_support/validations/stylesheets.rb +29 -0
  30. data/lib/zendesk_apps_support/validations/templates.rb +18 -0
  31. data/lib/zendesk_apps_support/validations/translations.rb +64 -0
  32. data/lib/zendesk_apps_support/validations/validation_error.rb +101 -0
  33. metadata +229 -0
@@ -0,0 +1,30 @@
1
+ module ZendeskAppsSupport
2
+ require 'zendesk_apps_support/sass_functions'
3
+ require 'zendesk_apps_support/engine'
4
+ require 'execjs'
5
+
6
+ autoload :AppFile, 'zendesk_apps_support/app_file'
7
+ autoload :BuildTranslation, 'zendesk_apps_support/build_translation'
8
+ autoload :I18n, 'zendesk_apps_support/i18n'
9
+ autoload :Location, 'zendesk_apps_support/location'
10
+ autoload :Product, 'zendesk_apps_support/product'
11
+ autoload :Package, 'zendesk_apps_support/package'
12
+ autoload :Installed, 'zendesk_apps_support/installed'
13
+ autoload :Installation, 'zendesk_apps_support/installation'
14
+ autoload :AppRequirement, 'zendesk_apps_support/app_requirement'
15
+ autoload :AppVersion, 'zendesk_apps_support/app_version'
16
+ autoload :StylesheetCompiler, 'zendesk_apps_support/stylesheet_compiler'
17
+
18
+ module Validations
19
+ autoload :ValidationError, 'zendesk_apps_support/validations/validation_error'
20
+ autoload :Manifest, 'zendesk_apps_support/validations/manifest'
21
+ autoload :Marketplace, 'zendesk_apps_support/validations/marketplace'
22
+ autoload :Source, 'zendesk_apps_support/validations/source'
23
+ autoload :Templates, 'zendesk_apps_support/validations/templates'
24
+ autoload :Translations, 'zendesk_apps_support/validations/translations'
25
+ autoload :ESLintValidationError, 'zendesk_apps_support/validations/validation_error'
26
+ autoload :Stylesheets, 'zendesk_apps_support/validations/stylesheets'
27
+ autoload :Requirements, 'zendesk_apps_support/validations/requirements'
28
+ autoload :Banner, 'zendesk_apps_support/validations/banner'
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ module ZendeskAppsSupport
2
+ class AppFile
3
+ attr_reader :relative_path
4
+ attr_reader :absolute_path
5
+
6
+ def initialize(package, relative_path)
7
+ @relative_path = relative_path
8
+ @file = File.new(package.path_to(relative_path))
9
+ @absolute_path = File.absolute_path @file.path
10
+ end
11
+
12
+ def read
13
+ File.read @file.path
14
+ end
15
+
16
+ def =~(regex)
17
+ relative_path =~ regex
18
+ end
19
+
20
+ def match(regex)
21
+ self =~ regex
22
+ end
23
+
24
+ alias_method :to_s, :relative_path
25
+
26
+ def method_missing(sym, *args, &block)
27
+ if @file.respond_to?(sym)
28
+ @file.call(sym, *args, &block)
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def respond_to_missing?(sym, include_private = false)
35
+ @file.send(:respond_to_missing?, sym, include_private) || super
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ module ZendeskAppsSupport
2
+ class AppRequirement
3
+ TYPES = %w(automations channel_integrations macros targets views ticket_fields triggers user_fields).freeze
4
+ end
5
+ end
@@ -0,0 +1,64 @@
1
+ require 'json'
2
+
3
+ module ZendeskAppsSupport
4
+ # At any point in time, we support up to three versions:
5
+ # * deprecated -- we will still serve apps targeting the deprecated version,
6
+ # but newly created or updated apps CANNOT target it
7
+ # * current -- we will serve apps targeting the current version;
8
+ # newly created or updated apps SHOULD target it
9
+ # * future -- we will serve apps targeting the future version;
10
+ # newly created or updates apps MAY target it, but it
11
+ # may change without notice
12
+ class AppVersion
13
+ DEPRECATED = '0.5'.freeze
14
+ CURRENT = '1.0'.freeze
15
+ FUTURE = '2.0'.freeze
16
+
17
+ TO_BE_SERVED = [DEPRECATED, CURRENT, FUTURE].compact.freeze
18
+ VALID_FOR_UPDATE = [CURRENT, FUTURE].compact.freeze
19
+
20
+ def initialize(version)
21
+ @version = version.to_s
22
+ @version.freeze
23
+ freeze
24
+ end
25
+
26
+ def servable?
27
+ TO_BE_SERVED.include?(@version)
28
+ end
29
+
30
+ def valid_for_update?
31
+ VALID_FOR_UPDATE.include?(@version)
32
+ end
33
+
34
+ def deprecated?
35
+ @version == DEPRECATED
36
+ end
37
+
38
+ def obsolete?
39
+ !servable?
40
+ end
41
+
42
+ def blank?
43
+ @version.nil? || @version == ''
44
+ end
45
+
46
+ def present?
47
+ !blank?
48
+ end
49
+
50
+ def to_s
51
+ @version
52
+ end
53
+
54
+ def to_json(*)
55
+ @version.inspect
56
+ end
57
+
58
+ def ==(other)
59
+ @version == other.to_s
60
+ end
61
+ end
62
+
63
+ AppVersion.freeze
64
+ end
@@ -0,0 +1,3 @@
1
+ header .logo {
2
+ background-image: app-asset-url("logo-small.png");
3
+ }
@@ -0,0 +1,10 @@
1
+ <header>
2
+ <span class="logo"></span>
3
+ <h3>{{setting "name"}}</h3>
4
+ </header>
5
+ <section data-main></section>
6
+ <footer>
7
+ <a href="mailto:{{author.email}}">
8
+ {{author.name}}
9
+ </a>
10
+ </footer>
@@ -0,0 +1,17 @@
1
+ (function() {
2
+ <% appsjs.each do |appjs| %>
3
+ <%= appjs %>
4
+ <% end %>
5
+
6
+ <% installations.each do |installation| %>
7
+ if (ZendeskApps[<%= installation.app_name.to_json %>]) {
8
+ ZendeskApps[<%= installation.app_name.to_json %>].install(<%= installation.to_json %>);
9
+ }
10
+ <% end %>
11
+
12
+ <% installation_orders.each do |loc_name, installation_list| %>
13
+ ZendeskApps.sortAppsForSite(<%= loc_name.to_json %>, <%= installation_list.to_json %>);
14
+ <% end %>
15
+ }());
16
+
17
+ ZendeskApps.trigger && ZendeskApps.trigger('ready');
@@ -0,0 +1,39 @@
1
+ <% unless iframe_only %>
2
+ with( ZendeskApps.AppScope.create() ) {
3
+ require.modules = {
4
+ <% modules.each do |path, content| %>
5
+ <%= path.to_json %>: function(exports, require, module) {
6
+ <%= content %>
7
+ },
8
+ <% end %>
9
+ <% if is_es2015 %>
10
+ '_root.js': function(exports, require, module) {
11
+ <%= source %>
12
+ }
13
+ <% else %>
14
+ eom: undefined
15
+ <% end %>
16
+ };
17
+
18
+ var source = <%= source %>;
19
+ }
20
+ <% end %>
21
+ var app = ZendeskApps.defineApp(<%= iframe_only ? 'null' : 'source' %>)
22
+ .reopenClass(<%= app_settings.to_json %>)
23
+ .reopen({
24
+ appName: <%= name.to_json %>,
25
+ appVersion: <%= version.to_json %>,
26
+ assetUrlPrefix: <%= asset_url_prefix.to_json %>,
27
+ appClassName: <%= app_class_name.to_json %>,
28
+ author: {
29
+ name: <%= author['name'].to_json %>,
30
+ email: <%= author['email'].to_json %>
31
+ },
32
+ <% unless iframe_only %>
33
+ translations: <%= translations.to_json %>,
34
+ templates: <%= templates.to_json %>,
35
+ <% end %>
36
+ frameworkVersion: <%= framework_version.to_json %>
37
+ });
38
+
39
+ ZendeskApps[<%= name.to_json %>] = app;
@@ -0,0 +1,50 @@
1
+ module ZendeskAppsSupport
2
+ module BuildTranslation
3
+ I18N_TITLE_KEY = 'title'
4
+ I18N_VALUE_KEY = 'value'
5
+ I18N_KEYS = [I18N_TITLE_KEY, I18N_VALUE_KEY]
6
+
7
+ def to_flattened_namespaced_hash(hash, target_key = nil, prefix = nil)
8
+ hash.inject({}) do |result, (key, value)|
9
+ key = [prefix, key].compact.join('.')
10
+ if value.is_a?(Hash)
11
+ if target_key && is_translation_hash?(value)
12
+ result[key] = value[target_key]
13
+ else
14
+ result.update(to_flattened_namespaced_hash(value, target_key, key))
15
+ end
16
+ else
17
+ result[key] = value
18
+ end
19
+ result
20
+ end
21
+ end
22
+
23
+ def remove_zendesk_keys(scope, translations = {})
24
+ scope.each_key do |key|
25
+ context = scope[key]
26
+
27
+ if context.is_a?(Hash)
28
+
29
+ if is_translation_hash?(context)
30
+ translations[key] = context[I18N_VALUE_KEY]
31
+ else
32
+ translations[key] ||= {}
33
+ translations[key] = remove_zendesk_keys(context, translations[key])
34
+ end
35
+
36
+ else
37
+ translations[key] = context
38
+ end
39
+ end
40
+
41
+ translations
42
+ end
43
+
44
+ private
45
+
46
+ def is_translation_hash?(hash)
47
+ hash.keys.sort == I18N_KEYS
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,9 @@
1
+ if Object.const_defined?(:Rails) && Rails.const_defined?(:Engine)
2
+
3
+ module ZendeskAppsSupport
4
+ class Engine < Rails::Engine
5
+ engine_name 'zendesk_apps_support'
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,28 @@
1
+ module ZendeskAppsSupport
2
+ module I18n
3
+ class << self
4
+ def t(key, *args)
5
+ i18n.t(key, *args)
6
+ end
7
+
8
+ def set_locale(locale)
9
+ i18n.locale = locale
10
+ end
11
+
12
+ private
13
+
14
+ def i18n
15
+ @i18n ||= begin
16
+ require 'i18n'
17
+ ::I18n.load_path += locale_files
18
+ ::I18n
19
+ end
20
+ end
21
+
22
+ def locale_files
23
+ Dir[File.expand_path('../../../config/locales/*.yml', __FILE__)] -
24
+ Dir[File.expand_path('../../../config/locales/*.zendesk.yml', __FILE__)]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ module ZendeskAppsSupport
2
+ class Installation
3
+
4
+ attr_accessor :id, :app_id, :app_name, :requirements, :settings, :enabled, :updated_at, :created_at
5
+
6
+ def initialize(options)
7
+ options.each do |k, v|
8
+ public_send("#{k}=", v)
9
+ end
10
+ end
11
+
12
+ def to_json
13
+ hash = {}
14
+ self.instance_variables.each do |var|
15
+ hash[var.to_s.sub('@', '')] = self.instance_variable_get var
16
+ end
17
+ hash.to_json
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ require 'erubis'
2
+
3
+ module ZendeskAppsSupport
4
+ class Installed
5
+ INSTALLED_TEMPLATE = Erubis::Eruby.new(File.read(File.expand_path('../assets/installed.js.erb', __FILE__)))
6
+
7
+ def initialize(appsjs, installations = [])
8
+ @appsjs = appsjs
9
+ @installations = installations
10
+ end
11
+
12
+ def compile_js(options = {})
13
+ installation_order = options.fetch(:installation_orders, {})
14
+
15
+ INSTALLED_TEMPLATE.result(
16
+ appsjs: @appsjs,
17
+ installations: @installations,
18
+ installation_orders: installation_order
19
+ )
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ module ZendeskAppsSupport
2
+ module Location
3
+ # the numbers below match the enum values on the database, do not change them!
4
+ LOCATIONS_AVAILABLE = {
5
+ 'zendesk' => {
6
+ 'top_bar' => { 'id' => 1, 'orderable' => true },
7
+ 'nav_bar' => { 'id' => 2, 'orderable' => true },
8
+ 'ticket_sidebar' => { 'id' => 3, 'orderable' => true },
9
+ 'new_ticket_sidebar' => { 'id' => 4, 'orderable' => true },
10
+ 'user_sidebar' => { 'id' => 5, 'orderable' => true },
11
+ 'organization_sidebar' => { 'id' => 6, 'orderable' => true },
12
+ 'background' => { 'id' => 7, 'orderable' => false }
13
+ },
14
+ 'zopim' => {
15
+ 'chat_sidebar' => { 'id' => 8, 'orderable' => false }
16
+ }
17
+ }.freeze
18
+
19
+ class << self
20
+ def hosts
21
+ LOCATIONS_AVAILABLE.keys
22
+ end
23
+
24
+ def names_for(host)
25
+ LOCATIONS_AVAILABLE[host.to_s].keys
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,339 @@
1
+ require 'pathname'
2
+ require 'erubis'
3
+ require 'json'
4
+ require 'babel/transpiler'
5
+
6
+ module ZendeskAppsSupport
7
+ class Package
8
+ include ZendeskAppsSupport::BuildTranslation
9
+
10
+ REQUIREMENTS_FILENAME = "requirements.json"
11
+
12
+ DEFAULT_LAYOUT = Erubis::Eruby.new(File.read(File.expand_path('../assets/default_template.html.erb', __FILE__)))
13
+ DEFAULT_SCSS = File.read(File.expand_path('../assets/default_styles.scss', __FILE__))
14
+ SRC_TEMPLATE = Erubis::Eruby.new(File.read(File.expand_path('../assets/src.js.erb', __FILE__)))
15
+
16
+ LEGACY_URI_STUB = '_legacy'
17
+
18
+ attr_reader :lib_root, :root, :warnings
19
+
20
+ def initialize(dir, is_cached = true)
21
+ @root = Pathname.new(File.expand_path(dir))
22
+ @lib_root = Pathname.new(File.join(root, 'lib'))
23
+
24
+ @is_cached = is_cached # disabled by ZAT for development
25
+ @warnings = []
26
+ end
27
+
28
+ def validate(marketplace: true)
29
+ [].tap do |errors|
30
+ errors << Validations::Marketplace.call(self) if marketplace
31
+
32
+ errors << Validations::Manifest.call(self)
33
+
34
+ if has_manifest?
35
+ errors << Validations::Source.call(self)
36
+ errors << Validations::Translations.call(self)
37
+
38
+ unless manifest_json['requirementsOnly']
39
+ errors << Validations::Templates.call(self)
40
+ errors << Validations::Stylesheets.call(self)
41
+ end
42
+
43
+ if has_requirements?
44
+ errors << Validations::Requirements.call(self)
45
+ end
46
+ end
47
+
48
+ if has_banner?
49
+ errors << Validations::Banner.call(self)
50
+ end
51
+
52
+ errors.flatten!.compact!
53
+ end
54
+ end
55
+
56
+ def validate!(marketplace: true)
57
+ errors = validate(marketplace: marketplace)
58
+ if errors.any?
59
+ raise errors.first
60
+ end
61
+ true
62
+ end
63
+
64
+ def assets
65
+ @assets ||= Dir.chdir(root) do
66
+ Dir['assets/**/*'].select { |f| File.file?(f) }
67
+ end
68
+ end
69
+
70
+ def path_to(file)
71
+ File.join(root, file)
72
+ end
73
+
74
+ def requirements_path
75
+ path_to(REQUIREMENTS_FILENAME)
76
+ end
77
+
78
+ def locales
79
+ translations.keys
80
+ end
81
+
82
+ def files
83
+ files = []
84
+ Dir[root.join('**/**')].each do |f|
85
+ next unless File.file?(f)
86
+ relative_file_name = f.sub(/#{root}\/?/, '')
87
+ next if relative_file_name =~ /^tmp\//
88
+ files << AppFile.new(self, relative_file_name)
89
+ end
90
+ files
91
+ end
92
+
93
+ def js_files
94
+ @js_files ||= files.select { |f| f.to_s == 'app.js' || ( f.to_s.start_with?('lib/') && f.to_s.end_with?('.js') ) }
95
+ end
96
+
97
+ def lib_files
98
+ @lib_files ||= js_files.select { |f| f =~ /^lib\// }
99
+ end
100
+
101
+ def template_files
102
+ files.select { |f| f =~ /^templates\/.*\.hdbs$/ }
103
+ end
104
+
105
+ def translation_files
106
+ files.select { |f| f =~ /^translations\// }
107
+ end
108
+
109
+ def compile_js(options)
110
+ begin
111
+ app_id = options.fetch(:app_id)
112
+ asset_url_prefix = options.fetch(:assets_dir)
113
+ name = options.fetch(:app_name)
114
+ rescue KeyError => e
115
+ raise ArgumentError, e.message
116
+ end
117
+
118
+ locale = options.fetch(:locale, 'en')
119
+
120
+ source = iframe_only? ? nil : app_js
121
+ version = manifest_json['version']
122
+ app_class_name = "app-#{app_id}"
123
+ author = manifest_json['author']
124
+ framework_version = manifest_json['frameworkVersion']
125
+ single_install = manifest_json['singleInstall'] || false
126
+ templates = is_no_template ? {} : compiled_templates(app_id, asset_url_prefix)
127
+
128
+ app_settings = {
129
+ location: locations,
130
+ noTemplate: no_template_locations,
131
+ singleInstall: single_install
132
+ }.select { |_k, v| !v.nil? }
133
+
134
+ SRC_TEMPLATE.result(
135
+ name: name,
136
+ version: version,
137
+ is_es2015: !!manifest_json['es2015'],
138
+ source: source,
139
+ app_settings: app_settings,
140
+ asset_url_prefix: asset_url_prefix,
141
+ app_class_name: app_class_name,
142
+ author: author,
143
+ translations: translations_for(locale),
144
+ framework_version: framework_version,
145
+ templates: templates,
146
+ modules: commonjs_modules,
147
+ iframe_only: iframe_only?
148
+ )
149
+ end
150
+
151
+ def manifest_json
152
+ @manifest ||= read_json('manifest.json')
153
+ end
154
+
155
+ def requirements_json
156
+ return nil unless has_requirements?
157
+ @requirements ||= read_json('requirements.json')
158
+ end
159
+
160
+ def is_no_template
161
+ if manifest_json['noTemplate'].is_a?(Array)
162
+ false
163
+ else
164
+ !!manifest_json['noTemplate']
165
+ end
166
+ end
167
+
168
+ def no_template_locations
169
+ if manifest_json['noTemplate'].is_a?(Array)
170
+ manifest_json['noTemplate']
171
+ else
172
+ !!manifest_json['noTemplate']
173
+ end
174
+ end
175
+
176
+ def compiled_templates(app_id, asset_url_prefix)
177
+ compiled_css = ZendeskAppsSupport::StylesheetCompiler.new(DEFAULT_SCSS + app_css, app_id, asset_url_prefix).compile
178
+
179
+ layout = templates['layout'] || DEFAULT_LAYOUT.result
180
+
181
+ templates.tap do |templates|
182
+ templates['layout'] = "<style>\n#{compiled_css}</style>\n#{layout}"
183
+ end
184
+ end
185
+
186
+ def market_translations!(locale)
187
+ result = translations[locale].fetch('app', {})
188
+ result.delete('name')
189
+ result.delete('description')
190
+ result.delete('long_description')
191
+ result.delete('installation_instructions')
192
+ result
193
+ end
194
+
195
+ def has_location?
196
+ manifest_json['location']
197
+ end
198
+
199
+ def has_file?(path)
200
+ File.exist?(path_to(path))
201
+ end
202
+
203
+ def app_css
204
+ css_file = path_to('app.css')
205
+ scss_file = path_to('app.scss')
206
+ File.exist?(scss_file) ? File.read(scss_file) : ( File.exist?(css_file) ? File.read(css_file) : '' )
207
+ end
208
+
209
+ def locations
210
+ locations = manifest_json['location']
211
+ case locations
212
+ when Hash
213
+ locations
214
+ when Array
215
+ { 'zendesk' => Hash[locations.map { |location| [ location, LEGACY_URI_STUB ] }] }
216
+ when String
217
+ { 'zendesk' => { locations => LEGACY_URI_STUB } }
218
+ else # NilClass
219
+ { 'zendesk' => {} }
220
+ end
221
+ end
222
+
223
+ def iframe_only?
224
+ !legacy_non_iframe_app?
225
+ end
226
+
227
+ private
228
+
229
+ def legacy_non_iframe_app?
230
+ iframe_urls = locations.values.flat_map(&:values)
231
+ iframe_urls.all? { |l| l == LEGACY_URI_STUB }
232
+ end
233
+
234
+ def templates
235
+ templates_dir = File.join(root, 'templates')
236
+ Dir["#{templates_dir}/*.hdbs"].inject({}) do |memo, file|
237
+ str = File.read(file)
238
+ str.chomp!
239
+ memo[File.basename(file, File.extname(file))] = str
240
+ memo
241
+ end
242
+ end
243
+
244
+ def translations_for(locale)
245
+ trans = translations
246
+ return trans[locale] if trans[locale]
247
+ trans[self.manifest_json['defaultLocale']]
248
+ end
249
+
250
+ def translations
251
+ return @translations if @is_cached && @translations
252
+
253
+ @translations = begin
254
+ translation_dir = File.join(root, 'translations')
255
+ return {} unless File.directory?(translation_dir)
256
+
257
+ locale_path = "#{translation_dir}/#{self.manifest_json['defaultLocale']}.json"
258
+ default_translations = process_translations(locale_path)
259
+
260
+ Dir["#{translation_dir}/*.json"].inject({}) do |memo, path|
261
+ locale = File.basename(path, File.extname(path))
262
+
263
+ locale_translations = if locale == self.manifest_json['defaultLocale']
264
+ default_translations
265
+ else
266
+ deep_merge_hash(default_translations, process_translations(path))
267
+ end
268
+
269
+ memo[locale] = locale_translations
270
+ memo
271
+ end
272
+ end
273
+ end
274
+
275
+ def process_translations(locale_path)
276
+ translations = File.exist?(locale_path) ? JSON.parse(File.read(locale_path)) : {}
277
+ translations['app'].delete('package') if translations.has_key?('app')
278
+ remove_zendesk_keys(translations)
279
+ end
280
+
281
+ def has_lib_js?
282
+ lib_files.any?
283
+ end
284
+
285
+ def has_manifest?
286
+ has_file?('manifest.json')
287
+ end
288
+
289
+ def has_requirements?
290
+ has_file?('requirements.json')
291
+ end
292
+
293
+ def has_banner?
294
+ has_file?('assets/banner.png')
295
+ end
296
+
297
+ def app_js
298
+ transpile(read_file('app.js'))
299
+ end
300
+
301
+ def transpile(code)
302
+ manifest_json['es2015'] ? Babel::Transpiler.transform(code)['code'] : code
303
+ end
304
+
305
+ def commonjs_modules
306
+ return {} unless has_lib_js?
307
+
308
+ lib_files.each_with_object({}) do |file, modules|
309
+ raise Validations::ValidationError.new('reserved_filename') if file.relative_path == 'lib/_root.js'
310
+ name = file.relative_path.gsub(/^lib\//, '')
311
+ content = file.read
312
+ modules[name] = transpile(content)
313
+ end
314
+ end
315
+
316
+ def deep_merge_hash(h, another_h)
317
+ result_h = h.dup
318
+ another_h.each do |key, value|
319
+ if h.has_key?(key) && h[key].is_a?(Hash) && value.is_a?(Hash)
320
+ result_h[key] = deep_merge_hash(h[key], value)
321
+ else
322
+ result_h[key] = value
323
+ end
324
+ end
325
+ result_h
326
+ end
327
+
328
+ def read_file(path)
329
+ File.read(path_to(path))
330
+ end
331
+
332
+ def read_json(path)
333
+ file = read_file(path)
334
+ unless file.nil?
335
+ JSON.parse(file)
336
+ end
337
+ end
338
+ end
339
+ end