tutuca 0.9.70 → 0.9.72
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/chai.js +3389 -0
- package/dist/immutable.js +4333 -0
- package/dist/tutuca-cli.js +141 -2
- package/dist/tutuca-dev.ext.js +113 -3392
- package/dist/tutuca-dev.js +25 -0
- package/dist/tutuca-dev.min.js +2 -2
- package/package.json +9 -1
- package/skill/tutuca/cli.md +11 -9
- package/skill/tutuca/core.md +32 -3
- package/skill/tutuca/testing.md +16 -10
package/dist/tutuca-cli.js
CHANGED
|
@@ -8960,6 +8960,23 @@ function attrOriginAttr(attr) {
|
|
|
8960
8960
|
return "@dangerouslysetinnerhtml";
|
|
8961
8961
|
return `:${attr.name}`;
|
|
8962
8962
|
}
|
|
8963
|
+
function checkHostBareDirectives(lx, attrs, tag, isMacroCall) {
|
|
8964
|
+
if (isMacroCall || !attrs)
|
|
8965
|
+
return;
|
|
8966
|
+
const kind = attrs.constructor.name;
|
|
8967
|
+
let names;
|
|
8968
|
+
if (kind === "ConstAttrs")
|
|
8969
|
+
names = Object.keys(attrs.items);
|
|
8970
|
+
else if (kind === "DynAttrs")
|
|
8971
|
+
names = attrs.items.map((a) => a?.name);
|
|
8972
|
+
else
|
|
8973
|
+
return;
|
|
8974
|
+
for (const name of names) {
|
|
8975
|
+
if (HOST_DIRECTIVE_ONLY_NAMES.has(name)) {
|
|
8976
|
+
lx.hint(MAYBE_ADD_AT_PREFIX, { name, tag, suggestion: `@${name}` }, { kind: "add-prefix", from: name, to: `@${name}` });
|
|
8977
|
+
}
|
|
8978
|
+
}
|
|
8979
|
+
}
|
|
8963
8980
|
function checkConsistentAttrs(lx, Comp, referencedAlters, referencedDynamics) {
|
|
8964
8981
|
const { views } = Comp;
|
|
8965
8982
|
const env = mkAttrValEnv(Comp, referencedAlters, referencedDynamics);
|
|
@@ -8968,6 +8985,7 @@ function checkConsistentAttrs(lx, Comp, referencedAlters, referencedDynamics) {
|
|
|
8968
8985
|
const view = views[viewName];
|
|
8969
8986
|
for (const entry of view.ctx.attrs) {
|
|
8970
8987
|
const { attrs, wrapperAttrs, textChild, isMacroCall, tag } = entry;
|
|
8988
|
+
checkHostBareDirectives(lx, attrs, tag, isMacroCall);
|
|
8971
8989
|
if (attrs?.constructor.name === "DynAttrs") {
|
|
8972
8990
|
const sourcesByName = new Map;
|
|
8973
8991
|
for (const attr of attrs.items) {
|
|
@@ -9144,7 +9162,7 @@ class LintContext {
|
|
|
9144
9162
|
this.reports.push({ id, info, level, context: { ...this.frame }, suggestion });
|
|
9145
9163
|
}
|
|
9146
9164
|
}
|
|
9147
|
-
var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET2, KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED", DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED", RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP", UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER", UNKNOWN_HANDLER_ARG_NAME = "UNKNOWN_HANDLER_ARG_NAME", INPUT_HANDLER_NOT_IMPLEMENTED = "INPUT_HANDLER_NOT_IMPLEMENTED", INPUT_HANDLER_NOT_REFERENCED = "INPUT_HANDLER_NOT_REFERENCED", INPUT_HANDLER_METHOD_NOT_IMPLEMENTED = "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED", INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD = "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD", INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER = "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER", FIELD_VAL_NOT_DEFINED = "FIELD_VAL_NOT_DEFINED", FIELD_VAL_IS_METHOD = "FIELD_VAL_IS_METHOD", METHOD_VAL_NOT_DEFINED = "METHOD_VAL_NOT_DEFINED", METHOD_VAL_IS_FIELD = "METHOD_VAL_IS_FIELD", DUPLICATE_ATTR_DEFINITION = "DUPLICATE_ATTR_DEFINITION", IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET", UNKNOWN_REQUEST_NAME = "UNKNOWN_REQUEST_NAME", UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME", UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG", UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE", UNKNOWN_X_OP = "UNKNOWN_X_OP", UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR", MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX", BAD_VALUE = "BAD_VALUE", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE", X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUES, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, fixTo = (from, to) => ({ kind: "rewrite", from, to }), ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, LintParseContext;
|
|
9165
|
+
var KNOWN_COMPONENT_SPEC_KEYS, EMPTY_SET2, KNOWN_DIRECTIVE_NAMES, ALT_HANDLER_NOT_DEFINED = "ALT_HANDLER_NOT_DEFINED", ALT_HANDLER_NOT_REFERENCED = "ALT_HANDLER_NOT_REFERENCED", DYN_VAL_NOT_DEFINED = "DYN_VAL_NOT_DEFINED", DYN_ALIAS_NOT_REFERENCED = "DYN_ALIAS_NOT_REFERENCED", RENDER_IT_OUTSIDE_OF_LOOP = "RENDER_IT_OUTSIDE_OF_LOOP", UNKNOWN_EVENT_MODIFIER = "UNKNOWN_EVENT_MODIFIER", UNKNOWN_HANDLER_ARG_NAME = "UNKNOWN_HANDLER_ARG_NAME", INPUT_HANDLER_NOT_IMPLEMENTED = "INPUT_HANDLER_NOT_IMPLEMENTED", INPUT_HANDLER_NOT_REFERENCED = "INPUT_HANDLER_NOT_REFERENCED", INPUT_HANDLER_METHOD_NOT_IMPLEMENTED = "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED", INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD = "INPUT_HANDLER_FOR_INPUT_HANDLER_METHOD", INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER = "INPUT_HANDLER_METHOD_FOR_INPUT_HANDLER", FIELD_VAL_NOT_DEFINED = "FIELD_VAL_NOT_DEFINED", FIELD_VAL_IS_METHOD = "FIELD_VAL_IS_METHOD", METHOD_VAL_NOT_DEFINED = "METHOD_VAL_NOT_DEFINED", METHOD_VAL_IS_FIELD = "METHOD_VAL_IS_FIELD", DUPLICATE_ATTR_DEFINITION = "DUPLICATE_ATTR_DEFINITION", IF_NO_BRANCH_SET = "IF_NO_BRANCH_SET", UNKNOWN_REQUEST_NAME = "UNKNOWN_REQUEST_NAME", UNKNOWN_COMPONENT_NAME = "UNKNOWN_COMPONENT_NAME", UNKNOWN_MACRO_ARG = "UNKNOWN_MACRO_ARG", UNKNOWN_DIRECTIVE = "UNKNOWN_DIRECTIVE", UNKNOWN_X_OP = "UNKNOWN_X_OP", UNKNOWN_X_ATTR = "UNKNOWN_X_ATTR", MAYBE_DROP_AT_PREFIX = "MAYBE_DROP_AT_PREFIX", MAYBE_ADD_AT_PREFIX = "MAYBE_ADD_AT_PREFIX", BAD_VALUE = "BAD_VALUE", UNSUPPORTED_EXPR_SYNTAX = "UNSUPPORTED_EXPR_SYNTAX", REDUNDANT_TEMPLATE_STRING = "REDUNDANT_TEMPLATE_STRING", PLACEHOLDERLESS_TEMPLATE_STRING = "PLACEHOLDERLESS_TEMPLATE_STRING", UNKNOWN_COMPONENT_SPEC_KEY = "UNKNOWN_COMPONENT_SPEC_KEY", COMP_FIELD_BAD_SHAPE = "COMP_FIELD_BAD_SHAPE", X_KNOWN_OP_NAMES, X_KNOWN_ATTR_NAMES, HOST_DIRECTIVE_ONLY_NAMES, LEVEL_WARN2 = "warn", LEVEL_ERROR2 = "error", LEVEL_HINT = "hint", PARSE_ISSUES, UNSUPPORTED_EXPR_GUIDANCE, HTML_LINT_OPTS, NO_WRAPPERS, KNOWN_HANDLER_NAMES, fixTo = (from, to) => ({ kind: "rewrite", from, to }), ATTR_VAL_CHECKERS, NODE_KIND_TO_CTX, LintParseContext;
|
|
9148
9166
|
var init_lint_check = __esm(() => {
|
|
9149
9167
|
init_anode();
|
|
9150
9168
|
init_htmllinter();
|
|
@@ -9174,6 +9192,7 @@ var init_lint_check = __esm(() => {
|
|
|
9174
9192
|
"hide"
|
|
9175
9193
|
]);
|
|
9176
9194
|
X_KNOWN_ATTR_NAMES = new Set(["as", "when", "loop-with", "show", "hide"]);
|
|
9195
|
+
HOST_DIRECTIVE_ONLY_NAMES = new Set(["when", "enrich-with", "loop-with", "show", "hide"]);
|
|
9177
9196
|
PARSE_ISSUES = {
|
|
9178
9197
|
"unknown-directive": { id: UNKNOWN_DIRECTIVE, candidates: KNOWN_DIRECTIVE_NAMES },
|
|
9179
9198
|
"unknown-x-op": { id: UNKNOWN_X_OP, candidates: X_KNOWN_OP_NAMES, atPrefix: X_KNOWN_OP_NAMES },
|
|
@@ -9459,6 +9478,12 @@ var init_lint_rules = __esm(() => {
|
|
|
9459
9478
|
group: "Templates / events",
|
|
9460
9479
|
summary: "An `<x>` op/attr was written with a leading `@` like a directive."
|
|
9461
9480
|
},
|
|
9481
|
+
{
|
|
9482
|
+
code: MAYBE_ADD_AT_PREFIX,
|
|
9483
|
+
level: "hint",
|
|
9484
|
+
group: "Templates / events",
|
|
9485
|
+
summary: "A directive name (`when`/`enrich-with`/`loop-with`/`show`/`hide`) was written as a plain attribute on a host element — add the `@` prefix."
|
|
9486
|
+
},
|
|
9462
9487
|
{
|
|
9463
9488
|
code: BAD_VALUE,
|
|
9464
9489
|
level: "error",
|
|
@@ -12806,6 +12831,111 @@ var init_chai = __esm(() => {
|
|
|
12806
12831
|
__name(use, "use");
|
|
12807
12832
|
});
|
|
12808
12833
|
|
|
12834
|
+
// src/chai-jest.js
|
|
12835
|
+
function jestMatchers(chai, utils) {
|
|
12836
|
+
const A = chai.Assertion;
|
|
12837
|
+
const m = (name, fn) => A.addMethod(name, fn);
|
|
12838
|
+
m("toBe", function(expected) {
|
|
12839
|
+
this.assert(Object.is(this._obj, expected), "expected #{this} to be #{exp}", "expected #{this} not to be #{exp}", expected, this._obj);
|
|
12840
|
+
});
|
|
12841
|
+
m("toEqual", function(expected) {
|
|
12842
|
+
this.assert(utils.eql(this._obj, expected), "expected #{this} to deeply equal #{exp}", "expected #{this} not to deeply equal #{exp}", expected, this._obj, true);
|
|
12843
|
+
});
|
|
12844
|
+
m("toBeTruthy", function() {
|
|
12845
|
+
this.assert(Boolean(this._obj), "expected #{this} to be truthy", "expected #{this} not to be truthy");
|
|
12846
|
+
});
|
|
12847
|
+
m("toBeFalsy", function() {
|
|
12848
|
+
this.assert(!this._obj, "expected #{this} to be falsy", "expected #{this} not to be falsy");
|
|
12849
|
+
});
|
|
12850
|
+
m("toBeNull", function() {
|
|
12851
|
+
this.assert(this._obj === null, "expected #{this} to be null", "expected #{this} not to be null");
|
|
12852
|
+
});
|
|
12853
|
+
m("toBeUndefined", function() {
|
|
12854
|
+
this.assert(this._obj === undefined, "expected #{this} to be undefined", "expected #{this} not to be undefined");
|
|
12855
|
+
});
|
|
12856
|
+
m("toBeDefined", function() {
|
|
12857
|
+
this.assert(this._obj !== undefined, "expected #{this} to be defined", "expected #{this} to be undefined");
|
|
12858
|
+
});
|
|
12859
|
+
m("toBeNaN", function() {
|
|
12860
|
+
this.assert(Number.isNaN(this._obj), "expected #{this} to be NaN", "expected #{this} not to be NaN");
|
|
12861
|
+
});
|
|
12862
|
+
const compare = (name, op, word) => m(name, function(expected) {
|
|
12863
|
+
this.assert(op(this._obj, expected), `expected #{this} to be ${word} #{exp}`, `expected #{this} not to be ${word} #{exp}`, expected);
|
|
12864
|
+
});
|
|
12865
|
+
compare("toBeGreaterThan", (a, b) => a > b, "greater than");
|
|
12866
|
+
compare("toBeGreaterThanOrEqual", (a, b) => a >= b, "greater than or equal to");
|
|
12867
|
+
compare("toBeLessThan", (a, b) => a < b, "less than");
|
|
12868
|
+
compare("toBeLessThanOrEqual", (a, b) => a <= b, "less than or equal to");
|
|
12869
|
+
m("toBeCloseTo", function(expected, numDigits = 2) {
|
|
12870
|
+
const pass = Math.abs(expected - this._obj) < 10 ** -numDigits / 2;
|
|
12871
|
+
this.assert(pass, `expected #{this} to be close to #{exp} (${numDigits} digits)`, `expected #{this} not to be close to #{exp} (${numDigits} digits)`, expected);
|
|
12872
|
+
});
|
|
12873
|
+
m("toContain", function(item) {
|
|
12874
|
+
const obj = this._obj;
|
|
12875
|
+
const ok = typeof obj === "string" ? obj.includes(item) : Array.from(obj).includes(item);
|
|
12876
|
+
this.assert(ok, "expected #{this} to contain #{exp}", "expected #{this} not to contain #{exp}", item);
|
|
12877
|
+
});
|
|
12878
|
+
m("toHaveLength", function(length) {
|
|
12879
|
+
const actual = this._obj == null ? undefined : this._obj.length;
|
|
12880
|
+
this.assert(actual === length, "expected #{this} to have length #{exp}", "expected #{this} not to have length #{exp}", length, actual);
|
|
12881
|
+
});
|
|
12882
|
+
m("toMatch", function(expected) {
|
|
12883
|
+
const obj = this._obj;
|
|
12884
|
+
const ok = expected instanceof RegExp ? expected.test(obj) : String(obj).includes(expected);
|
|
12885
|
+
this.assert(ok, "expected #{this} to match #{exp}", "expected #{this} not to match #{exp}", expected);
|
|
12886
|
+
});
|
|
12887
|
+
m("toHaveProperty", function(path, ...rest) {
|
|
12888
|
+
const keys = Array.isArray(path) ? path : String(path).split(".");
|
|
12889
|
+
let cur = this._obj;
|
|
12890
|
+
let found = true;
|
|
12891
|
+
for (const k of keys) {
|
|
12892
|
+
if (cur != null && k in Object(cur))
|
|
12893
|
+
cur = cur[k];
|
|
12894
|
+
else {
|
|
12895
|
+
found = false;
|
|
12896
|
+
break;
|
|
12897
|
+
}
|
|
12898
|
+
}
|
|
12899
|
+
const pass = found && (rest.length === 0 || utils.eql(cur, rest[0]));
|
|
12900
|
+
this.assert(pass, "expected #{this} to have property #{exp}", "expected #{this} not to have property #{exp}", path);
|
|
12901
|
+
});
|
|
12902
|
+
m("toBeInstanceOf", function(ctor) {
|
|
12903
|
+
this.assert(this._obj instanceof ctor, "expected #{this} to be an instance of #{exp}", "expected #{this} not to be an instance of #{exp}", ctor.name ?? ctor);
|
|
12904
|
+
});
|
|
12905
|
+
m("toThrow", function(expected) {
|
|
12906
|
+
const a = new chai.Assertion(this._obj);
|
|
12907
|
+
if (utils.flag(this, "negate")) {
|
|
12908
|
+
expected === undefined ? a.to.not.throw() : a.to.not.throw(expected);
|
|
12909
|
+
} else {
|
|
12910
|
+
expected === undefined ? a.to.throw() : a.to.throw(expected);
|
|
12911
|
+
}
|
|
12912
|
+
});
|
|
12913
|
+
}
|
|
12914
|
+
var JEST_MATCHERS;
|
|
12915
|
+
var init_chai_jest = __esm(() => {
|
|
12916
|
+
JEST_MATCHERS = [
|
|
12917
|
+
"toBe",
|
|
12918
|
+
"toEqual",
|
|
12919
|
+
"toBeTruthy",
|
|
12920
|
+
"toBeFalsy",
|
|
12921
|
+
"toBeNull",
|
|
12922
|
+
"toBeUndefined",
|
|
12923
|
+
"toBeDefined",
|
|
12924
|
+
"toBeNaN",
|
|
12925
|
+
"toBeGreaterThan",
|
|
12926
|
+
"toBeGreaterThanOrEqual",
|
|
12927
|
+
"toBeLessThan",
|
|
12928
|
+
"toBeLessThanOrEqual",
|
|
12929
|
+
"toBeCloseTo",
|
|
12930
|
+
"toContain",
|
|
12931
|
+
"toHaveLength",
|
|
12932
|
+
"toMatch",
|
|
12933
|
+
"toHaveProperty",
|
|
12934
|
+
"toBeInstanceOf",
|
|
12935
|
+
"toThrow"
|
|
12936
|
+
];
|
|
12937
|
+
});
|
|
12938
|
+
|
|
12809
12939
|
// tools/core/results.js
|
|
12810
12940
|
class ModuleInfo {
|
|
12811
12941
|
constructor({ path = null, present = new Set, counts = {}, warnings = [] }) {
|
|
@@ -14718,12 +14848,14 @@ function parseLimit(raw) {
|
|
|
14718
14848
|
var COMMANDS;
|
|
14719
14849
|
var init__registry = __esm(() => {
|
|
14720
14850
|
init_chai();
|
|
14851
|
+
init_chai_jest();
|
|
14721
14852
|
init_describe();
|
|
14722
14853
|
init_docs();
|
|
14723
14854
|
init_list();
|
|
14724
14855
|
init_lint();
|
|
14725
14856
|
init_render2();
|
|
14726
14857
|
init_test();
|
|
14858
|
+
use(jestMatchers);
|
|
14727
14859
|
COMMANDS = {
|
|
14728
14860
|
get: {
|
|
14729
14861
|
describe: "Summarize the module's exports and counts.",
|
|
@@ -14925,6 +15057,8 @@ function lintIdToMessage(id, info) {
|
|
|
14925
15057
|
const written = info.value !== undefined ? `${info.name}=${JSON.stringify(info.value)}` : info.name;
|
|
14926
15058
|
return `'${written}' on <x> looks like a directive but is actually an x op/attr written with a leading '@'`;
|
|
14927
15059
|
}
|
|
15060
|
+
case "MAYBE_ADD_AT_PREFIX":
|
|
15061
|
+
return `'${info.name}' on <${(info.tag ?? "").toLowerCase()}> is a plain attribute, but '@${info.name}' is a directive — add the leading '@'`;
|
|
14928
15062
|
case "BAD_VALUE":
|
|
14929
15063
|
return `${badValueMessage(info)}${fmtTagSuffix(info)}`;
|
|
14930
15064
|
case "UNSUPPORTED_EXPR_SYNTAX":
|
|
@@ -14935,6 +15069,8 @@ function lintIdToMessage(id, info) {
|
|
|
14935
15069
|
return `Template string has no dynamic parts — use the string literal ${info.literal} instead${fmtOriginSuffix(info)}`;
|
|
14936
15070
|
case "UNKNOWN_COMPONENT_SPEC_KEY":
|
|
14937
15071
|
return `Unknown component spec key '${info.key}' — value will be ignored at runtime`;
|
|
15072
|
+
case "COMP_FIELD_BAD_SHAPE":
|
|
15073
|
+
return info.kind === "args-not-object" ? `Field '${info.fieldName}': in { component, args }, 'args' must be a plain object, got ${info.got}` : `Field '${info.fieldName}': in { component, args }, 'component' must be the component name as a string, got ${info.gotName ? `the ${info.gotName} class` : info.got}`;
|
|
14938
15074
|
case "HTML_TAG_NAME_HAS_UPPERCASE":
|
|
14939
15075
|
return `Tag <${info.raw}> will be lowercased to <${info.lowercased}>${fmtLocationSuffix(info)}`;
|
|
14940
15076
|
case "HTML_SVG_TAG_WILL_LOWERCASE":
|
|
@@ -15589,6 +15725,7 @@ init__registry();
|
|
|
15589
15725
|
init_feedback();
|
|
15590
15726
|
|
|
15591
15727
|
// tools/cli/commands/help.js
|
|
15728
|
+
init_chai_jest();
|
|
15592
15729
|
var exports_help = {};
|
|
15593
15730
|
__export(exports_help, {
|
|
15594
15731
|
run: () => run4,
|
|
@@ -15641,7 +15778,9 @@ MODULE CONVENTION
|
|
|
15641
15778
|
describe(title, fn) // untagged
|
|
15642
15779
|
describe(title, { component }, fn)// explicit tag with custom title
|
|
15643
15780
|
and test(title, fn) // fn may be async
|
|
15644
|
-
and expect
|
|
15781
|
+
and expect is chai, with jest-style matchers added:
|
|
15782
|
+
${JEST_MATCHERS.join(", ")}
|
|
15783
|
+
(chai's BDD chain like .to.equal also works; use .not to negate)
|
|
15645
15784
|
|
|
15646
15785
|
export function getRoot() // optional; returned by info
|
|
15647
15786
|
|