-
Notifications
You must be signed in to change notification settings - Fork 52
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
provide suggestions and type checking for elements attributes #663
base: main
Are you sure you want to change the base?
Changes from all commits
bdafebd
a375688
b9388c7
511cb32
6db64a8
2464b95
c7dbee4
492a6d5
79823d5
eb16ae6
eb75445
651c76d
5bd90cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
import { unreachable, assert } from '../util.js'; | ||
import { EmbeddingSyntax, mapTemplateContents, RewriteResult } from './map-template-contents.js'; | ||
import ScopeStack from './scope-stack.js'; | ||
import { GlintEmitMetadata, GlintSpecialForm } from '@glint/core/config-types'; | ||
Check failure on line 5 in packages/core/src/transform/template/template-to-typescript.ts GitHub Actions / Test Windows
|
||
import { TextContent } from './mapping-tree.js'; | ||
|
||
const SPLATTRIBUTES = '...attributes'; | ||
|
@@ -42,7 +42,7 @@ | |
return mapTemplateContents(originalTemplate, { embeddingSyntax }, (ast, mapper) => { | ||
let { emit, record, rangeForLine, rangeForNode } = mapper; | ||
let scope = new ScopeStack([]); | ||
|
||
let inSVG = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it could be possible to have components inside svg, with this it would be possible to a glint-directive |
||
emitTemplateBoilerplate(() => { | ||
for (let statement of ast?.body ?? []) { | ||
emitTopLevelStatement(statement); | ||
|
@@ -148,6 +148,10 @@ | |
record.directive(kind, location, rangeForLine(node.loc.end.line + 1)); | ||
} else if (kind === 'nocheck') { | ||
record.directive('ignore', location, { start: 0, end: template.length - 1 }); | ||
} else if (kind === 'in-svg') { | ||
inSVG = true; | ||
} else if (kind === 'out-svg') { | ||
inSVG = false; | ||
} else { | ||
record.error(`Unknown directive @glint-${kind}`, location); | ||
} | ||
|
@@ -595,13 +599,15 @@ | |
emit.indent(); | ||
|
||
emit.text('const 𝛄 = χ.emitComponent(χ.resolve('); | ||
emitPathContents(path, start, kind); | ||
emit.forNode(node.nameNode, () => { | ||
emitPathContents(path, start, kind); | ||
}) | ||
emit.text(')('); | ||
|
||
let dataAttrs = node.attributes.filter(({ name }) => name.startsWith('@')); | ||
if (dataAttrs.length) { | ||
emit.text('{ '); | ||
emit.text('{ '); | ||
|
||
emit.forNode(node.startTag, () => { | ||
for (let attr of dataAttrs) { | ||
emit.forNode(attr, () => { | ||
start = template.indexOf(attr.name, start + 1); | ||
|
@@ -626,9 +632,11 @@ | |
start = rangeForNode(attr.value).end; | ||
emit.text(', '); | ||
} | ||
// in case there are no attributes, this would allow completions to trigger | ||
emit.text(' '); | ||
}) | ||
|
||
emit.text('...χ.NamedArgsMarker }'); | ||
} | ||
emit.text('...χ.NamedArgsMarker }'); | ||
|
||
emit.text('));'); | ||
emit.newline(); | ||
|
@@ -754,21 +762,31 @@ | |
emitComment(comment); | ||
} | ||
|
||
if (node.tag === 'svg') { | ||
inSVG = true; | ||
} | ||
|
||
emit.text('{'); | ||
emit.newline(); | ||
emit.indent(); | ||
|
||
emit.text('const 𝛄 = χ.emitElement('); | ||
emit.text(JSON.stringify(node.tag)); | ||
if (!inSVG) { | ||
emit.text('const 𝛄 = χ.emitElement('); | ||
} else { | ||
emit.text('const 𝛄 = χ.emitSVGElement('); | ||
} | ||
emit.forNode(node.nameNode, () => { | ||
emit.text(JSON.stringify(node.tag)); | ||
}); | ||
emit.text(');'); | ||
emit.newline(); | ||
|
||
emitAttributesAndModifiers(node); | ||
|
||
for (let child of node.children) { | ||
emitTopLevelStatement(child); | ||
} | ||
|
||
if (node.tag === 'svg') { | ||
inSVG = false; | ||
} | ||
emit.dedent(); | ||
emit.text('}'); | ||
emit.newline(); | ||
|
@@ -793,35 +811,36 @@ | |
(attr) => !attr.name.startsWith('@') && attr.name !== SPLATTRIBUTES | ||
); | ||
|
||
if (!attributes.length) return; | ||
|
||
emit.text('χ.applyAttributes(𝛄.element, {'); | ||
emit.newline(); | ||
emit.indent(); | ||
|
||
let start = template.indexOf(node.tag, rangeForNode(node).start) + node.tag.length; | ||
emit.forNode(node.startTag, () => { | ||
emit.newline(); | ||
emit.indent(); | ||
|
||
for (let attr of attributes) { | ||
emit.forNode(attr, () => { | ||
start = template.indexOf(attr.name, start + 1); | ||
let start = template.indexOf(node.tag, rangeForNode(node).start) + node.tag.length; | ||
|
||
emitHashKey(attr.name, start); | ||
emit.text(': '); | ||
for (let attr of attributes) { | ||
emit.forNode(attr, () => { | ||
start = template.indexOf(attr.name, start + 1); | ||
|
||
if (attr.value.type === 'MustacheStatement') { | ||
emitMustacheStatement(attr.value, 'attr'); | ||
} else if (attr.value.type === 'ConcatStatement') { | ||
emitConcatStatement(attr.value); | ||
} else { | ||
emit.text(JSON.stringify(attr.value.chars)); | ||
} | ||
emitHashKey(attr.name, start); | ||
emit.text(': '); | ||
|
||
emit.text(','); | ||
emit.newline(); | ||
}); | ||
} | ||
if (attr.value.type === 'MustacheStatement') { | ||
emitMustacheStatement(attr.value, 'attr'); | ||
} else if (attr.value.type === 'ConcatStatement') { | ||
emitConcatStatement(attr.value); | ||
} else { | ||
emit.text(JSON.stringify(attr.value.chars)); | ||
} | ||
|
||
emit.dedent(); | ||
emit.text(','); | ||
emit.newline(); | ||
}); | ||
} | ||
// in case there are no attributes, this would allow completions to trigger | ||
emit.text(' '); | ||
emit.dedent(); | ||
}); | ||
emit.text('});'); | ||
emit.newline(); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ import { | |
TemplateContext, | ||
NamedArgs, | ||
} from '../integration'; | ||
import { ElementForTagName } from './types'; | ||
import { AttributesForElement, ElementForTagName, SVGElementForTagName } from './types'; | ||
|
||
/** | ||
* Used during emit to denote an object literal that corresponds | ||
|
@@ -44,10 +44,14 @@ export declare function emitContent(value: ContentValue): void; | |
* applyModifier(𝛄.element, resolve(on)({}, 'click', this.clicked)); | ||
* }); | ||
*/ | ||
export declare function emitElement<Name extends string>( | ||
export declare function emitElement<Name extends string | keyof HTMLElementTagNameMap>( | ||
name: Name | ||
): { element: ElementForTagName<Name> }; | ||
|
||
export declare function emitSVGElement<Name extends string | keyof SVGElementTagNameMap>( | ||
name: Name | ||
): { element: SVGElementForTagName<Name> }; | ||
|
||
/* | ||
* Emits the given value as an entity that expects to receive blocks | ||
* rather than return a value. This corresponds to a block-form mustache | ||
|
@@ -133,7 +137,25 @@ export declare function applySplattributes< | |
* <div foo={{bar}}></div> | ||
* <AnotherComponent foo={{bar}} /> | ||
*/ | ||
export declare function applyAttributes(element: Element, attrs: Record<string, AttrValue>): void; | ||
|
||
type WithSvgStrings<T> = { | ||
[P in keyof T]?: T[P] extends SVGAnimatedString ? string : T[P]; | ||
}; | ||
|
||
// TODO: improve this to allow other keys | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. anyone knowns how to achieve this? Currently I do it with |
||
type WithOther<T> = keyof T; | ||
|
||
type ElementAttributes< | ||
T, | ||
A, | ||
P extends string | number | symbol = WithOther<A>, | ||
V = P extends keyof T ? T[P] : AttrValue | ||
> = Record<P, V>; | ||
|
||
export declare function applyAttributes<T extends Element>( | ||
element: T, | ||
attrs: WithSvgStrings<ElementAttributes<T, AttributesForElement<T>>> & Record<string, AttrValue> | ||
): void; | ||
|
||
/* | ||
* Applies a modifier to an element or component. | ||
|
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.
to remove
"
or'
from element attributes containing a-