@elenajs/plugin-cem-define 0.4.0 → 1.0.0-beta.2
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.
- package/README.md +85 -0
- package/package.json +11 -3
- package/src/index.js +40 -72
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://elenajs.com/img/elena-dark.png" alt="Elena" width="558" height="220">
|
|
4
|
+
</source>
|
|
5
|
+
<source media="(prefers-color-scheme: light)" srcset="https://elenajs.com/img/elena-light.png" alt="Elena" width="558" height="220">
|
|
6
|
+
</source>
|
|
7
|
+
<img src="https://elenajs.com/img/elena-light.png" alt="Elena" width="558" height="220">
|
|
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 value of the `static tagName` class field:
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
class Button extends Elena(HTMLElement) {
|
|
73
|
+
static tagName = "elena-button";
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
It 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.
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT
|
|
82
|
+
|
|
83
|
+
## Copyright
|
|
84
|
+
|
|
85
|
+
Copyright © 2026 [Ariel Salminen](https://arielsalminen.com)
|
package/package.json
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elenajs/plugin-cem-define",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-beta.2",
|
|
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/",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/getelena/elena.git",
|
|
10
|
+
"directory": "packages/plugin-cem-define"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/getelena/elena/issues"
|
|
14
|
+
},
|
|
7
15
|
"license": "MIT",
|
|
8
16
|
"publishConfig": {
|
|
9
17
|
"access": "public"
|
|
@@ -26,7 +34,7 @@
|
|
|
26
34
|
},
|
|
27
35
|
"devDependencies": {
|
|
28
36
|
"@custom-elements-manifest/analyzer": "0.11.0",
|
|
29
|
-
"vitest": "4.0
|
|
37
|
+
"vitest": "4.1.0"
|
|
30
38
|
},
|
|
31
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "18e13f305c8823f7633c739f2ec61cec2420267b"
|
|
32
40
|
}
|
package/src/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ██████████ ████
|
|
3
3
|
* ░░███░░░░░█░░███
|
|
4
|
-
* ░███ █ ░
|
|
5
|
-
* ░██████
|
|
6
|
-
* ░███░░█
|
|
7
|
-
* ░███ ░ █
|
|
4
|
+
* ░███ █ ░ ░███ ██████ ████████ ██████
|
|
5
|
+
* ░██████ ░███ ███░░███░░███░░███ ░░░░░███
|
|
6
|
+
* ░███░░█ ░███ ░███████ ░███ ░███ ███████
|
|
7
|
+
* ░███ ░ █ ░███ ░███░░░ ░███ ░███ ███░░███
|
|
8
8
|
* ██████████ █████░░██████ ████ █████░░████████
|
|
9
9
|
* ░░░░░░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░
|
|
10
10
|
*
|
|
@@ -13,37 +13,16 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* CEM analyzer plugin that reads `tagName` from
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* Supports both inline objects and variable references:
|
|
20
|
-
* - `Elena(HTMLElement, { tagName: "elena-button", ... })`
|
|
21
|
-
* - `const options = { tagName: "elena-button", ... }; Elena(HTMLElement, options)`
|
|
16
|
+
* CEM analyzer plugin that reads `static tagName` from an Elena component class
|
|
17
|
+
* and registers the element in the manifest.
|
|
22
18
|
*
|
|
23
19
|
* @returns {import("@custom-elements-manifest/analyzer").Plugin}
|
|
24
20
|
*/
|
|
25
21
|
export function elenaDefinePlugin() {
|
|
26
|
-
/** @type {Map<string, string>} variable name → tag name */
|
|
27
|
-
const optionsMap = new Map();
|
|
28
|
-
|
|
29
22
|
return {
|
|
30
23
|
name: "define-element",
|
|
31
24
|
|
|
32
25
|
analyzePhase({ ts, node, moduleDoc }) {
|
|
33
|
-
// Collect variable declarations: const options = { tagName: "...", ... }
|
|
34
|
-
if (
|
|
35
|
-
ts.isVariableDeclaration(node) &&
|
|
36
|
-
node.initializer &&
|
|
37
|
-
ts.isObjectLiteralExpression(node.initializer)
|
|
38
|
-
) {
|
|
39
|
-
const varName = node.name.getText();
|
|
40
|
-
const tagName = getTagName(ts, node.initializer);
|
|
41
|
-
if (tagName) {
|
|
42
|
-
optionsMap.set(varName, tagName);
|
|
43
|
-
}
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
26
|
if (!ts.isClassDeclaration(node)) {
|
|
48
27
|
return;
|
|
49
28
|
}
|
|
@@ -53,65 +32,54 @@ export function elenaDefinePlugin() {
|
|
|
53
32
|
return;
|
|
54
33
|
}
|
|
55
34
|
|
|
56
|
-
// Skip classes that don't extend
|
|
35
|
+
// Skip classes that don't extend Elena(...)
|
|
57
36
|
const heritageClause = node.heritageClauses?.[0];
|
|
58
|
-
if (
|
|
37
|
+
if (
|
|
38
|
+
!heritageClause?.types.some(
|
|
39
|
+
t => ts.isCallExpression(t.expression) && t.expression.expression.getText() === "Elena"
|
|
40
|
+
)
|
|
41
|
+
) {
|
|
59
42
|
return;
|
|
60
43
|
}
|
|
61
44
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const optionsArg = expr.arguments[1];
|
|
69
|
-
if (!optionsArg) {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
let tagName;
|
|
74
|
-
|
|
75
|
-
if (ts.isObjectLiteralExpression(optionsArg)) {
|
|
76
|
-
// Inline: Elena(HTMLElement, { tagName: "...", ... })
|
|
77
|
-
tagName = getTagName(ts, optionsArg);
|
|
78
|
-
} else if (ts.isIdentifier(optionsArg)) {
|
|
79
|
-
// Variable reference: Elena(HTMLElement, options)
|
|
80
|
-
tagName = optionsMap.get(optionsArg.getText());
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (!tagName) {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const declaration = moduleDoc.declarations?.find(d => d.name === className);
|
|
88
|
-
if (declaration) {
|
|
89
|
-
declaration.tagName = tagName;
|
|
90
|
-
}
|
|
45
|
+
const tagName = getStaticTagName(ts, node);
|
|
46
|
+
if (!tagName) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
91
49
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
name: tagName,
|
|
96
|
-
declaration: { name: className, module: moduleDoc.path },
|
|
97
|
-
});
|
|
50
|
+
const declaration = moduleDoc.declarations?.find(d => d.name === className);
|
|
51
|
+
if (declaration) {
|
|
52
|
+
declaration.tagName = tagName;
|
|
98
53
|
}
|
|
54
|
+
|
|
55
|
+
moduleDoc.exports ??= [];
|
|
56
|
+
moduleDoc.exports.push({
|
|
57
|
+
kind: "custom-element-definition",
|
|
58
|
+
name: tagName,
|
|
59
|
+
declaration: { name: className, module: moduleDoc.path },
|
|
60
|
+
});
|
|
99
61
|
},
|
|
100
62
|
};
|
|
101
63
|
}
|
|
102
64
|
|
|
103
65
|
/**
|
|
104
|
-
* Extracts the `tagName` string value from
|
|
66
|
+
* Extracts the `tagName` string value from a `static tagName = "..."` class field.
|
|
67
|
+
*
|
|
105
68
|
* @param {import("typescript")} ts
|
|
106
|
-
* @param {import("typescript").
|
|
69
|
+
* @param {import("typescript").ClassDeclaration} classNode
|
|
107
70
|
* @returns {string | undefined}
|
|
108
71
|
*/
|
|
109
|
-
function
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
72
|
+
function getStaticTagName(ts, classNode) {
|
|
73
|
+
for (const member of classNode.members) {
|
|
74
|
+
if (
|
|
75
|
+
ts.isPropertyDeclaration(member) &&
|
|
76
|
+
member.modifiers?.some(m => m.kind === ts.SyntaxKind.StaticKeyword) &&
|
|
77
|
+
member.name?.getText() === "tagName" &&
|
|
78
|
+
member.initializer &&
|
|
79
|
+
ts.isStringLiteral(member.initializer)
|
|
80
|
+
) {
|
|
81
|
+
return member.initializer.text;
|
|
82
|
+
}
|
|
115
83
|
}
|
|
116
84
|
return undefined;
|
|
117
85
|
}
|