Skip to content

Commit 1d08873

Browse files
committed
Bundle.formatPattern
1 parent 94dae4d commit 1d08873

File tree

110 files changed

+1918
-1337
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

110 files changed

+1918
-1337
lines changed

.eslintrc

Lines changed: 0 additions & 24 deletions
This file was deleted.

.gitattributes

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
*.ftl eol=lf
2-
test/fixtures/crlf.ftl eol=crlf
3-
test/fixtures/cr.ftl eol=cr
1+
**.ftl eol=lf

.prettierrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"bracketSpacing": false,
3+
"trailingComma": "es5",
4+
"printWidth": 100,
5+
"tabWidth": 4
6+
}

.vscode/settings.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"editor.formatOnSave": false,
3+
"[javascript]": {
4+
"editor.formatOnSave": false
5+
},
6+
"[json]": {
7+
"editor.formatOnSave": true
8+
},
9+
"[typescript]": {
10+
"editor.formatOnSave": true
11+
},
12+
"editor.codeActionsOnSave": {
13+
"source.organizeImports": true
14+
}
15+
}

.vscode/tasks.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"type": "npm",
6+
"script": "watch",
7+
"problemMatcher": [
8+
"$tsc-watch"
9+
],
10+
"isBackground": true
11+
}
12+
]
13+
}

bin/ebnf.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ function from_stdin() {
5252
const lines = [];
5353

5454
rl.on("line", line => lines.push(line));
55-
rl.on("close", () =>
56-
print_ebnf(lines.join("\n") + "\n"));
55+
rl.on("close", () => print_ebnf(lines.join("\n") + "\n"));
5756
}
5857

5958
function from_file(file_path) {

bin/format.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import parseArgs from "minimist";
2+
import {Resource} from "../format/resource";
3+
import {fromFile, fromStdin} from "../lib/input";
4+
import {formatResource} from "../lib/tools";
5+
6+
const argv = parseArgs(process.argv.slice(2), {
7+
boolean: ["help", "group"],
8+
alias: {
9+
help: "h",
10+
group: "g",
11+
},
12+
});
13+
14+
if (argv.help) {
15+
exitHelp(0);
16+
}
17+
18+
const [filePath] = argv._;
19+
20+
if (filePath === "-") {
21+
fromStdin(print);
22+
} else if (filePath) {
23+
fromFile(filePath, print);
24+
} else {
25+
exitHelp(1);
26+
}
27+
28+
function exitHelp(exitCode: number) {
29+
console.log(`
30+
Usage: node format.js [OPTIONS] <FILE>
31+
32+
When FILE is "-", read text from stdin.
33+
34+
Examples:
35+
36+
node format.js path/to/file.ftl
37+
cat path/to/file.ftl | node format.js -
38+
39+
Options:
40+
41+
-h, --help Display help and quit.
42+
`);
43+
process.exit(exitCode);
44+
}
45+
46+
function print(source: string) {
47+
let resource = new Resource(source);
48+
let results = formatResource(resource, new Map());
49+
console.log(JSON.stringify(results, null, 4));
50+
}

bin/parse.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ function parse_stdin() {
5252
const lines = [];
5353

5454
rl.on("line", line => lines.push(line));
55-
rl.on("close", () =>
56-
parse(lines.join("\n") + "\n"));
55+
rl.on("close", () => parse(lines.join("\n") + "\n"));
5756
}
5857

5958
function parse_file(file_path) {
@@ -66,9 +65,9 @@ function parse_file(file_path) {
6665
});
6766
}
6867

69-
7068
function parse(ftl) {
7169
Resource.run(ftl).fold(
7270
ast => console.log(JSON.stringify(ast, null, 4)),
73-
err => console.error(err));
71+
err => console.error(err)
72+
);
7473
}

format/ast.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
export enum NodeType {
2+
Identifer = "Identifier",
3+
VariableReference = "VariableReference",
4+
MessageReference = "MessageReference",
5+
SelectExpression = "SelectExpression",
6+
TextElement = "TextElement",
7+
Placeable = "Placeable",
8+
Variant = "Variant",
9+
Pattern = "Pattern",
10+
Message = "Message",
11+
Attribute = "Attribute",
12+
GroupComment = "GroupComment",
13+
Resource = "Resource",
14+
}
15+
16+
export interface SyntaxNode {
17+
readonly type: NodeType;
18+
}
19+
20+
export interface Identifier extends SyntaxNode {
21+
readonly type: NodeType.Identifer;
22+
readonly name: string;
23+
}
24+
25+
export interface VariableReference extends SyntaxNode {
26+
readonly type: NodeType.VariableReference;
27+
readonly id: Identifier;
28+
}
29+
30+
export interface MessageReference extends SyntaxNode {
31+
readonly type: NodeType.MessageReference;
32+
readonly id: Identifier;
33+
readonly attribute: Identifier | null;
34+
}
35+
36+
export interface SelectExpression extends SyntaxNode {
37+
readonly type: NodeType.SelectExpression;
38+
readonly selector: InlineExpression;
39+
readonly variants: Array<Variant>;
40+
}
41+
42+
export type InlineExpression = VariableReference | MessageReference;
43+
44+
export type Expression = InlineExpression | SelectExpression;
45+
46+
export interface TextElement extends SyntaxNode {
47+
readonly type: NodeType.TextElement;
48+
readonly value: string;
49+
}
50+
51+
export interface Placeable extends SyntaxNode {
52+
readonly type: NodeType.Placeable;
53+
readonly expression: Expression;
54+
}
55+
56+
export type PatternElement = TextElement | Placeable;
57+
58+
export interface Variant extends SyntaxNode {
59+
readonly type: NodeType.Variant;
60+
readonly key: Identifier;
61+
readonly value: Pattern;
62+
readonly default: boolean;
63+
}
64+
65+
export interface Pattern extends SyntaxNode {
66+
readonly type: NodeType.Pattern;
67+
readonly elements: Array<PatternElement>;
68+
}
69+
70+
export interface Message extends SyntaxNode {
71+
readonly type: NodeType.Message;
72+
readonly id: Identifier;
73+
readonly value: Pattern | null;
74+
readonly attributes: Array<Attribute>;
75+
}
76+
77+
export interface Attribute extends SyntaxNode {
78+
readonly type: NodeType.Attribute;
79+
readonly id: Identifier;
80+
readonly value: Pattern;
81+
}
82+
83+
export interface GroupComment extends SyntaxNode {
84+
readonly type: NodeType.GroupComment;
85+
readonly content: string;
86+
}
87+
88+
export type Entry = Message | GroupComment;
89+
90+
export interface Resource extends SyntaxNode {
91+
readonly type: NodeType.Resource;
92+
readonly body: Array<Entry>;
93+
}

format/bundle.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import {NodeType, Pattern} from "./ast";
2+
import {ScopeError} from "./error";
3+
import {Message} from "./message";
4+
import {Resource} from "./resource";
5+
import {Scope} from "./scope";
6+
import {Value} from "./value";
7+
8+
export interface FormatResult {
9+
readonly value: string;
10+
readonly errors: Array<ScopeError>;
11+
}
12+
13+
export class Bundle {
14+
public readonly messages: Map<string, Message> = new Map();
15+
16+
addResource(resource: Resource) {
17+
for (let message of resource.body) {
18+
if (message.type === NodeType.Message) {
19+
let attributes: Record<string, Pattern> = {};
20+
for (let attribute of message.attributes) {
21+
attributes[attribute.id.name] = attribute.value;
22+
}
23+
this.messages.set(message.id.name, <Message>{
24+
id: message.id.name,
25+
value: message.value,
26+
attributes,
27+
});
28+
}
29+
}
30+
}
31+
32+
hasMessage(id: string) {
33+
return this.messages.has(id);
34+
}
35+
36+
getMessage(id: string) {
37+
return this.messages.get(id);
38+
}
39+
40+
formatPattern(pattern: Pattern, variables: Map<string, Value>): FormatResult {
41+
let scope = new Scope(this.messages, variables);
42+
let value = scope.resolvePattern(pattern).format(scope);
43+
return {value, errors: scope.errors};
44+
}
45+
}

0 commit comments

Comments
 (0)