wrec 0.36.0 → 0.36.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.
@@ -1,5 +1,3 @@
1
- declare type AnyClass = new (...args: any[]) => any;
2
-
3
1
  declare type ChangeCallback = (change: StateChange) => void;
4
2
 
5
3
  export declare function createElement(name: string, attributes: StringToString, innerHTML: string): HTMLElement;
@@ -19,12 +17,14 @@ declare type PropertyConfig<T = any> = {
19
17
  computed?: string;
20
18
  dispatch?: boolean;
21
19
  required?: boolean;
22
- type: AnyClass;
20
+ type: PropertyType;
23
21
  usedBy?: string | string[];
24
22
  value?: T;
25
23
  values?: T extends string ? string[] : never;
26
24
  };
27
25
 
26
+ declare type PropertyType = typeof Array | typeof Boolean | typeof Number | typeof Object | typeof String;
27
+
28
28
  declare type StateChange = {
29
29
  state: WrecState;
30
30
  statePath: string;
package/dist/wrec.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- declare type AnyClass = new (...args: any[]) => any;
2
-
3
1
  declare type ChangeCallback = (change: StateChange) => void;
4
2
 
5
3
  export declare function createElement(name: string, attributes: StringToString, innerHTML: string): HTMLElement;
@@ -19,12 +17,14 @@ declare type PropertyConfig<T = any> = {
19
17
  computed?: string;
20
18
  dispatch?: boolean;
21
19
  required?: boolean;
22
- type: AnyClass;
20
+ type: PropertyType;
23
21
  usedBy?: string | string[];
24
22
  value?: T;
25
23
  values?: T extends string ? string[] : never;
26
24
  };
27
25
 
26
+ declare type PropertyType = typeof Array | typeof Boolean | typeof Number | typeof Object | typeof String;
27
+
28
28
  declare type StateChange = {
29
29
  state: WrecState;
30
30
  statePath: string;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "wrec",
3
3
  "description": "a library that greatly simplifies building web components",
4
4
  "author": "R. Mark Volkmann",
5
- "version": "0.36.0",
5
+ "version": "0.36.2",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
@@ -41,7 +41,9 @@ function analyzeSourceFile(sourceFile) {
41
41
  if (!ts.isPropertyAssignment(property)) continue;
42
42
 
43
43
  const propName = getMemberName(property);
44
- if (!propName || !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propName)) continue;
44
+ if (!propName || !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(propName)) {
45
+ continue;
46
+ }
45
47
 
46
48
  const declareType = getDeclareType(property.initializer);
47
49
  if (!declareType) continue;
package/scripts/lint.js CHANGED
@@ -842,7 +842,7 @@ function formatReport(
842
842
  findings.invalidUsedByReferences.length > 0 ||
843
843
  findings.invalidUseStateMaps.length > 0 ||
844
844
  findings.invalidValuesConfigurations.length > 0 ||
845
- findings.missingFormAssociatedProperty.length > 0 ||
845
+ findings.missingRequiredMembers.length > 0 ||
846
846
  findings.missingTypeProperties.length > 0 ||
847
847
  findings.reservedProperties.length > 0 ||
848
848
  findings.typeErrors.length > 0 ||
@@ -985,9 +985,9 @@ function formatReport(
985
985
  );
986
986
  }
987
987
 
988
- if (findings.missingFormAssociatedProperty.length > 0) {
989
- lines.push('missing formAssociated property:');
990
- findings.missingFormAssociatedProperty.forEach(message =>
988
+ if (findings.missingRequiredMembers.length > 0) {
989
+ lines.push('missing required members:');
990
+ findings.missingRequiredMembers.forEach(message =>
991
991
  lines.push(` ${message}`)
992
992
  );
993
993
  }
@@ -1180,6 +1180,15 @@ function getPropertyConfigTypeName(typeName) {
1180
1180
  }
1181
1181
  }
1182
1182
 
1183
+ // Gets the base constructor name from a generic-looking type expression.
1184
+ function getPropertyTypeGenericBaseName(sourceFile, expression) {
1185
+ if (!expression) return undefined;
1186
+
1187
+ const text = expression.getText(sourceFile).trim();
1188
+ const match = text.match(/^([A-Za-z_$][\w$]*)\s*<[\s\S]+>$/);
1189
+ return match?.[1];
1190
+ }
1191
+
1183
1192
  // Derives a readable property type string from syntax or the type checker.
1184
1193
  function getPropertyTypeText(checker, sourceFile, expression) {
1185
1194
  const typeText = getTypeSyntaxText(sourceFile, expression);
@@ -1287,6 +1296,16 @@ function getTypeSyntaxText(sourceFile, expression) {
1287
1296
  return undefined;
1288
1297
  }
1289
1298
 
1299
+ // Returns whether a component class defines its own static html property.
1300
+ function hasStaticHtmlDefinition(classNode) {
1301
+ for (const member of classNode.members) {
1302
+ if (!hasStaticModifier(member)) continue;
1303
+ if (!ts.isPropertyDeclaration(member)) continue;
1304
+ if (getMemberName(member) === 'html') return true;
1305
+ }
1306
+ return false;
1307
+ }
1308
+
1290
1309
  // Returns whether a token kind is one of the supported arithmetic operators.
1291
1310
  function isArithmeticOperator(kind) {
1292
1311
  return (
@@ -1329,6 +1348,27 @@ function isImportLikeDeclaration(node) {
1329
1348
  );
1330
1349
  }
1331
1350
 
1351
+ // Returns whether a type represents an object-like value other than an array.
1352
+ function isNonArrayObjectLikeType(checker, type) {
1353
+ if (type.isUnion()) {
1354
+ return type.types.every(member =>
1355
+ isNonArrayObjectLikeType(checker, member)
1356
+ );
1357
+ }
1358
+
1359
+ if (type.isIntersection()) {
1360
+ return type.types.every(member =>
1361
+ isNonArrayObjectLikeType(checker, member)
1362
+ );
1363
+ }
1364
+
1365
+ return (
1366
+ Boolean(type.flags & (ts.TypeFlags.Object | ts.TypeFlags.NonPrimitive)) &&
1367
+ !checker.isArrayType(type) &&
1368
+ !checker.isTupleType(type)
1369
+ );
1370
+ }
1371
+
1332
1372
  // Returns whether a type is fully numeric or any-like for arithmetic checks.
1333
1373
  function isNumericLikeType(type) {
1334
1374
  const parts = type.isUnion() ? type.types : [type];
@@ -1420,7 +1460,7 @@ export function lintSource(filePath, sourceText, options = {}) {
1420
1460
  invalidUsedByReferences: [],
1421
1461
  invalidUseStateMaps: [],
1422
1462
  invalidValuesConfigurations: [],
1423
- missingFormAssociatedProperty: [],
1463
+ missingRequiredMembers: [],
1424
1464
  missingTypeProperties: [],
1425
1465
  reservedProperties,
1426
1466
  typeErrors: [],
@@ -1445,10 +1485,15 @@ export function lintSource(filePath, sourceText, options = {}) {
1445
1485
  }));
1446
1486
 
1447
1487
  if (allMethods.has('formAssociatedCallback') && !formAssociated) {
1448
- findings.missingFormAssociatedProperty.push(
1488
+ findings.missingRequiredMembers.push(
1449
1489
  'formAssociatedCallback is defined, but static formAssociated is not true'
1450
1490
  );
1451
1491
  }
1492
+ if (!hasStaticHtmlDefinition(classNode)) {
1493
+ findings.missingRequiredMembers.push(
1494
+ 'static html property must be defined'
1495
+ );
1496
+ }
1452
1497
 
1453
1498
  const augmentedSource = buildAugmentedSource(
1454
1499
  sourceFile,
@@ -1528,7 +1573,7 @@ export function lintSource(filePath, sourceText, options = {}) {
1528
1573
  findings.invalidUsedByReferences.sort();
1529
1574
  findings.invalidUseStateMaps.sort();
1530
1575
  findings.invalidValuesConfigurations.sort();
1531
- findings.missingFormAssociatedProperty.sort();
1576
+ findings.missingRequiredMembers.sort();
1532
1577
  findings.missingTypeProperties.sort();
1533
1578
  findings.reservedProperties.sort();
1534
1579
  findings.typeErrors.sort((a, b) => a.expression.localeCompare(b.expression));
@@ -1602,23 +1647,6 @@ function requiresContextFunction(symbol, sourceFile) {
1602
1647
  });
1603
1648
  }
1604
1649
 
1605
- // Returns whether a type represents an object-like value other than an array.
1606
- function isNonArrayObjectLikeType(checker, type) {
1607
- if (type.isUnion()) {
1608
- return type.types.every(member => isNonArrayObjectLikeType(checker, member));
1609
- }
1610
-
1611
- if (type.isIntersection()) {
1612
- return type.types.every(member => isNonArrayObjectLikeType(checker, member));
1613
- }
1614
-
1615
- return (
1616
- Boolean(type.flags & (ts.TypeFlags.Object | ts.TypeFlags.NonPrimitive)) &&
1617
- !checker.isArrayType(type) &&
1618
- !checker.isTupleType(type)
1619
- );
1620
- }
1621
-
1622
1650
  // Resolves a relative import path to an existing source file.
1623
1651
  function resolveImportPath(baseDir, importPath) {
1624
1652
  if (!importPath.startsWith('.')) return undefined;
@@ -1963,6 +1991,16 @@ function validatePropertyConfigs(
1963
1991
  findings.missingTypeProperties.push(
1964
1992
  `property "${propName}" does not specify a type`
1965
1993
  );
1994
+ } else if (
1995
+ SUPPORTED_PROPERTY_TYPE_NAMES.has(
1996
+ getPropertyTypeGenericBaseName(sourceFile, typeExpression)
1997
+ )
1998
+ ) {
1999
+ findings.invalidTypeProperties.push(
2000
+ `property "${propName}" type cannot use generic syntax like ` +
2001
+ `"${typeExpression.getText(sourceFile).trim()}"; use ` +
2002
+ `"${getPropertyTypeGenericBaseName(sourceFile, typeExpression)}" instead`
2003
+ );
1966
2004
  } else if (
1967
2005
  !SUPPORTED_PROPERTY_TYPE_NAMES.has(typeExpressionKind(typeExpression))
1968
2006
  ) {
@@ -1971,7 +2009,13 @@ function validatePropertyConfigs(
1971
2009
  'Boolean, Number, String, Object, or Array'
1972
2010
  );
1973
2011
  } else if (declaredTypeNode) {
1974
- if (!typeExpressionMatchesDeclaredType(checker, typeExpression, declaredTypeNode)) {
2012
+ if (
2013
+ !typeExpressionMatchesDeclaredType(
2014
+ checker,
2015
+ typeExpression,
2016
+ declaredTypeNode
2017
+ )
2018
+ ) {
1975
2019
  findings.incompatibleDeclareTypes.push(
1976
2020
  `property "${propName}" declare type ` +
1977
2021
  `"${getPropertyTypeTextFromNode(sourceFile, declaredTypeNode)}" ` +
File without changes