diff --git a/packages/core/__tests__/transform/rewrite.test.ts b/packages/core/__tests__/transform/rewrite.test.ts index 986d7b6b4..badbf2237 100644 --- a/packages/core/__tests__/transform/rewrite.test.ts +++ b/packages/core/__tests__/transform/rewrite.test.ts @@ -33,6 +33,39 @@ describe('Transform: rewriteModule', () => { `); }); + test('handles the $ character', () => { + let script = { + filename: 'test.gts', + contents: ';', + }; + + let transformedModule = rewriteModule(ts, { script }, emberTemplateImportsEnvironment); + + expect(transformedModule?.errors).toEqual([]); + expect(transformedModule?.transformedContents).toMatchInlineSnapshot(` + "export default ({} as typeof import("@glint/environment-ember-template-imports/-private/dsl")).templateExpression(function(__glintRef__, __glintDSL__: typeof import("@glint/environment-ember-template-imports/-private/dsl")) { + __glintDSL__.emitContent(__glintDSL__.resolveOrReturn(dollarAmount)()); + __glintRef__; __glintDSL__; + });" + `); + }); + + test('handles the ` character', () => { + let script = { + filename: 'test.gts', + contents: ';', + }; + + let transformedModule = rewriteModule(ts, { script }, emberTemplateImportsEnvironment); + + expect(transformedModule?.errors).toEqual([]); + expect(transformedModule?.transformedContents).toMatchInlineSnapshot(` + "export default ({} as typeof import("@glint/environment-ember-template-imports/-private/dsl")).templateExpression(function(__glintRef__, __glintDSL__: typeof import("@glint/environment-ember-template-imports/-private/dsl")) { + __glintRef__; __glintDSL__; + });" + `); + }); + test('with a class with type parameters', () => { let script = { filename: 'test.ts', diff --git a/packages/environment-ember-template-imports/-private/environment/preprocess.ts b/packages/environment-ember-template-imports/-private/environment/preprocess.ts index cae1553f4..cf2690984 100644 --- a/packages/environment-ember-template-imports/-private/environment/preprocess.ts +++ b/packages/environment-ember-template-imports/-private/environment/preprocess.ts @@ -29,7 +29,7 @@ export const preprocess: GlintExtensionPreprocess = (source, pat let templates = p.parse(source, { filename: path }); let templateLocations: Array = []; - let segments: Array = []; + let contents = ''; let sourceOffsetBytes = 0; let deltaBytes = 0; @@ -41,46 +41,48 @@ export const preprocess: GlintExtensionPreprocess = (source, pat let startTagOffsetBytes = template.startRange.start; let endTagOffsetBytes = template.endRange.start; let transformedStartBytes = startTagOffsetBytes - deltaBytes; - /** * TODO: we want content-tag to manage all this for us, as managing indicies * can be error-prone. * * SEE: https://github.com/embroider-build/content-tag/issues/39#issuecomment-1832443310 */ - let prefixingSegment = sourceBuffer.slice(sourceOffsetBytes, startTagOffsetBytes); - segments.push(prefixingSegment.toString()); - segments.push(TEMPLATE_START); + let prefixingSegment = sourceBuffer.subarray(sourceOffsetBytes, startTagOffsetBytes); + contents = contents.concat(prefixingSegment.toString()); + contents = contents.concat(TEMPLATE_START); // For TEMPLATE_START & TEMPLATE_END, characters === bytes deltaBytes += startTagLengthBytes - TEMPLATE_START.length; let transformedEnd = endTagOffsetBytes - deltaBytes + TEMPLATE_END.length; - - let templateContentSegment = sourceBuffer.slice( + let templateContentSegment = sourceBuffer.subarray( startTagOffsetBytes + startTagLengthBytes, endTagOffsetBytes ); - segments.push(templateContentSegment.toString()); - segments.push(TEMPLATE_END); + let templateContentSegmentString = templateContentSegment.toString(); + let escapedTemplateContentSegment = templateContentSegmentString + .replace(/\$/g, '\\$') + .replace(/`/g, '\\`'); + deltaBytes += templateContentSegmentString.length - escapedTemplateContentSegment.length; + + contents = contents.concat(escapedTemplateContentSegment); + contents = contents.concat(TEMPLATE_END); deltaBytes += endTagLengthBytes - TEMPLATE_END.length; sourceOffsetBytes = endTagOffsetBytes + endTagLengthBytes; - templateLocations.push({ startTagOffset: byteToCharIndex(source, startTagOffsetBytes), endTagOffset: byteToCharIndex(source, endTagOffsetBytes), startTagLength: byteToCharIndex(source, startTagLengthBytes), endTagLength: byteToCharIndex(source, endTagLengthBytes), - transformedStart: byteToCharIndex(source, transformedStartBytes), - transformedEnd: byteToCharIndex(source, transformedEnd), + transformedStart: byteToCharIndex(contents, transformedStartBytes), + transformedEnd: byteToCharIndex(contents, transformedEnd), }); } - segments.push(sourceBuffer.slice(sourceOffsetBytes).toString()); - + contents = contents.concat(sourceBuffer.subarray(sourceOffsetBytes).toString()); return { - contents: segments.join(''), + contents, data: { templateLocations, }, @@ -89,7 +91,7 @@ export const preprocess: GlintExtensionPreprocess = (source, pat function byteToCharIndex(str: string, byteOffset: number): number { const buf = getBuffer(str); - return buf.slice(0, byteOffset).toString().length; + return buf.subarray(0, byteOffset).toString().length; } const BufferMap = new Map(); diff --git a/packages/environment-ember-template-imports/__tests__/transformation.test.ts b/packages/environment-ember-template-imports/__tests__/transformation.test.ts index 7ca361b13..ba24410b5 100644 --- a/packages/environment-ember-template-imports/__tests__/transformation.test.ts +++ b/packages/environment-ember-template-imports/__tests__/transformation.test.ts @@ -27,6 +27,40 @@ describe('Environment: ETI', () => { }); }); + test('handles multi-byte characters', () => { + let source = [ + `const a = ;`, + `const b = ;`, + `const c = "‘foo’";`, + `const d = ;`, + ].join('\n'); + + let result = preprocess(source, 'index.gts'); + + expect(result.contents).toMatchInlineSnapshot(` + "const a = [___T\`one 💩\`]; + const b = [___T\`two\`]; + const c = "‘foo’"; + const d = [___T\`four\`];" + `); + }); + + test('handles the $ character', () => { + let source = ';'; + + let result = preprocess(source, 'index.gts'); + + expect(result.contents).toMatchInlineSnapshot('"[___T`\\${{dollarAmount}}`];"'); + }); + + test('handles the ` character', () => { + let source = ';'; + + let result = preprocess(source, 'index.gts'); + + expect(result.contents).toMatchInlineSnapshot('"[___T`\\`code\\``];"'); + }); + test('multiple templates', () => { let source = stripIndent`