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 CHANGED
@@ -4,8 +4,9 @@
4
4
 
5
5
  [![Netlify Status](https://api.netlify.com/api/v1/badges/e718eac2-b3bc-4986-8569-49706a430beb/deploy-status)](https://app.netlify.com/sites/merry-caramel-524e61/deploys)
6
6
  [![GitHub release](https://img.shields.io/github/tag/ProjectEvergreen/wcc.svg)](https://github.com/ProjectEvergreen/wcc/tags)
7
- ![GitHub Actions status](https://github.com/ProjectEvergreen/wcc/workflows/Master%20Integration/badge.svg)
8
7
  [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/ProjectEvergreen/wcc/master/LICENSE.md)
8
+ [![NodeJS compatibility](https://img.shields.io/node/v/wc-compiler.svg)](https://nodejs.org/en/about/previous-releases")
9
+ [![Discord Chat](https://img.shields.io/badge/chat-discord-blue?style=flat&logo=discord)](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.16.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": ">=14"
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 --ignore-pattern \"*.json\" \"*.*js\" \"./src/**/**/*.js*\" \"./sandbox/**/**/*.js*\" \"./docs/**/*.md\" \"./test/**/**/*.js*\"",
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-exp-loader.js --watch src --watch sandbox -e js,md,css,html,jsx,ts ./sandbox.js\" \"http-server ./dist --open\" \"livereload ./dist\"",
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:exp": "c8 node --loader ./test-exp-loader.js ./node_modules/mocha/bin/mocha \"./test/**/**/*.spec.js\"",
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:exp": "npm run test:exp -- --watch",
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.24.1",
55
- "@babel/plugin-syntax-import-assertions": "^7.24.1",
56
- "@babel/preset-react": "^7.24.1",
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
- "@rollup/plugin-commonjs": "^28.0.0",
60
- "@rollup/plugin-node-resolve": "^15.2.3",
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": "^8.14.0",
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
- /* eslint-disable no-warning-comments */
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}=\$\{${expression.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}=\$\{${expression.object.name}.${expression.property.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 += `\$\{${element.expression.name}\}`;
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 += `\$\{${element.expression.object.name}.${element.expression.property.name}\}`;
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',
@@ -0,0 +1,3 @@
1
+ import { register } from 'node:module';
2
+
3
+ register('./jsx-loader.js', import.meta.url);
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
- // would like to decouple .jsx from the core, ideally
91
- // https://github.com/ProjectEvergreen/wcc/issues/122
92
- if (!isBareSpecifier && ['js', 'jsx', 'ts'].includes(extension)) {
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
- registerDependencies(dependencyModuleURL, definitions, nextDepth);
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 = [], isEntry, props = {}) {
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
- // https://github.com/ProjectEvergreen/wcc/pull/67/files#r902061804
154
- // https://github.com/ProjectEvergreen/wcc/pull/159
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); // eslint-disable-line new-cap
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);