wc-compiler 0.6.1 → 0.7.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/dist/wcc.dist.cjs CHANGED
@@ -3,8 +3,6 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var fs = require('fs');
6
- var path = require('path');
7
- var url = require('url');
8
6
 
9
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
8
 
@@ -27,7 +25,6 @@ function _interopNamespace(e) {
27
25
  }
28
26
 
29
27
  var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
30
- var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
31
28
 
32
29
  function noop() { }
33
30
 
@@ -86,7 +83,7 @@ class Document extends Node$1 {
86
83
  return new HTMLTemplateElement();
87
84
 
88
85
  default:
89
- return new HTMLElement();
86
+ return new HTMLElement$1();
90
87
 
91
88
  }
92
89
  }
@@ -98,7 +95,7 @@ class Document extends Node$1 {
98
95
 
99
96
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement
100
97
  // EventTarget <- Node <- Element <- HTMLElement
101
- class HTMLElement extends Element {
98
+ class HTMLElement$1 extends Element {
102
99
  attachShadow(options) {
103
100
  this.shadowRoot = new ShadowRoot(options);
104
101
 
@@ -129,7 +126,7 @@ class ShadowRoot extends DocumentFragment {
129
126
 
130
127
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement
131
128
  // EventTarget <- Node <- Element <- HTMLElement <- HTMLTemplateElement
132
- class HTMLTemplateElement extends HTMLElement {
129
+ class HTMLTemplateElement extends HTMLElement$1 {
133
130
  constructor() {
134
131
  super();
135
132
  this.content = new DocumentFragment();
@@ -171,7 +168,7 @@ class CustomElementsRegistry {
171
168
  globalThis.addEventListener = noop;
172
169
  globalThis.document = new Document();
173
170
  globalThis.customElements = new CustomElementsRegistry();
174
- globalThis.HTMLElement = HTMLElement;
171
+ globalThis.HTMLElement = HTMLElement$1;
175
172
 
176
173
  // Reserved word lists for various dialects of the language
177
174
 
@@ -27632,7 +27629,7 @@ var serialize = function(node, options) {
27632
27629
 
27633
27630
  /* eslint-disable max-depth, complexity */
27634
27631
 
27635
- url.pathToFileURL(`${process.cwd()}/`).href;
27632
+ new URL(`file://${process.cwd()}/`);
27636
27633
 
27637
27634
  // TODO same hack as definitions
27638
27635
  // https://github.com/ProjectEvergreen/wcc/discussions/74
@@ -27647,7 +27644,7 @@ function getParse$1(html) {
27647
27644
  }
27648
27645
 
27649
27646
  function getParser(moduleURL) {
27650
- const isJSX = path__default["default"].extname(moduleURL.pathname) === '.jsx';
27647
+ const isJSX = moduleURL.pathname.split('.').pop() === 'jsx';
27651
27648
 
27652
27649
  if (!isJSX) {
27653
27650
  return;
@@ -27819,14 +27816,52 @@ function parseJsxElement(element, moduleContents = '') {
27819
27816
  return string;
27820
27817
  }
27821
27818
 
27819
+ // TODO handle if / else statements
27820
+ // https://github.com/ProjectEvergreen/wcc/issues/88
27821
+ function findThisReferences(context, statement) {
27822
+ const references = [];
27823
+ const isRenderFunctionContext = context === 'render';
27824
+ const { expression, type } = statement;
27825
+ const isConstructorThisAssignment = context === 'constructor'
27826
+ && type === 'ExpressionStatement'
27827
+ && expression.type === 'AssignmentExpression'
27828
+ && expression.left.object.type === 'ThisExpression';
27829
+
27830
+ if (isConstructorThisAssignment) {
27831
+ // this.name = 'something'; // constructor
27832
+ references.push(expression.left.property.name);
27833
+ } else if (isRenderFunctionContext && type === 'VariableDeclaration') {
27834
+ statement.declarations.forEach(declaration => {
27835
+ const { init, id } = declaration;
27836
+
27837
+ if (init.object && init.object.type === 'ThisExpression') {
27838
+ // const { description } = this.todo;
27839
+ references.push(init.property.name);
27840
+ } else if (init.type === 'ThisExpression' && id && id.properties) {
27841
+ // const { description } = this.todo;
27842
+ id.properties.forEach((property) => {
27843
+ references.push(property.key.name);
27844
+ });
27845
+ }
27846
+ });
27847
+ }
27848
+
27849
+ return references;
27850
+ }
27851
+
27822
27852
  function parseJsx(moduleURL) {
27823
27853
  const moduleContents = fs__default["default"].readFileSync(moduleURL, 'utf-8');
27824
- string = '';
27825
-
27826
- const tree = Parser$2.extend(jsx()).parse(moduleContents, {
27854
+ // would be nice if we could do this instead, so we could know ahead of time
27855
+ // const { inferredObservability } = await import(moduleURL);
27856
+ // however, this requires making parseJsx async, but WCC acorn walking is done sync
27857
+ const hasOwnObservedAttributes = undefined;
27858
+ let inferredObservability = false;
27859
+ let observedAttributes = [];
27860
+ let tree = Parser$2.extend(jsx()).parse(moduleContents, {
27827
27861
  ecmaVersion: 'latest',
27828
27862
  sourceType: 'module'
27829
27863
  });
27864
+ string = '';
27830
27865
 
27831
27866
  simple(tree, {
27832
27867
  ClassDeclaration(node) {
@@ -27834,29 +27869,46 @@ function parseJsx(moduleURL) {
27834
27869
  const hasShadowRoot = moduleContents.slice(node.body.start, node.body.end).indexOf('this.attachShadow(') > 0;
27835
27870
 
27836
27871
  for (const n1 of node.body.body) {
27837
- if (n1.type === 'MethodDefinition' && n1.key.name === 'render') {
27838
- for (const n2 in n1.value.body.body) {
27839
- const n = n1.value.body.body[n2];
27840
-
27841
- if (n.type === 'ReturnStatement' && n.argument.type === 'JSXElement') {
27842
- const html = parseJsxElement(n.argument, moduleContents);
27843
- const elementTree = getParse$1(html)(html);
27844
- const elementRoot = hasShadowRoot ? 'this.shadowRoot' : 'this';
27845
-
27846
- applyDomDepthSubstitutions(elementTree, undefined, hasShadowRoot);
27847
-
27848
- const finalHtml = serialize(elementTree);
27849
- const transformed = parse$1(`${elementRoot}.innerHTML = \`${finalHtml}\`;`, {
27850
- ecmaVersion: 'latest',
27851
- sourceType: 'module'
27852
- });
27853
-
27854
- n1.value.body.body[n2] = transformed;
27872
+ if (n1.type === 'MethodDefinition') {
27873
+ const nodeName = n1.key.name;
27874
+ if (nodeName === 'render') {
27875
+ for (const n2 in n1.value.body.body) {
27876
+ const n = n1.value.body.body[n2];
27877
+
27878
+ if (n.type === 'VariableDeclaration') {
27879
+ observedAttributes = [
27880
+ ...observedAttributes,
27881
+ ...findThisReferences('render', n)
27882
+ ];
27883
+ } else if (n.type === 'ReturnStatement' && n.argument.type === 'JSXElement') {
27884
+ const html = parseJsxElement(n.argument, moduleContents);
27885
+ const elementTree = getParse$1(html)(html);
27886
+ const elementRoot = hasShadowRoot ? 'this.shadowRoot' : 'this';
27887
+
27888
+ applyDomDepthSubstitutions(elementTree, undefined, hasShadowRoot);
27889
+
27890
+ const finalHtml = serialize(elementTree);
27891
+ const transformed = parse$1(`${elementRoot}.innerHTML = \`${finalHtml}\`;`, {
27892
+ ecmaVersion: 'latest',
27893
+ sourceType: 'module'
27894
+ });
27895
+
27896
+ n1.value.body.body[n2] = transformed;
27897
+ }
27855
27898
  }
27856
27899
  }
27857
27900
  }
27858
27901
  }
27859
27902
  }
27903
+ },
27904
+ ExportNamedDeclaration(node) {
27905
+ const { declaration } = node;
27906
+
27907
+ if (declaration && declaration.type === 'VariableDeclaration' && declaration.kind === 'const' && declaration.declarations.length === 1) {
27908
+ if (declaration.declarations[0].id.name === 'inferredObservability') {
27909
+ inferredObservability = Boolean(node.declaration.declarations[0].init.raw);
27910
+ }
27911
+ }
27860
27912
  }
27861
27913
  }, {
27862
27914
  // https://github.com/acornjs/acorn/issues/829#issuecomment-1172586171
@@ -27864,6 +27916,68 @@ function parseJsx(moduleURL) {
27864
27916
  JSXElement: () => {}
27865
27917
  });
27866
27918
 
27919
+ // TODO - signals: use constructor, render, HTML attributes? some, none, or all?
27920
+ if (inferredObservability && observedAttributes.length > 0 && !hasOwnObservedAttributes) {
27921
+ let insertPoint;
27922
+ for (const line of tree.body) {
27923
+ // test for class MyComponent vs export default class MyComponent
27924
+ if (line.type === 'ClassDeclaration' || (line.declaration && line.declaration.type) === 'ClassDeclaration') {
27925
+ const children = !line.declaration
27926
+ ? line.body.body
27927
+ : line.declaration.body.body;
27928
+ for (const method of children) {
27929
+ if (method.key.name === 'constructor') {
27930
+ insertPoint = method.start - 1;
27931
+ break;
27932
+ }
27933
+ }
27934
+ }
27935
+ }
27936
+
27937
+ let newModuleContents = escodegen.generate(tree);
27938
+
27939
+ // TODO better way to determine value type?
27940
+ /* eslint-disable indent */
27941
+ newModuleContents = `${newModuleContents.slice(0, insertPoint)}
27942
+ static get observedAttributes() {
27943
+ return [${[...observedAttributes].map(attr => `'${attr}'`).join(',')}]
27944
+ }
27945
+
27946
+ attributeChangedCallback(name, oldValue, newValue) {
27947
+ function getValue(value) {
27948
+ return value.charAt(0) === '{' || value.charAt(0) === '['
27949
+ ? JSON.parse(value)
27950
+ : !isNaN(value)
27951
+ ? parseInt(value, 10)
27952
+ : value === 'true' || value === 'false'
27953
+ ? value === 'true' ? true : false
27954
+ : value;
27955
+ }
27956
+ if (newValue !== oldValue) {
27957
+ switch(name) {
27958
+ ${observedAttributes.map((attr) => {
27959
+ return `
27960
+ case '${attr}':
27961
+ this.${attr} = getValue(newValue);
27962
+ break;
27963
+ `;
27964
+ }).join('\n')}
27965
+ }
27966
+
27967
+ this.render();
27968
+ }
27969
+ }
27970
+
27971
+ ${newModuleContents.slice(insertPoint)}
27972
+ `;
27973
+ /* eslint-enable indent */
27974
+
27975
+ tree = Parser$2.extend(jsx()).parse(newModuleContents, {
27976
+ ecmaVersion: 'latest',
27977
+ sourceType: 'module'
27978
+ });
27979
+ }
27980
+
27867
27981
  return tree;
27868
27982
  }
27869
27983
 
@@ -27933,9 +28047,10 @@ function registerDependencies(moduleURL, definitions, depth = 0) {
27933
28047
  ImportDeclaration(node) {
27934
28048
  const specifier = node.source.value;
27935
28049
  const isBareSpecifier = specifier.indexOf('.') !== 0 && specifier.indexOf('/') !== 0;
28050
+ const extension = specifier.split('.').pop();
27936
28051
 
27937
28052
  // TODO would like to decouple .jsx from the core, ideally
27938
- if (!isBareSpecifier && ['.js', '.jsx'].includes(path__default["default"].extname(specifier))) {
28053
+ if (!isBareSpecifier && ['js', 'jsx'].includes(extension)) {
27939
28054
  const dependencyModuleURL = new URL(node.source.value, moduleURL);
27940
28055
 
27941
28056
  registerDependencies(dependencyModuleURL, definitions, nextDepth);
@@ -27998,19 +28113,25 @@ async function initializeCustomElement(elementURL, tagName, attrs = [], definiti
27998
28113
  : (await (function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(t)); }); })(pathname)).default;
27999
28114
  const dataLoader = (await (function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(t)); }); })(pathname)).getData;
28000
28115
  const data = dataLoader ? await dataLoader() : {};
28001
- const elementInstance = new element(data); // eslint-disable-line new-cap
28002
28116
 
28003
- attrs.forEach((attr) => {
28004
- elementInstance.setAttribute(attr.name, attr.value);
28005
-
28006
- if (attr.name === 'hydrate') {
28007
- definitions[tagName].hydrate = attr.value;
28008
- }
28009
- });
28117
+ if (element) {
28118
+ const elementInstance = new element(data); // eslint-disable-line new-cap
28010
28119
 
28011
- await elementInstance.connectedCallback();
28012
-
28013
- return elementInstance;
28120
+ attrs.forEach((attr) => {
28121
+ elementInstance.setAttribute(attr.name, attr.value);
28122
+
28123
+ if (attr.name === 'hydrate') {
28124
+ definitions[tagName].hydrate = attr.value;
28125
+ }
28126
+ });
28127
+
28128
+ await elementInstance.connectedCallback();
28129
+
28130
+ return elementInstance;
28131
+ } else {
28132
+ console.debug('No custom element class found for this file');
28133
+ return new HTMLElement();
28134
+ }
28014
28135
  }
28015
28136
 
28016
28137
  async function renderToString(elementURL) {
package/package.json CHANGED
@@ -1,10 +1,19 @@
1
1
  {
2
2
  "name": "wc-compiler",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Experimental native Web Components compiler.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/ProjectEvergreen/wcc.git"
8
+ },
5
9
  "main": "src/wcc.js",
6
10
  "type": "module",
7
11
  "author": "Owen Buckley <owen@thegreenhouse.io>",
12
+ "keywords": [
13
+ "Web Components",
14
+ "JSX",
15
+ "Greenwood"
16
+ ],
8
17
  "license": "MIT",
9
18
  "engines": {
10
19
  "node": ">=14"
@@ -23,7 +32,7 @@
23
32
  "build": "node ./build.js",
24
33
  "serve": "node ./build.js && http-server ./dist --open",
25
34
  "start": "npm run develop",
26
- "test": "mocha --exclude \"./test/cases/jsx/**\" --exclude \"./test/cases/custom-extension/**\" \"./test/**/**/*.spec.js\"",
35
+ "test": "mocha --exclude \"./test/cases/jsx*/**\" --exclude \"./test/cases/custom-extension/**\" \"./test/**/**/*.spec.js\"",
27
36
  "test:exp": "c8 node --experimental-loader ./test-exp-loader.js ./node_modules/mocha/bin/mocha \"./test/**/**/*.spec.js\"",
28
37
  "test:tdd": "npm run test -- --watch",
29
38
  "test:tdd:exp": "npm run test:exp -- --watch",
package/src/jsx-loader.js CHANGED
@@ -6,10 +6,8 @@ import escodegen from 'escodegen';
6
6
  import fs from 'fs';
7
7
  import jsx from 'acorn-jsx';
8
8
  import { parse, parseFragment, serialize } from 'parse5';
9
- import path from 'path';
10
- import { URL, pathToFileURL } from 'url';
11
9
 
12
- const baseURL = pathToFileURL(`${process.cwd()}/`).href;
10
+ const baseURL = new URL(`file://${process.cwd()}/`);
13
11
  const jsxRegex = /\.(jsx)$/;
14
12
 
15
13
  // TODO same hack as definitions
@@ -25,7 +23,7 @@ function getParse(html) {
25
23
  }
26
24
 
27
25
  export function getParser(moduleURL) {
28
- const isJSX = path.extname(moduleURL.pathname) === '.jsx';
26
+ const isJSX = moduleURL.pathname.split('.').pop() === 'jsx';
29
27
 
30
28
  if (!isJSX) {
31
29
  return;
@@ -197,14 +195,52 @@ function parseJsxElement(element, moduleContents = '') {
197
195
  return string;
198
196
  }
199
197
 
198
+ // TODO handle if / else statements
199
+ // https://github.com/ProjectEvergreen/wcc/issues/88
200
+ function findThisReferences(context, statement) {
201
+ const references = [];
202
+ const isRenderFunctionContext = context === 'render';
203
+ const { expression, type } = statement;
204
+ const isConstructorThisAssignment = context === 'constructor'
205
+ && type === 'ExpressionStatement'
206
+ && expression.type === 'AssignmentExpression'
207
+ && expression.left.object.type === 'ThisExpression';
208
+
209
+ if (isConstructorThisAssignment) {
210
+ // this.name = 'something'; // constructor
211
+ references.push(expression.left.property.name);
212
+ } else if (isRenderFunctionContext && type === 'VariableDeclaration') {
213
+ statement.declarations.forEach(declaration => {
214
+ const { init, id } = declaration;
215
+
216
+ if (init.object && init.object.type === 'ThisExpression') {
217
+ // const { description } = this.todo;
218
+ references.push(init.property.name);
219
+ } else if (init.type === 'ThisExpression' && id && id.properties) {
220
+ // const { description } = this.todo;
221
+ id.properties.forEach((property) => {
222
+ references.push(property.key.name);
223
+ });
224
+ }
225
+ });
226
+ }
227
+
228
+ return references;
229
+ }
230
+
200
231
  export function parseJsx(moduleURL) {
201
232
  const moduleContents = fs.readFileSync(moduleURL, 'utf-8');
202
- string = '';
203
-
204
- const tree = acorn.Parser.extend(jsx()).parse(moduleContents, {
233
+ // would be nice if we could do this instead, so we could know ahead of time
234
+ // const { inferredObservability } = await import(moduleURL);
235
+ // however, this requires making parseJsx async, but WCC acorn walking is done sync
236
+ const hasOwnObservedAttributes = undefined;
237
+ let inferredObservability = false;
238
+ let observedAttributes = [];
239
+ let tree = acorn.Parser.extend(jsx()).parse(moduleContents, {
205
240
  ecmaVersion: 'latest',
206
241
  sourceType: 'module'
207
242
  });
243
+ string = '';
208
244
 
209
245
  walk.simple(tree, {
210
246
  ClassDeclaration(node) {
@@ -212,29 +248,46 @@ export function parseJsx(moduleURL) {
212
248
  const hasShadowRoot = moduleContents.slice(node.body.start, node.body.end).indexOf('this.attachShadow(') > 0;
213
249
 
214
250
  for (const n1 of node.body.body) {
215
- if (n1.type === 'MethodDefinition' && n1.key.name === 'render') {
216
- for (const n2 in n1.value.body.body) {
217
- const n = n1.value.body.body[n2];
218
-
219
- if (n.type === 'ReturnStatement' && n.argument.type === 'JSXElement') {
220
- const html = parseJsxElement(n.argument, moduleContents);
221
- const elementTree = getParse(html)(html);
222
- const elementRoot = hasShadowRoot ? 'this.shadowRoot' : 'this';
223
-
224
- applyDomDepthSubstitutions(elementTree, undefined, hasShadowRoot);
225
-
226
- const finalHtml = serialize(elementTree);
227
- const transformed = acorn.parse(`${elementRoot}.innerHTML = \`${finalHtml}\`;`, {
228
- ecmaVersion: 'latest',
229
- sourceType: 'module'
230
- });
231
-
232
- n1.value.body.body[n2] = transformed;
251
+ if (n1.type === 'MethodDefinition') {
252
+ const nodeName = n1.key.name;
253
+ if (nodeName === 'render') {
254
+ for (const n2 in n1.value.body.body) {
255
+ const n = n1.value.body.body[n2];
256
+
257
+ if (n.type === 'VariableDeclaration') {
258
+ observedAttributes = [
259
+ ...observedAttributes,
260
+ ...findThisReferences('render', n)
261
+ ];
262
+ } else if (n.type === 'ReturnStatement' && n.argument.type === 'JSXElement') {
263
+ const html = parseJsxElement(n.argument, moduleContents);
264
+ const elementTree = getParse(html)(html);
265
+ const elementRoot = hasShadowRoot ? 'this.shadowRoot' : 'this';
266
+
267
+ applyDomDepthSubstitutions(elementTree, undefined, hasShadowRoot);
268
+
269
+ const finalHtml = serialize(elementTree);
270
+ const transformed = acorn.parse(`${elementRoot}.innerHTML = \`${finalHtml}\`;`, {
271
+ ecmaVersion: 'latest',
272
+ sourceType: 'module'
273
+ });
274
+
275
+ n1.value.body.body[n2] = transformed;
276
+ }
233
277
  }
234
278
  }
235
279
  }
236
280
  }
237
281
  }
282
+ },
283
+ ExportNamedDeclaration(node) {
284
+ const { declaration } = node;
285
+
286
+ if (declaration && declaration.type === 'VariableDeclaration' && declaration.kind === 'const' && declaration.declarations.length === 1) {
287
+ if (declaration.declarations[0].id.name === 'inferredObservability') {
288
+ inferredObservability = Boolean(node.declaration.declarations[0].init.raw);
289
+ }
290
+ }
238
291
  }
239
292
  }, {
240
293
  // https://github.com/acornjs/acorn/issues/829#issuecomment-1172586171
@@ -242,6 +295,68 @@ export function parseJsx(moduleURL) {
242
295
  JSXElement: () => {}
243
296
  });
244
297
 
298
+ // TODO - signals: use constructor, render, HTML attributes? some, none, or all?
299
+ if (inferredObservability && observedAttributes.length > 0 && !hasOwnObservedAttributes) {
300
+ let insertPoint;
301
+ for (const line of tree.body) {
302
+ // test for class MyComponent vs export default class MyComponent
303
+ if (line.type === 'ClassDeclaration' || (line.declaration && line.declaration.type) === 'ClassDeclaration') {
304
+ const children = !line.declaration
305
+ ? line.body.body
306
+ : line.declaration.body.body;
307
+ for (const method of children) {
308
+ if (method.key.name === 'constructor') {
309
+ insertPoint = method.start - 1;
310
+ break;
311
+ }
312
+ }
313
+ }
314
+ }
315
+
316
+ let newModuleContents = escodegen.generate(tree);
317
+
318
+ // TODO better way to determine value type?
319
+ /* eslint-disable indent */
320
+ newModuleContents = `${newModuleContents.slice(0, insertPoint)}
321
+ static get observedAttributes() {
322
+ return [${[...observedAttributes].map(attr => `'${attr}'`).join(',')}]
323
+ }
324
+
325
+ attributeChangedCallback(name, oldValue, newValue) {
326
+ function getValue(value) {
327
+ return value.charAt(0) === '{' || value.charAt(0) === '['
328
+ ? JSON.parse(value)
329
+ : !isNaN(value)
330
+ ? parseInt(value, 10)
331
+ : value === 'true' || value === 'false'
332
+ ? value === 'true' ? true : false
333
+ : value;
334
+ }
335
+ if (newValue !== oldValue) {
336
+ switch(name) {
337
+ ${observedAttributes.map((attr) => {
338
+ return `
339
+ case '${attr}':
340
+ this.${attr} = getValue(newValue);
341
+ break;
342
+ `;
343
+ }).join('\n')}
344
+ }
345
+
346
+ this.render();
347
+ }
348
+ }
349
+
350
+ ${newModuleContents.slice(insertPoint)}
351
+ `;
352
+ /* eslint-enable indent */
353
+
354
+ tree = acorn.Parser.extend(jsx()).parse(newModuleContents, {
355
+ ecmaVersion: 'latest',
356
+ sourceType: 'module'
357
+ });
358
+ }
359
+
245
360
  return tree;
246
361
  }
247
362
 
@@ -265,7 +380,8 @@ export async function load(url, context, defaultLoad) {
265
380
 
266
381
  return {
267
382
  format: 'module',
268
- source: escodegen.generate(jsFromJsx)
383
+ source: escodegen.generate(jsFromJsx),
384
+ shortCircuit: true
269
385
  };
270
386
  }
271
387
 
package/src/wcc.js CHANGED
@@ -8,7 +8,6 @@ import escodegen from 'escodegen';
8
8
  import { getParser, parseJsx } from './jsx-loader.js';
9
9
  import { parse, parseFragment, serialize } from 'parse5';
10
10
  import fs from 'fs';
11
- import path from 'path';
12
11
 
13
12
  function getParse(html) {
14
13
  return html.indexOf('<html>') >= 0 || html.indexOf('<body>') >= 0 || html.indexOf('<head>') >= 0
@@ -74,9 +73,10 @@ function registerDependencies(moduleURL, definitions, depth = 0) {
74
73
  ImportDeclaration(node) {
75
74
  const specifier = node.source.value;
76
75
  const isBareSpecifier = specifier.indexOf('.') !== 0 && specifier.indexOf('/') !== 0;
76
+ const extension = specifier.split('.').pop();
77
77
 
78
78
  // TODO would like to decouple .jsx from the core, ideally
79
- if (!isBareSpecifier && ['.js', '.jsx'].includes(path.extname(specifier))) {
79
+ if (!isBareSpecifier && ['js', 'jsx'].includes(extension)) {
80
80
  const dependencyModuleURL = new URL(node.source.value, moduleURL);
81
81
 
82
82
  registerDependencies(dependencyModuleURL, definitions, nextDepth);
@@ -139,19 +139,25 @@ async function initializeCustomElement(elementURL, tagName, attrs = [], definiti
139
139
  : (await import(pathname)).default;
140
140
  const dataLoader = (await import(pathname)).getData;
141
141
  const data = dataLoader ? await dataLoader() : {};
142
- const elementInstance = new element(data); // eslint-disable-line new-cap
143
142
 
144
- attrs.forEach((attr) => {
145
- elementInstance.setAttribute(attr.name, attr.value);
143
+ if (element) {
144
+ const elementInstance = new element(data); // eslint-disable-line new-cap
146
145
 
147
- if (attr.name === 'hydrate') {
148
- definitions[tagName].hydrate = attr.value;
149
- }
150
- });
151
-
152
- await elementInstance.connectedCallback();
153
-
154
- return elementInstance;
146
+ attrs.forEach((attr) => {
147
+ elementInstance.setAttribute(attr.name, attr.value);
148
+
149
+ if (attr.name === 'hydrate') {
150
+ definitions[tagName].hydrate = attr.value;
151
+ }
152
+ });
153
+
154
+ await elementInstance.connectedCallback();
155
+
156
+ return elementInstance;
157
+ } else {
158
+ console.debug('No custom element class found for this file');
159
+ return new HTMLElement();
160
+ }
155
161
  }
156
162
 
157
163
  async function renderToString(elementURL) {