From 9551f6910490c6afb74ed791de80ba8e7c8a7036 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Mon, 27 May 2024 14:04:47 +0200 Subject: [PATCH 1/2] Escape `${` in template string --- .../src/generator/grammar-serializer.ts | 6 +++- .../test/generator/grammar-serializer.test.ts | 33 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/langium-cli/src/generator/grammar-serializer.ts b/packages/langium-cli/src/generator/grammar-serializer.ts index 03aacec08..8bf0e8ee8 100644 --- a/packages/langium-cli/src/generator/grammar-serializer.ts +++ b/packages/langium-cli/src/generator/grammar-serializer.ts @@ -41,9 +41,13 @@ export function serializeGrammar(services: LangiumCoreServices, grammars: Gramma }); // The json serializer returns strings with \n line delimiter by default // We need to translate these line endings to the OS specific line ending - const json = normalizeEOL(serializedGrammar + let json = normalizeEOL(serializedGrammar .replace(/\\/g, '\\\\') .replace(new RegExp(delimiter, 'g'), '\\' + delimiter)); + if (!production) { + // Escape ${ in template strings + json = json.replace(/\${/g, '\\${'); + } return expandToNode` let loaded${grammar.name}Grammar: Grammar | undefined; diff --git a/packages/langium-cli/test/generator/grammar-serializer.test.ts b/packages/langium-cli/test/generator/grammar-serializer.test.ts index ff924d5d1..30116a413 100644 --- a/packages/langium-cli/test/generator/grammar-serializer.test.ts +++ b/packages/langium-cli/test/generator/grammar-serializer.test.ts @@ -6,15 +6,21 @@ import type { LangiumConfig } from '../../src/package-types.js'; import { EmptyFileSystem, URI, type Grammar } from 'langium'; -import { describe, expect, test } from 'vitest'; +import { afterEach, describe, expect, test } from 'vitest'; import { RelativePath } from '../../src/package-types.js'; import { serializeGrammar } from '../../src/generator/grammar-serializer.js'; import { createLangiumGrammarServices } from 'langium/grammar'; import { expandToString } from 'langium/generate'; +import { clearDocuments } from 'langium/test'; const grammarServices = createLangiumGrammarServices(EmptyFileSystem); describe('Grammar serializer', () => { + + afterEach(() => { + clearDocuments(grammarServices.shared); + }); + test('should include comments of AST elements', async () => { // arrange const config: LangiumConfig = { @@ -45,4 +51,29 @@ describe('Grammar serializer', () => { expect(moduleString).toMatch('"$comment": "/**\\\\n * This is the entry rule\\\\n */"'); }); + test('should escape template strings in development mode', async () => { + // arrange + const config: LangiumConfig = { + projectName: 'Magic', + languages: [], + [RelativePath]: '/path/to/magic', + }; + + const grammarText = expandToString` + grammar Test + entry Model: name='\${'; + `; + + const document = grammarServices.shared.workspace.LangiumDocumentFactory.fromString(grammarText, URI.file('test.langium')); + grammarServices.shared.workspace.LangiumDocuments.addDocument(document); + await grammarServices.shared.workspace.DocumentBuilder.build([document]); + const grammar = document.parseResult.value; + + // act + const moduleString = serializeGrammar(grammarServices.grammar, [grammar], config); + + // assert + expect(moduleString).toMatch('"value": "\\${"'); + }); + }); From 579456ac2322183cde9e727878b1b2432ef32699 Mon Sep 17 00:00:00 2001 From: Mark Sujew Date: Wed, 5 Jun 2024 10:47:28 +0000 Subject: [PATCH 2/2] Add better serializer tests --- .../test/generator/grammar-serializer.test.ts | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/langium-cli/test/generator/grammar-serializer.test.ts b/packages/langium-cli/test/generator/grammar-serializer.test.ts index 30116a413..19959d933 100644 --- a/packages/langium-cli/test/generator/grammar-serializer.test.ts +++ b/packages/langium-cli/test/generator/grammar-serializer.test.ts @@ -56,12 +56,13 @@ describe('Grammar serializer', () => { const config: LangiumConfig = { projectName: 'Magic', languages: [], + mode: 'development', [RelativePath]: '/path/to/magic', }; const grammarText = expandToString` grammar Test - entry Model: name='\${'; + entry Model: template='\${' backtick='\`' single="'"; `; const document = grammarServices.shared.workspace.LangiumDocumentFactory.fromString(grammarText, URI.file('test.langium')); @@ -73,7 +74,41 @@ describe('Grammar serializer', () => { const moduleString = serializeGrammar(grammarServices.grammar, [grammar], config); // assert + // Escapes `${` sequence correctly expect(moduleString).toMatch('"value": "\\${"'); + // Escapes the "`" character correctly + expect(moduleString).toMatch('"value": "\\`'); + // Does not escape single quotes + expect(moduleString).toMatch('"value": "\'"'); + }); + + test('should escape single quotes in production mode', async () => { + // arrange + const config: LangiumConfig = { + projectName: 'Magic', + languages: [], + mode: 'production', + [RelativePath]: '/path/to/magic', + }; + + const grammarText = expandToString` + grammar Test + entry Model: single="'" backtick='\`'; + `; + + const document = grammarServices.shared.workspace.LangiumDocumentFactory.fromString(grammarText, URI.file('test.langium')); + grammarServices.shared.workspace.LangiumDocuments.addDocument(document); + await grammarServices.shared.workspace.DocumentBuilder.build([document]); + const grammar = document.parseResult.value; + + // act + const moduleString = serializeGrammar(grammarServices.grammar, [grammar], config); + + // assert + // Escapes single quote character correctly + expect(moduleString).toMatch('"value":"\\\'"'); + // Does not escape backticks + expect(moduleString).toMatch('"value":"`"'); }); });