diff --git a/fluent-syntax/src/serializer.js b/fluent-syntax/src/serializer.js index 7cd20de30..46cab981f 100644 --- a/fluent-syntax/src/serializer.js +++ b/fluent-syntax/src/serializer.js @@ -18,6 +18,10 @@ export default class FluentSerializer { } serialize(resource) { + if (resource.type !== 'Resource') { + throw new Error(`Unknown resource type: ${resource.type}`); + } + const parts = []; if (resource.comment) { @@ -66,6 +70,10 @@ export default class FluentSerializer { throw new Error(`Unknown entry type: ${entry.type}`); } } + + serializeExpression(expr) { + return serializeExpression(expr); + } } @@ -176,7 +184,13 @@ function serializePlaceable(placeable) { case 'Placeable': return `{${serializePlaceable(expr)}}`; case 'SelectExpression': - return `{${serializeSelectExpression(expr)}}`; + // Special-case select expression to control the whitespace around the + // opening and the closing brace. + return expr.expression + // A select expression with a selector. + ? `{ ${serializeSelectExpression(expr)}}` + // A variant list without a selector. + : `{${serializeSelectExpression(expr)}}`; default: return `{ ${serializeExpression(expr)} }`; } @@ -199,6 +213,8 @@ function serializeExpression(expr) { return serializeVariantExpression(expr); case 'CallExpression': return serializeCallExpression(expr); + case 'SelectExpression': + return serializeSelectExpression(expr); default: throw new Error(`Unknown expression type: ${expr.type}`); } @@ -229,7 +245,7 @@ function serializeSelectExpression(expr) { const parts = []; if (expr.expression) { - const selector = ` ${serializeExpression(expr.expression)} ->`; + const selector = `${serializeExpression(expr.expression)} ->`; parts.push(selector); } diff --git a/fluent-syntax/test/serializer_test.js b/fluent-syntax/test/serializer_test.js index 4f65442cf..a71681017 100644 --- a/fluent-syntax/test/serializer_test.js +++ b/fluent-syntax/test/serializer_test.js @@ -1,17 +1,36 @@ import assert from 'assert'; import { ftl } from './util'; -import { parse, serialize } from '../src'; +import { FluentParser, FluentSerializer } from '../src'; -function pretty(text) { - const res = parse(text); - return serialize(res, { - withJunk: false +suite('Serialize resource', function() { + let pretty; + + setup(function() { + const parser = new FluentParser(); + const serializer = new FluentSerializer({ + withJunk: false + }); + + pretty = function pretty(text) { + const res = parser.parse(text); + return serializer.serialize(res); + } + }); + + test('invalid resource', function() { + const serializer = new FluentSerializer(); + assert.throws( + () => serializer.serialize(null), + /Cannot read property 'type'/ + ); + assert.throws( + () => serializer.serialize({}), + /Unknown resource type/ + ); }); -} -suite('Serializer', function() { test('simple message', function() { const input = ftl` foo = Foo @@ -400,3 +419,90 @@ suite('Serializer', function() { assert.equal(pretty(input), input); }); }); + +suite('Serialize expression', function() { + let pretty; + + setup(function() { + const parser = new FluentParser(); + const serializer = new FluentSerializer({ + withJunk: false + }); + + pretty = function pretty(text) { + const {value: {elements: [placeable]}} = parser.parseEntry(text); + return serializer.serializeExpression(placeable.expression); + } + }); + + test('invalid expression', function() { + const serializer = new FluentSerializer(); + assert.throws( + () => serializer.serializeExpression(null), + /Cannot read property 'type'/ + ); + assert.throws( + () => serializer.serializeExpression({}), + /Unknown expression type/ + ); + }); + + test('string expression', function() { + const input = ftl` + foo = { "str" } + `; + assert.equal(pretty(input), '"str"'); + }); + + test('number expression', function() { + const input = ftl` + foo = { 3 } + `; + assert.equal(pretty(input), '3'); + }); + + test('message reference', function() { + const input = ftl` + foo = { msg } + `; + assert.equal(pretty(input), 'msg'); + }); + + test('external argument', function() { + const input = ftl` + foo = { $ext } + `; + assert.equal(pretty(input), '$ext'); + }); + + test('attribute expression', function() { + const input = ftl` + foo = { msg.attr } + `; + assert.equal(pretty(input), 'msg.attr'); + }); + + test('variant expression', function() { + const input = ftl` + foo = { -msg[variant] } + `; + assert.equal(pretty(input), '-msg[variant]'); + }); + + test('call expression', function() { + const input = ftl` + foo = { BUILTIN(3.14, kwarg: "value") } + `; + assert.equal(pretty(input), 'BUILTIN(3.14, kwarg: "value")'); + }); + + test('select expression', function() { + const input = ftl` + foo = + { $num -> + *[one] One + } + `; + assert.equal(pretty(input), '$num ->\n *[one] One\n'); + }); +});