steel-frame 7.2.1 → 7.2.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.
package/README.md CHANGED
@@ -135,8 +135,6 @@ We respect semantic versioning:
135
135
 
136
136
  ### 7.0 - 7.2
137
137
 
138
- - **7.2**: Add tracking of the builtin models size. Reactive array/set/map can be converted into a builtin model.
139
- - **7.1**: Add support for ARIA attributes.
140
138
  - Add `debounced` and `field` functions, which extend the reativity system.
141
139
  - Add `Zombie` component, it keeps HTML elements alive some time after destruction.
142
140
  - Add `abortSignal` to cancel fetch requests and timers when component is destroyed.
@@ -154,6 +152,8 @@ We respect semantic versioning:
154
152
  - New reactivity marks `r_` & `wr_` insted of `$`,
155
153
  workaround with `v4-naming` compilation flag. **[API change]**
156
154
  - Component files have strict structure (imports, export, helpers, CSS). **[API change]**
155
+ - `7.1`: Add support for ARIA attributes.
156
+ - `7.2`: Add tracking of the builtin models size. Reactive array/set/map can be converted into a builtin model.
157
157
 
158
158
  ### 6.0 RC (never released)
159
159
 
@@ -1,7 +1,7 @@
1
1
  import * as t from "@babel/types";
2
2
  import { calledFn, calls, hintFunctions, isDiCall, unwrapFunctions } from "./call.js";
3
3
  import { ctx, V, VariablesStatus } from "./internal.js";
4
- import { checkNonReactiveName, err, Errors, forceReactiveName, reactivityTypeOfNode } from "./lib.js";
4
+ import { checkNonReactiveName, err, Errors, forceReactiveName, reactivityTypeOfNodePath } from "./lib.js";
5
5
  import { checkStyleUsage, ignoreParams, meshAllUnknown, meshExpression } from "./mesh.js";
6
6
  import { routerReplace } from "./router.js";
7
7
  import { stringify } from "./utils.js";
@@ -217,7 +217,7 @@ export function checkExpression(nodePath, search, allowOuterScope = false) {
217
217
  if (arg1 &&
218
218
  arg2 &&
219
219
  arg1.isExpression() &&
220
- reactivityTypeOfNode(arg1, search.external) === "writable" &&
220
+ reactivityTypeOfNodePath(arg1, search.external) === "writable" &&
221
221
  arg2.isExpression()) {
222
222
  checkExpression(arg2, search);
223
223
  path.replaceWith(search.external.updateIValue(path.node, arg1.node, arg2.node));
@@ -248,7 +248,7 @@ export function checkExpression(nodePath, search, allowOuterScope = false) {
248
248
  const path = nodePath;
249
249
  const left = path.get("left");
250
250
  const right = path.get("right");
251
- if (reactivityTypeOfNode(left, search.external)) {
251
+ if (reactivityTypeOfNodePath(left, search.external)) {
252
252
  err(Errors.RulesOfVasille, left, "Assigment are forbidden in live values and effects", search.external);
253
253
  }
254
254
  meshLValue(left, search.external);
@@ -90,9 +90,9 @@ export interface Internal {
90
90
  expr(func: types.Expression, values: types.Expression[], codes: string[], area: types.Node, name: string | undefined, safe: boolean): types.Expression;
91
91
  fieldRef(obj: types.Expression, field: types.Expression, area: types.Node, name?: string): types.Expression;
92
92
  deepFieldRef(obj: types.Expression, fields: types.Expression[], area: types.Node, name?: string): types.Expression;
93
- setModel(arg: types.Expression | types.SpreadElement | types.ArgumentPlaceholder | null, usage: types.Node, name: string | undefined): types.Expression;
94
- mapModel(arg: types.Expression | types.SpreadElement | types.ArgumentPlaceholder | null, usage: types.Node, name: string | undefined): types.Expression;
95
- arrayModel(arg: types.Expression | types.SpreadElement | types.ArgumentPlaceholder | null, usage: types.Node, name: string | undefined): types.Expression;
93
+ setModel(arg: types.Expression | types.SpreadElement | types.ArgumentPlaceholder | null, size: types.Expression | types.SpreadElement | types.ArgumentPlaceholder | null | undefined, usage: types.Node, name: string | undefined): types.Expression;
94
+ mapModel(arg: types.Expression | types.SpreadElement | types.ArgumentPlaceholder | null, size: types.Expression | types.SpreadElement | types.ArgumentPlaceholder | null | undefined, usage: types.Node, name: string | undefined): types.Expression;
95
+ arrayModel(arg: types.Expression | types.SpreadElement | types.ArgumentPlaceholder | null, length: types.Expression | types.SpreadElement | types.ArgumentPlaceholder | null | undefined, usage: types.Node, name: string | undefined): types.Expression;
96
96
  ensure(arg: types.MemberExpression | types.OptionalMemberExpression, area: types.Node): types.Expression;
97
97
  set(obj: types.Expression, field: types.Expression, value: types.Expression, area: types.Node): types.CallExpression;
98
98
  safeInit(arg: types.Expression): types.Expression;
@@ -1,16 +1,16 @@
1
1
  import * as t from "@babel/types";
2
2
  import { ctx } from "./internal.js";
3
3
  import { bodyHasJsx } from "./jsx-detect.js";
4
- import { checkNonReactiveName, checkReactiveName, err, Errors, exprCall, isReactiveName, isReadableName, isWritableName, nodeIsUnsafe, reactivityTypeOfNode, toKebabCase, } from "./lib.js";
4
+ import { checkNonReactiveName, checkReactiveName, err, Errors, exprCall, isReactiveName, isReadableName, isWritableName, nodeIsUnsafe, reactivityTypeByName, reactivityTypeOfNode, reactivityTypeOfNodePath, replaceReactivityMark, toKebabCase, } from "./lib.js";
5
5
  import { compose, meshExpression, processRefCall } from "./mesh.js";
6
6
  import { nodeToStaticPosition } from "./transformer.js";
7
7
  import { processFieldRefCall, toFieldRef } from "./field-reference.js";
8
8
  import { calls } from "./call.js";
9
9
  import { validateRouterPath } from "./router.js";
10
10
  const strongSlotMap = {
11
- ArrayView: ["reactive", "reactive"],
12
- ArrayModelView: ["passive", "reactive"],
13
- MapModelView: ["reactive", "passive"],
11
+ ArrayView: ["any", "readable"],
12
+ ArrayModelView: ["writable or passive", "readable"],
13
+ MapModelView: ["any", "passive"],
14
14
  };
15
15
  export function transformJsx(path, conditions, internal) {
16
16
  if (path.isJSXElement()) {
@@ -535,7 +535,7 @@ function transformJsxElement(path, conditions, internal) {
535
535
  const nameIsWritable = isWritableName(attrName, internal) && (!isReadableName(attrName, internal) || callsField);
536
536
  let valueIsWritable = nameIsWritable &&
537
537
  valuePath.isJSXExpressionContainer() &&
538
- reactivityTypeOfNode(valuePath.get("expression"), internal) === "writable";
538
+ reactivityTypeOfNodePath(valuePath.get("expression"), internal) === "writable";
539
539
  if (nameIsWritable !== valueIsWritable) {
540
540
  let fieldRef;
541
541
  // attr={field(wr_x.y)}
@@ -743,6 +743,22 @@ function transformJsxElement(path, conditions, internal) {
743
743
  err(Errors.RulesOfVasille, path, "Malformed JSX ForEach tag must have value and slot", internal);
744
744
  }
745
745
  }
746
+ if (mapped && ["ArrayView", "ArrayModelView", "MapModelView"].includes(mapped)) {
747
+ const model = props.find(item => t.isObjectProperty(item) && t.isIdentifier(item.key) && item.key.name === "r_of");
748
+ const slot = props.find(item => t.isObjectProperty(item) && t.isIdentifier(item.key) && item.key.name === "slot");
749
+ if (slot &&
750
+ (t.isArrowFunctionExpression(slot.value) || t.isFunctionExpression(slot.value)) &&
751
+ t.isIdentifier(slot.value.params[1]) &&
752
+ reactivityTypeByName(slot.value.params[1].name, internal) === "writable") {
753
+ if (mapped === "ArrayView" && !(model && reactivityTypeOfNode(model.value, internal) === "writable")) {
754
+ const fixedName = replaceReactivityMark(slot.value.params[1].name, "");
755
+ err(Errors.RulesOfVasille, path, `Malformed ${mapped}: writable item in slot requires a writable collection. ` +
756
+ `Renaming ${slot.value.params[1].name} parameter to "${fixedName}" will fix this error.`, internal);
757
+ }
758
+ // add writable param to properties
759
+ props.push(t.objectProperty(t.identifier("writable"), t.booleanLiteral(true)));
760
+ }
761
+ }
746
762
  function addAwait(node) {
747
763
  if (internal.asyncComposing) {
748
764
  return t.awaitExpression(node);
@@ -777,18 +793,27 @@ function precheckSlotParams(mapped, slot, internal) {
777
793
  }
778
794
  for (let i = 0; i < params.length && i < args.length; i++) {
779
795
  const param = params[i];
780
- if (args[i] === "reactive") {
796
+ const expected = args[i];
797
+ if (expected === "passive") {
798
+ /* istanbul ignore else */
799
+ if (param.isIdentifier()) {
800
+ checkNonReactiveName(param, internal);
801
+ }
802
+ }
803
+ else {
781
804
  if (!param.isIdentifier()) {
782
805
  err(Errors.ParserError, slot, `The ${mapped} slot must have only identifiers as parameters`, internal);
783
806
  }
784
807
  else {
785
- checkReactiveName(param, "readable", internal);
786
- }
787
- }
788
- else {
789
- /* istanbul ignore else */
790
- if (param.isIdentifier()) {
791
- checkNonReactiveName(param, internal);
808
+ if (expected === "writable or passive") {
809
+ const type = reactivityTypeByName(param.node.name, internal);
810
+ if (type !== "writable" && type !== false) {
811
+ err(Errors.RulesOfVasille, param, "Expected writable or non reactive parameter.", internal);
812
+ }
813
+ }
814
+ else {
815
+ checkReactiveName(param, expected, internal);
816
+ }
792
817
  }
793
818
  }
794
819
  }
@@ -24,17 +24,23 @@ export declare function ref(expr: types.Node | null | undefined, internal: Inter
24
24
  export declare function arrayModel(args: types.CallExpression["arguments"], usage: types.Node, internal: Internal, name?: string): types.Expression;
25
25
  export declare function setModel(args: types.CallExpression["arguments"], usage: types.Node, internal: Internal, name?: string): types.Expression;
26
26
  export declare function mapModel(args: types.CallExpression["arguments"], usage: types.Node, internal: Internal, name?: string): types.Expression;
27
+ export declare function checkBuildInModelArgs(path: NodePath<types.CallExpression | types.OptionalCallExpression | types.NewExpression>, internal: Internal): (types.ArgumentPlaceholder | types.SpreadElement | types.Expression)[];
27
28
  export declare function processModelCall(path: NodePath<types.CallExpression | types.NewExpression>, usage: types.Node, type: "Map" | "Set" | "Array", isConst: boolean, internal: Internal, name?: string): void;
28
29
  export declare function checkReactiveName(idPath: NodePath<unknown>, type: ReactiveType, internal: Internal): void;
29
30
  export declare function checkNonReactiveName(idPath: NodePath<Identifier>, internal: Internal): void;
30
31
  export declare function toKebabCase(name: string): string;
31
- export declare function pathIsReactiveValue(path: NodePath<unknown>, _type: "any", internal: Internal, checkForBreakpoints?: boolean): path is NodePath<types.MemberExpression | types.OptionalMemberExpression | types.Identifier>;
32
+ export declare function pathIsReactiveValue(path: NodePath<types.Node | null | undefined>, _type: "any", internal: Internal, checkForBreakpoints?: boolean): path is NodePath<types.MemberExpression | types.OptionalMemberExpression | types.Identifier>;
32
33
  export declare function removeExtension(name: string): string;
33
34
  export declare function isReadableName(name: string, internal: Internal): boolean;
34
35
  export declare function isWritableName(name: string, internal: Internal): boolean;
35
36
  export declare function reactivityTypeByName(name: string, internal: Internal): false | "any" | "readable" | "writable";
36
- export declare function reactivityTypeOfNode(node: NodePath<unknown>, internal: Internal, checkForBreakpoints?: boolean): ReactiveType | false;
37
+ export declare function reactivityTypeOfNode(node: types.Node, internal: Internal): ReactiveType | false;
38
+ export declare function reactivityTypeOfNodePath(path: NodePath<types.Node | null | undefined>, internal: Internal, checkForBreakpoints?: boolean): ReactiveType | false;
37
39
  export type ReactiveType = "readable" | "writable" | "any";
38
40
  export declare function isReactiveName(name: string, internal: Internal): boolean;
39
41
  export declare function forceReactiveName(name: string, type: ReactiveType, path: NodePath<unknown>, internal: Internal): boolean;
42
+ export declare function forcePathIsWritable(path: NodePath<types.SpreadElement | types.ArgumentPlaceholder | types.Expression | null>, internal: Internal, accepts?: "undefined" | "any"): true | undefined;
40
43
  export declare function leftIsThisInConstructor(left: NodePath<unknown>): boolean;
44
+ export type ReactivityMarks = "" | "wr_" | "r_" | "$";
45
+ export declare function replaceReactivityMark(name: string, with_: ReactivityMarks, v4?: boolean): string;
46
+ export declare function extractReactivityMark(name: string, v4?: boolean): ReactivityMarks;
@@ -2,7 +2,7 @@ import * as t from "@babel/types";
2
2
  import { checkNode, exprIsSure } from "./expression.js";
3
3
  import { ctx } from "./internal.js";
4
4
  import { bindFunctions, calledFn, calls, hintFunctions, safeFunctions } from "./call.js";
5
- import { meshAllUnknown } from "./mesh.js";
5
+ import { meshAllUnknown, meshExpression } from "./mesh.js";
6
6
  import { nodeToStaticPosition } from "./transformer.js";
7
7
  import { hasBreakPoint } from "./field-reference.js";
8
8
  export var Errors;
@@ -137,20 +137,29 @@ export function ref(expr, internal, area, name, safe) {
137
137
  return internal.ref(t.isExpression(expr) ? expr : null, area, name, safe);
138
138
  }
139
139
  export function arrayModel(args, usage, internal, name) {
140
- return internal.arrayModel(args[0], usage, name);
140
+ return internal.arrayModel(args[0], args[1], usage, name);
141
141
  }
142
142
  export function setModel(args, usage, internal, name) {
143
- return internal.setModel(args[0], usage, name);
143
+ return internal.setModel(args[0], args[1], usage, name);
144
144
  }
145
145
  export function mapModel(args, usage, internal, name) {
146
- return internal.mapModel(args[0], usage, name);
146
+ return internal.mapModel(args[0], args[1], usage, name);
147
+ }
148
+ export function checkBuildInModelArgs(path, internal) {
149
+ const args = path.get("arguments");
150
+ if (args.length > 0) {
151
+ forcePathIsWritable(args[0], internal, "any");
152
+ if (args.length > 1) {
153
+ forcePathIsWritable(args[1], internal, "undefined");
154
+ }
155
+ }
156
+ return path.node.arguments;
147
157
  }
148
158
  export function processModelCall(path, usage, type, isConst, internal, name) {
149
- const args = path.node.arguments;
159
+ const args = checkBuildInModelArgs(path, internal);
150
160
  if (!isConst) {
151
161
  err(Errors.RulesOfVasille, path, `${type} models must be declared as constants`, internal);
152
162
  }
153
- meshAllUnknown(path.get("arguments"), internal, true);
154
163
  path.replaceWith(type === "Map"
155
164
  ? mapModel(args, usage, internal, name)
156
165
  : type === "Set"
@@ -181,7 +190,7 @@ export function toKebabCase(name) {
181
190
  return fixed;
182
191
  }
183
192
  export function pathIsReactiveValue(path, _type, internal, checkForBreakpoints = true) {
184
- return reactivityTypeOfNode(path, internal, checkForBreakpoints) !== false;
193
+ return reactivityTypeOfNodePath(path, internal, checkForBreakpoints) !== false;
185
194
  }
186
195
  export function removeExtension(name) {
187
196
  if (name.endsWith(".ts") || name.endsWith(".js")) {
@@ -207,14 +216,16 @@ export function reactivityTypeByName(name, internal) {
207
216
  ? "readable"
208
217
  : false;
209
218
  }
210
- export function reactivityTypeOfNode(node, internal, checkForBreakpoints = true) {
211
- return ((node.isIdentifier() && reactivityTypeByName(node.node.name, internal)) ||
212
- ((node.isMemberExpression() || node.isOptionalMemberExpression()) &&
213
- ((!node.node.computed &&
214
- t.isIdentifier(node.node.property) &&
215
- reactivityTypeByName(node.node.property.name, internal)) ||
216
- (t.isStringLiteral(node.node.property) && reactivityTypeByName(node.node.property.value, internal)) ||
217
- (checkForBreakpoints && hasBreakPoint(node, internal) && "readable"))));
219
+ export function reactivityTypeOfNode(node, internal) {
220
+ return ((t.isIdentifier(node) && reactivityTypeByName(node.name, internal)) ||
221
+ ((t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) &&
222
+ ((!node.computed && t.isIdentifier(node.property) && reactivityTypeByName(node.property.name, internal)) ||
223
+ (t.isStringLiteral(node.property) && reactivityTypeByName(node.property.value, internal)))));
224
+ }
225
+ export function reactivityTypeOfNodePath(path, internal, checkForBreakpoints = true) {
226
+ const node = path.node;
227
+ return ((node && reactivityTypeOfNode(node, internal)) ||
228
+ (checkForBreakpoints && path.isExpression() && hasBreakPoint(path, internal) && "readable"));
218
229
  }
219
230
  export function isReactiveName(name, internal) {
220
231
  return isReadableName(name, internal) || isWritableName(name, internal);
@@ -231,6 +242,40 @@ export function forceReactiveName(name, type, path, internal) {
231
242
  isReadableName(name, internal) &&
232
243
  err(Errors.RulesOfVasille, path, `Expected "writable" reactivity mark, the name "${name}" has "readonly" mark`, internal, true)))));
233
244
  }
245
+ export function forcePathIsWritable(path, internal, accepts) {
246
+ if (path.isIdentifier() && isWritableName(path.node.name, internal)) {
247
+ return true;
248
+ }
249
+ if (path.isMemberExpression() &&
250
+ !path.node.computed &&
251
+ t.isIdentifier(path.node.property) &&
252
+ isWritableName(path.node.property.name, internal)) {
253
+ meshExpression(path.get("object"), internal, false);
254
+ return true;
255
+ }
256
+ /* istanbul ignore else */
257
+ if (accepts === "undefined" || accepts === "any") {
258
+ if (path.isOptionalMemberExpression() &&
259
+ !path.node.computed &&
260
+ t.isIdentifier(path.node.property) &&
261
+ isWritableName(path.node.property.name, internal)) {
262
+ meshExpression(path.get("object"), internal, false);
263
+ return true;
264
+ }
265
+ if (path.isReferencedIdentifier() && path.node.name === "undefined") {
266
+ return true;
267
+ }
268
+ }
269
+ if (path.isExpression() && exprCall(path, path.node, internal, {}, path.node, false)) {
270
+ err(Errors.RulesOfVasille, path, "Unexpected readonly reactive value. Expected a writable one." +
271
+ (accepts === "any" ? " Use `unwrap` to take the current value of reactive value in runtime." : ""), internal);
272
+ }
273
+ if (accepts === "any") {
274
+ meshAllUnknown([path], internal, false);
275
+ return true;
276
+ }
277
+ err(Errors.RulesOfVasille, path, "Expected writable reactive value here.", internal);
278
+ }
234
279
  export function leftIsThisInConstructor(left) {
235
280
  if (left.isMemberExpression()) {
236
281
  let iterator = left;
@@ -247,3 +292,12 @@ export function leftIsThisInConstructor(left) {
247
292
  }
248
293
  return false;
249
294
  }
295
+ const newReactivityMarks = ["wr_", "r_"];
296
+ const allReactivityMarks = [...newReactivityMarks, "$"];
297
+ export function replaceReactivityMark(name, with_, v4) {
298
+ const start = (v4 ? allReactivityMarks : newReactivityMarks).find(s => name.startsWith(s));
299
+ return with_ + name.substring(start ? start.length : 0);
300
+ }
301
+ export function extractReactivityMark(name, v4) {
302
+ return (v4 ? allReactivityMarks : newReactivityMarks).find(s => name.startsWith(s)) ?? "";
303
+ }
@@ -3,7 +3,7 @@ import { asyncFunctions, bindFunctions, calledFn, calls, composeFunctions, dynam
3
3
  import { exprIsSure, idIsIValue, memberIsIValue, nodeIsMeshed } from "./expression.js";
4
4
  import { ctx, V, VariablesStatus } from "./internal.js";
5
5
  import { processConditions, transformJsx } from "./jsx.js";
6
- import { checkNonReactiveName, checkReactiveName, err, Errors, exprCall, forceReactiveName, isReactiveName, isReadableName, isWritableName, leftIsThisInConstructor, parseCalculateCall, pathIsReactiveValue, processModelCall, reactivityTypeByName, reactivityTypeOfNode, ref, removeExtension, toKebabCase, } from "./lib.js";
6
+ import { checkBuildInModelArgs, checkNonReactiveName, checkReactiveName, err, Errors, exprCall, extractReactivityMark, forceReactiveName, isReactiveName, isReadableName, isWritableName, leftIsThisInConstructor, parseCalculateCall, pathIsReactiveValue, processModelCall, reactivityTypeByName, reactivityTypeOfNodePath, ref, removeExtension, replaceReactivityMark, toKebabCase, } from "./lib.js";
7
7
  import { checkOrder } from "./order-check.js";
8
8
  import { routerReplace } from "./router.js";
9
9
  import { stringify } from "./utils.js";
@@ -252,22 +252,19 @@ export function meshExpression(nodePath, internal, canHasRef) {
252
252
  }
253
253
  // arrayModel/setModel/mapModel call
254
254
  else if (!internal.isComposing && calls(path, modelFunctions, internal)) {
255
- /* istanbul ignore else */
256
- if (argPath) {
257
- meshAllUnknown([argPath], internal, true);
258
- }
255
+ const [source, size] = checkBuildInModelArgs(path, internal);
256
+ const called = calledFn(path, modelFunctions, internal);
259
257
  const loc = path.node.loc;
260
- if (calls(path, ["arrayModel"], internal)) {
261
- path.replaceWith(internal.arrayModel(argPath?.node, path.node, undefined));
262
- }
263
- else if (calls(path, ["mapModel"], internal)) {
264
- path.replaceWith(internal.mapModel(argPath?.node, path.node, undefined));
265
- }
266
- else {
267
- /* istanbul ignore else */
268
- if (calls(path, ["setModel"], internal)) {
269
- path.replaceWith(internal.setModel(argPath?.node, path.node, undefined));
270
- }
258
+ switch (called) {
259
+ case "arrayModel":
260
+ path.replaceWith(internal.arrayModel(source, size, path.node, undefined));
261
+ break;
262
+ case "mapModel":
263
+ path.replaceWith(internal.mapModel(source, size, path.node, undefined));
264
+ break;
265
+ case "setModel":
266
+ path.replaceWith(internal.setModel(source, size, path.node, undefined));
267
+ break;
271
268
  }
272
269
  path.node.loc = loc;
273
270
  }
@@ -331,8 +328,8 @@ export function meshExpression(nodePath, internal, canHasRef) {
331
328
  const path = nodePath;
332
329
  const left = path.get("left");
333
330
  const right = path.get("right");
334
- const leftReactivity = reactivityTypeOfNode(left, internal);
335
- const rightReactivity = reactivityTypeOfNode(right, internal);
331
+ const leftReactivity = reactivityTypeOfNodePath(left, internal);
332
+ const rightReactivity = reactivityTypeOfNodePath(right, internal);
336
333
  let bypassMesh = false;
337
334
  if (left.isMemberExpression() && hasBreakPoint(left, internal) && !memberIsIValue(left, "any", internal)) {
338
335
  err(Errors.RulesOfVasille, left, "This assignment breaks the reactivity", internal);
@@ -562,8 +559,7 @@ function ignoreObjectPattern(pattern, internal) {
562
559
  if (originName &&
563
560
  newName &&
564
561
  reactivityTypeByName(originName, internal) !== reactivityTypeByName(newName, internal)) {
565
- const correctName = originName.substring(0, originName.search(/[_$]/) + 1) +
566
- newName.substring(newName.search(/[_$]/) + 1);
562
+ const correctName = replaceReactivityMark(newName, extractReactivityMark(originName, internal.v4Naming), internal.v4Naming);
567
563
  err(Errors.RulesOfVasille, path.get("value"), `Property "${originName}" can not be renamed to "${newName}": rename it to "${correctName}"`, internal);
568
564
  }
569
565
  const valuePath = path.get("value");
@@ -203,6 +203,7 @@ export function transformProgram(path, filename, opts) {
203
203
  return expression;
204
204
  }
205
205
  const reports = [];
206
+ const errors = [];
206
207
  const relativePath = filename.substring(process.cwd().length);
207
208
  const internal = {
208
209
  appData: opts.appData,
@@ -265,23 +266,38 @@ export function transformProgram(path, filename, opts) {
265
266
  }
266
267
  return call("toDeepFieldRef", [getCtx(), obj, t.arrayExpression(fields)]);
267
268
  },
268
- setModel(arg, usage, name) {
269
+ setModel(arg, size, usage, name) {
269
270
  if (opts.devLayer) {
270
- return named(call("setModel", [nodeToStaticPosition(usage), getCtx(), arg ?? t.buildUndefinedNode()]), name);
271
+ return named(call("setModel", [
272
+ nodeToStaticPosition(usage),
273
+ getCtx(),
274
+ arg ?? t.buildUndefinedNode(),
275
+ size ?? t.buildUndefinedNode(),
276
+ ]), name);
271
277
  }
272
- return call("setModel", arg ? [getCtx(), arg] : [getCtx()]);
278
+ return call("setModel", [getCtx(), ...(arg ? [arg] : []), ...(arg && size ? [size] : [])]);
273
279
  },
274
- mapModel(arg, usage, name) {
280
+ mapModel(arg, size, usage, name) {
275
281
  if (opts.devLayer) {
276
- return named(call("mapModel", [nodeToStaticPosition(usage), getCtx(), arg ?? t.buildUndefinedNode()]), name);
282
+ return named(call("mapModel", [
283
+ nodeToStaticPosition(usage),
284
+ getCtx(),
285
+ arg ?? t.buildUndefinedNode(),
286
+ size ?? t.buildUndefinedNode(),
287
+ ]), name);
277
288
  }
278
- return call("mapModel", arg ? [getCtx(), arg] : [getCtx()]);
289
+ return call("mapModel", [getCtx(), ...(arg ? [arg] : []), ...(arg && size ? [size] : [])]);
279
290
  },
280
- arrayModel(arg, usage, name) {
291
+ arrayModel(arg, length, usage, name) {
281
292
  if (opts.devLayer) {
282
- return named(call("arrayModel", [nodeToStaticPosition(usage), getCtx(), arg ?? t.buildUndefinedNode()]), name);
293
+ return named(call("arrayModel", [
294
+ nodeToStaticPosition(usage),
295
+ getCtx(),
296
+ arg ?? t.buildUndefinedNode(),
297
+ length ?? t.buildUndefinedNode(),
298
+ ]), name);
283
299
  }
284
- return call("arrayModel", arg ? [getCtx(), arg] : [getCtx()]);
300
+ return call("arrayModel", [getCtx(), ...(arg ? [arg] : []), ...(arg && length ? [length] : [])]);
285
301
  },
286
302
  ensure(arg, area) {
287
303
  const object = arg.object;
@@ -362,9 +378,7 @@ export function transformProgram(path, filename, opts) {
362
378
  if (opts.throwAtFirstError) {
363
379
  throw e;
364
380
  }
365
- process.stderr.write(`in ${filename}:${pos?.start.line}:${pos?.start.column}\n`);
366
- process.stderr.write(e.message);
367
- process.stderr.write(`\n\n`);
381
+ errors.push(`\nin file://${filename}:${pos?.start.line}:${pos?.start.column}\n${e.message}\n`);
368
382
  }
369
383
  else {
370
384
  console.warn(message);
@@ -418,7 +432,8 @@ export function transformProgram(path, filename, opts) {
418
432
  reports: reports,
419
433
  });
420
434
  if (reports.some(item => item.class === "error")) {
421
- throw new Error("Compilation failed");
435
+ Error.stackTraceLimit = 1;
436
+ throw new Error(errors.join(""));
422
437
  }
423
438
  if (opts.devLayer) {
424
439
  path.node.body.unshift(t.variableDeclaration("const", [t.variableDeclarator(filePathId, t.stringLiteral(internal.steelFilePath))]));
@@ -2,8 +2,10 @@ import { cp, mkdir, opendir, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { statSync } from "node:fs";
3
3
  import path, { dirname } from "node:path";
4
4
  import { transformAsync } from "@babel/core";
5
+ // @ts-expect-error
5
6
  import pluginJsxSyntax from "@babel/plugin-syntax-jsx";
6
7
  import vasillePlugin from "../../babel-plugin/index.js";
8
+ // @ts-expect-error
7
9
  import typeScriptPlugin from "@babel/plugin-transform-typescript";
8
10
  import { watchFolder } from "./watch-folder.js";
9
11
  export async function compileComponentsLib(srcDir, outputDir) {
@@ -1,5 +1,7 @@
1
+ // @ts-expect-error
1
2
  import pluginJsxSyntax from "@babel/plugin-syntax-jsx";
2
3
  import pluginVasille from "../../babel-plugin/index.js";
4
+ // @ts-expect-error
3
5
  import pluginTypescript from "@babel/plugin-transform-typescript";
4
6
  import babel from "vite-plugin-babel";
5
7
  export function getVitePlugins(target, options) {
@@ -101,11 +101,11 @@ export declare function effect(f: () => void): void;
101
101
  * */
102
102
  export declare function bypassWriteRestriction<T>(reactive: T, value: T): T;
103
103
  /** Returns an array model of the array */
104
- export declare function arrayModel<T>(v?: T[]): ArrayModel<T>;
104
+ export declare function arrayModel<T>(v?: readonly T[], length?: number): ArrayModel<T>;
105
105
  /** Returns a set model of array values */
106
- export declare function setModel<T>(v?: T[]): SetModel<T>;
106
+ export declare function setModel<T>(v?: readonly T[] | ReadonlySet<T>, size?: number): SetModel<T>;
107
107
  /** Returns a map model of map data */
108
- export declare function mapModel<K, T>(v?: [K, T][]): MapModel<K, T>;
108
+ export declare function mapModel<K, T>(v?: readonly (readonly [K, T])[] | ReadonlyMap<K, T>, size?: number): MapModel<K, T>;
109
109
  /** Awaits async data in a sync component */
110
110
  export declare function awaited<T>(target: (signal?: AbortSignal) => Promise<T>): [unknown, T | undefined];
111
111
  /** Returns an abort signal to cancel requests/timers when context is destroyed */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "steel-frame",
3
- "version": "7.2.1",
3
+ "version": "7.2.2",
4
4
  "description": "SteelFrame: Build Fault-Tolerant Web Applications Effortlessly.",
5
5
  "main": "build/cli/run.js",
6
6
  "types": "./index.d.ts",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "scripts": {
31
31
  "prepack": "cp -f ../README.md ./README.md",
32
- "prettier": "prettier src --write",
32
+ "prettier": "prettier src test --write",
33
33
  "prebuild": "rm -rf build",
34
34
  "build": "tsc --build tsconfig.json",
35
35
  "tsc-tests": "tsc --build test/tsconfig.json",
@@ -60,7 +60,7 @@
60
60
  "@inquirer/select": "^5.2.6",
61
61
  "csstype": "^3.1.3",
62
62
  "esbuild": "^0.28.2",
63
- "vasille-web": "7.2.1",
63
+ "vasille-web": "7.2.2",
64
64
  "vite": "^8.3.4",
65
65
  "vite-plugin-babel": "^1.7.3",
66
66
  "vite-plugin-inspect": "^12.1.0",
@@ -75,6 +75,6 @@
75
75
  "prettier": "^3.6.2",
76
76
  "steel-frame": "file:.",
77
77
  "ts-jest": "^29.4.14",
78
- "typescript": "^5.9.3"
78
+ "typescript": "^6.0.3"
79
79
  }
80
80
  }
package/build/cli/sf.d.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import "run.ts";
package/build/cli/sf.js DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import "run.ts";