ckeditor5 1.23.3 → 1.23.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d717a5397379dffdfd252d92c6b6f58a524430c0ae99798a46ff7f71556d3b9
4
- data.tar.gz: 046715b5823ae3f38b3b2c1175fa4f1bdd5e34f9e260777dbcb714be287ec18b
3
+ metadata.gz: 4f7600884a50bcb68e1f98db4063a3d885f9fbd4aa62b8ac6a793b8c5c4bc4af
4
+ data.tar.gz: e6a4f58340359d0c6c84fbd3e6aed0ce7bfe6cab733dc70b8e6640ed399cee3f
5
5
  SHA512:
6
- metadata.gz: a552a32f61e75023f913b6df8c414b9af075942b97144e10582c4cfa1955bf988de8be6f2e652618ae3640364691f20c9ae3b679f3eb2017f78d3aa740580616
7
- data.tar.gz: 934d9853eb527d2f8a65ecc87e6c4fac989a0bb1133f5a3b6154ebe8aca3239199a84a801c048e6909919b76295bae3e29f6af9e60f98f2cee46cfb8167647d7
6
+ metadata.gz: 448bd74e867966489554bf08eb40ef2b43069ad9f2fd884aadbae94295b5fc819bdc20deceb037bcda3ed73e57c74754bc860b39ceb45ee43f5938e2a1bc457c
7
+ data.tar.gz: d0560e88dacdce2e9f73f2001f701077a67bf6724cbc008c844ff458e71c556b9e0c7e14406cd06b16f4fc8b322bf53cc38094b188dc08995b658674e0cd6c10
@@ -19,13 +19,24 @@ module CKEditor5::Rails::Assets
19
19
  @lazy = lazy
20
20
  end
21
21
 
22
- def to_html
22
+ def to_html(nonce: nil)
23
23
  tags = [
24
- WebComponentBundle.instance.to_html
24
+ WebComponentBundle.instance.to_html(nonce: nonce)
25
25
  ]
26
26
 
27
- tags.prepend(preload_tags, styles_tags, window_scripts_tags) unless lazy
28
- tags.prepend(AssetsImportMap.new(bundle).to_html) if importmap
27
+ unless lazy
28
+ tags.prepend(
29
+ preload_tags(nonce: nonce),
30
+ styles_tags(nonce: nonce),
31
+ window_scripts_tags(nonce: nonce)
32
+ )
33
+ end
34
+
35
+ if importmap
36
+ tags.prepend(
37
+ AssetsImportMap.new(bundle).to_html(nonce: nonce)
38
+ )
39
+ end
29
40
 
30
41
  safe_join(tags)
31
42
  end
@@ -40,34 +51,42 @@ module CKEditor5::Rails::Assets
40
51
 
41
52
  private
42
53
 
43
- def window_scripts_tags
44
- @window_scripts_tags ||= safe_join(bundle.scripts.filter_map do |script|
45
- tag.script(src: script.url, nonce: true, crossorigin: 'anonymous') if script.window?
46
- end)
54
+ def window_scripts_tags(nonce: nil)
55
+ scripts = bundle.scripts.filter_map do |script|
56
+ tag.script(src: script.url, nonce: nonce, crossorigin: 'anonymous') if script.window?
57
+ end
58
+
59
+ safe_join(scripts)
47
60
  end
48
61
 
49
- def styles_tags
50
- @styles_tags ||= safe_join(bundle.stylesheets.map do |url|
51
- tag.link(href: url, rel: 'stylesheet', crossorigin: 'anonymous')
52
- end)
62
+ def styles_tags(nonce: nil)
63
+ styles = bundle.stylesheets.map do |url|
64
+ tag.link(href: url, nonce: nonce, rel: 'stylesheet', crossorigin: 'anonymous')
65
+ end
66
+
67
+ safe_join(styles)
53
68
  end
54
69
 
55
- def preload_tags
56
- @preload_tags ||= safe_join(bundle.preloads.map do |preload|
70
+ def preload_tags(nonce: nil)
71
+ preloads = bundle.preloads.map do |preload|
57
72
  if preload.is_a?(Hash) && preload[:as] && preload[:href]
58
73
  tag.link(
59
74
  **preload,
75
+ nonce: nonce,
60
76
  crossorigin: 'anonymous'
61
77
  )
62
78
  else
63
79
  tag.link(
64
80
  href: preload,
65
81
  rel: 'preload',
82
+ nonce: nonce,
66
83
  as: self.class.url_resource_preload_type(preload),
67
84
  crossorigin: 'anonymous'
68
85
  )
69
86
  end
70
- end)
87
+ end
88
+
89
+ safe_join(preloads)
71
90
  end
72
91
  end
73
92
 
@@ -90,11 +109,11 @@ module CKEditor5::Rails::Assets
90
109
  { imports: import_map }.to_json
91
110
  end
92
111
 
93
- def to_html
112
+ def to_html(nonce: nil)
94
113
  tag.script(
95
114
  to_json.html_safe,
96
115
  type: 'importmap',
97
- nonce: true
116
+ nonce: nonce
98
117
  )
99
118
  end
100
119
 
@@ -3,54 +3,38 @@
3
3
  require 'singleton'
4
4
  require 'terser'
5
5
 
6
- require_relative '../editor/props_inline_plugin'
7
-
8
- module CKEditor5::Rails
9
- module Assets
10
- class WebComponentBundle
11
- include ActionView::Helpers::TagHelper
12
- include Singleton
13
-
14
- WEBCOMPONENTS_PATH = File.join(__dir__, 'webcomponents')
15
- WEBCOMPONENTS_MODULES = [
16
- 'utils.mjs',
17
- 'components/editable.mjs',
18
- 'components/ui-part.mjs',
19
- 'components/editor.mjs',
20
- 'components/context.mjs'
21
- ].freeze
22
-
23
- def source
24
- @source ||= compress_source(raw_source)
25
- end
26
-
27
- def to_html
28
- @to_html ||= tag.script(source, type: 'module', nonce: true)
29
- end
30
-
31
- private
32
-
33
- def raw_source
34
- @raw_source ||= WEBCOMPONENTS_MODULES.map do |file|
35
- content = File.read(File.join(WEBCOMPONENTS_PATH, file))
36
-
37
- if file == 'utils.mjs'
38
- inject_inline_code_signatures(content)
39
- else
40
- content
41
- end
42
- end.join("\n")
43
- end
44
-
45
- def inject_inline_code_signatures(content)
46
- json_signatures = Editor::InlinePluginsSignaturesRegistry.instance.to_a.to_json
47
-
48
- content.sub('__INLINE_CODE_SIGNATURES_PLACEHOLDER__', json_signatures)
49
- end
50
-
51
- def compress_source(code)
52
- Terser.new(compress: true, mangle: true).compile(code).html_safe
53
- end
6
+ module CKEditor5::Rails::Assets
7
+ class WebComponentBundle
8
+ include ActionView::Helpers::TagHelper
9
+ include Singleton
10
+
11
+ WEBCOMPONENTS_PATH = File.join(__dir__, 'webcomponents')
12
+ WEBCOMPONENTS_MODULES = [
13
+ 'utils.mjs',
14
+ 'components/editable.mjs',
15
+ 'components/ui-part.mjs',
16
+ 'components/editor.mjs',
17
+ 'components/context.mjs'
18
+ ].freeze
19
+
20
+ def source
21
+ @source ||= compress_source(raw_source)
22
+ end
23
+
24
+ def to_html(nonce: nil)
25
+ tag.script(source, type: 'module', nonce: nonce)
26
+ end
27
+
28
+ private
29
+
30
+ def raw_source
31
+ @raw_source ||= WEBCOMPONENTS_MODULES.map do |file|
32
+ File.read(File.join(WEBCOMPONENTS_PATH, file))
33
+ end.join("\n")
34
+ end
35
+
36
+ def compress_source(code)
37
+ Terser.new(compress: true, mangle: true).compile(code).html_safe
54
38
  end
55
39
  end
56
40
  end
@@ -568,14 +568,10 @@ class CKEditorComponent extends HTMLElement {
568
568
  }
569
569
 
570
570
  /**
571
- * Gets bundle JSON description from translations attribute.
572
- *
573
- * **warning**: It's present only if the editor is loaded lazily.
571
+ * Gets bundle JSON description from translations attribute
574
572
  */
575
573
  #getBundle() {
576
- return this.#bundle ||= JSON.parse(
577
- this.getAttribute('bundle') || '{ "scripts": [], "stylesheets": [] }'
578
- );
574
+ return this.#bundle ||= JSON.parse(this.getAttribute('bundle'));
579
575
  }
580
576
 
581
577
 
@@ -20,22 +20,6 @@ function execIfDOMReady(callback) {
20
20
  }
21
21
  }
22
22
 
23
- /**
24
- * Verifies code signature using SHA-256 hash.
25
- *
26
- * @param {string} code - Source code to verify
27
- * @returns {Promise<boolean>} True if code signature is valid
28
- */
29
- async function verifyCodeSignature(code) {
30
- const signature = await crypto.subtle
31
- .digest('SHA-256', new TextEncoder().encode(code))
32
- .then(hash => Array.from(new Uint8Array(hash))
33
- .map(b => b.toString(16).padStart(2, '0'))
34
- .join(''));
35
-
36
- return __INLINE_CODE_SIGNATURES_PLACEHOLDER__.includes(signature);
37
- }
38
-
39
23
  /**
40
24
  * Dynamically imports modules based on configuration
41
25
  *
@@ -47,14 +31,10 @@ async function verifyCodeSignature(code) {
47
31
  * @param {Object} imports[].window_name - Global window object name (for external type)
48
32
  * @param {('inline'|'external')} imports[].type - Type of import
49
33
  * @returns {Promise<Array<any>>} Array of loaded modules
50
- * @throws {Error} When plugin loading fails or signature validation fails
34
+ * @throws {Error} When plugin loading fails
51
35
  */
52
36
  function loadAsyncImports(imports = []) {
53
37
  const loadInlinePlugin = async ({ name, code }) => {
54
- if (!await verifyCodeSignature(code)) {
55
- throw new Error(`Invalid code signature for inline plugin "${name}"!`);
56
- }
57
-
58
38
  const module = await import(`data:text/javascript,${encodeURIComponent(code)}`);
59
39
 
60
40
  if (!module.default) {
@@ -99,16 +99,15 @@ module CKEditor5::Rails
99
99
 
100
100
  if importmap_available?
101
101
  @__ckeditor_context = {
102
- bundle: combined_bundle,
103
- lazy: true
102
+ bundle: combined_bundle
104
103
  }
105
104
 
106
- return Assets::WebComponentBundle.instance.to_html
105
+ return Assets::WebComponentBundle.instance.to_html(nonce: content_security_policy_nonce)
107
106
  end
108
107
 
109
108
  safe_join([
110
- Assets::AssetsImportMap.new(combined_bundle).to_html,
111
- Assets::WebComponentBundle.instance.to_html
109
+ Assets::AssetsImportMap.new(combined_bundle).to_html(nonce: content_security_policy_nonce),
110
+ Assets::WebComponentBundle.instance.to_html(nonce: content_security_policy_nonce)
112
111
  ])
113
112
  end
114
113
 
@@ -183,7 +182,7 @@ module CKEditor5::Rails
183
182
  lazy: lazy
184
183
  )
185
184
 
186
- html = serializer.to_html
185
+ html = serializer.to_html(nonce: content_security_policy_nonce)
187
186
 
188
187
  if importmap_available?
189
188
  @__ckeditor_context[:html_tags] = html
@@ -40,8 +40,8 @@ module CKEditor5::Rails::Context
40
40
  # <% preset = ckeditor5_context_preset do
41
41
  # plugins :Comments, :TrackChanges, :Collaboration # Shared functionality plugins
42
42
  # end %>
43
- def ckeditor5_context_preset(**kwargs, &block)
44
- PresetBuilder.new(disallow_inline_plugins: true, **kwargs, &block)
43
+ def ckeditor5_context_preset(&block)
44
+ PresetBuilder.new(&block)
45
45
  end
46
46
  end
47
47
  end
@@ -38,8 +38,7 @@ module CKEditor5::Rails
38
38
  # version '43.3.1'
39
39
  # toolbar :bold, :italic
40
40
  # end
41
- def initialize(disallow_inline_plugins: false, &block)
42
- @disallow_inline_plugins = disallow_inline_plugins
41
+ def initialize(&block)
43
42
  @config = {
44
43
  plugins: []
45
44
  }
@@ -56,7 +56,7 @@ module CKEditor5::Rails
56
56
  # <%= form_for @post do |f| %>
57
57
  # <%= f.ckeditor5 :content, required: true %>
58
58
  # <% end %>
59
- def ckeditor5_editor( # rubocop:disable Metrics/ParameterLists,Metrics/CyclomaticComplexity
59
+ def ckeditor5_editor( # rubocop:disable Metrics/ParameterLists
60
60
  preset: nil,
61
61
  config: nil, extra_config: {}, type: nil,
62
62
  initial_data: nil, watchdog: true,
@@ -78,9 +78,7 @@ module CKEditor5::Rails
78
78
 
79
79
  editor_props = Editor::Props.new(
80
80
  type, config,
81
- # Use fallback only if there is no `ckeditor5_assets` in the head.
82
- # So web-component will be loaded from the CDN.
83
- bundle: context[:lazy] ? context[:bundle] : nil,
81
+ bundle: context[:bundle],
84
82
  watchdog: watchdog,
85
83
  editable_height: editable_height || preset.editable_height
86
84
  )
@@ -161,15 +159,13 @@ module CKEditor5::Rails
161
159
 
162
160
  return {
163
161
  bundle: create_preset_bundle(found_preset),
164
- preset: found_preset,
165
- lazy: true
162
+ preset: found_preset
166
163
  }
167
164
  end
168
165
 
169
166
  {
170
167
  bundle: nil,
171
- preset: Engine.default_preset,
172
- lazy: true
168
+ preset: Engine.default_preset
173
169
  }
174
170
  end
175
171
  end
@@ -45,7 +45,7 @@ module CKEditor5::Rails::Editor
45
45
 
46
46
  def serialized_attributes
47
47
  {
48
- bundle: bundle&.to_json,
48
+ bundle: bundle.to_json,
49
49
  plugins: serialize_plugins,
50
50
  config: serialize_config,
51
51
  watchdog: watchdog
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'singleton'
4
- require 'digest'
5
-
6
3
  require_relative 'props_base_plugin'
7
4
 
8
5
  module CKEditor5::Rails::Editor
@@ -14,8 +11,6 @@ module CKEditor5::Rails::Editor
14
11
 
15
12
  @code = code
16
13
  validate_code!
17
-
18
- InlinePluginsSignaturesRegistry.instance.register(code)
19
14
  end
20
15
 
21
16
  def to_h
@@ -37,28 +32,4 @@ module CKEditor5::Rails::Editor
37
32
  'Code must include `export default` that exports plugin definition!'
38
33
  end
39
34
  end
40
-
41
- class InlinePluginsSignaturesRegistry
42
- include Singleton
43
-
44
- def initialize
45
- @signatures = Set.new
46
- end
47
-
48
- def register(code)
49
- signature = generate_signature(code)
50
- @signatures.add(signature)
51
- signature
52
- end
53
-
54
- def to_a
55
- @signatures.to_a
56
- end
57
-
58
- private
59
-
60
- def generate_signature(code)
61
- Digest::SHA256.hexdigest(code)
62
- end
63
- end
64
35
  end
@@ -47,6 +47,7 @@ module CKEditor5::Rails::Hooks
47
47
  def merge_import_maps_json(a_json, b_json)
48
48
  a = JSON.parse(a_json)
49
49
  b = JSON.parse(b_json)
50
+
50
51
  a['imports'].merge!(b['imports'])
51
52
  a.to_json
52
53
  rescue JSON::ParserError => e
@@ -2,7 +2,7 @@
2
2
 
3
3
  module CKEditor5
4
4
  module Rails
5
- VERSION = '1.23.3'
5
+ VERSION = '1.23.5'
6
6
 
7
7
  DEFAULT_CKEDITOR_VERSION = '44.1.0'
8
8
  end
@@ -32,7 +32,7 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsBundleHtmlSerializer do
32
32
  end
33
33
 
34
34
  describe '#to_html' do
35
- subject(:html) { serializer.to_html }
35
+ subject(:html) { serializer.to_html(nonce: 'true') }
36
36
 
37
37
  it 'includes window script tags' do
38
38
  expect(html).to have_tag('script', with: {
@@ -240,7 +240,7 @@ RSpec.describe CKEditor5::Rails::Assets::AssetsImportMap do
240
240
 
241
241
  describe '#to_html' do
242
242
  it 'generates script tag with import map' do
243
- html = import_map.to_html
243
+ html = import_map.to_html(nonce: 'true')
244
244
  expect(html).to have_tag('script', with: { type: 'importmap', nonce: 'true' }) do
245
245
  with_text('{"imports":{"@ckeditor/module":"https://cdn.com/script2.js"}}')
246
246
  end
@@ -10,6 +10,10 @@ RSpec.describe CKEditor5::Rails::Cdn::Helpers do
10
10
  def importmap_rendered?
11
11
  false
12
12
  end
13
+
14
+ def content_security_policy_nonce
15
+ 'test-nonce'
16
+ end
13
17
  end
14
18
  end
15
19
 
@@ -107,18 +107,5 @@ RSpec.describe CKEditor5::Rails::Context::Helpers do
107
107
  preset: :custom
108
108
  )
109
109
  end
110
-
111
- it 'raises error when trying to define inline plugin' do
112
- expect do
113
- helper.ckeditor5_context_preset do
114
- inline_plugin :TestPlugin, <<~JS
115
- export default class TestPlugin { }
116
- JS
117
- end
118
- end.to raise_error(
119
- CKEditor5::Rails::Presets::Concerns::PluginMethods::DisallowedInlinePlugin,
120
- 'Inline plugins are not allowed here.'
121
- )
122
- end
123
110
  end
124
111
  end
@@ -58,7 +58,6 @@ RSpec.describe CKEditor5::Rails::Editor::Helpers::Editor do
58
58
  result = helper.ckeditor5_context_or_fallback(:custom)
59
59
  expect(result).to match({
60
60
  bundle: 'custom-bundle',
61
- lazy: true,
62
61
  preset: custom_preset
63
62
  })
64
63
  end
@@ -72,33 +71,9 @@ RSpec.describe CKEditor5::Rails::Editor::Helpers::Editor do
72
71
  result = helper.ckeditor5_context_or_fallback(nil)
73
72
  expect(result).to match({
74
73
  bundle: nil,
75
- lazy: true,
76
74
  preset: :default
77
75
  })
78
76
  end
79
-
80
- it 'includes lazy flag in fallback context' do
81
- allow(CKEditor5::Rails::Engine).to receive(:default_preset)
82
- .and_return(:default)
83
-
84
- result = helper.ckeditor5_context_or_fallback(nil)
85
- expect(result[:lazy]).to be true
86
- end
87
-
88
- it 'includes lazy flag in preset context' do
89
- custom_preset = instance_double(CKEditor5::Rails::Presets::PresetBuilder)
90
-
91
- allow(CKEditor5::Rails::Engine).to receive(:find_preset)
92
- .with(:custom)
93
- .and_return(custom_preset)
94
-
95
- allow(helper).to receive(:create_preset_bundle)
96
- .with(custom_preset)
97
- .and_return('custom-bundle')
98
-
99
- result = helper.ckeditor5_context_or_fallback(:custom)
100
- expect(result[:lazy]).to be true
101
- end
102
77
  end
103
78
 
104
79
  describe '#ckeditor5_editor' do
@@ -250,31 +225,6 @@ RSpec.describe CKEditor5::Rails::Editor::Helpers::Editor do
250
225
  expect(helper.ckeditor5_editor(editable_height: 600)).to include('editable-height="600px"')
251
226
  end
252
227
  end
253
-
254
- context 'when using bundles' do
255
- let(:bundle) { 'test-bundle' }
256
- let(:context) { { preset: :default, bundle: bundle, lazy: true } }
257
-
258
- it 'uses bundle from context when lazy is true' do
259
- expect(CKEditor5::Rails::Editor::Props).to receive(:new)
260
- .with(anything, anything, hash_including(bundle: bundle))
261
- .and_call_original
262
-
263
- helper.ckeditor5_editor
264
- end
265
-
266
- context 'when lazy is false' do
267
- let(:context) { { preset: :default, bundle: bundle, lazy: false } }
268
-
269
- it 'does not use bundle from context' do
270
- expect(CKEditor5::Rails::Editor::Props).to receive(:new)
271
- .with(anything, anything, hash_including(bundle: nil))
272
- .and_call_original
273
-
274
- helper.ckeditor5_editor
275
- end
276
- end
277
- end
278
228
  end
279
229
 
280
230
  describe '#ckeditor5_editable' do
@@ -7,7 +7,11 @@ RSpec.describe CKEditor5::Rails::Hooks::Form do
7
7
  let(:post) { Post.new(content: 'Initial content') }
8
8
  let(:builder) { described_class.new(:post, post, template) }
9
9
  let(:template) do
10
- ActionView::Base.new(ActionView::LookupContext.new([]), {}, nil)
10
+ Class.new(ActionView::Base) do
11
+ def content_security_policy_nonce
12
+ 'test-nonce'
13
+ end
14
+ end.new(ActionView::LookupContext.new([]), {}, nil)
11
15
  end
12
16
 
13
17
  before do
@@ -18,10 +22,24 @@ RSpec.describe CKEditor5::Rails::Hooks::Form do
18
22
  subject(:rendered_editor) { builder.build_editor(:content) }
19
23
 
20
24
  it 'renders ckeditor element' do
25
+ bundle_json = {
26
+ scripts: [
27
+ {
28
+ import_name: 'ckeditor5',
29
+ url: 'https://cdn.jsdelivr.net/npm/ckeditor5@34.1.0/dist/browser/ckeditor5.js',
30
+ translation: false
31
+ }
32
+ ],
33
+ stylesheets: [
34
+ 'https://cdn.jsdelivr.net/npm/ckeditor5@34.1.0/dist/browser/ckeditor5.css'
35
+ ]
36
+ }.to_json
37
+
21
38
  attrs = {
22
39
  name: 'post[content]',
23
40
  id: 'post_content',
24
41
  type: 'ClassicEditor',
42
+ bundle: bundle_json,
25
43
  watchdog: 'true'
26
44
  }
27
45
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ckeditor5
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.23.3
4
+ version: 1.23.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Bagiński
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-12-19 00:00:00.000000000 Z
12
+ date: 2024-12-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails