@elenajs/plugin-cem-define 0.1.0 → 0.3.0

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 (3) hide show
  1. package/README.md +97 -0
  2. package/package.json +9 -2
  3. package/src/index.js +35 -11
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ <div align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://elenajs.com/img/elena-dark.png" alt="Elena" width="201" height="230">
4
+ </source>
5
+ <source media="(prefers-color-scheme: light)" srcset="https://elenajs.com/img/elena.png" alt="Elena" width="201" height="230">
6
+ </source>
7
+ <img src="https://elenajs.com/img/elena.png" alt="Elena" width="201" height="230">
8
+ </picture>
9
+
10
+ ### CEM analyzer plugin that registers the Elena components in the manifest.
11
+
12
+ <br/>
13
+
14
+ <a href="https://arielsalminen.com"><img src="https://img.shields.io/badge/creator-@arielle-F95B1F" alt="Creator @arielle"/></a>
15
+ <a href="https://www.npmjs.com/package/@elenajs/plugin-cem-define"><img src="https://img.shields.io/npm/v/@elenajs/plugin-cem-define.svg" alt="Latest version on npm" /></a>
16
+ <a href="https://github.com/getelena/elena/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-yellow.svg" alt="Elena is released under the MIT license." /></a>
17
+ <a href="https://github.com/getelena/elena/actions/workflows/tests.yml"><img src="https://github.com/getelena/elena/actions/workflows/tests.yml/badge.svg" alt="Tests status" /></a>
18
+
19
+ </div>
20
+
21
+ <br/>
22
+
23
+ <p align="center"><strong>@elenajs/plugin-cem-define</strong> is a <a href="https://custom-elements-manifest.open-wc.org/">Custom Elements Manifest</a> analyzer plugin that extracts the <code>tagName</code> from <a href="https://elenajs.com">Elena</a> Progressive Web Component definitions and registers the custom element in the manifest.</p>
24
+
25
+ <br/>
26
+
27
+ ## Table of contents
28
+
29
+ - **[Install](#install)**
30
+ - **[Usage](#usage)**
31
+ - **[How it works](#how-it-works)**
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ npm install --save-dev @elenajs/plugin-cem-define
37
+ ```
38
+
39
+ **Peer dependency:** `@custom-elements-manifest/analyzer >= 0.10.0`
40
+
41
+ ## Usage
42
+
43
+ ```js
44
+ import { elenaDefinePlugin } from "@elenajs/plugin-cem-define";
45
+
46
+ // In your CEM analyzer config
47
+ export default {
48
+ plugins: [elenaDefinePlugin()],
49
+ };
50
+ ```
51
+
52
+ Or when using `@elenajs/bundler`, add it to the `analyze.plugins` array in `elena.config.mjs`:
53
+
54
+ ```js
55
+ import { elenaDefinePlugin } from "@elenajs/plugin-cem-define";
56
+
57
+ export default {
58
+ analyze: {
59
+ plugins: [elenaDefinePlugin()],
60
+ },
61
+ };
62
+ ```
63
+
64
+ > [!NOTE]
65
+ > The `@elenajs/bundler` already includes this plugin by default. You only need to add it manually if you’re using the CEM analyzer independently.
66
+
67
+ ## How it works
68
+
69
+ The plugin scans Elena component source files and extracts the `tagName` from the options object passed to `Elena()`. It supports two patterns:
70
+
71
+ **Inline options:**
72
+
73
+ ```js
74
+ class Button extends Elena(HTMLElement, {
75
+ tagName: "elena-button",
76
+ }) {}
77
+ ```
78
+
79
+ **Variable reference:**
80
+
81
+ ```js
82
+ const options = {
83
+ tagName: "elena-button",
84
+ };
85
+
86
+ class Button extends Elena(HTMLElement, options) {}
87
+ ```
88
+
89
+ In both cases, the plugin adds the `tagName` to the CEM class declaration and creates a `custom-element-definition` export entry, making the component discoverable by IDEs and documentation tools.
90
+
91
+ ## License
92
+
93
+ MIT
94
+
95
+ ## Copyright
96
+
97
+ Copyright © 2026 [Ariel Salminen](https://arielsalminen.com)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elenajs/plugin-cem-define",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "CEM analyzer plugin that registers the Elena components in the manifest.",
5
5
  "author": "Elena <hi@elenajs.com>",
6
6
  "homepage": "https://elenajs.com/",
@@ -15,11 +15,18 @@
15
15
  "files": [
16
16
  "src"
17
17
  ],
18
+ "scripts": {
19
+ "test": "vitest run"
20
+ },
18
21
  "engines": {
19
22
  "node": ">= 20"
20
23
  },
21
24
  "peerDependencies": {
22
25
  "@custom-elements-manifest/analyzer": ">=0.10.0"
23
26
  },
24
- "gitHead": "3da2b4411c0a4d36085b1095feabbaabea50909f"
27
+ "devDependencies": {
28
+ "@custom-elements-manifest/analyzer": "0.11.0",
29
+ "vitest": "4.0.18"
30
+ },
31
+ "gitHead": "c181af0625f691a95a5cb1ef9adb0d7f2c796eae"
25
32
  }
package/src/index.js CHANGED
@@ -1,3 +1,17 @@
1
+ /**
2
+ * ██████████ ████
3
+ * ░░███░░░░░█░░███
4
+ * ░███ █ ░ ███ ██████ ████████ ██████
5
+ * ░██████ ███ ███░░███░░███░░███ ░░░░░███
6
+ * ░███░░█ ███ ░███████ ░███ ░███ ███████
7
+ * ░███ ░ █ ███ ░███░░░ ░███ ░███ ███░░███
8
+ * ██████████ █████░░██████ ████ █████░░████████
9
+ * ░░░░░░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░
10
+ *
11
+ * Elena CEM Define Plugin
12
+ * https://elenajs.com
13
+ */
14
+
1
15
  /**
2
16
  * CEM analyzer plugin that reads `tagName` from the options object passed to
3
17
  * `Elena(superClass, { tagName: "...", ... })` and registers the element in the manifest.
@@ -23,11 +37,9 @@ export function elenaDefinePlugin() {
23
37
  ts.isObjectLiteralExpression(node.initializer)
24
38
  ) {
25
39
  const varName = node.name.getText();
26
- const tagNameProp = node.initializer.properties.find(
27
- p => ts.isPropertyAssignment(p) && p.name.getText() === "tagName"
28
- );
29
- if (tagNameProp && ts.isStringLiteral(tagNameProp.initializer)) {
30
- optionsMap.set(varName, tagNameProp.initializer.text);
40
+ const tagName = getTagName(ts, node.initializer);
41
+ if (tagName) {
42
+ optionsMap.set(varName, tagName);
31
43
  }
32
44
  return;
33
45
  }
@@ -41,6 +53,7 @@ export function elenaDefinePlugin() {
41
53
  return;
42
54
  }
43
55
 
56
+ // Skip classes that don't extend anything (no `extends` clause)
44
57
  const heritageClause = node.heritageClauses?.[0];
45
58
  if (!heritageClause) {
46
59
  return;
@@ -61,12 +74,7 @@ export function elenaDefinePlugin() {
61
74
 
62
75
  if (ts.isObjectLiteralExpression(optionsArg)) {
63
76
  // Inline: Elena(HTMLElement, { tagName: "...", ... })
64
- const tagNameProp = optionsArg.properties.find(
65
- p => ts.isPropertyAssignment(p) && p.name.getText() === "tagName"
66
- );
67
- if (tagNameProp && ts.isStringLiteral(tagNameProp.initializer)) {
68
- tagName = tagNameProp.initializer.text;
69
- }
77
+ tagName = getTagName(ts, optionsArg);
70
78
  } else if (ts.isIdentifier(optionsArg)) {
71
79
  // Variable reference: Elena(HTMLElement, options)
72
80
  tagName = optionsMap.get(optionsArg.getText());
@@ -91,3 +99,19 @@ export function elenaDefinePlugin() {
91
99
  },
92
100
  };
93
101
  }
102
+
103
+ /**
104
+ * Extracts the `tagName` string value from an object literal expression.
105
+ * @param {import("typescript")} ts
106
+ * @param {import("typescript").ObjectLiteralExpression} objectLiteral
107
+ * @returns {string | undefined}
108
+ */
109
+ function getTagName(ts, objectLiteral) {
110
+ const prop = objectLiteral.properties.find(
111
+ p => ts.isPropertyAssignment(p) && p.name.getText() === "tagName"
112
+ );
113
+ if (prop && ts.isStringLiteral(prop.initializer)) {
114
+ return prop.initializer.text;
115
+ }
116
+ return undefined;
117
+ }