xat_support 1.29.3

Sign up to get free protection for your applications and to get access to all the features.
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