wrec 0.35.0 → 0.35.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/scripts/lint.js +59 -1
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.35.0",
5
+ "version": "0.35.1",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
package/scripts/lint.js CHANGED
@@ -451,6 +451,21 @@ function collectSupportedPropertyNames(classNode) {
451
451
  return supportedProps;
452
452
  }
453
453
 
454
+ // Collects declared TypeScript property types from a component class.
455
+ function collectDeclaredPropertyTypes(classNode) {
456
+ const declaredProps = new Map();
457
+
458
+ for (const member of classNode.members) {
459
+ if (!isDeclarePropertyDeclaration(member)) continue;
460
+
461
+ const propName = getMemberName(member);
462
+ if (!propName || !member.type) continue;
463
+ declaredProps.set(propName, member.type);
464
+ }
465
+
466
+ return declaredProps;
467
+ }
468
+
454
469
  // Validates that useState mappings point at existing component properties.
455
470
  function collectUseStateMapErrors(classNode, supportedProps, findings) {
456
471
  // Walks the class body looking for useState calls with mapping objects.
@@ -815,6 +830,7 @@ function formatReport(
815
830
  findings.duplicateProperties.length > 0 ||
816
831
  findings.extraArguments.length > 0 ||
817
832
  findings.incompatibleArguments.length > 0 ||
833
+ findings.incompatibleDeclareTypes.length > 0 ||
818
834
  findings.invalidCheckedBindings.length > 0 ||
819
835
  findings.invalidComputedProperties.length > 0 ||
820
836
  findings.invalidDefaultValues.length > 0 ||
@@ -889,6 +905,13 @@ function formatReport(
889
905
  });
890
906
  }
891
907
 
908
+ if (findings.incompatibleDeclareTypes.length > 0) {
909
+ lines.push('incompatible declare types:');
910
+ findings.incompatibleDeclareTypes.forEach(message =>
911
+ lines.push(` ${message}`)
912
+ );
913
+ }
914
+
892
915
  if (findings.invalidCheckedBindings.length > 0) {
893
916
  lines.push('invalid checked bindings:');
894
917
  findings.invalidCheckedBindings.forEach(message =>
@@ -1166,6 +1189,11 @@ function getPropertyTypeText(checker, sourceFile, expression) {
1166
1189
  return checker.typeToString(type);
1167
1190
  }
1168
1191
 
1192
+ // Gets the TypeScript type text for a declared property member.
1193
+ function getPropertyTypeTextFromNode(sourceFile, typeNode) {
1194
+ return typeNode.getText(sourceFile).trim();
1195
+ }
1196
+
1169
1197
  // Returns an array of string literal values from a property when possible.
1170
1198
  function getStringArrayLiteral(property) {
1171
1199
  if (!property || !ts.isPropertyAssignment(property)) return undefined;
@@ -1275,6 +1303,17 @@ function isCallCallee(node) {
1275
1303
  return ts.isCallExpression(node.parent) && node.parent.expression === node;
1276
1304
  }
1277
1305
 
1306
+ // Returns whether a class member is a declared property declaration.
1307
+ function isDeclarePropertyDeclaration(member) {
1308
+ return (
1309
+ ts.isPropertyDeclaration(member) &&
1310
+ ts.canHaveModifiers(member) &&
1311
+ ts
1312
+ .getModifiers(member)
1313
+ ?.some(mod => mod.kind === ts.SyntaxKind.DeclareKeyword)
1314
+ );
1315
+ }
1316
+
1278
1317
  // Returns whether a reference refers to a getter method.
1279
1318
  function isGetterReference(reference) {
1280
1319
  return reference.startsWith(GETTER_PREFIX);
@@ -1362,12 +1401,14 @@ export function lintSource(filePath, sourceText, options = {}) {
1362
1401
  propertyEntries,
1363
1402
  reservedProperties
1364
1403
  } = extractProperties(sourceFile, checker, classNode);
1404
+ const declaredPropertyTypes = collectDeclaredPropertyTypes(classNode);
1365
1405
  const getterNames = collectGetterNames(classNode);
1366
1406
  const allMethods = collectClassMethods(classNode);
1367
1407
  const findings = {
1368
1408
  duplicateProperties,
1369
1409
  extraArguments: [],
1370
1410
  incompatibleArguments: [],
1411
+ incompatibleDeclareTypes: [],
1371
1412
  invalidCheckedBindings: [],
1372
1413
  invalidComputedProperties: [],
1373
1414
  invalidDefaultValues: [],
@@ -1430,6 +1471,8 @@ export function lintSource(filePath, sourceText, options = {}) {
1430
1471
 
1431
1472
  validatePropertyConfigs(
1432
1473
  checker,
1474
+ sourceFile,
1475
+ declaredPropertyTypes,
1433
1476
  supportedProps,
1434
1477
  propertyEntries,
1435
1478
  getterNames,
@@ -1473,6 +1516,7 @@ export function lintSource(filePath, sourceText, options = {}) {
1473
1516
  a.methodName.localeCompare(b.methodName) ||
1474
1517
  a.parameterName.localeCompare(b.parameterName)
1475
1518
  );
1519
+ findings.incompatibleDeclareTypes.sort();
1476
1520
  findings.invalidCheckedBindings.sort();
1477
1521
  findings.invalidComputedProperties.sort();
1478
1522
  findings.invalidDefaultValues.sort();
@@ -1865,6 +1909,8 @@ function validateHtmlNesting(node, findings) {
1865
1909
  // Validates all configured component property metadata entries.
1866
1910
  function validatePropertyConfigs(
1867
1911
  checker,
1912
+ sourceFile,
1913
+ declaredPropertyTypes,
1868
1914
  supportedProps,
1869
1915
  propertyEntries,
1870
1916
  getterNames,
@@ -1872,9 +1918,10 @@ function validatePropertyConfigs(
1872
1918
  findings
1873
1919
  ) {
1874
1920
  for (const {config, propName} of propertyEntries) {
1921
+ const computedProp = getObjectProperty(config, 'computed');
1922
+ const declaredTypeNode = declaredPropertyTypes.get(propName);
1875
1923
  const typeProp = getObjectProperty(config, 'type');
1876
1924
  const usedByProp = getObjectProperty(config, 'usedBy');
1877
- const computedProp = getObjectProperty(config, 'computed');
1878
1925
  const valueProp = getObjectProperty(config, 'value');
1879
1926
  const valuesProp = getObjectProperty(config, 'values');
1880
1927
 
@@ -1894,6 +1941,17 @@ function validatePropertyConfigs(
1894
1941
  `property "${propName}" type must be one of ` +
1895
1942
  'Boolean, Number, String, Object, or Array'
1896
1943
  );
1944
+ } else if (declaredTypeNode) {
1945
+ const runtimeType = checker.getTypeAtLocation(typeExpression);
1946
+ const declaredType = checker.getTypeFromTypeNode(declaredTypeNode);
1947
+ if (!checker.isTypeAssignableTo(runtimeType, declaredType)) {
1948
+ findings.incompatibleDeclareTypes.push(
1949
+ `property "${propName}" declare type ` +
1950
+ `"${getPropertyTypeTextFromNode(sourceFile, declaredTypeNode)}" ` +
1951
+ `is not compatible with static properties type ` +
1952
+ `"${getPropertyConfigTypeName(typeExpressionKind(typeExpression))}"`
1953
+ );
1954
+ }
1897
1955
  }
1898
1956
 
1899
1957
  if (usedByProp && ts.isPropertyAssignment(usedByProp)) {