wc-compiler 0.16.0 → 0.17.1
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 +2 -8
- package/package.json +26 -19
- package/src/dom-shim.js +1 -3
- package/src/index.d.ts +22 -0
- package/src/jsx-loader.js +15 -7
- package/src/register.js +3 -0
- package/src/wcc.js +18 -17
- package/dist/wcc.dist.cjs +0 -32811
- package/src/ts-loader.js +0 -32
package/README.md
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://app.netlify.com/sites/merry-caramel-524e61/deploys)
|
|
6
6
|
[](https://github.com/ProjectEvergreen/wcc/tags)
|
|
7
|
-

|
|
8
7
|
[](https://raw.githubusercontent.com/ProjectEvergreen/wcc/master/LICENSE.md)
|
|
8
|
+
[](https://nodejs.org/en/about/previous-releases")
|
|
9
|
+
[](https://www.greenwoodjs.dev/discord/)
|
|
9
10
|
|
|
10
11
|
> _Experimental Web Components compiler. It's Web Components all the way down!_ 🐢
|
|
11
12
|
|
|
@@ -73,13 +74,6 @@
|
|
|
73
74
|
$ npm install wc-compiler --save-dev
|
|
74
75
|
```
|
|
75
76
|
|
|
76
|
-
### CommonJS
|
|
77
|
-
|
|
78
|
-
If you need CommonJS support, a separate pre-bundled (with Rollup) distribution of **WCC** is available at _dist/wcc.dist.cjs_. Example:
|
|
79
|
-
```js
|
|
80
|
-
const { renderToString } = require('wc-compiler/dist/wcc.dist.cjs');
|
|
81
|
-
```
|
|
82
|
-
|
|
83
77
|
## Documentation
|
|
84
78
|
|
|
85
79
|
See our [website](https://merry-caramel-524e61.netlify.app/) for API docs and examples.
|
package/package.json
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wc-compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "Experimental native Web Components compiler.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/ProjectEvergreen/wcc.git"
|
|
8
8
|
},
|
|
9
|
-
"main": "src/wcc.js",
|
|
10
9
|
"type": "module",
|
|
10
|
+
"main": "src/wcc.js",
|
|
11
|
+
"types": "./src/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./src/wcc.js",
|
|
15
|
+
"types": "./src/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./register": "./src/register.js",
|
|
18
|
+
"./src/jsx-loader.js": "./src/jsx-loader.js"
|
|
19
|
+
},
|
|
11
20
|
"author": "Owen Buckley <owen@thegreenhouse.io>",
|
|
12
21
|
"keywords": [
|
|
13
22
|
"Web Components",
|
|
@@ -17,29 +26,27 @@
|
|
|
17
26
|
],
|
|
18
27
|
"license": "MIT",
|
|
19
28
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
29
|
+
"node": ">=18"
|
|
21
30
|
},
|
|
22
31
|
"files": [
|
|
23
|
-
"src/"
|
|
24
|
-
"dist/wcc.dist.cjs"
|
|
32
|
+
"src/"
|
|
25
33
|
],
|
|
26
34
|
"publishConfig": {
|
|
27
35
|
"access": "public"
|
|
28
36
|
},
|
|
29
37
|
"scripts": {
|
|
30
38
|
"clean": "rimraf ./dist",
|
|
31
|
-
"lint": "eslint
|
|
39
|
+
"lint": "eslint",
|
|
40
|
+
"lint:types": "tsc --project tsconfig.json",
|
|
32
41
|
"docs:dev": "concurrently \"nodemon --watch src --watch docs -e js,md,css,html,jsx ./build.js\" \"http-server ./dist --open\"",
|
|
33
42
|
"docs:build": "node ./build.js",
|
|
34
43
|
"docs:serve": "npm run clean && npm run docs:build && http-server ./dist --open",
|
|
35
|
-
"sandbox": "npm run clean && concurrently \"nodemon --loader ./test-
|
|
44
|
+
"sandbox": "npm run clean && concurrently \"nodemon --loader ./test-loader.js --watch src --watch sandbox -e js,md,css,html,jsx,ts ./sandbox.js\" \"http-server ./dist --open\" \"livereload ./dist\"",
|
|
36
45
|
"start": "npm run docs:serve",
|
|
37
46
|
"test": "mocha --exclude \"./test/cases/jsx*/**\" --exclude \"./test/cases/ts*/**\" --exclude \"./test/cases/custom-extension/**\" \"./test/**/**/*.spec.js\"",
|
|
38
|
-
"test:
|
|
47
|
+
"test:jsx": "c8 node --import ./test-register.js --experimental-strip-types ./node_modules/mocha/bin/mocha \"./test/**/**/*.spec.js\"",
|
|
39
48
|
"test:tdd": "npm run test -- --watch",
|
|
40
|
-
"test:tdd:
|
|
41
|
-
"dist": "rollup -c rollup.config.js",
|
|
42
|
-
"prepublishOnly": "npm run clean && npm run dist"
|
|
49
|
+
"test:tdd:jsx": "npm run test:jsx -- --watch"
|
|
43
50
|
},
|
|
44
51
|
"dependencies": {
|
|
45
52
|
"@projectevergreen/acorn-jsx-esm": "~0.1.0",
|
|
@@ -51,19 +58,19 @@
|
|
|
51
58
|
},
|
|
52
59
|
"devDependencies": {
|
|
53
60
|
"@babel/core": "^7.24.4",
|
|
54
|
-
"@babel/eslint-parser": "^7.
|
|
55
|
-
"@babel/plugin-syntax-import-assertions": "^7.
|
|
56
|
-
"@
|
|
61
|
+
"@babel/eslint-parser": "^7.25.7",
|
|
62
|
+
"@babel/plugin-syntax-import-assertions": "^7.25.7",
|
|
63
|
+
"@eslint/js": "^9.11.1",
|
|
57
64
|
"@ls-lint/ls-lint": "^1.10.0",
|
|
58
65
|
"@mapbox/rehype-prism": "^0.8.0",
|
|
59
|
-
"@
|
|
60
|
-
"@
|
|
66
|
+
"@types/mocha": "^10.0.10",
|
|
67
|
+
"@types/node": "^22.13.4",
|
|
61
68
|
"c8": "^7.11.2",
|
|
62
69
|
"chai": "^4.3.6",
|
|
63
70
|
"concurrently": "^7.1.0",
|
|
64
|
-
"eslint": "^
|
|
65
|
-
"eslint-plugin-markdown": "^3.0.0",
|
|
71
|
+
"eslint": "^9.11.1",
|
|
66
72
|
"eslint-plugin-no-only-tests": "^2.6.0",
|
|
73
|
+
"globals": "^15.10.0",
|
|
67
74
|
"http-server": "^14.1.0",
|
|
68
75
|
"jsdom": "^19.0.0",
|
|
69
76
|
"livereload": "^0.9.3",
|
|
@@ -78,8 +85,8 @@
|
|
|
78
85
|
"remark-rehype": "^10.1.0",
|
|
79
86
|
"remark-toc": "^8.0.1",
|
|
80
87
|
"rimraf": "^3.0.2",
|
|
81
|
-
"rollup": "^4.26.0",
|
|
82
88
|
"simple.css": "^0.1.3",
|
|
89
|
+
"typescript": "^5.8.2",
|
|
83
90
|
"unified": "^10.1.2"
|
|
84
91
|
}
|
|
85
92
|
}
|
package/src/dom-shim.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// @ts-nocheck
|
|
3
2
|
import { parse, parseFragment, serialize } from 'parse5';
|
|
4
3
|
|
|
5
4
|
export function getParse(html) {
|
|
@@ -47,7 +46,6 @@ function getParse5ElementDefaults(element, tagName) {
|
|
|
47
46
|
nodeName: tagName,
|
|
48
47
|
tagName: tagName,
|
|
49
48
|
namespaceURI: 'http://www.w3.org/1999/xhtml',
|
|
50
|
-
// eslint-disable-next-line no-extra-parens
|
|
51
49
|
...(tagName === 'template' ? { content: { nodeName: '#document-fragment', childNodes: [] } } : {})
|
|
52
50
|
};
|
|
53
51
|
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type Metadata = {
|
|
2
|
+
[key: string]: {
|
|
3
|
+
instanceName: string;
|
|
4
|
+
moduleURL: URL;
|
|
5
|
+
isEntry: boolean
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type renderToString = (elementURL: URL, wrappingEntryTag?: boolean, props?: any) => Promise<{
|
|
10
|
+
html: string;
|
|
11
|
+
metadata: Metadata
|
|
12
|
+
}>
|
|
13
|
+
|
|
14
|
+
export type renderFromHTML = (html: string, elementURLs: URL[]) => Promise<{
|
|
15
|
+
html: string;
|
|
16
|
+
metadata: Metadata
|
|
17
|
+
}>
|
|
18
|
+
|
|
19
|
+
declare module "wc-compiler" {
|
|
20
|
+
export const renderToString: renderToString;
|
|
21
|
+
export const renderFromHTML: renderFromHTML;
|
|
22
|
+
}
|
package/src/jsx-loader.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable max-depth, complexity */
|
|
2
1
|
// https://nodejs.org/api/esm.html#esm_loaders
|
|
3
2
|
import * as acorn from 'acorn';
|
|
4
3
|
import * as walk from 'acorn-walk';
|
|
@@ -6,6 +5,8 @@ import { generate } from 'astring';
|
|
|
6
5
|
import fs from 'fs';
|
|
7
6
|
// ideally we can eventually adopt an ESM compatible version of this plugin
|
|
8
7
|
// https://github.com/acornjs/acorn-jsx/issues/112
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
// but it does have a default export???
|
|
9
10
|
import jsx from '@projectevergreen/acorn-jsx-esm';
|
|
10
11
|
import { parse, parseFragment, serialize } from 'parse5';
|
|
11
12
|
import { transform } from 'sucrase';
|
|
@@ -138,13 +139,13 @@ function parseJsxElement(element, moduleContents = '') {
|
|
|
138
139
|
const { expression } = value;
|
|
139
140
|
|
|
140
141
|
if (expression.type === 'Identifier') {
|
|
141
|
-
string += ` ${name}
|
|
142
|
+
string += ` ${name}=$\{${expression.name}}`;
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
if (expression.type === 'MemberExpression') {
|
|
145
146
|
if (expression.object.type === 'Identifier') {
|
|
146
147
|
if (expression.property.type === 'Identifier') {
|
|
147
|
-
string += ` ${name}
|
|
148
|
+
string += ` ${name}=$\{${expression.object.name}.${expression.property.name}}`;
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
151
|
}
|
|
@@ -174,7 +175,7 @@ function parseJsxElement(element, moduleContents = '') {
|
|
|
174
175
|
|
|
175
176
|
if (type === 'Identifier') {
|
|
176
177
|
// You have {count} TODOs left to complete
|
|
177
|
-
string +=
|
|
178
|
+
string += `$\{${element.expression.name}}`;
|
|
178
179
|
} else if (type === 'MemberExpression') {
|
|
179
180
|
const { object } = element.expression.object;
|
|
180
181
|
|
|
@@ -187,7 +188,7 @@ function parseJsxElement(element, moduleContents = '') {
|
|
|
187
188
|
// const { todos } = this;
|
|
188
189
|
// ....
|
|
189
190
|
// You have {todos.length} Todos left to complete
|
|
190
|
-
string +=
|
|
191
|
+
string += `$\{${element.expression.object.name}.${element.expression.property.name}}`;
|
|
191
192
|
}
|
|
192
193
|
}
|
|
193
194
|
}
|
|
@@ -251,11 +252,13 @@ export function parseJsx(moduleURL) {
|
|
|
251
252
|
|
|
252
253
|
walk.simple(tree, {
|
|
253
254
|
ClassDeclaration(node) {
|
|
255
|
+
// @ts-ignore
|
|
254
256
|
if (node.superClass.name === 'HTMLElement') {
|
|
255
257
|
const hasShadowRoot = moduleContents.slice(node.body.start, node.body.end).indexOf('this.attachShadow(') > 0;
|
|
256
258
|
|
|
257
259
|
for (const n1 of node.body.body) {
|
|
258
260
|
if (n1.type === 'MethodDefinition') {
|
|
261
|
+
// @ts-ignore
|
|
259
262
|
const nodeName = n1.key.name;
|
|
260
263
|
if (nodeName === 'render') {
|
|
261
264
|
for (const n2 in n1.value.body.body) {
|
|
@@ -266,6 +269,7 @@ export function parseJsx(moduleURL) {
|
|
|
266
269
|
...observedAttributes,
|
|
267
270
|
...findThisReferences('render', n)
|
|
268
271
|
];
|
|
272
|
+
// @ts-ignore
|
|
269
273
|
} else if (n.type === 'ReturnStatement' && n.argument.type === 'JSXElement') {
|
|
270
274
|
const html = parseJsxElement(n.argument, moduleContents);
|
|
271
275
|
const elementTree = getParse(html)(html);
|
|
@@ -297,6 +301,7 @@ export function parseJsx(moduleURL) {
|
|
|
297
301
|
sourceType: 'module'
|
|
298
302
|
});
|
|
299
303
|
|
|
304
|
+
// @ts-ignore
|
|
300
305
|
n1.value.body.body[n2] = transformed;
|
|
301
306
|
}
|
|
302
307
|
}
|
|
@@ -309,7 +314,9 @@ export function parseJsx(moduleURL) {
|
|
|
309
314
|
const { declaration } = node;
|
|
310
315
|
|
|
311
316
|
if (declaration && declaration.type === 'VariableDeclaration' && declaration.kind === 'const' && declaration.declarations.length === 1) {
|
|
317
|
+
// @ts-ignore
|
|
312
318
|
if (declaration.declarations[0].id.name === 'inferredObservability') {
|
|
319
|
+
// @ts-ignore
|
|
313
320
|
inferredObservability = Boolean(node.declaration.declarations[0].init.raw);
|
|
314
321
|
}
|
|
315
322
|
}
|
|
@@ -317,6 +324,7 @@ export function parseJsx(moduleURL) {
|
|
|
317
324
|
}, {
|
|
318
325
|
// https://github.com/acornjs/acorn/issues/829#issuecomment-1172586171
|
|
319
326
|
...walk.base,
|
|
327
|
+
// @ts-ignore
|
|
320
328
|
JSXElement: () => {}
|
|
321
329
|
});
|
|
322
330
|
|
|
@@ -325,7 +333,9 @@ export function parseJsx(moduleURL) {
|
|
|
325
333
|
let insertPoint;
|
|
326
334
|
for (const line of tree.body) {
|
|
327
335
|
// test for class MyComponent vs export default class MyComponent
|
|
336
|
+
// @ts-ignore
|
|
328
337
|
if (line.type === 'ClassDeclaration' || (line.declaration && line.declaration.type) === 'ClassDeclaration') {
|
|
338
|
+
// @ts-ignore
|
|
329
339
|
insertPoint = line.declaration.body.start + 1;
|
|
330
340
|
}
|
|
331
341
|
}
|
|
@@ -333,7 +343,6 @@ export function parseJsx(moduleURL) {
|
|
|
333
343
|
let newModuleContents = generate(tree);
|
|
334
344
|
|
|
335
345
|
// TODO better way to determine value type?
|
|
336
|
-
/* eslint-disable indent */
|
|
337
346
|
newModuleContents = `${newModuleContents.slice(0, insertPoint)}
|
|
338
347
|
static get observedAttributes() {
|
|
339
348
|
return [${[...observedAttributes].map(attr => `'${attr}'`).join(',')}]
|
|
@@ -366,7 +375,6 @@ export function parseJsx(moduleURL) {
|
|
|
366
375
|
|
|
367
376
|
${newModuleContents.slice(insertPoint)}
|
|
368
377
|
`;
|
|
369
|
-
/* eslint-enable indent */
|
|
370
378
|
|
|
371
379
|
tree = acorn.Parser.extend(jsx()).parse(newModuleContents, {
|
|
372
380
|
ecmaVersion: 'latest',
|
package/src/register.js
ADDED
package/src/wcc.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable max-depth */
|
|
2
1
|
// this must come first
|
|
3
2
|
import { getParse } from './dom-shim.js';
|
|
4
3
|
|
|
@@ -84,19 +83,23 @@ function registerDependencies(moduleURL, definitions, depth = 0) {
|
|
|
84
83
|
}), {
|
|
85
84
|
ImportDeclaration(node) {
|
|
86
85
|
const specifier = node.source.value;
|
|
87
|
-
const isBareSpecifier = specifier.indexOf('.') !== 0 && specifier.indexOf('/') !== 0;
|
|
88
|
-
const extension = specifier.split('.').pop();
|
|
89
86
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const dependencyModuleURL = new URL(node.source.value, moduleURL);
|
|
87
|
+
if (typeof specifier === 'string') {
|
|
88
|
+
const isBareSpecifier = specifier.indexOf('.') !== 0 && specifier.indexOf('/') !== 0;
|
|
89
|
+
const extension = typeof specifier === "string" ? specifier.split('.').pop() : "";
|
|
94
90
|
|
|
95
|
-
|
|
91
|
+
// would like to decouple .jsx from the core, ideally
|
|
92
|
+
// https://github.com/ProjectEvergreen/wcc/issues/122
|
|
93
|
+
if (!isBareSpecifier && ['js', 'jsx', 'ts'].includes(extension)) {
|
|
94
|
+
const dependencyModuleURL = new URL(specifier, moduleURL);
|
|
95
|
+
|
|
96
|
+
registerDependencies(dependencyModuleURL, definitions, nextDepth);
|
|
97
|
+
}
|
|
96
98
|
}
|
|
97
99
|
},
|
|
98
100
|
ExpressionStatement(node) {
|
|
99
101
|
if (isCustomElementDefinitionNode(node)) {
|
|
102
|
+
// @ts-ignore
|
|
100
103
|
const { arguments: args } = node.expression;
|
|
101
104
|
const tagName = args[0].type === 'Literal'
|
|
102
105
|
? args[0].value // single and double quotes
|
|
@@ -135,6 +138,7 @@ async function getTagName(moduleURL) {
|
|
|
135
138
|
}), {
|
|
136
139
|
ExpressionStatement(node) {
|
|
137
140
|
if (isCustomElementDefinitionNode(node)) {
|
|
141
|
+
// @ts-ignore
|
|
138
142
|
tagName = node.expression.arguments[0].value;
|
|
139
143
|
}
|
|
140
144
|
}
|
|
@@ -143,22 +147,19 @@ async function getTagName(moduleURL) {
|
|
|
143
147
|
return tagName;
|
|
144
148
|
}
|
|
145
149
|
|
|
146
|
-
async function initializeCustomElement(elementURL, tagName, node = {}, definitions =
|
|
150
|
+
async function initializeCustomElement(elementURL, tagName, node = {}, definitions = {}, isEntry, props = {}) {
|
|
147
151
|
|
|
148
152
|
if (!tagName) {
|
|
149
153
|
const depth = isEntry ? 1 : 0;
|
|
150
154
|
registerDependencies(elementURL, definitions, depth);
|
|
151
155
|
}
|
|
152
156
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const { href } = elementURL;
|
|
156
|
-
const element = customElements.get(tagName) ?? (await import(href)).default;
|
|
157
|
-
const dataLoader = (await import(href)).getData;
|
|
157
|
+
const element = customElements.get(tagName) ?? (await import(elementURL)).default;
|
|
158
|
+
const dataLoader = (await import(elementURL)).getData;
|
|
158
159
|
const data = props ? props : dataLoader ? await dataLoader(props) : {};
|
|
159
160
|
|
|
160
161
|
if (element) {
|
|
161
|
-
const elementInstance = new element(data);
|
|
162
|
+
const elementInstance = new element(data);
|
|
162
163
|
|
|
163
164
|
Object.assign(elementInstance, node);
|
|
164
165
|
|
|
@@ -169,7 +170,7 @@ async function initializeCustomElement(elementURL, tagName, node = {}, definitio
|
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
async function renderToString(elementURL, wrappingEntryTag = true, props = {}) {
|
|
172
|
-
const definitions =
|
|
173
|
+
const definitions = {};
|
|
173
174
|
const elementTagName = wrappingEntryTag && await getTagName(elementURL);
|
|
174
175
|
const isEntry = !!elementTagName;
|
|
175
176
|
const elementInstance = await initializeCustomElement(elementURL, undefined, undefined, definitions, isEntry, props);
|
|
@@ -209,7 +210,7 @@ async function renderToString(elementURL, wrappingEntryTag = true, props = {}) {
|
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
async function renderFromHTML(html, elements = []) {
|
|
212
|
-
const definitions =
|
|
213
|
+
const definitions = {};
|
|
213
214
|
|
|
214
215
|
for (const url of elements) {
|
|
215
216
|
registerDependencies(url, definitions, 1);
|