Skip to content

Commit

Permalink
Merge pull request #22 from NullVoxPopuli/argument-components
Browse files Browse the repository at this point in the history
`<@argument>` Parsing error: Cannot read properties of null (reading 'index')
  • Loading branch information
NullVoxPopuli authored Dec 30, 2023
2 parents a798404 + f45607b commit 35751d1
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 11 deletions.
23 changes: 12 additions & 11 deletions src/parser/transforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,9 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
const templateVisitorKeys = {};
const codeLines = new DocumentLines(code);
const comments = [];
const textNodes = [];
for (const tpl of templateInfos) {
const currentComments = [];
const textNodes = [];
const range = tpl.range;
const template = code.slice(...range);
const docLines = new DocumentLines(template);
Expand All @@ -221,6 +222,7 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
allNodes.push(node);
if (node.type === 'CommentStatement' || node.type === 'MustacheCommentStatement') {
comments.push(node);
currentComments.push(node);
}
if (node.type === 'TextNode') {
n.value = node.chars;
Expand Down Expand Up @@ -267,13 +269,8 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
let start = n.range[0];
let codeSlice = code.slice(...n.range);
for (const part of n.tag.split('.')) {
let pattern = `\\b${part}\\b`;
if (part.startsWith(':')) {
pattern = `${part}\\b`;
}
const regex = new RegExp(pattern);
const match = codeSlice.match(regex);
const range = [start + match.index, 0];
const idx = codeSlice.indexOf(part);
const range = [start + idx, 0];
range[1] = range[0] + part.length;
codeSlice = code.slice(range[1], n.range[1]);
start = range[1];
Expand All @@ -295,8 +292,11 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
n.params = [];
}
if ('blockParams' in n && n.parent) {
let part = code.slice(...n.parent.range);
let start = n.parent.range[0];
// for blocks {{x as |b|}} the block range does not contain the block params...
// for element tag it does <x as b />
const blockRange = n.type === 'Block' ? n.parent.range : n.range;
let part = code.slice(...blockRange);
let start = blockRange[0];
let idx = part.indexOf('|') + 1;
start += idx;
part = part.slice(idx, -1);
Expand All @@ -323,7 +323,7 @@ module.exports.preprocessGlimmerTemplates = function preprocessGlimmerTemplates(
allNodeTypes.add(n.type);
}
// ast should not contain comment nodes
for (const comment of comments) {
for (const comment of currentComments) {
const parentBody = comment.parent.body || comment.parent.children;
const idx = parentBody.indexOf(comment);
if (idx >= 0) {
Expand Down Expand Up @@ -441,6 +441,7 @@ module.exports.convertAst = function convertAst(result, preprocessedResult, visi
if (
n.name !== 'this' &&
!n.name.startsWith(':') &&
!n.name.startsWith('@') &&
scope &&
(variable ||
isUpperCase(n.name[0]) ||
Expand Down
149 changes: 149 additions & 0 deletions test-projects/gts/src/menu.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// @ts-ignore-expect-error
import { hash } from '@ember/helper';

import HeadlessMenu from 'ember-headlessui/components/menu';
import { Popover } from 'ember-primitives';

import type { TOC } from '@ember/component/template-only';
import type { ModifierLike, WithBoundArgs } from '@glint/template';
import type * as MenuTypes from 'ember-headlessui/components/menu';

const Button: TOC<{
Element: HTMLButtonElement;
Args: {
item: MenuTypes.Item;
};
Blocks: {
default: [];
};
}> = <template>
<@item as |i|>
<i.Element
@tagName="button"
class="bg-transparent block w-full select-none py-2 px-4 text-left text-black hover:bg-gray-100 focus:ring-4 ring-inset focus:outline-none"
tabindex="0"
data-test-menu-button
...attributes
>
{{yield}}
</i.Element>
</@item>
</template>;

const DefaultTrigger: TOC<{
Element: HTMLButtonElement;
Args: {
menu: MenuTypes.Menu;
trigger: ModifierLike<any>;
};
Blocks: {
default: [MenuTypes.Menu];
};
}> = <template>
<@menu.Button
{{@trigger}}
class="text-black rounded border bg-white px-2 py-1 -my-1 text-left transition ease-in-out duration-150 sm:text-sm drop-shadow-md hover:drop-shadow-xl focus:ring-4 focus-visible:outline-none ring-ember-brand focus:outline-none"
...attributes
>
{{yield @menu}}
</@menu.Button>
</template>;

const PlainTrigger: TOC<{
Element: HTMLButtonElement;
Args: {
menu: MenuTypes.Menu;
trigger: ModifierLike<any>;
};
Blocks: {
default: [MenuTypes.Menu];
};
}> = <template>
<@menu.Button {{@trigger}} ...attributes>
{{yield @menu}}
</@menu.Button>
</template>;

const Items: TOC<{
Element: HTMLDivElement;
Args: {
items: MenuTypes.Items;
};
Blocks: {
default: [button: WithBoundArgs<typeof Button, 'item'>];
};
}> = <template>
<@items
class="z-20 grid rounded border bg-white drop-shadow-xl min-w-max"
data-test-menu-items
...attributes
as |items|
>
{{yield (component Button item=items.Item)}}
</@items>
</template>;

const Menu: TOC<{
Element: HTMLDivElement;
Args: {
inline?: boolean;
};
Blocks: {
trigger: [
{
menu: MenuTypes.Menu;
isOpen: boolean;
Default: WithBoundArgs<typeof DefaultTrigger, 'menu' | 'trigger'>;
Button: WithBoundArgs<typeof PlainTrigger, 'menu' | 'trigger'>;
},
];
options: [button: WithBoundArgs<typeof Button, 'item'>];
};
}> = <template>
<Popover
@inline={{@inline}}
@placement="bottom"
@offsetOptions={{8}}
@shiftOptions={{hash padding=8}}
@flipOptions={{hash padding=8}}
as |p|
>
<HeadlessMenu as |menu|>

{{yield
(hash
menu=menu
isOpen=menu.isOpen
Button=(component PlainTrigger menu=menu trigger=p.hook)
Default=(component DefaultTrigger menu=menu trigger=p.hook)
)
to="trigger"
}}

{{#if menu.isOpen}}
{{! template-lint-disable no-inline-styles }}
<p.Content style="width: max-content;z-index:1;">
{{! template-lint-disable no-inline-styles }}
<div
class="border"
style="
position: absolute;
background: white;
width: 8px;
height: 8px;
transform: rotate(45deg);
z-index: 0;
"
{{p.arrow}}
></div>
<Items @items={{menu.Items}} ...attributes as |Button|>
{{yield Button to="options"}}
</Items>
</p.Content>
{{/if}}

</HeadlessMenu>
</Popover>
</template>;

export default Menu;

0 comments on commit 35751d1

Please sign in to comment.