Skip to content

Commit 38ae684

Browse files
committed
chore: Updated content-tag
1 parent 1addba6 commit 38ae684

File tree

5 files changed

+100
-71
lines changed

5 files changed

+100
-71
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
},
4949
"dependencies": {
5050
"@babel/core": "^7.27.4",
51-
"content-tag": "^3.1.2"
51+
"content-tag": "^4.0.0"
5252
},
5353
"devDependencies": {
5454
"@babel/eslint-parser": "^7.27.5",

src/parse/preprocess.ts

Lines changed: 37 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,24 @@
1-
import { Preprocessor } from 'content-tag';
1+
import {
2+
getBuffer,
3+
parse,
4+
type Range,
5+
replaceContents,
6+
sliceByteRange,
7+
} from '../utils/content-tag.js';
28

39
export interface Template {
10+
contentRange: Range;
411
contents: string;
5-
type: string;
6-
range: {
7-
start: number;
8-
end: number;
9-
};
12+
range: Range;
13+
type: 'class-member' | 'expression';
1014
utf16Range: {
11-
start: number;
1215
end: number;
16+
start: number;
1317
};
1418
}
1519

16-
const BufferMap: Map<string, Buffer> = new Map();
17-
1820
const PLACEHOLDER = '~';
1921

20-
function getBuffer(s: string): Buffer {
21-
let buf = BufferMap.get(s);
22-
if (!buf) {
23-
buf = Buffer.from(s);
24-
BufferMap.set(s, buf);
25-
}
26-
return buf;
27-
}
28-
29-
/** Slice string using byte range */
30-
function sliceByteRange(s: string, a: number, b?: number): string {
31-
const buf = getBuffer(s);
32-
return buf.subarray(a, b).toString();
33-
}
34-
35-
/** Converts byte index to js char index (utf16) */
36-
function byteToCharIndex(s: string, byteOffset: number): number {
37-
const buf = getBuffer(s);
38-
return buf.subarray(0, byteOffset).toString().length;
39-
}
40-
41-
/** Calculate byte length */
42-
function byteLength(s: string): number {
43-
return getBuffer(s).length;
44-
}
45-
46-
function replaceRange(
47-
s: string,
48-
start: number,
49-
end: number,
50-
substitute: string,
51-
): string {
52-
return sliceByteRange(s, 0, start) + substitute + sliceByteRange(s, end);
53-
}
54-
5522
/**
5623
* Replace the template with a parsable placeholder that takes up the same
5724
* range.
@@ -72,7 +39,7 @@ export function preprocessTemplateRange(
7239
prefix = '{/*';
7340
suffix = '*/}';
7441

75-
const nextToken = code.slice(template.range.end).toString().match(/\S+/);
42+
const nextToken = sliceByteRange(code, template.range.endByte).match(/\S+/);
7643

7744
if (nextToken && (nextToken[0] === 'as' || nextToken[0] === 'satisfies')) {
7845
// Replace with parenthesized ObjectExpression
@@ -83,32 +50,38 @@ export function preprocessTemplateRange(
8350

8451
// We need to replace forward slash with _something else_, because
8552
// forward slash breaks the parsed templates.
86-
const content = template.contents.replaceAll('/', PLACEHOLDER);
53+
const contents = template.contents.replaceAll('/', PLACEHOLDER);
8754

88-
const tplLength = template.range.end - template.range.start;
55+
const templateLength = template.range.endByte - template.range.startByte;
8956
const spaces =
90-
tplLength - byteLength(content) - prefix.length - suffix.length;
91-
const total = prefix + content + ' '.repeat(spaces) + suffix;
57+
templateLength - getBuffer(contents).length - prefix.length - suffix.length;
9258

93-
return replaceRange(code, template.range.start, template.range.end, total);
59+
return replaceContents(code, {
60+
contents: [prefix, contents, ' '.repeat(spaces), suffix].join(''),
61+
range: template.range,
62+
});
9463
}
9564

96-
const p = new Preprocessor();
97-
9865
/** Pre-processes the template info, parsing the template content to Glimmer AST. */
9966
export function codeToGlimmerAst(code: string, filename: string): Template[] {
100-
const rawTemplates = p.parse(code, { filename });
101-
102-
const templates: Template[] = rawTemplates.map((r) => ({
103-
type: r.type,
104-
range: r.range,
105-
contentRange: r.contentRange,
106-
contents: r.contents,
107-
utf16Range: {
108-
start: byteToCharIndex(code, r.range.start),
109-
end: byteToCharIndex(code, r.range.end),
110-
},
111-
}));
67+
const contentTags = parse(code, { filename });
68+
69+
const templates: Template[] = contentTags.map((contentTag) => {
70+
const { contentRange, contents, range, type } = contentTag;
71+
72+
const utf16Range = {
73+
end: sliceByteRange(code, 0, range.endByte).length,
74+
start: sliceByteRange(code, 0, range.startByte).length,
75+
};
76+
77+
return {
78+
contentRange,
79+
contents,
80+
range,
81+
type,
82+
utf16Range,
83+
};
84+
});
11285

11386
return templates;
11487
}

src/types/glimmer.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import type {
77
StaticBlock,
88
TSAsExpression,
99
} from '@babel/types';
10-
import type { Parsed as RawGlimmerTemplate } from 'content-tag';
10+
11+
import type { ContentTag } from '../utils/content-tag.js';
1112

1213
type GlimmerTemplateProperties = (
1314
| BlockStatement
@@ -28,7 +29,7 @@ type GlimmerTemplateProperties = (
2829

2930
extra: {
3031
isGlimmerTemplate: true;
31-
template: RawGlimmerTemplate;
32+
template: ContentTag;
3233
[key: string]: unknown;
3334
};
3435
};

src/utils/content-tag.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* eslint-disable jsdoc/require-jsdoc, unicorn/prefer-export-from, unicorn/prevent-abbreviations */
2+
import {
3+
type Parsed as ContentTag,
4+
Preprocessor,
5+
type PreprocessorOptions,
6+
type Range,
7+
} from 'content-tag';
8+
9+
const BufferMap = new Map<string, Buffer>();
10+
11+
export function getBuffer(str: string): Buffer {
12+
let buffer = BufferMap.get(str);
13+
14+
if (!buffer) {
15+
buffer = Buffer.from(str);
16+
BufferMap.set(str, buffer);
17+
}
18+
19+
return buffer;
20+
}
21+
22+
export function sliceByteRange(
23+
str: string,
24+
indexStart: number,
25+
indexEnd?: number,
26+
): string {
27+
const buffer = getBuffer(str);
28+
29+
return buffer.slice(indexStart, indexEnd).toString();
30+
}
31+
32+
export function parse(
33+
file: string,
34+
options?: PreprocessorOptions,
35+
): ContentTag[] {
36+
const preprocessor = new Preprocessor();
37+
38+
return preprocessor.parse(file, options);
39+
}
40+
41+
export function replaceContents(
42+
file: string,
43+
options: {
44+
contents: string;
45+
range: Range;
46+
},
47+
): string {
48+
const { contents, range } = options;
49+
50+
return [
51+
sliceByteRange(file, 0, range.startByte),
52+
contents,
53+
sliceByteRange(file, range.endByte),
54+
].join('');
55+
}
56+
57+
export type { ContentTag, Range };

tests/helpers/ambiguous.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Preprocessor } from 'content-tag';
21
import { describe, expect, test } from 'vitest';
32

43
import type { Options } from '../../src/options.js';
4+
import { parse } from '../../src/utils/content-tag.js';
55
import type { TestCase } from '../helpers/cases.js';
66
import { getAllCases } from '../helpers/cases.js';
77
import { format } from '../helpers/format.js';
@@ -13,8 +13,6 @@ import type { Config } from './make-suite.js';
1313
*/
1414
export const AMBIGUOUS_PLACEHOLDER = '/*AMBIGUOUS*/';
1515

16-
const preprocessor = new Preprocessor();
17-
1816
const AMBIGUOUS_EXPRESSIONS = [
1917
'(oops) => {}',
2018
'(oh, no) => {}',
@@ -96,7 +94,7 @@ async function behavesLikeFormattedAmbiguousCase(
9694
try {
9795
const result = await format(code, formatOptions);
9896
expect(result).toMatchSnapshot();
99-
preprocessor.parse(result);
97+
parse(result);
10098
} catch (error: unknown) {
10199
// Some of the ambiguous cases are Syntax Errors when parsed
102100
const isSyntaxError =

0 commit comments

Comments
 (0)