wrec 0.39.4 → 0.39.5
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/package.json +1 -1
- package/scripts/lint.js +84 -0
package/package.json
CHANGED
package/scripts/lint.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// - incompatible declare property types
|
|
17
17
|
// - arithmetic and other type errors in expressions
|
|
18
18
|
// - invalid computed property references
|
|
19
|
+
// - computed property cycles
|
|
19
20
|
// - invalid event handler references
|
|
20
21
|
// - unsupported event names
|
|
21
22
|
// - duplicate property names
|
|
@@ -370,6 +371,20 @@ function collectGetterNames(classNode) {
|
|
|
370
371
|
return getters;
|
|
371
372
|
}
|
|
372
373
|
|
|
374
|
+
// Collects computed-property dependencies on other computed properties.
|
|
375
|
+
function collectComputedDependencies(computedText, computedPropNames) {
|
|
376
|
+
const dependencies = new Set();
|
|
377
|
+
|
|
378
|
+
for (const match of computedText.matchAll(THIS_REF_RE)) {
|
|
379
|
+
const referencedName = match[1];
|
|
380
|
+
if (computedPropNames.has(referencedName)) {
|
|
381
|
+
dependencies.add(referencedName);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return [...dependencies].sort();
|
|
386
|
+
}
|
|
387
|
+
|
|
373
388
|
// Finds the synthetic `__wrec_expr_*` helper functions that were added by
|
|
374
389
|
// `buildAugmentedSource` and returns their bodies in index order.
|
|
375
390
|
// This gives the linter a stable list of typed code nodes
|
|
@@ -1835,6 +1850,51 @@ function validateComputedProperty(
|
|
|
1835
1850
|
}
|
|
1836
1851
|
}
|
|
1837
1852
|
|
|
1853
|
+
// Validates that computed properties do not form dependency cycles.
|
|
1854
|
+
function validateComputedPropertyCycles(computedDependencies, findings) {
|
|
1855
|
+
const computedNames = [...computedDependencies.keys()].sort();
|
|
1856
|
+
const dependencyCountMap = new Map();
|
|
1857
|
+
const dependentsMap = new Map();
|
|
1858
|
+
const queue = [];
|
|
1859
|
+
|
|
1860
|
+
for (const computedName of computedNames) {
|
|
1861
|
+
const dependencies = computedDependencies.get(computedName) ?? [];
|
|
1862
|
+
dependencyCountMap.set(computedName, dependencies.length);
|
|
1863
|
+
if (dependencies.length === 0) queue.push(computedName);
|
|
1864
|
+
|
|
1865
|
+
for (const dependencyName of dependencies) {
|
|
1866
|
+
let dependents = dependentsMap.get(dependencyName);
|
|
1867
|
+
if (!dependents) {
|
|
1868
|
+
dependents = [];
|
|
1869
|
+
dependentsMap.set(dependencyName, dependents);
|
|
1870
|
+
}
|
|
1871
|
+
dependents.push(computedName);
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
const orderedNames = [];
|
|
1876
|
+
for (let index = 0; index < queue.length; index++) {
|
|
1877
|
+
const computedName = queue[index];
|
|
1878
|
+
orderedNames.push(computedName);
|
|
1879
|
+
|
|
1880
|
+
const dependents = (dependentsMap.get(computedName) ?? []).sort();
|
|
1881
|
+
for (const dependentName of dependents) {
|
|
1882
|
+
const nextCount = dependencyCountMap.get(dependentName) - 1;
|
|
1883
|
+
dependencyCountMap.set(dependentName, nextCount);
|
|
1884
|
+
if (nextCount === 0) queue.push(dependentName);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
if (orderedNames.length === computedNames.length) return;
|
|
1889
|
+
|
|
1890
|
+
const cycleNames = computedNames.filter(
|
|
1891
|
+
computedName => dependencyCountMap.get(computedName) > 0
|
|
1892
|
+
);
|
|
1893
|
+
findings.invalidComputedProperties.push(
|
|
1894
|
+
`computed properties form a cycle: ${cycleNames.join(', ')}`
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1838
1898
|
// Validates that a default value matches the declared property type.
|
|
1839
1899
|
function validateDefaultValue(checker, typeExpression, valueExpression) {
|
|
1840
1900
|
if (!typeExpression || !valueExpression) return undefined;
|
|
@@ -2022,6 +2082,21 @@ function validatePropertyConfigs(
|
|
|
2022
2082
|
classMethods,
|
|
2023
2083
|
findings
|
|
2024
2084
|
) {
|
|
2085
|
+
const computedDependencies = new Map();
|
|
2086
|
+
const computedPropNames = new Set();
|
|
2087
|
+
|
|
2088
|
+
for (const {config, propName} of propertyEntries) {
|
|
2089
|
+
const computedProp = getObjectProperty(config, 'computed');
|
|
2090
|
+
if (
|
|
2091
|
+
computedProp &&
|
|
2092
|
+
ts.isPropertyAssignment(computedProp) &&
|
|
2093
|
+
(ts.isStringLiteral(computedProp.initializer) ||
|
|
2094
|
+
ts.isNoSubstitutionTemplateLiteral(computedProp.initializer))
|
|
2095
|
+
) {
|
|
2096
|
+
computedPropNames.add(propName);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2025
2100
|
for (const {config, propName} of propertyEntries) {
|
|
2026
2101
|
const computedProp = getObjectProperty(config, 'computed');
|
|
2027
2102
|
const declaredTypeNode = declaredPropertyTypes.get(propName);
|
|
@@ -2104,6 +2179,13 @@ function validatePropertyConfigs(
|
|
|
2104
2179
|
(ts.isStringLiteral(computedProp.initializer) ||
|
|
2105
2180
|
ts.isNoSubstitutionTemplateLiteral(computedProp.initializer))
|
|
2106
2181
|
) {
|
|
2182
|
+
computedDependencies.set(
|
|
2183
|
+
propName,
|
|
2184
|
+
collectComputedDependencies(
|
|
2185
|
+
computedProp.initializer.text,
|
|
2186
|
+
computedPropNames
|
|
2187
|
+
)
|
|
2188
|
+
);
|
|
2107
2189
|
validateComputedProperty(
|
|
2108
2190
|
propName,
|
|
2109
2191
|
computedProp.initializer.text,
|
|
@@ -2150,6 +2232,8 @@ function validatePropertyConfigs(
|
|
|
2150
2232
|
}
|
|
2151
2233
|
}
|
|
2152
2234
|
}
|
|
2235
|
+
|
|
2236
|
+
validateComputedPropertyCycles(computedDependencies, findings);
|
|
2153
2237
|
}
|
|
2154
2238
|
|
|
2155
2239
|
// Validates that a ref attribute targets a unique HTMLElement property.
|