Skip to content

Commit 894f6bf

Browse files
committed
trying a tagged template for preprocessing
1 parent f0b8e0e commit 894f6bf

9 files changed

Lines changed: 62 additions & 100 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"eslint-plugin-simple-import-sort": "^12.1.1",
7676
"eslint-plugin-unicorn": "^59.0.1",
7777
"globals": "^16.2.0",
78-
"prettier": "^3.7.1",
78+
"prettier": "3.7.1",
7979
"prettier-plugin-jsdoc": "^1.3.2",
8080
"release-plan": "^0.11.0",
8181
"typescript": "^5.8.3",

pnpm-lock.yaml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/parse/index.ts

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import type {
33
BlockStatement,
44
File,
55
Node,
6-
ObjectExpression,
76
StaticBlock,
7+
TaggedTemplateExpression,
88
} from '@babel/types';
99
import type { Parser } from 'prettier';
1010
import { parsers as babelParsers } from 'prettier/plugins/babel.js';
@@ -18,7 +18,7 @@ const typescript = babelParsers['babel-ts'] as Parser<Node | undefined>;
1818

1919
/** Converts a node into a GlimmerTemplate node */
2020
function convertNode(
21-
node: BlockStatement | ObjectExpression | StaticBlock,
21+
node: BlockStatement | TaggedTemplateExpression | StaticBlock,
2222
rawTemplate: Template,
2323
): void {
2424
node.innerComments = [];
@@ -36,17 +36,8 @@ function convertAst(ast: File, templates: Template[]): void {
3636

3737
switch (node.type) {
3838
case 'BlockStatement':
39-
case 'ObjectExpression':
40-
case 'StaticBlock': {
41-
if (
42-
!node.range &&
43-
typeof node.start === 'number' &&
44-
typeof node.end === 'number'
45-
) {
46-
// prettier 3.6.0 onwards doesn't have `node.range`
47-
// as it was removed in babel
48-
node.range = [node.start, node.end];
49-
}
39+
case 'StaticBlock':
40+
case 'TaggedTemplateExpression': {
5041
assert('expected range', node.range);
5142
const [start, end] = node.range;
5243

@@ -57,11 +48,7 @@ function convertAst(ast: File, templates: Template[]): void {
5748
return true;
5849
}
5950

60-
return (
61-
node.type === 'ObjectExpression' &&
62-
utf16Range.start === start - 1 &&
63-
utf16Range.end === end + 1
64-
);
51+
return false;
6552
});
6653

6754
if (templateIndex === -1) {
@@ -76,14 +63,6 @@ function convertAst(ast: File, templates: Template[]): void {
7663
);
7764
}
7865

79-
const index =
80-
node.innerComments?.[0] &&
81-
ast.comments?.indexOf(node.innerComments[0]);
82-
83-
if (ast.comments && index !== undefined && index >= 0) {
84-
ast.comments.splice(index, 1);
85-
}
86-
8766
convertNode(node, rawTemplate);
8867
}
8968
}

src/parse/preprocess.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export interface Template {
1717
};
1818
}
1919

20+
export const TEMPLATE_IDENTIFIER = '__p_x';
21+
2022
const PLACEHOLDER = '~';
2123

2224
/**
@@ -32,20 +34,20 @@ export function preprocessTemplateRange(
3234

3335
if (template.type === 'class-member') {
3436
// Replace with StaticBlock
35-
prefix = 'static{t:`';
37+
prefix = `static{${TEMPLATE_IDENTIFIER}\``;
3638
suffix = '`}';
3739
} else {
38-
// Replace with BlockStatement or ObjectExpression
39-
prefix = '{t:`';
40-
suffix = '`}';
40+
// Replace with a TaggedTemplateExpression
41+
prefix = `${TEMPLATE_IDENTIFIER}\``;
42+
suffix = '`';
4143

42-
const nextToken = sliceByteRange(code, template.range.endByte).match(/\S+/);
44+
// const nextToken = sliceByteRange(code, template.range.endByte).match(/\S+/);
4345

44-
if (nextToken && (nextToken[0] === 'as' || nextToken[0] === 'satisfies')) {
45-
// Replace with parenthesized ObjectExpression
46-
prefix = '(' + prefix;
47-
suffix = suffix + ')';
48-
}
46+
// if (nextToken && (nextToken[0] === 'as' || nextToken[0] === 'satisfies')) {
47+
// // Replace with parenthesized TaggedTemplateExpression
48+
// prefix = `({${TEMPLATE_IDENTIFIER}:\`` + prefix;
49+
// suffix = suffix + '})';
50+
// }
4951
}
5052

5153
// We need to replace forward slash with _something else_, because
@@ -54,7 +56,8 @@ export function preprocessTemplateRange(
5456

5557
const templateLength = template.range.endByte - template.range.startByte;
5658
const spaces =
57-
templateLength - getBuffer(contents).length - prefix.length - suffix.length;
59+
Math.max(templateLength - getBuffer(contents).length - prefix.length - suffix.length, 0);
60+
5861

5962
return replaceContents(code, {
6063
contents: [prefix, contents, ' '.repeat(spaces), suffix].join(''),

src/print/ambiguity.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export function fixPreviousPrint(
4141
print,
4242
args,
4343
);
44+
console.log('Printed with semi:false:', printedSemiFalse);
4445
const flat = flattenDoc(printedSemiFalse);
4546
const previousFlat = flattenDoc(previousTemplatePrinted);
4647
if (flat[0]?.startsWith(';') && previousFlat.at(-1) !== ';') {

src/print/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,10 @@ export const printer: Printer<Node | undefined> = {
108108
embedOptions as Options,
109109
);
110110

111+
111112
const printed = printTemplateTag(content);
113+
112114
saveCurrentPrintOnSiblingNode(path, printed);
113-
console.log(printed);
114115
return printed;
115116
} catch (error) {
116117
console.error(error);

src/types/glimmer.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ import type {
33
ExportDefaultDeclaration,
44
ExpressionStatement,
55
Node,
6-
ObjectExpression,
76
StaticBlock,
7+
TaggedTemplateExpression,
88
TSAsExpression,
99
} from '@babel/types';
1010

1111
import type { ContentTag } from '../utils/content-tag.js';
1212

1313
type GlimmerTemplateProperties = (
1414
| BlockStatement
15-
| ObjectExpression
15+
| TaggedTemplateExpression
1616
| StaticBlock
1717
) & {
1818
/**
@@ -34,7 +34,7 @@ type GlimmerTemplateProperties = (
3434
};
3535
};
3636

37-
type GlimmerTemplate = (BlockStatement | ObjectExpression | StaticBlock) &
37+
type GlimmerTemplate = (BlockStatement | TaggedTemplateExpression | StaticBlock) &
3838
GlimmerTemplateProperties;
3939

4040
/** Returns true if the node is a GlimmerTemplate. */
@@ -56,16 +56,19 @@ export function isGlimmerTemplateParent(
5656
): node is GlimmerTemplateParent {
5757
if (!node) return false;
5858

59-
return (
59+
const check = (
6060
isGlimmerStatementTS(node) ||
6161
isGlimmerExportDefaultDeclaration(node) ||
6262
isGlimmerExportDefaultDeclarationTS(node)
6363
);
64+
65+
66+
return check;
6467
}
6568

6669
type GlimmerStatementTS = ExpressionStatement & {
6770
expression: TSAsExpression & {
68-
expression: ObjectExpression & GlimmerTemplateProperties;
71+
expression: TaggedTemplateExpression & GlimmerTemplateProperties;
6972
};
7073
};
7174

@@ -77,16 +80,19 @@ type GlimmerStatementTS = ExpressionStatement & {
7780
* ```
7881
*/
7982
function isGlimmerStatementTS(node: Node): node is GlimmerStatementTS {
80-
return (
83+
const check = (
8184
node.type === 'ExpressionStatement' &&
8285
node.expression.type === 'TSAsExpression' &&
83-
node.expression.expression.type === 'ObjectExpression' &&
86+
node.expression.expression.type === 'TaggedTemplateExpression' &&
8487
isGlimmerTemplate(node.expression.expression)
8588
);
89+
90+
91+
return check;
8692
}
8793

8894
type GlimmerExportDefaultDeclaration = ExportDefaultDeclaration & {
89-
declaration: ObjectExpression & GlimmerTemplateProperties;
95+
declaration: TaggedTemplateExpression & GlimmerTemplateProperties;
9096
};
9197

9298
/**
@@ -101,14 +107,14 @@ function isGlimmerExportDefaultDeclaration(
101107
): node is GlimmerExportDefaultDeclaration {
102108
return (
103109
node.type === 'ExportDefaultDeclaration' &&
104-
node.declaration.type === 'ObjectExpression' &&
110+
node.declaration.type === 'TaggedTemplateExpression' &&
105111
isGlimmerTemplate(node.declaration)
106112
);
107113
}
108114

109115
type GlimmerExportDefaultDeclarationTS = ExportDefaultDeclaration & {
110116
declaration: TSAsExpression & {
111-
expression: ObjectExpression & GlimmerTemplateProperties;
117+
expression: TaggedTemplateExpression & GlimmerTemplateProperties;
112118
};
113119
};
114120

@@ -125,7 +131,7 @@ function isGlimmerExportDefaultDeclarationTS(
125131
return (
126132
node.type === 'ExportDefaultDeclaration' &&
127133
node.declaration.type === 'TSAsExpression' &&
128-
node.declaration.expression.type === 'ObjectExpression' &&
134+
node.declaration.expression.type === 'TaggedTemplateExpression' &&
129135
isGlimmerTemplate(node.declaration.expression)
130136
);
131137
}

tests/cases/gts/rendering-test.gts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@ module('Render with template tag', function (hooks) {
66
setupRenderingTest(hooks);
77

88
test('it has a template tag', async function (assert) {
9-
await render(<template>
9+
await render(
10+
<template>
1011
what
1112
</template>);
1213
assert.dom().hasText('what');
1314
});
1415

1516

1617
test('it has a template tag with a tag', async function (assert) {
17-
await render(<template>
18-
<h1>what</h1>
19-
</template>);
18+
await render(
19+
<template>
20+
<h1>what</h1>
21+
</template>
22+
);
2023
assert.dom().hasText('what');
2124
});
2225

@@ -43,4 +46,4 @@ module('Render with template tag', function (hooks) {
4346
</template>);
4447
assert.dom().hasText('what');
4548
});
46-
});
49+
});

tests/unit-tests/preprocess.test.ts

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,83 +3,52 @@ import { describe, expect, test } from 'vitest';
33
import {
44
codeToGlimmerAst,
55
preprocessTemplateRange,
6+
TEMPLATE_IDENTIFIER
67
} from '../../src/parse/preprocess.js';
78

89
const TEST_CASES = [
910
{
1011
code: '<template>hi</template>',
11-
expected: ['{t:`hi `}'],
12+
expected: [`${TEMPLATE_IDENTIFIER}\`hi \``],
1213
},
1314
{
1415
code: '<template>/* hi */</template>',
15-
expected: ['{t:`~* hi *~ `}'],
16+
expected: [`${TEMPLATE_IDENTIFIER}\`~* hi *~ \``],
1617
},
1718
{
1819
code: '<template><div>hi</div></template>',
19-
expected: ['{t:`<div>hi<~div> `}'],
20+
expected: [`${TEMPLATE_IDENTIFIER}\`<div>hi<~div> \``],
2021
},
2122
{
2223
code: '<template>{{#if true}}hi{{/if}}</template>',
23-
expected: ['{t:`{{#if true}}hi{{~if}} `}'],
24+
expected: [`${TEMPLATE_IDENTIFIER}\`{{#if true}}hi{{~if}} \``],
2425
},
2526
{
2627
code: '<template>////////////////</template>',
27-
expected: ['{t:`~~~~~~~~~~~~~~~~ `}'],
28+
expected: [`${TEMPLATE_IDENTIFIER}\`~~~~~~~~~~~~~~~~ \``],
2829
},
2930
{
3031
code: '<template>💩</template>',
31-
expected: ['{t:`💩 `}'],
32+
expected: [`${TEMPLATE_IDENTIFIER}\`💩 \``],
3233
},
3334
{
3435
code: 'const a = <template>foo</template>; const b = <template>bar</template>;',
3536
expected: [
36-
'const a = {t:`foo `}; const b = <template>bar</template>;',
37-
'const a = <template>foo</template>; const b = {t:`bar `};',
37+
`const a = ${TEMPLATE_IDENTIFIER}\`foo \`; const b = <template>bar</template>;`,
38+
`const a = <template>foo</template>; const b = ${TEMPLATE_IDENTIFIER}\`bar \`;`,
3839
],
3940
},
4041
{
4142
code: `const a = <template>💩💩💩💩💩💩💩</template>; const b = <template>💩</template>`,
4243
expected: [
43-
'const a = {t:`💩💩💩💩💩💩💩 `}; const b = <template>💩</template>',
44-
'const a = <template>💩💩💩💩💩💩💩</template>; const b = {t:`💩 `}',
44+
`const a = ${TEMPLATE_IDENTIFIER}\`💩💩💩💩💩💩💩 \`; const b = <template>💩</template>`,
45+
`const a = <template>💩💩💩💩💩💩💩</template>; const b = ${TEMPLATE_IDENTIFIER}\`💩 \``,
4546
],
4647
},
4748
{
4849
code: 'class Thing { <template>hello</template> }',
49-
expected: ['class Thing { static{t:`hello `} }'],
50-
},
51-
{
52-
code: `export default <template> Explicit default export module top level component. Explicit default export module top level component. Explicit default export module top level component. Explicit default export module top level component. Explicit default export module top level component. </template>
53-
/*AMBIGUOUS*/`,
54-
expected: [
55-
`export default {t:` +
56-
'` Explicit default export module top level component. Explicit default export module top level component. Explicit default export module top level component. Explicit default export module top level component. Explicit default export module top level component. `}' +
57-
`
58-
/*AMBIGUOUS*/`,
59-
],
60-
},
61-
{
62-
code: `class MyComponent
63-
extends Component {
64-
// prettier-ignore
65-
<template>
66-
67-
68-
<h1> Class top level template. Class top level template. Class top level template. Class top level template. Class top level template. </h1>
69-
</template>
70-
}`,
71-
expected: [
72-
`class MyComponent
73-
extends Component {
74-
// prettier-ignore
75-
static{t:\`
76-
77-
78-
<h1> Class top level template. Class top level template. Class top level template. Class top level template. Class top level template. <~h1>
79-
\`}
80-
}`,
81-
],
82-
},
50+
expected: [`class Thing { static{${TEMPLATE_IDENTIFIER}\`hello \`} }`],
51+
}
8352
];
8453
const FILE_NAME = 'foo.gts';
8554

0 commit comments

Comments
 (0)