diff --git a/packages/core/__tests__/language-server/diagnostics.test.ts b/packages/core/__tests__/language-server/diagnostics.test.ts
index a53fe1d9a..aba4ddbff 100644
--- a/packages/core/__tests__/language-server/diagnostics.test.ts
+++ b/packages/core/__tests__/language-server/diagnostics.test.ts
@@ -384,7 +384,7 @@ describe('Language Server: Diagnostics (ts plugin)', () => {
"code": 2554,
"end": {
"line": 15,
- "offset": 17,
+ "offset": 14,
},
"relatedInformation": [
{
@@ -406,7 +406,7 @@ describe('Language Server: Diagnostics (ts plugin)', () => {
],
"start": {
"line": 15,
- "offset": 5,
+ "offset": 6,
},
"text": "Expected 1 arguments, but got 0.",
},
diff --git a/packages/core/__tests__/transform/template-to-typescript.test.ts b/packages/core/__tests__/transform/template-to-typescript.test.ts
index 47b6a0287..b86681dbf 100644
--- a/packages/core/__tests__/transform/template-to-typescript.test.ts
+++ b/packages/core/__tests__/transform/template-to-typescript.test.ts
@@ -821,6 +821,9 @@ describe('Transform: rewriteTemplate', () => {
expect(templateBody(template, { globals: [] })).toMatchInlineSnapshot(`
"{
const __glintY__ = __glintDSL__.emitElement("div");
+ __glintDSL__.applyAttributes(__glintY__.element, {
+
+ });
__glintDSL__.applyModifier(__glintDSL__.resolve(modifier)(__glintY__.element, { foo: "bar" , ...__glintDSL__.NamedArgsMarker }));
}"
`);
@@ -832,6 +835,9 @@ describe('Transform: rewriteTemplate', () => {
expect(templateBody(template, { globals: [] })).toMatchInlineSnapshot(`
"{
const __glintY__ = __glintDSL__.emitComponent(__glintDSL__.resolve(MyComponent)());
+ __glintDSL__.applyAttributes(__glintY__.element, {
+
+ });
__glintDSL__.applyModifier(__glintDSL__.resolve(modifier)(__glintY__.element, { foo: "bar" , ...__glintDSL__.NamedArgsMarker }));
}"
`);
@@ -973,6 +979,9 @@ describe('Transform: rewriteTemplate', () => {
"{
const __glintY__ = __glintDSL__.emitElement("div");
__glintDSL__.applySplattributes(__glintRef__.element, __glintY__.element);
+ __glintDSL__.applyAttributes(__glintY__.element, {
+
+ });
}"
`);
});
@@ -1017,6 +1026,9 @@ describe('Transform: rewriteTemplate', () => {
"{
const __glintY__ = __glintDSL__.emitComponent(__glintDSL__.resolve(Foo)());
__glintDSL__.applySplattributes(__glintRef__.element, __glintY__.element);
+ __glintDSL__.applyAttributes(__glintY__.element, {
+
+ });
}"
`);
});
diff --git a/packages/core/package.json b/packages/core/package.json
index 56495f281..2a836699b 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -34,6 +34,7 @@
"test:tsc": "echo 'no standalone typecheck within this project'",
"test:watch": "vitest watch",
"build": "tsc --build",
+ "dev": "tsc --watch",
"prepack": "pnpm build"
},
"peerDependencies": {
diff --git a/packages/core/src/transform/diagnostics/augmentation.ts b/packages/core/src/transform/diagnostics/augmentation.ts
index e0dbdebc0..d6674a9ed 100644
--- a/packages/core/src/transform/diagnostics/augmentation.ts
+++ b/packages/core/src/transform/diagnostics/augmentation.ts
@@ -235,7 +235,7 @@ function checkImplicitAnyError(
// This error may appear either on `` or `{{foo}}`/`(foo)`
let globalName =
sourceNode.type === 'ElementNode'
- ? sourceNode.tag.split('.')[0]
+ ? sourceNode.path.head.original
: sourceNode.type === 'PathExpression' && sourceNode.head.type === 'VarHead'
? sourceNode.head.name
: null;
diff --git a/packages/core/src/transform/template/map-template-contents.ts b/packages/core/src/transform/template/map-template-contents.ts
index 5d44fea57..bf75a23c0 100644
--- a/packages/core/src/transform/template/map-template-contents.ts
+++ b/packages/core/src/transform/template/map-template-contents.ts
@@ -27,7 +27,7 @@ export type Mapper = {
* Given a @glimmer/syntax AST node, returns the corresponding start
* and end offsets of that node in the original source.
*/
- rangeForNode: (node: AST.Node) => Range;
+ rangeForNode: (node: AST.Node, span?: AST.Node['loc']) => Range;
/**
* Captures the existence of a directive specified by the given source
@@ -81,6 +81,12 @@ export type Mapper = {
* corresponding to the given AST node in the original source.
*/
forNode(node: AST.Node, callback: () => void, codeFeaturesForNode?: CodeInformation): void;
+ forNodeWithSpan(
+ node: AST.Node,
+ span: AST.Node['loc'],
+ callback: () => void,
+ codeFeaturesForNode?: CodeInformation,
+ ): void;
/**
* This needs to be called after any node that "consumes" a `glint-expect-error` directive.
@@ -311,6 +317,15 @@ export function mapTemplateContents(
captureMapping(mapper.rangeForNode(node), node, false, callback, codeFeaturesForNode);
},
+ forNodeWithSpan(
+ node: AST.Node,
+ span: AST.Node['loc'],
+ callback: () => void,
+ codeFeaturesForNode?: CodeInformation,
+ ) {
+ captureMapping(mapper.rangeForNode(node, span), node, false, callback, codeFeaturesForNode);
+ },
+
error(message: string, location: Range) {
errors.push({ message, location });
},
@@ -460,9 +475,14 @@ function calculateLineOffsets(template: string, contentOffset: number): Array): (node: AST.Node) => Range {
- return (node) => {
+function buildRangeForNode(
+ offsets: Array,
+): (node: AST.Node, span?: AST.Node['loc']) => Range {
+ return (node, span) => {
let { loc } = node;
+ if (span) {
+ loc = span;
+ }
let start = offsets[loc.start.line] + loc.start.column;
let end = offsets[loc.end.line] + loc.end.column;
diff --git a/packages/core/src/transform/template/template-to-typescript.ts b/packages/core/src/transform/template/template-to-typescript.ts
index 3c7956714..2060f8623 100644
--- a/packages/core/src/transform/template/template-to-typescript.ts
+++ b/packages/core/src/transform/template/template-to-typescript.ts
@@ -695,7 +695,7 @@ export function templateToTypescript(
mapper.text('const __glintY__ = __glintDSL__.emitComponent(');
// Error boundary: "Expected 1 arguments, but got 0." e.g. when invoking ``
- mapper.forNode(node, () => {
+ mapper.forNode(node.path, () => {
mapper.text('__glintDSL__.resolve(');
emitPathContents(path, start, kind);
mapper.text(')');
@@ -707,7 +707,7 @@ export function templateToTypescript(
let dataAttrs = node.attributes.filter(({ name }) => name.startsWith('@'));
if (dataAttrs.length) {
// Error boundary: "Expected 0 arguments, but got 1." e.g. when invoking ``
- mapper.forNode(node, () => {
+ mapper.forNodeWithSpan(node, node.openTag, () => {
mapper.text('{ ');
for (let attr of dataAttrs) {
@@ -883,7 +883,9 @@ export function templateToTypescript(
mapper.indent();
mapper.text('const __glintY__ = __glintDSL__.emitElement(');
- mapper.text(JSON.stringify(node.tag));
+ mapper.forNode(node.path, () => {
+ mapper.text(JSON.stringify(node.tag));
+ });
mapper.text(');');
mapper.newline();
@@ -930,39 +932,46 @@ export function templateToTypescript(
(attr) => !attr.name.startsWith('@') && attr.name !== SPLATTRIBUTES,
);
- if (!attributes.length) return;
-
mapper.text('__glintDSL__.applyAttributes(__glintY__.element, {');
- mapper.newline();
- mapper.indent();
- let start = template.indexOf(node.tag, rangeForNode(node).start) + node.tag.length;
+ mapper.forNodeWithSpan(node, node.openTag, () => {
+ mapper.newline();
+ mapper.indent();
- for (let attr of attributes) {
- mapper.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);
- mapper.text(': ');
+ for (let attr of attributes) {
+ mapper.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 {
- mapper.text(JSON.stringify(attr.value.chars));
- }
+ emitHashKey(attr.name, start);
+ mapper.text(': ');
- mapper.text(',');
- mapper.newline();
- });
+ if (attr.value.type === 'MustacheStatement') {
+ emitMustacheStatement(attr.value, 'attr');
+ } else if (attr.value.type === 'ConcatStatement') {
+ emitConcatStatement(attr.value);
+ } else {
+ mapper.text(JSON.stringify(attr.value.chars));
+ }
- mapper.terminateDirectiveAreaOfEffect('emitPlainAttributes');
- }
+ mapper.text(',');
+ mapper.newline();
+ });
- mapper.dedent();
- mapper.text('});');
- mapper.newline();
+ mapper.terminateDirectiveAreaOfEffect('emitPlainAttributes');
+ }
+
+ // in case there are no attributes, this would allow completions to trigger
+ if (attributes.length === 0) {
+ mapper.text(' ');
+ mapper.newline();
+ }
+
+ mapper.dedent();
+ mapper.text('});');
+ mapper.newline();
+ });
}
function emitSplattributes(node: AST.ElementNode): void {