New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduces slots #356
Introduces slots #356
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't call it a full review, but took a quick pass and left some comments @rpaul-stripe.
src/ast/node.ts
Outdated
} | ||
|
||
*walk(): Generator<Node, void, unknown> { | ||
for (const slot of Object.values(this.slots)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be faster to make this.slots
default to undefined
and avoid the Object.values(this.slots)
in the default case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess alternatively you could simplify this function by doing:
for (const child of this.children.concat(Object.values(this.slots))) {
...
or something similar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm changing it to: for (const child of [...Object.values(this.slots), ...this.children]) {
. I don't think it materially impacts performance either way.
tag === 'slot' && | ||
typeof node.attributes.primary === 'string' | ||
) | ||
parent.slots[node.attributes.primary] = node; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we make children
a reserved slot key? Or is there no reason to avoid this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is necessary. In the event that someone uses children
as the name of the slot, the value will appear as expected in the render tree but will be replaced by the actual children in the React props.
This is already the behavior we have today with attributes and we don't enforce any restrictions to prevent that. React itself does pretty much the same thing if use a "children" attribute in JSX as well as passing in actual children.
errors.push({ | ||
id: 'slot-missing-required', | ||
level: 'error', | ||
message: `Missing required slot: '${key}'`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add a unit test for this string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a marktest case for this: https://github.com/markdoc/markdoc/pull/356/files#diff-e0ce4764821307e283506300a5110e8c134dad772d1cd61afce332d087832834R1729
|
||
export type ParserArgs = { | ||
file?: string; | ||
slots?: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm usually a little wary of adding boolean config options like this. WDYT about using an enum instead? Like slotMode: 'enabled'
or something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the boolean for simplicity and because it's consistent with how tokenizer options work.
|
||
export default { | ||
if: tagIf, | ||
else: tagElse, | ||
partial, | ||
table, | ||
slot, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alphabetize? 🙂
@mfix thank you for feedback! I tweaked the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This PR implements the slots proposal in Markdoc. Slots make it possible for a custom tag to accept multiple sets of children, providing an to attributes that is suitable for cases where you want to accept arbitrary Markdoc content instead of a conventional attribute type.
slot
tag and adds it to theNode
object'sslots
property instead of theNode
's children.Node
class stores slots in aslots
property on theNode
objectNode.walk
iterator function recursively yields the slots and their children before yielding theNode
's childrenparse
function now accepts an object with parse settings, which takes the shape{file: string, slots: boolean}
slots
configuration property istrue
, otherwise eachslot
in a document is treated like a conventional tag namedslot
.parse
function can also be a string by itself, which is treated as{file: string}
.parser.test.ts
slots: true
in the top-level test objectslot-missing-required
error when one is missingslot-undefined
error when one is detectedslot
tag is defined in the schemaString
type value that reflects the slot's nametrasnsformer.attributes
function was updated to process slots on a givenNode
instance after processing attributes. It iterates over slots that are declared in the schema for theNode
instance, renders eachslot
node, and adds them to the rendered attribute object