@sprlab/wccompiler 0.13.0 → 0.14.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.
- package/README.md +998 -998
- package/adapters/angular-compiled/angular.d.ts +197 -197
- package/adapters/angular-compiled/angular.mjs +488 -488
- package/adapters/angular.js +54 -54
- package/adapters/angular.ts +630 -630
- package/adapters/react.js +114 -114
- package/adapters/vue.js +103 -103
- package/bin/wcc.js +412 -412
- package/bin/wcc.test.js +126 -126
- package/integrations/angular.js +73 -73
- package/integrations/react.js +859 -859
- package/integrations/vue.js +253 -253
- package/lib/codegen.js +2074 -2074
- package/lib/compiler-browser.js +545 -545
- package/lib/compiler.js +483 -479
- package/lib/config.js +71 -71
- package/lib/css-scoper.js +180 -180
- package/lib/dev-server.js +193 -193
- package/lib/import-resolver.js +160 -160
- package/lib/parser-extractors.js +1240 -1169
- package/lib/parser.js +273 -269
- package/lib/reactive-runtime.js +143 -143
- package/lib/sfc-parser.js +333 -333
- package/lib/template-normalizer.js +114 -114
- package/lib/tree-walker.js +1013 -1013
- package/lib/types.js +262 -262
- package/lib/wcc-runtime.js +68 -68
- package/package.json +85 -85
- package/types/wcc.d.ts +28 -28
- package/types/wcc.test.js +46 -46
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Template Normalizer — pre-processes WCC template HTML before DOM parsing.
|
|
3
|
-
*
|
|
4
|
-
* Handles two transformations:
|
|
5
|
-
* 1. PascalCase tags → kebab-case (e.g. <WccBadge> → <wcc-badge>)
|
|
6
|
-
* 2. Self-closing custom elements → open+close (e.g. <wcc-badge /> → <wcc-badge></wcc-badge>)
|
|
7
|
-
*
|
|
8
|
-
* Must run BEFORE linkedom/jsdom parsing because HTML parsers:
|
|
9
|
-
* - Lowercase all tag names (losing PascalCase word boundaries)
|
|
10
|
-
* - Don't recognize self-closing syntax for non-void elements
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
// HTML void elements that are legitimately self-closing
|
|
14
|
-
const VOID_ELEMENTS = new Set([
|
|
15
|
-
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
|
|
16
|
-
'link', 'meta', 'param', 'source', 'track', 'wbr',
|
|
17
|
-
]);
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Convert a PascalCase tag name to kebab-case.
|
|
21
|
-
* e.g. "WccBadge" → "wcc-badge", "WccCardHeader" → "wcc-card-header"
|
|
22
|
-
*
|
|
23
|
-
* Only converts if the name starts with an uppercase letter (PascalCase).
|
|
24
|
-
*
|
|
25
|
-
* @param {string} name
|
|
26
|
-
* @returns {string}
|
|
27
|
-
*/
|
|
28
|
-
export function pascalToKebab(name) {
|
|
29
|
-
// Insert hyphen before each uppercase letter that follows a lowercase letter or digit
|
|
30
|
-
return name
|
|
31
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
32
|
-
.toLowerCase();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Check if a tag name is PascalCase (starts with uppercase, has at least
|
|
37
|
-
* one more uppercase letter indicating a word boundary).
|
|
38
|
-
*
|
|
39
|
-
* @param {string} name
|
|
40
|
-
* @returns {boolean}
|
|
41
|
-
*/
|
|
42
|
-
export function isPascalCase(name) {
|
|
43
|
-
return /^[A-Z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*$/.test(name);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Normalize a WCC template string:
|
|
48
|
-
* 1. Convert PascalCase tags to kebab-case (validated against importMap if provided)
|
|
49
|
-
* 2. Expand self-closing custom elements to open+close pairs
|
|
50
|
-
*
|
|
51
|
-
* @param {string} html — Raw template HTML
|
|
52
|
-
* @param {object} [options]
|
|
53
|
-
* @param {Map<string, string>} [options.importMap] — PascalCase identifier → kebab-case tag
|
|
54
|
-
* @param {string} [options.fileName] — Source file for error messages
|
|
55
|
-
* @returns {string} — Normalized HTML ready for DOM parsing
|
|
56
|
-
* @throws {Error} with code 'UNRESOLVED_COMPONENT' if PascalCase tag has no matching import
|
|
57
|
-
*/
|
|
58
|
-
export function normalizeTemplate(html, options) {
|
|
59
|
-
const { importMap, fileName } = options || {};
|
|
60
|
-
|
|
61
|
-
// Match opening tags (including self-closing): <TagName ...> or <TagName ... />
|
|
62
|
-
// Also match closing tags: </TagName>
|
|
63
|
-
//
|
|
64
|
-
// Regex breakdown:
|
|
65
|
-
// < — opening angle bracket
|
|
66
|
-
// (\/?)? — optional slash (closing tag)
|
|
67
|
-
// ([A-Za-z][\w-]*) — tag name
|
|
68
|
-
// ((?:\s[^>]*)?) — attributes (anything that's not >)
|
|
69
|
-
// (\s*\/)? — optional self-closing slash
|
|
70
|
-
// > — closing angle bracket
|
|
71
|
-
const TAG_RE = /<(\/?)([A-Za-z][\w-]*)((?:\s[^>]*?)?)(\/?)>/g;
|
|
72
|
-
|
|
73
|
-
return html.replace(TAG_RE, (match, closingSlash, tagName, attrs, selfClosing) => {
|
|
74
|
-
// Guard: preserve <component> tags as-is — this is a compiler directive, not a custom element
|
|
75
|
-
if (tagName.toLowerCase() === 'component') {
|
|
76
|
-
return match;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
let normalizedTag = tagName;
|
|
80
|
-
|
|
81
|
-
// Step 1: Convert PascalCase to kebab-case
|
|
82
|
-
if (isPascalCase(tagName)) {
|
|
83
|
-
if (importMap) {
|
|
84
|
-
// Validate against the import map
|
|
85
|
-
if (importMap.has(tagName)) {
|
|
86
|
-
normalizedTag = importMap.get(tagName);
|
|
87
|
-
} else {
|
|
88
|
-
const error = new Error(
|
|
89
|
-
`Unresolved component '<${tagName}>' in '${fileName || 'unknown'}'. Did you forget to import it?`
|
|
90
|
-
);
|
|
91
|
-
error.code = 'UNRESOLVED_COMPONENT';
|
|
92
|
-
throw error;
|
|
93
|
-
}
|
|
94
|
-
} else {
|
|
95
|
-
// Backward compatible: convert all PascalCase to kebab-case
|
|
96
|
-
normalizedTag = pascalToKebab(tagName);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Step 2: Handle self-closing tags
|
|
101
|
-
if (selfClosing === '/') {
|
|
102
|
-
// If it's a void element, keep it self-closing
|
|
103
|
-
if (VOID_ELEMENTS.has(normalizedTag.toLowerCase())) {
|
|
104
|
-
return `<${closingSlash}${normalizedTag}${attrs} />`;
|
|
105
|
-
}
|
|
106
|
-
// Otherwise expand to open+close pair (trim trailing whitespace from attrs)
|
|
107
|
-
const trimmedAttrs = attrs.trimEnd();
|
|
108
|
-
return `<${normalizedTag}${trimmedAttrs}></${normalizedTag}>`;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Regular open or close tag — just replace the name
|
|
112
|
-
return `<${closingSlash}${normalizedTag}${attrs}>`;
|
|
113
|
-
});
|
|
114
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Template Normalizer — pre-processes WCC template HTML before DOM parsing.
|
|
3
|
+
*
|
|
4
|
+
* Handles two transformations:
|
|
5
|
+
* 1. PascalCase tags → kebab-case (e.g. <WccBadge> → <wcc-badge>)
|
|
6
|
+
* 2. Self-closing custom elements → open+close (e.g. <wcc-badge /> → <wcc-badge></wcc-badge>)
|
|
7
|
+
*
|
|
8
|
+
* Must run BEFORE linkedom/jsdom parsing because HTML parsers:
|
|
9
|
+
* - Lowercase all tag names (losing PascalCase word boundaries)
|
|
10
|
+
* - Don't recognize self-closing syntax for non-void elements
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// HTML void elements that are legitimately self-closing
|
|
14
|
+
const VOID_ELEMENTS = new Set([
|
|
15
|
+
'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
|
|
16
|
+
'link', 'meta', 'param', 'source', 'track', 'wbr',
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Convert a PascalCase tag name to kebab-case.
|
|
21
|
+
* e.g. "WccBadge" → "wcc-badge", "WccCardHeader" → "wcc-card-header"
|
|
22
|
+
*
|
|
23
|
+
* Only converts if the name starts with an uppercase letter (PascalCase).
|
|
24
|
+
*
|
|
25
|
+
* @param {string} name
|
|
26
|
+
* @returns {string}
|
|
27
|
+
*/
|
|
28
|
+
export function pascalToKebab(name) {
|
|
29
|
+
// Insert hyphen before each uppercase letter that follows a lowercase letter or digit
|
|
30
|
+
return name
|
|
31
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
32
|
+
.toLowerCase();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if a tag name is PascalCase (starts with uppercase, has at least
|
|
37
|
+
* one more uppercase letter indicating a word boundary).
|
|
38
|
+
*
|
|
39
|
+
* @param {string} name
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
42
|
+
export function isPascalCase(name) {
|
|
43
|
+
return /^[A-Z][a-zA-Z0-9]*[A-Z][a-zA-Z0-9]*$/.test(name);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Normalize a WCC template string:
|
|
48
|
+
* 1. Convert PascalCase tags to kebab-case (validated against importMap if provided)
|
|
49
|
+
* 2. Expand self-closing custom elements to open+close pairs
|
|
50
|
+
*
|
|
51
|
+
* @param {string} html — Raw template HTML
|
|
52
|
+
* @param {object} [options]
|
|
53
|
+
* @param {Map<string, string>} [options.importMap] — PascalCase identifier → kebab-case tag
|
|
54
|
+
* @param {string} [options.fileName] — Source file for error messages
|
|
55
|
+
* @returns {string} — Normalized HTML ready for DOM parsing
|
|
56
|
+
* @throws {Error} with code 'UNRESOLVED_COMPONENT' if PascalCase tag has no matching import
|
|
57
|
+
*/
|
|
58
|
+
export function normalizeTemplate(html, options) {
|
|
59
|
+
const { importMap, fileName } = options || {};
|
|
60
|
+
|
|
61
|
+
// Match opening tags (including self-closing): <TagName ...> or <TagName ... />
|
|
62
|
+
// Also match closing tags: </TagName>
|
|
63
|
+
//
|
|
64
|
+
// Regex breakdown:
|
|
65
|
+
// < — opening angle bracket
|
|
66
|
+
// (\/?)? — optional slash (closing tag)
|
|
67
|
+
// ([A-Za-z][\w-]*) — tag name
|
|
68
|
+
// ((?:\s[^>]*)?) — attributes (anything that's not >)
|
|
69
|
+
// (\s*\/)? — optional self-closing slash
|
|
70
|
+
// > — closing angle bracket
|
|
71
|
+
const TAG_RE = /<(\/?)([A-Za-z][\w-]*)((?:\s[^>]*?)?)(\/?)>/g;
|
|
72
|
+
|
|
73
|
+
return html.replace(TAG_RE, (match, closingSlash, tagName, attrs, selfClosing) => {
|
|
74
|
+
// Guard: preserve <component> tags as-is — this is a compiler directive, not a custom element
|
|
75
|
+
if (tagName.toLowerCase() === 'component') {
|
|
76
|
+
return match;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let normalizedTag = tagName;
|
|
80
|
+
|
|
81
|
+
// Step 1: Convert PascalCase to kebab-case
|
|
82
|
+
if (isPascalCase(tagName)) {
|
|
83
|
+
if (importMap) {
|
|
84
|
+
// Validate against the import map
|
|
85
|
+
if (importMap.has(tagName)) {
|
|
86
|
+
normalizedTag = importMap.get(tagName);
|
|
87
|
+
} else {
|
|
88
|
+
const error = new Error(
|
|
89
|
+
`Unresolved component '<${tagName}>' in '${fileName || 'unknown'}'. Did you forget to import it?`
|
|
90
|
+
);
|
|
91
|
+
error.code = 'UNRESOLVED_COMPONENT';
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
// Backward compatible: convert all PascalCase to kebab-case
|
|
96
|
+
normalizedTag = pascalToKebab(tagName);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Step 2: Handle self-closing tags
|
|
101
|
+
if (selfClosing === '/') {
|
|
102
|
+
// If it's a void element, keep it self-closing
|
|
103
|
+
if (VOID_ELEMENTS.has(normalizedTag.toLowerCase())) {
|
|
104
|
+
return `<${closingSlash}${normalizedTag}${attrs} />`;
|
|
105
|
+
}
|
|
106
|
+
// Otherwise expand to open+close pair (trim trailing whitespace from attrs)
|
|
107
|
+
const trimmedAttrs = attrs.trimEnd();
|
|
108
|
+
return `<${normalizedTag}${trimmedAttrs}></${normalizedTag}>`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Regular open or close tag — just replace the name
|
|
112
|
+
return `<${closingSlash}${normalizedTag}${attrs}>`;
|
|
113
|
+
});
|
|
114
|
+
}
|