diff --git a/.changeset/yellow-eagles-fail.md b/.changeset/yellow-eagles-fail.md new file mode 100644 index 0000000000..43da7d83c4 --- /dev/null +++ b/.changeset/yellow-eagles-fail.md @@ -0,0 +1,7 @@ +--- +"marko": patch +--- + +Deprecated the Marko.RepeatableAttrTag type (which is now an alias of Marko.AttrTag). This type was overcomplicating things and leading people to incorrectly handle the single item case. Update docs to avoid recommending relying on the array case since this behavior changes in Marko 6 to always be a single (iterable) item. + +Updates the `Marko.Input` type to handle changes to the `Marko.Body` type from `@marko/language-tools`. diff --git a/packages/marko/docs/body-content.md b/packages/marko/docs/body-content.md index 0c553d6452..2ba8caebe0 100644 --- a/packages/marko/docs/body-content.md +++ b/packages/marko/docs/body-content.md @@ -111,9 +111,9 @@ _components/layout.marko_ > **ProTip:** The `renderBody` property can be omitted. You could use `<${input.heading}/>`, for example. -### Repeatable attribute tags +### Repeated body content -Attribute tags can be repeated. Rendering the same attribute tag name multiple times will cause the input value for that attribute to become an array instead of an single object. +When an attribute tag is repeated, the child component can consume all instances using the [iterable protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol). This allows us to, for example, build a custom table component which allows its user to specify any number of columns, while still giving the user control over how each column is rendered. @@ -131,15 +131,11 @@ _Marko Source:_ ``` > _Note_ -> Attribute tags are _repeatable_. -> -> - Zero: if you don't pass any `@column` tags, the `fancy-table` receives `undefined`. -> - One: if you pass a single `@column` tag, the `fancy-table` receives a single attribute tag object. (For convenience this object is [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol) meaning it can be directly passed to the `` tag.) -> - Many: if you pass multiple `@column` tags, the `fancy-table` receives an array of attribute tags. -> For TypeScript the [`Marko.AttrTag` or `Marko.RepeatableAttrTag` helpers](./typescript.md#built-in-marko-types) should be used here. +> For TypeScript the [`Marko.AttrTag` helper](./typescript.md#built-in-marko-types) should be used here. > _Protip_ -> To `.map`, `.filter` or otherwise work with attribute tags as an array: +> Since attribute tags are iterable you could pass `input.column` to a `for of` loop, or `[...spread]` it into an array. +> To `.map`, `.filter` or otherwise work with attribute tags as an array you can use the following pattern: > > ```marko > $ const columns = [...input.column || []]; @@ -212,7 +208,7 @@ _Marko Source:_ ``` -Now, each object in the `input.column` array will contain a `heading` property in addition to its `renderBody`. We can use another `` and render the headings in `` tags: +Now, each attribute tag in `input.column` will contain a `heading` property in addition to its `renderBody`. We can use another `` and render the headings in `` tags: _components/fancy-table/index.marko:_ @@ -256,21 +252,6 @@ _HTML Output:_ ``` -> _Note_ -> You may also specify that the attribute tag can be repeated in a [`marko-tag.json`](./marko-json.md#single-component-definition) file. -> This will cause an array to _always_ be passed if there are any items, rather than working up from `undefined`, single object and then an array. -> -> _components/fancy-table/marko-tag.json:_ -> -> ```js -> { -> "@data": "array", -> "": { -> "is-repeated": true -> } -> } -> ``` - ### Nested attribute tags Continuing to build on our example, what if we want to add some custom content or even components into the column headings? In this case, we can extend our `` to use nested attribute tags. We'll now have `<@heading>` and `<@cell>` tags nested under `<@column>`. This gives users of our tag full control over how to render both column headings and the cells within the column! diff --git a/packages/marko/docs/typescript.md b/packages/marko/docs/typescript.md index dc9a3a38c1..f1f605bf50 100644 --- a/packages/marko/docs/typescript.md +++ b/packages/marko/docs/typescript.md @@ -137,10 +137,9 @@ Marko exposes [type definitions](https://github.com/marko-js/marko/blob/main/pac - Helpers to extract the input and return types native tags (when a string is passed) or a custom tag. - **`Marko.BodyParameters`** and **`Marko.BodyReturnType`** - Helpers to extract the parameters and return types from the specified `Marko.Body` -- **`Marko.AttrTag`** and **`Marko.RepeatableAttrTag`** +- **`Marko.AttrTag`** - Used to represent types for [attributes tags](./body-content.md#named-body-content) - - `Marko.AttrTag`: A single attribute tag - - `Marko.RepeatableAttrTag`: One or more attribute tags + - A single attribute tag, with a `[Symbol.iterator]` to consume any repeated tags. ### Typing `renderBody` diff --git a/packages/marko/index.d.ts b/packages/marko/index.d.ts index fea4172b8f..5172ce4e54 100644 --- a/packages/marko/index.d.ts +++ b/packages/marko/index.d.ts @@ -310,10 +310,14 @@ declare global { removeAllListeners(eventName?: PropertyKey): this; } - export type AttrTag = T & Iterable>; - export type RepeatableAttrTag = - | AttrTag - | [AttrTag, AttrTag, ...AttrTag[]]; + export type AttrTag = T & { + [Symbol.iterator](): T; + }; + + /** + * @deprecated Prefer to use AttrTag + */ + export type RepeatableAttrTag = AttrTag; export interface NativeTag< Input extends Record, @@ -341,10 +345,10 @@ declare global { length: infer Length; } ? number extends Length - ? { value?: Args } + ? Args[0] | undefined : 0 extends Length - ? { value?: [] } - : { value: Args } + ? undefined + : Args[0] : never : never; diff --git a/packages/marko/test/render/fixtures/nested-tags-repeatable/components/fancy-table.marko b/packages/marko/test/render/fixtures/nested-tags-repeatable/components/fancy-table.marko index fa6898e8e2..d4e5b85dc4 100644 --- a/packages/marko/test/render/fixtures/nested-tags-repeatable/components/fancy-table.marko +++ b/packages/marko/test/render/fixtures/nested-tags-repeatable/components/fancy-table.marko @@ -3,7 +3,7 @@ * @template T * @typedef {{ * data: T[], - * column: Marko.RepeatableAttrTag<{ renderBody: Marko.Body<[T]> }> + * column: Marko.AttrTag<{ renderBody: Marko.Body<[T]> }> * }} Input */