From 60f0f17f995ce8897c0efca6065594ac1e972eb2 Mon Sep 17 00:00:00 2001 From: Isaac Lee <16869656+ijlee2@users.noreply.github.com> Date: Fri, 13 Jun 2025 05:29:35 +0200 Subject: [PATCH 1/7] refactor: Extracted ContentTag, parse(), Range --- src/parse/preprocess.ts | 11 +++-------- src/types/glimmer.ts | 5 +++-- src/utils/content-tag.ts | 22 ++++++++++++++++++++++ tests/helpers/ambiguous.ts | 6 ++---- 4 files changed, 30 insertions(+), 14 deletions(-) create mode 100644 src/utils/content-tag.ts diff --git a/src/parse/preprocess.ts b/src/parse/preprocess.ts index bfcab46b..3188fb85 100644 --- a/src/parse/preprocess.ts +++ b/src/parse/preprocess.ts @@ -1,12 +1,9 @@ -import { Preprocessor } from 'content-tag'; +import { parse, type Range } from '../utils/content-tag.js'; export interface Template { contents: string; type: string; - range: { - start: number; - end: number; - }; + range: Range; utf16Range: { start: number; end: number; @@ -93,11 +90,9 @@ export function preprocessTemplateRange( return replaceRange(code, template.range.start, template.range.end, total); } -const p = new Preprocessor(); - /** Pre-processes the template info, parsing the template content to Glimmer AST. */ export function codeToGlimmerAst(code: string, filename: string): Template[] { - const rawTemplates = p.parse(code, { filename }); + const rawTemplates = parse(code, { filename }); const templates: Template[] = rawTemplates.map((r) => ({ type: r.type, diff --git a/src/types/glimmer.ts b/src/types/glimmer.ts index 02bd049a..057062a6 100644 --- a/src/types/glimmer.ts +++ b/src/types/glimmer.ts @@ -7,7 +7,8 @@ import type { StaticBlock, TSAsExpression, } from '@babel/types'; -import type { Parsed as RawGlimmerTemplate } from 'content-tag'; + +import type { ContentTag } from '../utils/content-tag.js'; type GlimmerTemplateProperties = ( | BlockStatement @@ -28,7 +29,7 @@ type GlimmerTemplateProperties = ( extra: { isGlimmerTemplate: true; - template: RawGlimmerTemplate; + template: ContentTag; [key: string]: unknown; }; }; diff --git a/src/utils/content-tag.ts b/src/utils/content-tag.ts new file mode 100644 index 00000000..5905ee6a --- /dev/null +++ b/src/utils/content-tag.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/consistent-type-definitions, eslint-comments/disable-enable-pair, jsdoc/require-jsdoc, unicorn/prefer-export-from */ +import { + type Parsed as ContentTag, + Preprocessor, + type PreprocessorOptions, +} from 'content-tag'; + +type Range = { + end: number; + start: number; +}; + +export function parse( + file: string, + options?: PreprocessorOptions, +): ContentTag[] { + const preprocessor = new Preprocessor(); + + return preprocessor.parse(file, options); +} + +export type { ContentTag, Range }; diff --git a/tests/helpers/ambiguous.ts b/tests/helpers/ambiguous.ts index 6d6ac366..84a4f21d 100644 --- a/tests/helpers/ambiguous.ts +++ b/tests/helpers/ambiguous.ts @@ -1,7 +1,7 @@ -import { Preprocessor } from 'content-tag'; import { describe, expect, test } from 'vitest'; import type { Options } from '../../src/options.js'; +import { parse } from '../../src/utils/content-tag.js'; import type { TestCase } from '../helpers/cases.js'; import { getAllCases } from '../helpers/cases.js'; import { format } from '../helpers/format.js'; @@ -13,8 +13,6 @@ import type { Config } from './make-suite.js'; */ export const AMBIGUOUS_PLACEHOLDER = '/*AMBIGUOUS*/'; -const preprocessor = new Preprocessor(); - const AMBIGUOUS_EXPRESSIONS = [ '(oops) => {}', '(oh, no) => {}', @@ -96,7 +94,7 @@ async function behavesLikeFormattedAmbiguousCase( try { const result = await format(code, formatOptions); expect(result).toMatchSnapshot(); - preprocessor.parse(result); + parse(result); } catch (error: unknown) { // Some of the ambiguous cases are Syntax Errors when parsed const isSyntaxError = From c6b388000a588f52d767686c1a3912d1f17d11b6 Mon Sep 17 00:00:00 2001 From: Isaac Lee <16869656+ijlee2@users.noreply.github.com> Date: Fri, 13 Jun 2025 05:29:46 +0200 Subject: [PATCH 2/7] refactor: Extracted getBuffer() --- src/parse/preprocess.ts | 20 ++------------------ src/utils/content-tag.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/parse/preprocess.ts b/src/parse/preprocess.ts index 3188fb85..a1e03489 100644 --- a/src/parse/preprocess.ts +++ b/src/parse/preprocess.ts @@ -1,4 +1,4 @@ -import { parse, type Range } from '../utils/content-tag.js'; +import { getBuffer, parse, type Range } from '../utils/content-tag.js'; export interface Template { contents: string; @@ -10,19 +10,8 @@ export interface Template { }; } -const BufferMap: Map = new Map(); - const PLACEHOLDER = '~'; -function getBuffer(s: string): Buffer { - let buf = BufferMap.get(s); - if (!buf) { - buf = Buffer.from(s); - BufferMap.set(s, buf); - } - return buf; -} - /** Slice string using byte range */ function sliceByteRange(s: string, a: number, b?: number): string { const buf = getBuffer(s); @@ -35,11 +24,6 @@ function byteToCharIndex(s: string, byteOffset: number): number { return buf.subarray(0, byteOffset).toString().length; } -/** Calculate byte length */ -function byteLength(s: string): number { - return getBuffer(s).length; -} - function replaceRange( s: string, start: number, @@ -84,7 +68,7 @@ export function preprocessTemplateRange( const tplLength = template.range.end - template.range.start; const spaces = - tplLength - byteLength(content) - prefix.length - suffix.length; + tplLength - getBuffer(content).length - prefix.length - suffix.length; const total = prefix + content + ' '.repeat(spaces) + suffix; return replaceRange(code, template.range.start, template.range.end, total); diff --git a/src/utils/content-tag.ts b/src/utils/content-tag.ts index 5905ee6a..078ffbe3 100644 --- a/src/utils/content-tag.ts +++ b/src/utils/content-tag.ts @@ -10,6 +10,19 @@ type Range = { start: number; }; +const BufferMap = new Map(); + +export function getBuffer(string_: string): Buffer { + let buffer = BufferMap.get(string_); + + if (!buffer) { + buffer = Buffer.from(string_); + BufferMap.set(string_, buffer); + } + + return buffer; +} + export function parse( file: string, options?: PreprocessorOptions, From 862c1490a2447324e2965c46d2f3a62bd5a668ad Mon Sep 17 00:00:00 2001 From: Isaac Lee <16869656+ijlee2@users.noreply.github.com> Date: Fri, 13 Jun 2025 05:29:52 +0200 Subject: [PATCH 3/7] refactor: Extracted sliceByteRange() --- src/parse/preprocess.ts | 25 +++++++++---------------- src/utils/content-tag.ts | 10 ++++++++++ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/parse/preprocess.ts b/src/parse/preprocess.ts index a1e03489..894399f1 100644 --- a/src/parse/preprocess.ts +++ b/src/parse/preprocess.ts @@ -1,4 +1,9 @@ -import { getBuffer, parse, type Range } from '../utils/content-tag.js'; +import { + getBuffer, + parse, + type Range, + sliceByteRange, +} from '../utils/content-tag.js'; export interface Template { contents: string; @@ -12,18 +17,6 @@ export interface Template { const PLACEHOLDER = '~'; -/** Slice string using byte range */ -function sliceByteRange(s: string, a: number, b?: number): string { - const buf = getBuffer(s); - return buf.subarray(a, b).toString(); -} - -/** Converts byte index to js char index (utf16) */ -function byteToCharIndex(s: string, byteOffset: number): number { - const buf = getBuffer(s); - return buf.subarray(0, byteOffset).toString().length; -} - function replaceRange( s: string, start: number, @@ -53,7 +46,7 @@ export function preprocessTemplateRange( prefix = '{/*'; suffix = '*/}'; - const nextToken = code.slice(template.range.end).toString().match(/\S+/); + const nextToken = sliceByteRange(code, template.range.end).match(/\S+/); if (nextToken && (nextToken[0] === 'as' || nextToken[0] === 'satisfies')) { // Replace with parenthesized ObjectExpression @@ -84,8 +77,8 @@ export function codeToGlimmerAst(code: string, filename: string): Template[] { contentRange: r.contentRange, contents: r.contents, utf16Range: { - start: byteToCharIndex(code, r.range.start), - end: byteToCharIndex(code, r.range.end), + start: sliceByteRange(code, 0, r.range.start).length, + end: sliceByteRange(code, 0, r.range.end).length, }, })); diff --git a/src/utils/content-tag.ts b/src/utils/content-tag.ts index 078ffbe3..4affb51e 100644 --- a/src/utils/content-tag.ts +++ b/src/utils/content-tag.ts @@ -32,4 +32,14 @@ export function parse( return preprocessor.parse(file, options); } +export function sliceByteRange( + string_: string, + indexStart: number, + indexEnd?: number, +): string { + const buffer = getBuffer(string_); + + return buffer.slice(indexStart, indexEnd).toString(); +} + export type { ContentTag, Range }; From 94496896967b1a96dca5c8ff64815c77a2b85c26 Mon Sep 17 00:00:00 2001 From: Isaac Lee <16869656+ijlee2@users.noreply.github.com> Date: Fri, 13 Jun 2025 05:29:59 +0200 Subject: [PATCH 4/7] refactor: Extracted replaceContents() --- src/parse/preprocess.ts | 20 +++++++------------- src/utils/content-tag.ts | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/parse/preprocess.ts b/src/parse/preprocess.ts index 894399f1..a6660227 100644 --- a/src/parse/preprocess.ts +++ b/src/parse/preprocess.ts @@ -2,6 +2,7 @@ import { getBuffer, parse, type Range, + replaceContents, sliceByteRange, } from '../utils/content-tag.js'; @@ -17,15 +18,6 @@ export interface Template { const PLACEHOLDER = '~'; -function replaceRange( - s: string, - start: number, - end: number, - substitute: string, -): string { - return sliceByteRange(s, 0, start) + substitute + sliceByteRange(s, end); -} - /** * Replace the template with a parsable placeholder that takes up the same * range. @@ -57,14 +49,16 @@ export function preprocessTemplateRange( // We need to replace forward slash with _something else_, because // forward slash breaks the parsed templates. - const content = template.contents.replaceAll('/', PLACEHOLDER); + const contents = template.contents.replaceAll('/', PLACEHOLDER); const tplLength = template.range.end - template.range.start; const spaces = - tplLength - getBuffer(content).length - prefix.length - suffix.length; - const total = prefix + content + ' '.repeat(spaces) + suffix; + tplLength - getBuffer(contents).length - prefix.length - suffix.length; - return replaceRange(code, template.range.start, template.range.end, total); + return replaceContents(code, { + contents: [prefix, contents, ' '.repeat(spaces), suffix].join(''), + range: template.range, + }); } /** Pre-processes the template info, parsing the template content to Glimmer AST. */ diff --git a/src/utils/content-tag.ts b/src/utils/content-tag.ts index 4affb51e..96d2ac6a 100644 --- a/src/utils/content-tag.ts +++ b/src/utils/content-tag.ts @@ -32,6 +32,22 @@ export function parse( return preprocessor.parse(file, options); } +export function replaceContents( + file: string, + options: { + contents: string; + range: Range; + }, +): string { + const { contents, range } = options; + + return [ + sliceByteRange(file, 0, range.start), + contents, + sliceByteRange(file, range.end), + ].join(''); +} + export function sliceByteRange( string_: string, indexStart: number, From bef55d3265f70694dc5ffa05f642f691f328a0c6 Mon Sep 17 00:00:00 2001 From: Isaac Lee <16869656+ijlee2@users.noreply.github.com> Date: Fri, 13 Jun 2025 05:30:03 +0200 Subject: [PATCH 5/7] refactor: Sorted keys --- src/parse/preprocess.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/parse/preprocess.ts b/src/parse/preprocess.ts index a6660227..c9ee80c1 100644 --- a/src/parse/preprocess.ts +++ b/src/parse/preprocess.ts @@ -8,11 +8,11 @@ import { export interface Template { contents: string; - type: string; range: Range; + type: string; utf16Range: { - start: number; end: number; + start: number; }; } @@ -66,13 +66,13 @@ export function codeToGlimmerAst(code: string, filename: string): Template[] { const rawTemplates = parse(code, { filename }); const templates: Template[] = rawTemplates.map((r) => ({ - type: r.type, - range: r.range, contentRange: r.contentRange, contents: r.contents, + range: r.range, + type: r.type, utf16Range: { - start: sliceByteRange(code, 0, r.range.start).length, end: sliceByteRange(code, 0, r.range.end).length, + start: sliceByteRange(code, 0, r.range.start).length, }, })); @@ -97,5 +97,5 @@ export function preprocess( code = preprocessTemplateRange(template, code); } - return { templates, code }; + return { code, templates }; } From b2e643783fec4006ed15c0b44747cac869d7b0e6 Mon Sep 17 00:00:00 2001 From: Isaac Lee <16869656+ijlee2@users.noreply.github.com> Date: Fri, 13 Jun 2025 05:30:07 +0200 Subject: [PATCH 6/7] refactor: Renamed variables --- src/parse/preprocess.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parse/preprocess.ts b/src/parse/preprocess.ts index c9ee80c1..befed28f 100644 --- a/src/parse/preprocess.ts +++ b/src/parse/preprocess.ts @@ -51,9 +51,9 @@ export function preprocessTemplateRange( // forward slash breaks the parsed templates. const contents = template.contents.replaceAll('/', PLACEHOLDER); - const tplLength = template.range.end - template.range.start; + const templateLength = template.range.end - template.range.start; const spaces = - tplLength - getBuffer(contents).length - prefix.length - suffix.length; + templateLength - getBuffer(contents).length - prefix.length - suffix.length; return replaceContents(code, { contents: [prefix, contents, ' '.repeat(spaces), suffix].join(''), @@ -63,9 +63,9 @@ export function preprocessTemplateRange( /** Pre-processes the template info, parsing the template content to Glimmer AST. */ export function codeToGlimmerAst(code: string, filename: string): Template[] { - const rawTemplates = parse(code, { filename }); + const contentTags = parse(code, { filename }); - const templates: Template[] = rawTemplates.map((r) => ({ + const templates: Template[] = contentTags.map((r) => ({ contentRange: r.contentRange, contents: r.contents, range: r.range, From f41a3c4126c16516bd54d1088d3b173a20e1ae73 Mon Sep 17 00:00:00 2001 From: Isaac Lee <16869656+ijlee2@users.noreply.github.com> Date: Fri, 13 Jun 2025 05:30:11 +0200 Subject: [PATCH 7/7] refactor: Destructured objects --- src/parse/preprocess.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/parse/preprocess.ts b/src/parse/preprocess.ts index befed28f..7a06f11d 100644 --- a/src/parse/preprocess.ts +++ b/src/parse/preprocess.ts @@ -65,16 +65,22 @@ export function preprocessTemplateRange( export function codeToGlimmerAst(code: string, filename: string): Template[] { const contentTags = parse(code, { filename }); - const templates: Template[] = contentTags.map((r) => ({ - contentRange: r.contentRange, - contents: r.contents, - range: r.range, - type: r.type, - utf16Range: { - end: sliceByteRange(code, 0, r.range.end).length, - start: sliceByteRange(code, 0, r.range.start).length, - }, - })); + const templates: Template[] = contentTags.map((contentTag) => { + const { contentRange, contents, range, type } = contentTag; + + const utf16Range = { + end: sliceByteRange(code, 0, range.end).length, + start: sliceByteRange(code, 0, range.start).length, + }; + + return { + contentRange, + contents, + range, + type, + utf16Range, + }; + }); return templates; }