release: WRNexusJS 0.8.0
This commit is contained in:
@@ -39,9 +39,24 @@ function runtimeType(type) {
|
||||
const value = String(type || "")
|
||||
.trim()
|
||||
.replace(/^readonly\s+/, "");
|
||||
if (/^(?:string|String)(?:\s*\|\s*(?:null|undefined))*$/.test(value)) return "string";
|
||||
if (/^(?:number|Number)(?:\s*\|\s*(?:null|undefined))*$/.test(value)) return "number";
|
||||
if (/^(?:boolean|Boolean)(?:\s*\|\s*(?:null|undefined))*$/.test(value)) return "boolean";
|
||||
const unionParts = value.split("|").map((part) => part.trim());
|
||||
const concreteParts = unionParts.filter((part) => !/^(?:null|undefined)$/.test(part));
|
||||
if (
|
||||
/^(?:string|String)(?:\s*\|\s*(?:null|undefined))*$/.test(value) ||
|
||||
(concreteParts.length > 0 && concreteParts.every((part) => /^(?:"[^"]*"|'[^']*')$/.test(part)))
|
||||
)
|
||||
return "string";
|
||||
if (
|
||||
/^(?:number|Number)(?:\s*\|\s*(?:null|undefined))*$/.test(value) ||
|
||||
(concreteParts.length > 0 &&
|
||||
concreteParts.every((part) => /^-?(?:\d+\.?\d*|\.\d+)$/.test(part)))
|
||||
)
|
||||
return "number";
|
||||
if (
|
||||
/^(?:boolean|Boolean)(?:\s*\|\s*(?:null|undefined))*$/.test(value) ||
|
||||
(concreteParts.length > 0 && concreteParts.every((part) => /^(?:true|false)$/.test(part)))
|
||||
)
|
||||
return "boolean";
|
||||
if (/^bigint(?:\s*\|\s*(?:null|undefined))*$/.test(value)) return "bigint";
|
||||
if (/^(?:Array\s*<|ReadonlyArray\s*<|.+\[\])/.test(value) || /^\[/.test(value)) return "array";
|
||||
if (/^(?:Record\s*<|object\b|\{)/.test(value)) return "object";
|
||||
@@ -138,15 +153,47 @@ function unwrapAttributeValue(value) {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function isWrappedExpression(value) {
|
||||
const trimmed = value.trim();
|
||||
return trimmed.startsWith("{") && trimmed.endsWith("}");
|
||||
}
|
||||
|
||||
function expressionLiteralType(value) {
|
||||
const expression = value.trim();
|
||||
if (/^(?:true|false)$/.test(expression)) return "boolean";
|
||||
if (/^-?(?:\d+\.?\d*|\.\d+)$/.test(expression)) return "number";
|
||||
if (/^(?:"[\s\S]*"|'[\s\S]*'|`[\s\S]*`)$/.test(expression)) return "string";
|
||||
if (expression.startsWith("[")) return "array";
|
||||
if (expression.startsWith("{")) return "object";
|
||||
if (expression === "null") return "null";
|
||||
return null;
|
||||
}
|
||||
|
||||
function attributeValueType(value, symbols = new Map()) {
|
||||
const expression = isWrappedExpression(value);
|
||||
const unwrapped = unwrapAttributeValue(value);
|
||||
if (/^[A-Za-z_$][\w$]*$/.test(unwrapped) && symbols.has(unwrapped)) {
|
||||
return symbols.get(unwrapped);
|
||||
}
|
||||
if (/^(?:true|false)$/.test(unwrapped)) return "boolean";
|
||||
if (/^-?(?:\d+\.?\d*|\.\d+)$/.test(unwrapped)) return "number";
|
||||
if (unwrapped.startsWith("[")) return "array";
|
||||
if (unwrapped.startsWith("{")) return "object";
|
||||
|
||||
const literal = expressionLiteralType(unwrapped);
|
||||
if (literal) return literal;
|
||||
|
||||
if (expression) {
|
||||
if (
|
||||
/^!\s*/.test(unwrapped) ||
|
||||
/(?:===|!==|==|!=|<=|>=|<|>|\bin\b|\binstanceof\b)/.test(unwrapped)
|
||||
) {
|
||||
return "boolean";
|
||||
}
|
||||
|
||||
// Dynamic member access, calls, logical expressions and ternaries cannot be
|
||||
// proven from component metadata alone. Treat them as unknown instead of as
|
||||
// quoted strings so valid expressions such as `{item.external || false}` do
|
||||
// not produce false boolean/string diagnostics.
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
return "string";
|
||||
}
|
||||
|
||||
@@ -154,6 +201,7 @@ function isTypeCompatible(prop, value, symbols = new Map()) {
|
||||
const expected = runtimeType(prop.type);
|
||||
if (expected === "unknown" || expected === "null") return true;
|
||||
const actual = runtimeType(attributeValueType(value, symbols));
|
||||
if (actual === "unknown") return true;
|
||||
if (expected === actual) return true;
|
||||
if (expected === "number" && actual === "string") return Number.isFinite(Number(value));
|
||||
if (expected === "boolean" && actual === "string")
|
||||
@@ -255,8 +303,10 @@ function validateComponentTags(source, components) {
|
||||
end: attribute.nameEnd,
|
||||
});
|
||||
}
|
||||
const literal = stringLiteral(attribute.value) ?? attribute.value;
|
||||
if (prop.options.length > 0 && !prop.options.includes(literal)) {
|
||||
const optionValue = isWrappedExpression(attribute.value)
|
||||
? stringLiteral(unwrapAttributeValue(attribute.value))
|
||||
: (stringLiteral(attribute.value) ?? attribute.value);
|
||||
if (optionValue !== null && prop.options.length > 0 && !prop.options.includes(optionValue)) {
|
||||
diagnostics.push({
|
||||
severity: "warning",
|
||||
code: "wrn-component-prop-option",
|
||||
|
||||
Reference in New Issue
Block a user