From 6b188e7ba6d4c088b71ab043001b216e23740ba2 Mon Sep 17 00:00:00 2001 From: Niklas Haeusele Date: Tue, 3 Dec 2024 23:49:42 +0100 Subject: [PATCH 1/4] Support foo_bar -> --- app/assets/javascript/custom_elements-rails.js | 2 +- test/custom_elements/integration_test.rb | 6 ++++++ .../app/javascript/custom_elements/foo_bar_element.js | 9 +++++++++ test/dummy/app/views/elements/show.html.erb | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/dummy/app/javascript/custom_elements/foo_bar_element.js diff --git a/app/assets/javascript/custom_elements-rails.js b/app/assets/javascript/custom_elements-rails.js index 8dc8a60..3be23b7 100644 --- a/app/assets/javascript/custom_elements-rails.js +++ b/app/assets/javascript/custom_elements-rails.js @@ -4,7 +4,7 @@ export function eagerDefineCustomElementsFrom(namespace, options = {}) { const regex = new RegExp(`${namespace}\/(.*?)_element`) Object.entries(imports) .filter(([name, _]) => name.match(regex) ) - .map(([name, importPath]) => [name.match(regex)[1], importPath]) + .map(([name, importPath]) => [name.match(regex)[1].replace('_', '-'), importPath]) .forEach(([name, importPath]) => { import(importPath) .then((module) => { diff --git a/test/custom_elements/integration_test.rb b/test/custom_elements/integration_test.rb index afadad4..29747bd 100644 --- a/test/custom_elements/integration_test.rb +++ b/test/custom_elements/integration_test.rb @@ -6,4 +6,10 @@ class CustomElements::IntegrationTest < ApplicationSystemTestCase assert_text "connectedCallback(): hello_element.js" end + + test "foo_bar_element.js connects" do + visit elements_path + + assert_text "connectedCallback(): foo_bar_element.js" + end end diff --git a/test/dummy/app/javascript/custom_elements/foo_bar_element.js b/test/dummy/app/javascript/custom_elements/foo_bar_element.js new file mode 100644 index 0000000..08e42f7 --- /dev/null +++ b/test/dummy/app/javascript/custom_elements/foo_bar_element.js @@ -0,0 +1,9 @@ +export default class extends HTMLElement { + constructor() { + super() + } + + connectedCallback() { + this.textContent = "connectedCallback(): foo_bar_element.js" + } +} diff --git a/test/dummy/app/views/elements/show.html.erb b/test/dummy/app/views/elements/show.html.erb index 9347b36..6a8e4f0 100644 --- a/test/dummy/app/views/elements/show.html.erb +++ b/test/dummy/app/views/elements/show.html.erb @@ -1,3 +1,4 @@

Elements#show

+ From 8a3fa4f8ecdf9cb436c1260e6557d23e21b2227c Mon Sep 17 00:00:00 2001 From: Niklas Haeusele Date: Tue, 3 Dec 2024 23:53:54 +0100 Subject: [PATCH 2/4] Support some-name-element.js -> --- app/assets/javascript/custom_elements-rails.js | 2 +- test/custom_elements/integration_test.rb | 6 ++++++ .../app/javascript/custom_elements/some-name-element.js | 9 +++++++++ test/dummy/app/views/elements/show.html.erb | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/dummy/app/javascript/custom_elements/some-name-element.js diff --git a/app/assets/javascript/custom_elements-rails.js b/app/assets/javascript/custom_elements-rails.js index 3be23b7..0918454 100644 --- a/app/assets/javascript/custom_elements-rails.js +++ b/app/assets/javascript/custom_elements-rails.js @@ -1,7 +1,7 @@ export function eagerDefineCustomElementsFrom(namespace, options = {}) { const importmap = document.querySelector('script[type="importmap"]') const { imports } = JSON.parse(importmap.textContent) - const regex = new RegExp(`${namespace}\/(.*?)_element`) + const regex = new RegExp(`${namespace}\/(.*?)[_-]element`) Object.entries(imports) .filter(([name, _]) => name.match(regex) ) .map(([name, importPath]) => [name.match(regex)[1].replace('_', '-'), importPath]) diff --git a/test/custom_elements/integration_test.rb b/test/custom_elements/integration_test.rb index 29747bd..b61b53d 100644 --- a/test/custom_elements/integration_test.rb +++ b/test/custom_elements/integration_test.rb @@ -12,4 +12,10 @@ class CustomElements::IntegrationTest < ApplicationSystemTestCase assert_text "connectedCallback(): foo_bar_element.js" end + + test "some-name-element.js connects" do + visit elements_path + + assert_text "connectedCallback(): some-name-element.js" + end end diff --git a/test/dummy/app/javascript/custom_elements/some-name-element.js b/test/dummy/app/javascript/custom_elements/some-name-element.js new file mode 100644 index 0000000..6470b9a --- /dev/null +++ b/test/dummy/app/javascript/custom_elements/some-name-element.js @@ -0,0 +1,9 @@ +export default class extends HTMLElement { + constructor() { + super() + } + + connectedCallback() { + this.textContent = "connectedCallback(): some-name-element.js" + } +} diff --git a/test/dummy/app/views/elements/show.html.erb b/test/dummy/app/views/elements/show.html.erb index 6a8e4f0..e7fd271 100644 --- a/test/dummy/app/views/elements/show.html.erb +++ b/test/dummy/app/views/elements/show.html.erb @@ -2,3 +2,4 @@ + From 51968c3f3cb6e6cdb04f4e59baefb617af3b0038 Mon Sep 17 00:00:00 2001 From: Niklas Haeusele Date: Wed, 4 Dec 2024 00:35:17 +0100 Subject: [PATCH 3/4] support different filepatterns --- Gemfile | 1 + app/assets/javascript/custom_elements-rails.js | 10 +++++++--- test/custom_elements/integration_test.rb | 6 ++++++ .../custom_elements/namespace/demo_element.js | 9 +++++++++ test/dummy/app/views/elements/show.html.erb | 1 + 5 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 test/dummy/app/javascript/custom_elements/namespace/demo_element.js diff --git a/Gemfile b/Gemfile index b58f7c7..a973ea1 100644 --- a/Gemfile +++ b/Gemfile @@ -10,3 +10,4 @@ gem "puma" gem "debug", ">= 1.0.0" gem "capybara" gem "selenium-webdriver" +gem "sqlite3", "~> 1.4" diff --git a/app/assets/javascript/custom_elements-rails.js b/app/assets/javascript/custom_elements-rails.js index 0918454..73dc1c7 100644 --- a/app/assets/javascript/custom_elements-rails.js +++ b/app/assets/javascript/custom_elements-rails.js @@ -1,14 +1,18 @@ export function eagerDefineCustomElementsFrom(namespace, options = {}) { + const pathToElementName = (path) => { + const parts = path.split('/').map(p => p.replace(/_/g, '-')); + return `${options.prefix}-${parts.slice(0, -1).join('--')}${parts.length > 1 ? '--' : ''}${parts.at(-1)}`; + } const importmap = document.querySelector('script[type="importmap"]') const { imports } = JSON.parse(importmap.textContent) - const regex = new RegExp(`${namespace}\/(.*?)[_-]element`) + const regex = new RegExp(`${namespace}/(.*?)[-_]element`) Object.entries(imports) .filter(([name, _]) => name.match(regex) ) - .map(([name, importPath]) => [name.match(regex)[1].replace('_', '-'), importPath]) + .map(([name, importPath]) => { return [pathToElementName(name.match(regex)[1]), importPath] }) .forEach(([name, importPath]) => { import(importPath) .then((module) => { - customElements.define(`${options.prefix}-${name}`, module.default) + customElements.define(name, module.default) }) .catch((error) => { console.error(`custom_elements-rails: Could not import custom element <${options.prefix}-${name}>`) diff --git a/test/custom_elements/integration_test.rb b/test/custom_elements/integration_test.rb index b61b53d..49c8a5c 100644 --- a/test/custom_elements/integration_test.rb +++ b/test/custom_elements/integration_test.rb @@ -18,4 +18,10 @@ class CustomElements::IntegrationTest < ApplicationSystemTestCase assert_text "connectedCallback(): some-name-element.js" end + + test "namespace/demo_element.js connects" do + visit elements_path + + assert_text "connectedCallback(): demo_element.js" + end end diff --git a/test/dummy/app/javascript/custom_elements/namespace/demo_element.js b/test/dummy/app/javascript/custom_elements/namespace/demo_element.js new file mode 100644 index 0000000..3cbdafd --- /dev/null +++ b/test/dummy/app/javascript/custom_elements/namespace/demo_element.js @@ -0,0 +1,9 @@ +export default class extends HTMLElement { + constructor() { + super() + } + + connectedCallback() { + this.textContent = "connectedCallback(): demo_element.js" + } +} diff --git a/test/dummy/app/views/elements/show.html.erb b/test/dummy/app/views/elements/show.html.erb index e7fd271..d386720 100644 --- a/test/dummy/app/views/elements/show.html.erb +++ b/test/dummy/app/views/elements/show.html.erb @@ -3,3 +3,4 @@ + From 6342f8e28efc31a91a824f85c7a8187e863ddac8 Mon Sep 17 00:00:00 2001 From: Niklas Haeusele Date: Wed, 4 Dec 2024 00:46:42 +0100 Subject: [PATCH 4/4] Add docs --- README.md | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f50aec9..444040c 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,33 @@ custom_elements/hello_element.js // will register automatically Your `*_element.js` files have to `export default` custom elements for this to work properly. -> [!WARNING] -> Only single word elements are supported currently. See https://github.com/codergeek121/custom_elements-rails/issues/1 +### Naming Convention for Custom Elements + +When defining custom elements from files, their filenames are used to generate the element names automatically. The following rules and examples clarify how file paths are converted to custom element names: + +#### Usage + +Register all files in the `custom_elements` folder as custom elements using a prefix (e.g., `app`): + +```js +eagerDefineCustomElementsFrom("custom_elements", { prefix: "app" }); +``` + +#### Conversion Rules + +- Filenames are transformed into kebab-case (lowercase with hyphens). +- Words are separated by underscores (`_`) or hyphens (`-`) in the filename. +- The folder structure is reflected in the name using double hyphens (`--`) to separate folder names from the file name. +- A prefix (e.g., `app`) is added to the beginning of each custom element name. + +#### Examples + +| Filepath | Generated Custom Element Name | +|-------------------------------------|--------------------------------| +| `custom_elements/demo_element.js` | `` | +| `custom_elements/demo-element.js` | `` | +| `custom_elements/foo_bar_element.js`| `` | +| `custom_elements/folder/foo_bar_element.js` | `` | ## Add a custom element with the built-in generator