From 8ffeb4bc31782a7c636c689bb4da82af64a9ecc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 5 Jun 2019 16:53:17 +0200 Subject: [PATCH 01/36] Rename all files to *.js; invoke with node -r esm --- README.md | 2 +- bin/{ebnf.mjs => ebnf.js} | 8 ++++---- bin/{parse.mjs => parse.js} | 8 ++++---- lib/README.md | 14 +++++++------- lib/{combinators.mjs => combinators.js} | 6 +++--- lib/{ebnf.mjs => ebnf.js} | 11 +++++------ lib/{mappers.mjs => mappers.js} | 2 +- lib/{parser.mjs => parser.js} | 4 ++-- lib/{result.mjs => result.js} | 0 lib/{serializer.mjs => serializer.js} | 0 lib/{stream.mjs => stream.js} | 0 lib/{visitor.mjs => visitor.js} | 0 lib/{walker.mjs => walker.js} | 0 package-lock.json | 7 ++++++- package.json | 13 +++++++------ spec/CHANGELOG.md | 6 +++--- spec/fluent.ebnf | 2 +- spec/valid.md | 4 ++-- syntax/{abstract.mjs => abstract.js} | 8 ++++---- syntax/{ast.mjs => ast.js} | 0 syntax/{grammar.mjs => grammar.js} | 10 +++++----- test/{bench.mjs => bench.js} | 16 ++++++++-------- test/{ebnf.mjs => ebnf.js} | 6 +++--- test/fixtures/Makefile | 2 +- test/{literals.mjs => literals.js} | 7 +++---- test/{parser.mjs => parser.js} | 6 +++--- test/{suite.mjs => suite.js} | 2 +- test/{util.mjs => util.js} | 0 28 files changed, 74 insertions(+), 70 deletions(-) rename bin/{ebnf.mjs => ebnf.js} (83%) rename bin/{parse.mjs => parse.js} (83%) rename lib/{combinators.mjs => combinators.js} (96%) rename lib/{ebnf.mjs => ebnf.js} (60%) rename lib/{mappers.mjs => mappers.js} (96%) rename lib/{parser.mjs => parser.js} (92%) rename lib/{result.mjs => result.js} (100%) rename lib/{serializer.mjs => serializer.js} (100%) rename lib/{stream.mjs => stream.js} (100%) rename lib/{visitor.mjs => visitor.js} (100%) rename lib/{walker.mjs => walker.js} (100%) rename syntax/{abstract.mjs => abstract.js} (96%) rename syntax/{ast.mjs => ast.js} (100%) rename syntax/{grammar.mjs => grammar.js} (98%) rename test/{bench.mjs => bench.js} (81%) rename test/{ebnf.mjs => ebnf.js} (89%) rename test/{literals.mjs => literals.js} (94%) rename test/{parser.mjs => parser.js} (94%) rename test/{suite.mjs => suite.js} (96%) rename test/{util.mjs => util.js} (100%) diff --git a/README.md b/README.md index 590d7e2..15d3380 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ validate your work: npm test # Test the parser against JSON AST fixtures. npm run lint # Lint the parser code. - npm run generate:ebnf # Generate the EBNF from syntax/grammar.mjs. + npm run generate:ebnf # Generate the EBNF from syntax/grammar.js. npm run generate:fixtures # Generate test fixtures (FTL → JSON AST). npm run build:guide # Build the HTML version of the Guide. diff --git a/bin/ebnf.mjs b/bin/ebnf.js similarity index 83% rename from bin/ebnf.mjs rename to bin/ebnf.js index b755b70..34ef6ae 100644 --- a/bin/ebnf.mjs +++ b/bin/ebnf.js @@ -1,7 +1,7 @@ import fs from "fs"; import readline from "readline"; import parse_args from "minimist"; -import ebnf from "../lib/ebnf.mjs"; +import ebnf from "../lib/ebnf.js"; const argv = parse_args(process.argv.slice(2), { boolean: ["help"], @@ -26,14 +26,14 @@ if (file_path === "-") { function exit_help(exit_code) { console.log(` - Usage: node --experimental-modules ebnf.mjs [OPTIONS] + Usage: node -r esm ebnf.js [OPTIONS] When FILE is "-", read text from stdin. Examples: - node --experimental-modules ebnf.mjs path/to/grammar.mjs - cat path/to/grammar.mjs | node --experimental-modules ebnf.mjs - + node -r esm ebnf.js path/to/grammar.js + cat path/to/grammar.js | node -r esm ebnf.js - Options: diff --git a/bin/parse.mjs b/bin/parse.js similarity index 83% rename from bin/parse.mjs rename to bin/parse.js index e71b3a1..ba5ee1f 100644 --- a/bin/parse.mjs +++ b/bin/parse.js @@ -1,7 +1,7 @@ import fs from "fs"; import readline from "readline"; import parse_args from "minimist"; -import {Resource} from "../syntax/grammar.mjs"; +import {Resource} from "../syntax/grammar.js"; const argv = parse_args(process.argv.slice(2), { boolean: ["help"], @@ -26,14 +26,14 @@ if (file_path === "-") { function exit_help(exit_code) { console.log(` - Usage: node --experimental-modules parse.mjs [OPTIONS] + Usage: node -r esm parse.js [OPTIONS] When FILE is "-", read text from stdin. Examples: - node --experimental-modules parse.mjs path/to/file.ftl - cat path/to/file.ftl | node --experimental-modules parse.mjs - + node -r esm parse.js path/to/file.ftl + cat path/to/file.ftl | node -r esm parse.js - Options: diff --git a/lib/README.md b/lib/README.md index 0872778..0ce9faf 100644 --- a/lib/README.md +++ b/lib/README.md @@ -1,19 +1,19 @@ # Fluent Specification Support Code -This directory contains support code for the parser-combinator underlying the syntax code, as well as the code to transform `grammar.mjs` to `fluent.ebnf`. The combination allows for a succinct formulation of the syntax in `grammar.mjs` and a readable representation of that in `fluent.ebnf`. +This directory contains support code for the parser-combinator underlying the syntax code, as well as the code to transform `grammar.js` to `fluent.ebnf`. The combination allows for a succinct formulation of the syntax in `grammar.js` and a readable representation of that in `fluent.ebnf`. ## Parser-Combinator -**`parser.mjs`** is the base parser class, **`stream.mjs`** holds a iterator over the strings to be parsed. +**`parser.js`** is the base parser class, **`stream.js`** holds a iterator over the strings to be parsed. -**`combinators.mjs`** holds the actual basic grammar productions and logical combinations of grammar productions. +**`combinators.js`** holds the actual basic grammar productions and logical combinations of grammar productions. -Both use `Success` and `Failure` from **`result.mjs`** to pass success and failure conditions forward. +Both use `Success` and `Failure` from **`result.js`** to pass success and failure conditions forward. -After the definition of grammar productions, the utilities in **`mappers.mjs`** are used to concat, flatten, and extract the matched data to prepare it for AST generation. +After the definition of grammar productions, the utilities in **`mappers.js`** are used to concat, flatten, and extract the matched data to prepare it for AST generation. ## EBNF Generation -The `fluent.ebnf` is created by parsing the `grammar.mjs` and transpilation to an EBNF file. The `babylon` JavaScript parser is used to load a Babel AST. +The `fluent.ebnf` is created by parsing the `grammar.js` and transpilation to an EBNF file. The `babylon` JavaScript parser is used to load a Babel AST. -**`ebnf.mjs`** is the top-level entry point used by `bin/ebnf.mjs`. It uses the iteration logic from **`walker.mjs`** to go over the Babel AST and to extract the information relevant to the EBNF via the Visitor in **`visitor.mjs`**. The resulting data is then serialiazed to EBNF via **`serializer.mjs`**. +**`ebnf.js`** is the top-level entry point used by `bin/ebnf.js`. It uses the iteration logic from **`walker.js`** to go over the Babel AST and to extract the information relevant to the EBNF via the Visitor in **`visitor.js`**. The resulting data is then serialiazed to EBNF via **`serializer.js`**. diff --git a/lib/combinators.mjs b/lib/combinators.js similarity index 96% rename from lib/combinators.mjs rename to lib/combinators.js index c0da4b6..5f00fb9 100644 --- a/lib/combinators.mjs +++ b/lib/combinators.js @@ -1,6 +1,6 @@ -import Parser from "./parser.mjs"; -import {Success, Failure} from "./result.mjs"; -import {join} from "./mappers.mjs"; +import Parser from "./parser.js"; +import {Success, Failure} from "./result.js"; +import {join} from "./mappers.js"; export function defer(fn) { // Parsers may be defined as defer(() => parser) to avoid cyclic diff --git a/lib/ebnf.mjs b/lib/ebnf.js similarity index 60% rename from lib/ebnf.mjs rename to lib/ebnf.js index 9cbae73..5b92a4e 100644 --- a/lib/ebnf.mjs +++ b/lib/ebnf.js @@ -1,11 +1,11 @@ -import babylon from "babylon"; -import walk from "./walker.mjs"; -import visitor from "./visitor.mjs"; -import serialize from "./serializer.mjs"; +import {parse} from "babylon"; +import walk from "./walker.js"; +import visitor from "./visitor.js"; +import serialize from "./serializer.js"; export default function ebnf(source, min_name_length = 0) { - let grammar_ast = babylon.parse(source, {sourceType: "module"}); + let grammar_ast = parse(source, {sourceType: "module"}); let rules = walk(grammar_ast, visitor); let state = { max_name_length: Math.max( @@ -16,4 +16,3 @@ function ebnf(source, min_name_length = 0) { .map(rule => serialize(rule, state)) .join(""); } - diff --git a/lib/mappers.mjs b/lib/mappers.js similarity index 96% rename from lib/mappers.mjs rename to lib/mappers.js index b9945d7..6c923fb 100644 --- a/lib/mappers.mjs +++ b/lib/mappers.js @@ -1,4 +1,4 @@ -import {Abstract} from "./parser.mjs"; +import {Abstract} from "./parser.js"; // Flatten a list up to a given depth. // This is useful when a parser uses nested sequences and repeats. diff --git a/lib/parser.mjs b/lib/parser.js similarity index 92% rename from lib/parser.mjs rename to lib/parser.js index fed57d8..73f89e3 100644 --- a/lib/parser.mjs +++ b/lib/parser.js @@ -1,5 +1,5 @@ -import Stream from "./stream.mjs"; -import {Failure} from "./result.mjs"; +import Stream from "./stream.js"; +import {Failure} from "./result.js"; export class Abstract { constructor(value) { diff --git a/lib/result.mjs b/lib/result.js similarity index 100% rename from lib/result.mjs rename to lib/result.js diff --git a/lib/serializer.mjs b/lib/serializer.js similarity index 100% rename from lib/serializer.mjs rename to lib/serializer.js diff --git a/lib/stream.mjs b/lib/stream.js similarity index 100% rename from lib/stream.mjs rename to lib/stream.js diff --git a/lib/visitor.mjs b/lib/visitor.js similarity index 100% rename from lib/visitor.mjs rename to lib/visitor.js diff --git a/lib/walker.mjs b/lib/walker.js similarity index 100% rename from lib/walker.mjs rename to lib/walker.js diff --git a/package-lock.json b/package-lock.json index bd66aba..9769d6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "fluent-spec", - "version": "0.5.0", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -504,6 +504,11 @@ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "dev": true }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "espree": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", diff --git a/package.json b/package.json index da30f18..9a90806 100644 --- a/package.json +++ b/package.json @@ -4,19 +4,19 @@ "version": "1.0.0", "private": true, "scripts": { - "bench": "node --experimental-modules --harmony-async-iteration ./test/bench.mjs ./test/benchmarks/gecko_strings.ftl", + "bench": "node -r esm --harmony-async-iteration ./test/bench.js ./test/benchmarks/gecko_strings.ftl", "build:guide": "gitbook build guide build/guide", "build": "npm run --silent build:guide", "ci": "npm run --silent lint && npm test && npm run --silent test:ebnf", "clean": "rm -rf build", "deploy": "gh-pages -d build", - "generate:ebnf": "node --experimental-modules bin/ebnf.mjs ./syntax/grammar.mjs > ./spec/fluent.ebnf", + "generate:ebnf": "node -r esm bin/ebnf.js ./syntax/grammar.js > ./spec/fluent.ebnf", "generate:fixtures": "make -sC test/fixtures", "generate": "npm run --silent generate:ebnf && npm run --silent generate:fixtures", - "lint": "eslint **/*.mjs", - "test:ebnf": "node --experimental-modules test/ebnf.mjs ./syntax/grammar.mjs ./spec/fluent.ebnf", - "test:fixtures": "node --experimental-modules test/parser.mjs ./test/fixtures", - "test:unit": "node --experimental-modules test/literals.mjs", + "lint": "eslint **/*.js", + "test:ebnf": "node -r esm test/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", + "test:fixtures": "node -r esm test/parser.js ./test/fixtures", + "test:unit": "node -r esm test/literals.js", "test": "npm run --silent test:fixtures && npm run --silent test:unit" }, "homepage": "https://projectfluent.org", @@ -41,6 +41,7 @@ "json-diff": "^0.5.2" }, "dependencies": { + "esm": "^3.2.25", "minimist": "^1.2.0" } } diff --git a/spec/CHANGELOG.md b/spec/CHANGELOG.md index b4458a9..9e7d4d4 100644 --- a/spec/CHANGELOG.md +++ b/spec/CHANGELOG.md @@ -233,7 +233,7 @@ compatibility strategy for future releases. The `Function` production and its corresponding AST node have been removed. The logic validating that function names are all upper-case has - been moved to `abstract.mjs`. + been moved to `abstract.js`. ## 0.7.0 (October 15, 2018) @@ -282,11 +282,11 @@ compatibility strategy for future releases. correctness at a cost of reduced performance. The ASDL description of the AST has been removed in favor of - `syntax/ast.mjs` which defines the actual AST nodes returned by the + `syntax/ast.js` which defines the actual AST nodes returned by the reference parser. The EBNF is now auto-generated from the reference parser's - `syntax/grammar.mjs` file. It provides an easy to read overview of the + `syntax/grammar.js` file. It provides an easy to read overview of the grammar and will continue to be updated in the future. Going forward, all changes to the grammar will be implemented in the diff --git a/spec/fluent.ebnf b/spec/fluent.ebnf index abc12b1..ec29c8c 100644 --- a/spec/fluent.ebnf +++ b/spec/fluent.ebnf @@ -49,7 +49,7 @@ block_placeable ::= blank_block blank_inline? inline_placeable /* Rules for validating expressions in Placeables and as selectors of * SelectExpressions are documented in spec/valid.md and enforced in - * syntax/abstract.mjs. */ + * syntax/abstract.js. */ InlineExpression ::= StringLiteral | NumberLiteral | FunctionReference diff --git a/spec/valid.md b/spec/valid.md index f374dbd..c21aff9 100644 --- a/spec/valid.md +++ b/spec/valid.md @@ -4,12 +4,12 @@ Fluent Syntax distinguishes between well-formed and valid resources. - Well-formed Fluent resources conform to the Fluent grammar described by the Fluent EBNF (`spec/fluent.ebnf`). The EBNF is auto-generated from - `syntax/grammar.mjs`. + `syntax/grammar.js`. - Valid Fluent resources must be well-formed and are additionally checked for semantic correctness. The validation process may reject syntax which is well-formed. The validation rules are expressed in code in - `syntax/abstract.mjs`. + `syntax/abstract.js`. For example, the `message.attr(param: "value")` syntax is _well-formed_. `message.attr` is an `AttributeExpression` which may be the callee of a diff --git a/syntax/abstract.mjs b/syntax/abstract.js similarity index 96% rename from syntax/abstract.mjs rename to syntax/abstract.js index 708803e..1ca1de3 100644 --- a/syntax/abstract.mjs +++ b/syntax/abstract.js @@ -1,12 +1,12 @@ /* * AST Validation * - * The parse result of the grammar.mjs parser is a well-formed AST which is + * The parse result of the grammar.js parser is a well-formed AST which is * validated according to the rules documented in `spec/valid.md`. */ -import * as FTL from "./ast.mjs"; -import {always, never} from "../lib/combinators.mjs"; +import * as FTL from "./ast.js"; +import {always, never} from "../lib/combinators.js"; export function list_into(Type) { switch (Type) { @@ -155,7 +155,7 @@ function attach_comments(acc, cur) { } // Remove the largest common indentation from a list of elements of a Pattern. -// The indents are parsed in grammar.mjs and passed to abstract.mjs as string +// The indents are parsed in grammar.js and passed to abstract.js as string // primitives along with other PatternElements. function dedent(elements) { // Calculate the maximum common indent. diff --git a/syntax/ast.mjs b/syntax/ast.js similarity index 100% rename from syntax/ast.mjs rename to syntax/ast.js diff --git a/syntax/grammar.mjs b/syntax/grammar.js similarity index 98% rename from syntax/grammar.mjs rename to syntax/grammar.js index 4836eb8..6f94bda 100644 --- a/syntax/grammar.mjs +++ b/syntax/grammar.js @@ -1,12 +1,12 @@ -import * as FTL from "./ast.mjs"; -import {list_into, into} from "./abstract.mjs"; +import * as FTL from "./ast.js"; +import {list_into, into} from "./abstract.js"; import { always, and, charset, defer, either, eof, maybe, not, regex, repeat, repeat1, sequence, string -} from "../lib/combinators.mjs"; +} from "../lib/combinators.js"; import { element_at, flatten, join, keep_abstract, mutate, print, prune -} from "../lib/mappers.mjs"; +} from "../lib/mappers.js"; /* ----------------------------------------------------- */ /* An FTL file defines a Resource consisting of Entries. */ @@ -191,7 +191,7 @@ let block_placeable = defer(() => /* ------------------------------------------------------------------- */ /* Rules for validating expressions in Placeables and as selectors of * SelectExpressions are documented in spec/valid.md and enforced in - * syntax/abstract.mjs. */ + * syntax/abstract.js. */ let InlineExpression = defer(() => either( StringLiteral, diff --git a/test/bench.mjs b/test/bench.js similarity index 81% rename from test/bench.mjs rename to test/bench.js index b8c1da0..0a411c1 100644 --- a/test/bench.mjs +++ b/test/bench.js @@ -2,17 +2,17 @@ import fs from "fs"; import perf from "perf_hooks"; const {PerformanceObserver, performance} = perf; -import FluentSyntax from "fluent-syntax"; -import FluentRuntime from "fluent"; -import {Resource} from "../syntax/grammar.mjs"; -import {readfile} from "./util.mjs"; +import {parse} from "fluent-syntax"; +import {_parse} from "fluent"; +import {Resource} from "../syntax/grammar.js"; +import {readfile} from "./util.js"; let args = process.argv.slice(2); if (args.length < 1 || 2 < args.length) { console.error( - "Usage: node --experimental-modules --harmony-async-iteration " + - "bench.mjs FTL_FILE [SAMPLE SIZE = 30]"); + "Usage: node -r esm --harmony-async-iteration " + + "bench.js FTL_FILE [SAMPLE SIZE = 30]"); process.exit(1); } @@ -31,8 +31,8 @@ async function main(ftl_file, sample_size = 30) { let subjects = new Map([ ["Reference", new Subject("Reference", () => Resource.run(ftl))], - ["Tooling", new Subject("Tooling", () => FluentSyntax.parse(ftl))], - ["Runtime", new Subject("Runtime", () => FluentRuntime._parse(ftl))], + ["Tooling", new Subject("Tooling", () => parse(ftl))], + ["Runtime", new Subject("Runtime", () => _parse(ftl))], ]); new PerformanceObserver(items => { diff --git a/test/ebnf.mjs b/test/ebnf.js similarity index 89% rename from test/ebnf.mjs rename to test/ebnf.js index 19a1a60..2fcbd11 100644 --- a/test/ebnf.mjs +++ b/test/ebnf.js @@ -1,13 +1,13 @@ import color from "cli-color"; import difflib from "difflib"; -import ebnf from "../lib/ebnf.mjs"; -import {readfile, PASS, FAIL} from "./util.mjs"; +import ebnf from "../lib/ebnf.js"; +import {readfile, PASS, FAIL} from "./util.js"; let args = process.argv.slice(2); if (args.length !== 2) { console.error( - "Usage: node --experimental-modules ebnf.mjs " + + "Usage: node -r esm ebnf.js " + "GRAMMAR_FILE EXPECTED_EBNF"); process.exit(1); } diff --git a/test/fixtures/Makefile b/test/fixtures/Makefile index 49c98e7..3caf947 100644 --- a/test/fixtures/Makefile +++ b/test/fixtures/Makefile @@ -5,7 +5,7 @@ all: $(AST_FIXTURES) .PHONY: $(AST_FIXTURES) $(AST_FIXTURES): %.json: %.ftl - @node --experimental-modules ../../bin/parse.mjs $< \ + @node -r esm ../../bin/parse.js $< \ 2> /dev/null \ 1> $@; @echo "$< → $@" diff --git a/test/literals.mjs b/test/literals.js similarity index 94% rename from test/literals.mjs rename to test/literals.js index be8a580..7b0fd69 100644 --- a/test/literals.mjs +++ b/test/literals.js @@ -1,9 +1,9 @@ /* eslint quotes: "off" */ -import suite from "./suite.mjs"; -import {StringLiteral, NumberLiteral} from "../syntax/grammar.mjs"; +import suite from "./suite.js"; +import {StringLiteral, NumberLiteral} from "../syntax/grammar.js"; if (process.argv.length > 2) { - console.error("Usage: node --experimental-modules literals.mjs"); + console.error("Usage: node -r esm literals.js"); process.exit(1); } @@ -71,4 +71,3 @@ suite(tester => { test(NumberLiteral.run("-01.0300"), {value: -1.03, precision: 4}); }; }); - diff --git a/test/parser.mjs b/test/parser.js similarity index 94% rename from test/parser.mjs rename to test/parser.js index f71e7e2..e1e0bc1 100644 --- a/test/parser.mjs +++ b/test/parser.js @@ -1,13 +1,13 @@ import assert from "assert"; import path from "path"; -import {Resource} from "../syntax/grammar.mjs"; -import {readdir, readfile, diff, PASS, FAIL} from "./util.mjs"; +import {Resource} from "../syntax/grammar.js"; +import {readdir, readfile, diff, PASS, FAIL} from "./util.js"; const fixtures_dir = process.argv[2]; if (!fixtures_dir) { console.error( - "Usage: node --experimental-modules parser.mjs FIXTURE"); + "Usage: node -r esm parser.js FIXTURE"); process.exit(1); } diff --git a/test/suite.mjs b/test/suite.js similarity index 96% rename from test/suite.mjs rename to test/suite.js index 94de928..6743162 100644 --- a/test/suite.mjs +++ b/test/suite.js @@ -1,6 +1,6 @@ import assert from "assert"; import color from "cli-color"; -import {diff, PASS, FAIL} from "./util.mjs"; +import {diff, PASS, FAIL} from "./util.js"; export default function suite(fn) { diff --git a/test/util.mjs b/test/util.js similarity index 100% rename from test/util.mjs rename to test/util.js From cb05d148868a6cde833bf4b77b5311ad8fd999ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 5 Jun 2019 17:19:08 +0200 Subject: [PATCH 02/36] Use Prettier --- .eslintrc | 24 -- .prettierrc | 6 + bin/ebnf.js | 3 +- bin/parse.js | 7 +- lib/combinators.js | 67 ++-- lib/ebnf.js | 11 +- lib/mappers.js | 31 +- lib/parser.js | 8 +- lib/serializer.js | 16 +- lib/stream.js | 9 +- lib/visitor.js | 23 +- lib/walker.js | 3 +- package-lock.json | 980 +-------------------------------------------- package.json | 7 +- syntax/abstract.js | 87 ++-- syntax/ast.js | 13 +- syntax/grammar.js | 434 ++++++++------------ test/bench.js | 10 +- test/ebnf.js | 14 +- test/literals.js | 11 +- test/parser.js | 14 +- test/suite.js | 7 +- 22 files changed, 327 insertions(+), 1458 deletions(-) delete mode 100644 .eslintrc create mode 100644 .prettierrc diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index f0a1a00..0000000 --- a/.eslintrc +++ /dev/null @@ -1,24 +0,0 @@ -{ - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "env": { - "es6": true, - "node": true - }, - "rules": { - "brace-style": ["error", "1tbs"], - "eqeqeq": ["error", "always"], - "indent": ["error", 4, { - "CallExpression": {"arguments": 1}, - "MemberExpression": "off", - "SwitchCase": 1 - }], - "quotes": ["error", "double"], - "semi": ["error", "always"], - "space-infix-ops": ["error"], - "no-tabs": "error", - "no-undef": "error" - } -} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..ac8d76d --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "bracketSpacing": false, + "trailingComma": "es5", + "printWidth": 100, + "tabWidth": 4 +} diff --git a/bin/ebnf.js b/bin/ebnf.js index 34ef6ae..355f9a3 100644 --- a/bin/ebnf.js +++ b/bin/ebnf.js @@ -52,8 +52,7 @@ function from_stdin() { const lines = []; rl.on("line", line => lines.push(line)); - rl.on("close", () => - print_ebnf(lines.join("\n") + "\n")); + rl.on("close", () => print_ebnf(lines.join("\n") + "\n")); } function from_file(file_path) { diff --git a/bin/parse.js b/bin/parse.js index ba5ee1f..88f7e89 100644 --- a/bin/parse.js +++ b/bin/parse.js @@ -52,8 +52,7 @@ function parse_stdin() { const lines = []; rl.on("line", line => lines.push(line)); - rl.on("close", () => - parse(lines.join("\n") + "\n")); + rl.on("close", () => parse(lines.join("\n") + "\n")); } function parse_file(file_path) { @@ -66,9 +65,9 @@ function parse_file(file_path) { }); } - function parse(ftl) { Resource.run(ftl).fold( ast => console.log(JSON.stringify(ast, null, 4)), - err => console.error(err)); + err => console.error(err) + ); } diff --git a/lib/combinators.js b/lib/combinators.js index 5f00fb9..dee0b94 100644 --- a/lib/combinators.js +++ b/lib/combinators.js @@ -5,15 +5,15 @@ import {join} from "./mappers.js"; export function defer(fn) { // Parsers may be defined as defer(() => parser) to avoid cyclic // dependecies. - return new Parser(stream => - fn().run(stream)); + return new Parser(stream => fn().run(stream)); } export function string(str) { return new Parser(stream => stream.head(str.length) === str ? new Success(str, stream.move(str.length)) - : new Failure(`${str} not found`, stream)); + : new Failure(`${str} not found`, stream) + ); } export function regex(re) { @@ -38,16 +38,16 @@ export function eof() { return new Parser(stream => stream.head() === Symbol.for("eof") ? new Success(stream.head(), stream.move(1)) - : new Failure("not at EOF", stream)); + : new Failure("not at EOF", stream) + ); } export function lookahead(parser) { return new Parser(stream => parser .run(stream) - .fold( - value => new Success(value, stream), - value => new Failure(value, stream))); + .fold(value => new Success(value, stream), value => new Failure(value, stream)) + ); } export function not(parser) { @@ -56,13 +56,14 @@ export function not(parser) { .run(stream) .fold( (value, tail) => new Failure("not failed", stream), - (value, tail) => new Success(null, stream))); + (value, tail) => new Success(null, stream) + ) + ); } export function and(...parsers) { const final = parsers.pop(); - return sequence(...parsers.map(lookahead), final) - .map(results => results[results.length - 1]); + return sequence(...parsers.map(lookahead), final).map(results => results[results.length - 1]); } export function either(...parsers) { @@ -91,45 +92,43 @@ export function maybe(parser) { .run(stream) .fold( (value, tail) => new Success(value, tail), - (value, tail) => new Success(null, stream))); + (value, tail) => new Success(null, stream) + ) + ); } export function append(p1, p2) { - return p1.chain(values => - p2.map(value => values.concat([value]))); + return p1.chain(values => p2.map(value => values.concat([value]))); } export function after(prefix, parser) { - return sequence(prefix, parser) - .map(([left, value]) => value); + return sequence(prefix, parser).map(([left, value]) => value); } export function sequence(...parsers) { - return parsers.reduce( - (acc, parser) => append(acc, parser), - always([])); + return parsers.reduce((acc, parser) => append(acc, parser), always([])); } export function repeat(parser) { return new Parser(stream => - parser - .run(stream) - .fold( - (value, tail) => - repeat(parser) - .map(rest => [value].concat(rest)) - .run(tail), - (value, tail) => new Success([], stream))); + parser.run(stream).fold( + (value, tail) => + repeat(parser) + .map(rest => [value].concat(rest)) + .run(tail), + (value, tail) => new Success([], stream) + ) + ); } export function repeat1(parser) { return new Parser(stream => - parser - .run(stream) - .fold( - (value, tail) => - repeat(parser) - .map(rest => [value].concat(rest)) - .run(tail), - (value, tail) => new Failure("repeat1 failed", stream))); + parser.run(stream).fold( + (value, tail) => + repeat(parser) + .map(rest => [value].concat(rest)) + .run(tail), + (value, tail) => new Failure("repeat1 failed", stream) + ) + ); } diff --git a/lib/ebnf.js b/lib/ebnf.js index 5b92a4e..ecb6cc7 100644 --- a/lib/ebnf.js +++ b/lib/ebnf.js @@ -3,16 +3,11 @@ import walk from "./walker.js"; import visitor from "./visitor.js"; import serialize from "./serializer.js"; -export default -function ebnf(source, min_name_length = 0) { +export default function ebnf(source, min_name_length = 0) { let grammar_ast = parse(source, {sourceType: "module"}); let rules = walk(grammar_ast, visitor); let state = { - max_name_length: Math.max( - min_name_length, - ...rules.map(rule => rule.name.length)), + max_name_length: Math.max(min_name_length, ...rules.map(rule => rule.name.length)), }; - return rules - .map(rule => serialize(rule, state)) - .join(""); + return rules.map(rule => serialize(rule, state)).join(""); } diff --git a/lib/mappers.js b/lib/mappers.js index 6c923fb..b689e52 100644 --- a/lib/mappers.js +++ b/lib/mappers.js @@ -2,38 +2,29 @@ import {Abstract} from "./parser.js"; // Flatten a list up to a given depth. // This is useful when a parser uses nested sequences and repeats. -export const flatten = depth => - list => list.reduce( - (acc, cur) => acc.concat( - !Array.isArray(cur) || depth === 1 - ? cur - : flatten(depth - 1)(cur)), - []); +export const flatten = depth => list => + list.reduce( + (acc, cur) => + acc.concat(!Array.isArray(cur) || depth === 1 ? cur : flatten(depth - 1)(cur)), + [] + ); // Mutate an object by merging properties of another object into it. -export const mutate = state => - obj => Object.assign(obj, state); +export const mutate = state => obj => Object.assign(obj, state); // Join the list of parsed values into a string. -export const join = list => - list - .filter(value => value !== Symbol.for("eof")) - .join(""); +export const join = list => list.filter(value => value !== Symbol.for("eof")).join(""); // Prune unmatched maybes from a list. -export const prune = list => - list.filter(value => value !== null); +export const prune = list => list.filter(value => value !== null); // Map a list of {name, value} aliases into an array of values. export const keep_abstract = list => - list - .filter(value => value instanceof Abstract) - .map(({value}) => value); + list.filter(value => value instanceof Abstract).map(({value}) => value); // Map a list to the element at the specified index. Useful for parsers which // define a prefix or a surrounding delimiter. export const element_at = index => list => list[index]; // Print the parse result of a parser. -export const print = x => - (console.log(JSON.stringify(x, null, 4)), x); +export const print = x => (console.log(JSON.stringify(x, null, 4)), x); diff --git a/lib/parser.js b/lib/parser.js index 73f89e3..70bd3b3 100644 --- a/lib/parser.js +++ b/lib/parser.js @@ -13,9 +13,7 @@ export default class Parser { } run(iterable) { - let stream = iterable instanceof Stream - ? iterable - : new Stream(iterable); + let stream = iterable instanceof Stream ? iterable : new Stream(iterable); return this.parse(stream); } @@ -32,9 +30,7 @@ export default class Parser { } chain(f) { - return new Parser(stream => - this.run(stream).chain( - (value, tail) => f(value).run(tail))); + return new Parser(stream => this.run(stream).chain((value, tail) => f(value).run(tail))); } fold(s, f) { diff --git a/lib/serializer.js b/lib/serializer.js index 7a147c8..9240d5f 100644 --- a/lib/serializer.js +++ b/lib/serializer.js @@ -1,5 +1,4 @@ -export default -function serialize_rule(rule, state) { +export default function serialize_rule(rule, state) { let {name, expression, block_comments} = rule; let lhs = name.padEnd(state.max_name_length); let rhs = serialize_expression(expression, state); @@ -16,9 +15,7 @@ function serialize_comments(comments) { contents.push(`/*${value}*/`); } } - return contents.length - ? contents.join("\n") + "\n" - : ""; + return contents.length ? contents.join("\n") + "\n" : ""; } function serialize_expression(expression, state) { @@ -33,8 +30,7 @@ function serialize_expression(expression, state) { } function serialize_operator({name, args}, state) { - let serialized_args = args.map(arg => - serialize_expression(arg, {...state, parent: name})); + let serialized_args = args.map(arg => serialize_expression(arg, {...state, parent: name})); function ensure_prec(text) { return state.parent ? `(${text})` : text; @@ -45,8 +41,7 @@ function serialize_operator({name, args}, state) { return null; } case "and": { - return ensure_prec( - serialized_args.reverse().join(" ")); + return ensure_prec(serialized_args.reverse().join(" ")); } case "char": { return `"${serialized_args[0]}"`; @@ -85,8 +80,7 @@ function serialize_operator({name, args}, state) { return `${serialized_args[0]}+`; } case "sequence": { - return ensure_prec( - serialized_args.filter(Boolean).join(" ")); + return ensure_prec(serialized_args.filter(Boolean).join(" ")); } case "string": { return `"${serialized_args[0]}"`; diff --git a/lib/stream.js b/lib/stream.js index 92aa386..6d2c95a 100644 --- a/lib/stream.js +++ b/lib/stream.js @@ -2,9 +2,7 @@ export default class Stream { constructor(iterable, cursor, length) { this.iterable = iterable; this.cursor = cursor || 0; - this.length = length === undefined - ? iterable.length - this.cursor - : length; + this.length = length === undefined ? iterable.length - this.cursor : length; } // Get the element at the cursor. @@ -33,9 +31,6 @@ export default class Stream { // Consume the stream by moving the cursor. move(distance) { - return new Stream( - this.iterable, - this.cursor + distance, - this.length - distance); + return new Stream(this.iterable, this.cursor + distance, this.length - distance); } } diff --git a/lib/visitor.js b/lib/visitor.js index 933a50b..4085399 100644 --- a/lib/visitor.js +++ b/lib/visitor.js @@ -12,7 +12,7 @@ export default { let comments = leadingComments && { block_comments: leadingComments .filter(comm => comm.type === "CommentBlock") - .map(comm => cont(comm, state)) + .map(comm => cont(comm, state)), }; return cont(declaration, {...state, ...comments}); }, @@ -22,12 +22,15 @@ export default { let comments = leadingComments && { block_comments: leadingComments .filter(comm => comm.type === "CommentBlock") - .map(comm => cont(comm, state)) + .map(comm => cont(comm, state)), }; return cont(declaration, {...state, ...comments}); }, VariableDeclarator(node, state, cont) { - let {id: {name}, init} = node; + let { + id: {name}, + init, + } = node; let {block_comments = []} = state; let expression = cont(init, state); return {type: "Rule", name, expression, block_comments}; @@ -78,12 +81,14 @@ export default { }; function escape(str) { - return str - // Escape backslash and double quote, which are special in EBNF. - .replace(/\\/g, "\\\\") - .replace(/"/g, "\\\"") - // Replace all Control and non-Basic Latin characters. - .replace(/([^\u0021-\u007E])/g, unicode_sequence); + return ( + str + // Escape backslash and double quote, which are special in EBNF. + .replace(/\\/g, "\\\\") + .replace(/"/g, '\\"') + // Replace all Control and non-Basic Latin characters. + .replace(/([^\u0021-\u007E])/g, unicode_sequence) + ); } function unicode_sequence(char) { diff --git a/lib/walker.js b/lib/walker.js index 1b44c45..cde9f3a 100644 --- a/lib/walker.js +++ b/lib/walker.js @@ -1,5 +1,4 @@ -export default -function walk(node, visitor, state) { +export default function walk(node, visitor, state) { function cont(node, state) { let visit = visitor[node.type]; if (visit) { diff --git a/package-lock.json b/package-lock.json index 9769d6c..3601eaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,74 +4,12 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "^3.0.4" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } - } - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true - }, - "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", - "dev": true - }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -87,12 +25,6 @@ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", "dev": true }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, "async": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", @@ -102,41 +34,6 @@ "lodash": "^4.14.0" } }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - }, - "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, "babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", @@ -171,70 +68,6 @@ "concat-map": "0.0.1" } }, - "buffer-from": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", - "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", - "dev": true - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, "cli-color": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.2.0.tgz", @@ -249,42 +82,6 @@ "timers-ext": "0.1" } }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, "commander": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", @@ -297,35 +94,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "d": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", @@ -335,36 +103,6 @@ "es5-ext": "^0.10.9" } }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - } - }, "difflib": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", @@ -374,15 +112,6 @@ "heap": ">= 0.2.0" } }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, "dreamopt": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/dreamopt/-/dreamopt-0.6.0.tgz", @@ -436,125 +165,11 @@ "es6-symbol": "^3.1.1" } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", - "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", - "dev": true, - "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", - "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", - "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^1.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" - } - }, - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true - }, "esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" }, - "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", - "dev": true, - "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" - } - }, - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, "event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", @@ -565,66 +180,6 @@ "es5-ext": "~0.10.14" } }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } - }, - "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", - "dev": true, - "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" - } - }, "fluent": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/fluent/-/fluent-0.6.4.tgz", @@ -654,12 +209,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "gh-pages": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-1.1.0.tgz", @@ -766,74 +315,18 @@ "path-is-absolute": "^1.0.0" } }, - "globals": { - "version": "11.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.5.0.tgz", - "integrity": "sha512-hYyf+kI8dm3nORsiiXUQigOU62hDLfJ9G01uyGMxhc6BKsircrUhC4uJPQPUSuq2GrTmiiEt7ewxlMdBewfmKQ==", - "dev": true - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "heap": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", "integrity": "sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw=", "dev": true }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.8.tgz", - "integrity": "sha512-pUh+xUQQhQzevjRHHFqqcTy0/dP/kS9I8HSrUydhihjuD09W6ldVWFtIrwhXdUJHis3i2rZNqEHpZH/cbinFbg==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -850,98 +343,12 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.0.4", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", - "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, "json-diff": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/json-diff/-/json-diff-0.5.2.tgz", @@ -970,18 +377,6 @@ } } }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -991,32 +386,12 @@ "graceful-fs": "^4.1.6" } }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", "dev": true }, - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "lru-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", @@ -1042,12 +417,6 @@ "timers-ext": "^0.1.2" } }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1062,41 +431,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -5695,15 +5029,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", @@ -5728,20 +5053,6 @@ } } }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -5760,12 +5071,6 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -5787,34 +5092,10 @@ "pinkie": "^2.0.0" } }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "prettier": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.1.tgz", + "integrity": "sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==", "dev": true }, "q": { @@ -5823,53 +5104,6 @@ "integrity": "sha1-3QG6ydBtMObyGa7LglPunr3DCPE=", "dev": true }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "regexpp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", - "dev": true - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - } - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, "rimraf": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", @@ -5879,158 +5113,6 @@ "glob": "^7.0.5" } }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true - }, - "rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", - "dev": true, - "requires": { - "rx-lite": "*" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", - "dev": true, - "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, "timers-ext": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.5.tgz", @@ -6041,30 +5123,6 @@ "next-tick": "1" } }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, "universalify": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", @@ -6080,21 +5138,6 @@ "os-homedir": "^1.0.0" } }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -6106,21 +5149,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true } } } diff --git a/package.json b/package.json index 9a90806..e29aa81 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "generate:ebnf": "node -r esm bin/ebnf.js ./syntax/grammar.js > ./spec/fluent.ebnf", "generate:fixtures": "make -sC test/fixtures", "generate": "npm run --silent generate:ebnf && npm run --silent generate:fixtures", - "lint": "eslint **/*.js", + "lint": "prettier --check **/*.js", + "pretty": "prettier --write **/*.js", "test:ebnf": "node -r esm test/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", "test:fixtures": "node -r esm test/parser.js ./test/fixtures", "test:unit": "node -r esm test/literals.js", @@ -33,12 +34,12 @@ "babylon": "^6.18.0", "cli-color": "^1.2.0", "difflib": "^0.2.4", - "eslint": "^4.19.1", "fluent": "^0.6.4", "fluent-syntax": "^0.7.0", "gh-pages": "^1.1.0", "gitbook-cli": "^2.3.0", - "json-diff": "^0.5.2" + "json-diff": "^0.5.2", + "prettier": "^1.17.1" }, "dependencies": { "esm": "^3.2.25", diff --git a/syntax/abstract.js b/syntax/abstract.js index 1ca1de3..a0acaa9 100644 --- a/syntax/abstract.js +++ b/syntax/abstract.js @@ -30,35 +30,41 @@ export function list_into(Type) { return always(new Type(identifier, args)); } return never( - `Invalid function name: ${identifier.name}. ` - + "Function names must be all upper-case ASCII letters."); + `Invalid function name: ${identifier.name}. ` + + "Function names must be all upper-case ASCII letters." + ); }; case FTL.Pattern: return elements => - always(new FTL.Pattern( - dedent(elements) - .reduce(join_adjacent(FTL.TextElement), []) - .map(trim_text_at_extremes) - .filter(remove_empty_text))); + always( + new FTL.Pattern( + dedent(elements) + .reduce(join_adjacent(FTL.TextElement), []) + .map(trim_text_at_extremes) + .filter(remove_empty_text) + ) + ); case FTL.Resource: return entries => - always(new FTL.Resource( - entries - .reduce(join_adjacent( - FTL.Comment, - FTL.GroupComment, - FTL.ResourceComment), []) - .reduce(attach_comments, []) - .filter(remove_blank_lines))); + always( + new FTL.Resource( + entries + .reduce( + join_adjacent(FTL.Comment, FTL.GroupComment, FTL.ResourceComment), + [] + ) + .reduce(attach_comments, []) + .filter(remove_blank_lines) + ) + ); case FTL.SelectExpression: return ([selector, variants]) => { let selector_is_valid = - selector instanceof FTL.StringLiteral - || selector instanceof FTL.NumberLiteral - || selector instanceof FTL.VariableReference - || selector instanceof FTL.FunctionReference - || (selector instanceof FTL.TermReference - && selector.attribute); + selector instanceof FTL.StringLiteral || + selector instanceof FTL.NumberLiteral || + selector instanceof FTL.VariableReference || + selector instanceof FTL.FunctionReference || + (selector instanceof FTL.TermReference && selector.attribute); if (!selector_is_valid) { return never(`Invalid selector type: ${selector.type}.`); } @@ -66,8 +72,7 @@ export function list_into(Type) { return always(new Type(selector, variants)); }; default: - return elements => - always(new Type(...elements)); + return elements => always(new Type(...elements)); } } @@ -85,27 +90,22 @@ export function into(Type) { } named.set(name, arg); } else if (named.size > 0) { - return never("Positional arguments must not follow " - + "named arguments"); + return never("Positional arguments must not follow " + "named arguments"); } else { positional.push(arg); } } - return always(new Type( - positional, Array.from(named.values()))); + return always(new Type(positional, Array.from(named.values()))); }; case FTL.Placeable: return expression => { - if (expression instanceof FTL.TermReference - && expression.attribute) { - return never( - "Term attributes may not be used as placeables."); + if (expression instanceof FTL.TermReference && expression.attribute) { + return never("Term attributes may not be used as placeables."); } return always(new Type(expression)); }; default: - return (...args) => - always(new Type(...args)); + return (...args) => always(new Type(...args)); } } @@ -131,21 +131,17 @@ function join_of_type(Type, ...elements) { // TODO Join annotations and spans. switch (Type) { case FTL.TextElement: - return elements.reduce((a, b) => - new Type(a.value + b.value)); + return elements.reduce((a, b) => new Type(a.value + b.value)); case FTL.Comment: case FTL.GroupComment: case FTL.ResourceComment: - return elements.reduce((a, b) => - new Type(a.content + "\n" + b.content)); + return elements.reduce((a, b) => new Type(a.content + "\n" + b.content)); } } function attach_comments(acc, cur) { let prev = acc[acc.length - 1]; - if (prev instanceof FTL.Comment - && (cur instanceof FTL.Message - || cur instanceof FTL.Term)) { + if (prev instanceof FTL.Comment && (cur instanceof FTL.Message || cur instanceof FTL.Term)) { cur.comment = prev; acc[acc.length - 1] = cur; return acc; @@ -180,22 +176,19 @@ const TRAILING_BLANK_INLINE = / *$/; function trim_text_at_extremes(element, index, array) { if (element instanceof FTL.TextElement) { if (index === 0) { - element.value = element.value.replace( - LEADING_BLANK_BLOCK, ""); + element.value = element.value.replace(LEADING_BLANK_BLOCK, ""); } if (index === array.length - 1) { - element.value = element.value.replace( - TRAILING_BLANK_INLINE, ""); + element.value = element.value.replace(TRAILING_BLANK_INLINE, ""); } } return element; } function remove_empty_text(element) { - return !(element instanceof FTL.TextElement) - || element.value !== ""; + return !(element instanceof FTL.TextElement) || element.value !== ""; } function remove_blank_lines(element) { - return typeof(element) !== "string"; + return typeof element !== "string"; } diff --git a/syntax/ast.js b/syntax/ast.js index 1a645eb..0431e06 100644 --- a/syntax/ast.js +++ b/syntax/ast.js @@ -97,18 +97,17 @@ export class StringLiteral extends Literal { parse() { // Backslash backslash, backslash double quote, uHHHH, UHHHHHH. - const KNOWN_ESCAPES = - /(?:\\\\|\\\"|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g; + const KNOWN_ESCAPES = /(?:\\\\|\\\"|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g; function from_escape_sequence(match, codepoint4, codepoint6) { switch (match) { case "\\\\": return "\\"; - case "\\\"": - return "\""; + case '\\"': + return '"'; default: let codepoint = parseInt(codepoint4 || codepoint6, 16); - if (codepoint <= 0xD7FF || 0xE000 <= codepoint) { + if (codepoint <= 0xd7ff || 0xe000 <= codepoint) { // It's a Unicode scalar value. return String.fromCodePoint(codepoint); } @@ -133,9 +132,7 @@ export class NumberLiteral extends Literal { parse() { let value = parseFloat(this.value); let decimal_position = this.value.indexOf("."); - let precision = decimal_position > 0 - ? this.value.length - decimal_position - 1 - : 0; + let precision = decimal_position > 0 ? this.value.length - decimal_position - 1 : 0; return {value, precision}; } } diff --git a/syntax/grammar.js b/syntax/grammar.js index 6f94bda..f215b33 100644 --- a/syntax/grammar.js +++ b/syntax/grammar.js @@ -1,23 +1,27 @@ import * as FTL from "./ast.js"; import {list_into, into} from "./abstract.js"; import { - always, and, charset, defer, either, eof, maybe, not, - regex, repeat, repeat1, sequence, string + always, + and, + charset, + defer, + either, + eof, + maybe, + not, + regex, + repeat, + repeat1, + sequence, + string, } from "../lib/combinators.js"; -import { - element_at, flatten, join, keep_abstract, mutate, print, prune -} from "../lib/mappers.js"; +import {element_at, flatten, join, keep_abstract, mutate, print, prune} from "../lib/mappers.js"; /* ----------------------------------------------------- */ /* An FTL file defines a Resource consisting of Entries. */ -export -let Resource = defer(() => - repeat( - either( - Entry, - blank_block, - Junk)) - .chain(list_into(FTL.Resource))); +export let Resource = defer(() => + repeat(either(Entry, blank_block, Junk)).chain(list_into(FTL.Resource)) +); /* ------------------------------------------------------------------------- */ /* Entries are the main building blocks of Fluent. They define translations and @@ -26,16 +30,13 @@ let Resource = defer(() => * the number of #) are joined together. Single-# comments directly preceding * Messages and Terms are attached to the Message or Term and are not * standalone Entries. */ -export -let Entry = defer(() => +export let Entry = defer(() => either( - sequence( - Message, - line_end).map(element_at(0)), - sequence( - Term, - line_end).map(element_at(0)), - CommentLine)); + sequence(Message, line_end).map(element_at(0)), + sequence(Term, line_end).map(element_at(0)), + CommentLine + ) +); let Message = defer(() => sequence( @@ -44,15 +45,14 @@ let Message = defer(() => string("="), maybe(blank_inline), either( - sequence( - Pattern.abstract, - repeat(Attribute).abstract), - sequence( - always(null).abstract, - repeat1(Attribute).abstract))) - .map(flatten(1)) - .map(keep_abstract) - .chain(list_into(FTL.Message))); + sequence(Pattern.abstract, repeat(Attribute).abstract), + sequence(always(null).abstract, repeat1(Attribute).abstract) + ) + ) + .map(flatten(1)) + .map(keep_abstract) + .chain(list_into(FTL.Message)) +); let Term = defer(() => sequence( @@ -62,33 +62,27 @@ let Term = defer(() => string("="), maybe(blank_inline), Pattern.abstract, - repeat(Attribute).abstract) - .map(keep_abstract) - .chain(list_into(FTL.Term))); + repeat(Attribute).abstract + ) + .map(keep_abstract) + .chain(list_into(FTL.Term)) +); /* -------------------------------------------------------------------------- */ /* Adjacent comment lines of the same comment type are joined together during * the AST construction. */ let CommentLine = defer(() => sequence( - either( - string("###"), - string("##"), - string("#")).abstract, - maybe( - sequence( - string(" "), - repeat(comment_char) - .map(join).abstract)), - line_end) - .map(flatten(1)) - .map(keep_abstract) - .chain(list_into(FTL.Comment))); + either(string("###"), string("##"), string("#")).abstract, + maybe(sequence(string(" "), repeat(comment_char).map(join).abstract)), + line_end + ) + .map(flatten(1)) + .map(keep_abstract) + .chain(list_into(FTL.Comment)) +); -let comment_char = defer(() => - and( - not(line_end), - any_char)); +let comment_char = defer(() => and(not(line_end), any_char)); /* -------------------------------------------------------------------------- */ /* Junk represents unparsed content. @@ -100,23 +94,14 @@ let comment_char = defer(() => let Junk = defer(() => sequence( junk_line, - repeat( - and( - not(charset("a-zA-Z")), - not(string("-")), - not(string("#")), - junk_line))) - .map(flatten(1)) - .map(join) - .chain(into(FTL.Junk))); + repeat(and(not(charset("a-zA-Z")), not(string("-")), not(string("#")), junk_line)) + ) + .map(flatten(1)) + .map(join) + .chain(into(FTL.Junk)) +); -let junk_line = - sequence( - regex(/[^\n]*/), - either( - string("\u000A"), - eof())) - .map(join); +let junk_line = sequence(regex(/[^\n]*/), either(string("\u000A"), eof())).map(join); /* --------------------------------- */ /* Attributes of Messages and Terms. */ @@ -129,18 +114,20 @@ let Attribute = defer(() => maybe(blank_inline), string("="), maybe(blank_inline), - Pattern.abstract) - .map(keep_abstract) - .chain(list_into(FTL.Attribute))); + Pattern.abstract + ) + .map(keep_abstract) + .chain(list_into(FTL.Attribute)) +); /* ---------------------------------------------------------------- */ /* Patterns are values of Messages, Terms, Attributes and Variants. */ let Pattern = defer(() => - repeat1( - PatternElement) - // Flatten block_text and block_placeable which return lists. - .map(flatten(1)) - .chain(list_into(FTL.Pattern))); + repeat1(PatternElement) + // Flatten block_text and block_placeable which return lists. + .map(flatten(1)) + .chain(list_into(FTL.Pattern)) +); /* ----------------------------------------------------------------- */ /* TextElement and Placeable can occur inline or as block. @@ -149,24 +136,23 @@ let Pattern = defer(() => * Adjacent TextElements are joined in AST creation. */ let PatternElement = defer(() => - either( - inline_text, - block_text, - inline_placeable, - block_placeable)); + either(inline_text, block_text, inline_placeable, block_placeable) +); let inline_text = defer(() => repeat1(text_char) - .map(join) - .chain(into(FTL.TextElement))); + .map(join) + .chain(into(FTL.TextElement)) +); let block_text = defer(() => sequence( blank_block.chain(into(FTL.TextElement)), blank_inline, indented_char.chain(into(FTL.TextElement)), - maybe(inline_text)) - .map(prune)); + maybe(inline_text) + ).map(prune) +); let inline_placeable = defer(() => sequence( @@ -175,18 +161,23 @@ let inline_placeable = defer(() => either( // Order matters! SelectExpression, - InlineExpression), + InlineExpression + ), maybe(blank), - string("}")) - .map(element_at(2)) - .chain(into(FTL.Placeable))); + string("}") + ) + .map(element_at(2)) + .chain(into(FTL.Placeable)) +); let block_placeable = defer(() => sequence( blank_block.chain(into(FTL.TextElement)), // No indent before a placeable counts as 0 in dedention logic. maybe(blank_inline).map(s => s || ""), - inline_placeable)); + inline_placeable + ) +); /* ------------------------------------------------------------------- */ /* Rules for validating expressions in Placeables and as selectors of @@ -200,96 +191,71 @@ let InlineExpression = defer(() => MessageReference, TermReference, VariableReference, - inline_placeable)); + inline_placeable + ) +); /* -------- */ /* Literals */ -export -let StringLiteral = defer(() => - sequence( - string("\""), - repeat(quoted_char), - string("\"")) - .map(element_at(1)) - .map(join) - .chain(into(FTL.StringLiteral))); - -export -let NumberLiteral = defer(() => - sequence( - maybe(string("-")), - digits, - maybe( - sequence( - string("."), - digits))) - .map(flatten(1)) - .map(join) - .chain(into(FTL.NumberLiteral))); +export let StringLiteral = defer(() => + sequence(string('"'), repeat(quoted_char), string('"')) + .map(element_at(1)) + .map(join) + .chain(into(FTL.StringLiteral)) +); + +export let NumberLiteral = defer(() => + sequence(maybe(string("-")), digits, maybe(sequence(string("."), digits))) + .map(flatten(1)) + .map(join) + .chain(into(FTL.NumberLiteral)) +); /* ------------------ */ /* Inline Expressions */ let FunctionReference = defer(() => - sequence( - Identifier, - CallArguments) - .chain(list_into(FTL.FunctionReference))); + sequence(Identifier, CallArguments).chain(list_into(FTL.FunctionReference)) +); let MessageReference = defer(() => - sequence( - Identifier, - maybe(AttributeAccessor)) - .chain(list_into(FTL.MessageReference))); + sequence(Identifier, maybe(AttributeAccessor)).chain(list_into(FTL.MessageReference)) +); let TermReference = defer(() => sequence( string("-"), Identifier.abstract, maybe(AttributeAccessor).abstract, - maybe(CallArguments).abstract) - .map(keep_abstract) - .chain(list_into(FTL.TermReference))); + maybe(CallArguments).abstract + ) + .map(keep_abstract) + .chain(list_into(FTL.TermReference)) +); let VariableReference = defer(() => - sequence( - string("$"), - Identifier) - .map(element_at(1)) - .chain(into(FTL.VariableReference))); + sequence(string("$"), Identifier) + .map(element_at(1)) + .chain(into(FTL.VariableReference)) +); -let AttributeAccessor = defer(() => - sequence( - string("."), - Identifier)) - .map(element_at(1)); +let AttributeAccessor = defer(() => sequence(string("."), Identifier)).map(element_at(1)); let CallArguments = defer(() => - sequence( - maybe(blank), - string("("), - maybe(blank), - argument_list, - maybe(blank), - string(")")) - .map(element_at(3)) - .chain(into(FTL.CallArguments))); + sequence(maybe(blank), string("("), maybe(blank), argument_list, maybe(blank), string(")")) + .map(element_at(3)) + .chain(into(FTL.CallArguments)) +); let argument_list = defer(() => sequence( - repeat( - sequence( - Argument.abstract, - maybe(blank), - string(","), - maybe(blank))), - maybe(Argument.abstract)) - .map(flatten(2)) - .map(keep_abstract)); - -let Argument = defer(() => - either( - NamedArgument, - InlineExpression)); + repeat(sequence(Argument.abstract, maybe(blank), string(","), maybe(blank))), + maybe(Argument.abstract) + ) + .map(flatten(2)) + .map(keep_abstract) +); + +let Argument = defer(() => either(NamedArgument, InlineExpression)); let NamedArgument = defer(() => sequence( @@ -297,11 +263,11 @@ let NamedArgument = defer(() => maybe(blank), string(":"), maybe(blank), - either( - StringLiteral, - NumberLiteral).abstract) - .map(keep_abstract) - .chain(list_into(FTL.NamedArgument))); + either(StringLiteral, NumberLiteral).abstract + ) + .map(keep_abstract) + .chain(list_into(FTL.NamedArgument)) +); /* ----------------- */ /* Block Expressions */ @@ -311,28 +277,23 @@ let SelectExpression = defer(() => maybe(blank), string("->"), maybe(blank_inline), - variant_list.abstract) - .map(keep_abstract) - .chain(list_into(FTL.SelectExpression))); + variant_list.abstract + ) + .map(keep_abstract) + .chain(list_into(FTL.SelectExpression)) +); let variant_list = defer(() => - sequence( - repeat(Variant).abstract, - DefaultVariant.abstract, - repeat(Variant).abstract, - line_end) - .map(keep_abstract) - .map(flatten(1))); + sequence(repeat(Variant).abstract, DefaultVariant.abstract, repeat(Variant).abstract, line_end) + .map(keep_abstract) + .map(flatten(1)) +); let Variant = defer(() => - sequence( - line_end, - maybe(blank), - VariantKey.abstract, - maybe(blank_inline), - Pattern.abstract) - .map(keep_abstract) - .chain(list_into(FTL.Variant))); + sequence(line_end, maybe(blank), VariantKey.abstract, maybe(blank_inline), Pattern.abstract) + .map(keep_abstract) + .chain(list_into(FTL.Variant)) +); let DefaultVariant = defer(() => sequence( @@ -341,30 +302,27 @@ let DefaultVariant = defer(() => string("*"), VariantKey.abstract, maybe(blank_inline), - Pattern.abstract) - .map(keep_abstract) - .chain(list_into(FTL.Variant)) - .map(mutate({default: true}))); + Pattern.abstract + ) + .map(keep_abstract) + .chain(list_into(FTL.Variant)) + .map(mutate({default: true})) +); let VariantKey = defer(() => sequence( string("["), maybe(blank), - either( - NumberLiteral, - Identifier), + either(NumberLiteral, Identifier), maybe(blank), - string("]")) - .map(element_at(2))); + string("]") + ).map(element_at(2)) +); /* ---------- */ /* Identifier */ -let Identifier = - sequence( - charset("a-zA-Z"), - repeat( - charset("a-zA-Z0-9_-"))) +let Identifier = sequence(charset("a-zA-Z"), repeat(charset("a-zA-Z0-9_-"))) .map(flatten(1)) .map(join) .chain(into(FTL.Identifier)); @@ -380,8 +338,7 @@ let Identifier = * resources. See spec/recommendations.md. */ -let any_char = - charset("\\u{0}-\\u{10FFFF}"); +let any_char = charset("\\u{0}-\\u{10FFFF}"); /* -------------------------------------------------------------------------- */ /* Text elements @@ -394,23 +351,11 @@ let any_char = * new line. */ -let special_text_char = - either( - string("{"), - string("}")); +let special_text_char = either(string("{"), string("}")); -let text_char = defer(() => - and( - not(line_end), - not(special_text_char), - any_char)); +let text_char = defer(() => and(not(line_end), not(special_text_char), any_char)); -let indented_char = - and( - not(string(".")), - not(string("*")), - not(string("[")), - text_char); +let indented_char = and(not(string(".")), not(string("*")), not(string("[")), text_char); /* -------------------------------------------------------------------------- */ /* String literals @@ -424,71 +369,40 @@ let indented_char = * comprise placeables. */ -let special_quoted_char = - either( - string("\""), - string("\\")); +let special_quoted_char = either(string('"'), string("\\")); -let special_escape = - sequence( - string("\\"), - special_quoted_char) - .map(join); +let special_escape = sequence(string("\\"), special_quoted_char).map(join); -let unicode_escape = - either( - sequence( - string("\\u"), - regex(/[0-9a-fA-F]{4}/)), - sequence( - string("\\U"), - regex(/[0-9a-fA-F]{6}/))) - .map(join); +let unicode_escape = either( + sequence(string("\\u"), regex(/[0-9a-fA-F]{4}/)), + sequence(string("\\U"), regex(/[0-9a-fA-F]{6}/)) +).map(join); let quoted_char = defer(() => - either( - and( - not(line_end), - not(special_quoted_char), - any_char), - special_escape, - unicode_escape)); + either(and(not(line_end), not(special_quoted_char), any_char), special_escape, unicode_escape) +); /* ------- */ /* Numbers */ -let digits = - repeat1( - charset("0-9")) - .map(join); +let digits = repeat1(charset("0-9")).map(join); /* ---------- */ /* Whitespace */ -let blank_inline = - repeat1( - string("\u0020")) - .map(join); +let blank_inline = repeat1(string("\u0020")).map(join); -let line_end = - either( - // Normalize CRLF to LF. - string("\u000D\u000A").map(() => "\n"), - string("\u000A"), - eof()); - -let blank_block = - repeat1( - sequence( - maybe(blank_inline), - line_end.abstract)) +let line_end = either( + // Normalize CRLF to LF. + string("\u000D\u000A").map(() => "\n"), + string("\u000A"), + eof() +); + +let blank_block = repeat1(sequence(maybe(blank_inline), line_end.abstract)) .map(flatten(1)) // Discard the indents and only keep the newlines // for multiline Patterns. .map(keep_abstract) .map(join); -let blank = - repeat1( - either( - blank_inline, - line_end)); +let blank = repeat1(either(blank_inline, line_end)); diff --git a/test/bench.js b/test/bench.js index 0a411c1..ee6e697 100644 --- a/test/bench.js +++ b/test/bench.js @@ -11,8 +11,8 @@ let args = process.argv.slice(2); if (args.length < 1 || 2 < args.length) { console.error( - "Usage: node -r esm --harmony-async-iteration " + - "bench.js FTL_FILE [SAMPLE SIZE = 30]"); + "Usage: node -r esm --harmony-async-iteration " + "bench.js FTL_FILE [SAMPLE SIZE = 30]" + ); process.exit(1); } @@ -72,13 +72,11 @@ function shuffle(...elements) { } function mean(elements) { - let miu = elements.reduce((acc, cur) => acc + cur) - / elements.length; + let miu = elements.reduce((acc, cur) => acc + cur) / elements.length; return +miu.toFixed(2); } function stdev(elements, mean) { - let sigma = elements.reduce((acc, cur) => acc + (cur - mean) ** 2, 0) - / (elements.length - 1); + let sigma = elements.reduce((acc, cur) => acc + (cur - mean) ** 2, 0) / (elements.length - 1); return +Math.sqrt(sigma).toFixed(2); } diff --git a/test/ebnf.js b/test/ebnf.js index 2fcbd11..cf88861 100644 --- a/test/ebnf.js +++ b/test/ebnf.js @@ -6,9 +6,7 @@ import {readfile, PASS, FAIL} from "./util.js"; let args = process.argv.slice(2); if (args.length !== 2) { - console.error( - "Usage: node -r esm ebnf.js " + - "GRAMMAR_FILE EXPECTED_EBNF"); + console.error("Usage: node -r esm ebnf.js " + "GRAMMAR_FILE EXPECTED_EBNF"); process.exit(1); } @@ -18,12 +16,10 @@ async function main(grammar_mjs, fluent_ebnf) { let grammar_source = await readfile(grammar_mjs); let grammar_ebnf = await readfile(fluent_ebnf); - let diffs = difflib.unifiedDiff( - lines(grammar_ebnf), - lines(ebnf(grammar_source)), { - fromfile: "Expected", - tofile: "Actual", - }); + let diffs = difflib.unifiedDiff(lines(grammar_ebnf), lines(ebnf(grammar_source)), { + fromfile: "Expected", + tofile: "Actual", + }); for (let diff of diffs) { if (diff.startsWith("+")) { diff --git a/test/literals.js b/test/literals.js index 7b0fd69..6effe8f 100644 --- a/test/literals.js +++ b/test/literals.js @@ -10,14 +10,12 @@ if (process.argv.length > 2) { suite(tester => { let title = node => `${node.type} {value: "${node.value}"}`; let test = (result, expected) => - result.fold( - node => tester.deep_equal(title(node), node.parse(), expected), - tester.fail); + result.fold(node => tester.deep_equal(title(node), node.parse(), expected), tester.fail); // Unescape raw value of StringLiterals. { test(StringLiteral.run(`"abc"`), {value: "abc"}); - test(StringLiteral.run(`"\\""`), {value: "\""}); + test(StringLiteral.run(`"\\""`), {value: '"'}); test(StringLiteral.run(`"\\\\"`), {value: "\\"}); // Unicode escapes. @@ -33,11 +31,10 @@ suite(tester => { // Literal braces. test(StringLiteral.run(`"{"`), {value: "{"}); test(StringLiteral.run(`"}"`), {value: "}"}); - }; + } // Parse float value and precision of NumberLiterals. { - // Integers. test(NumberLiteral.run("0"), {value: 0, precision: 0}); test(NumberLiteral.run("1"), {value: 1, precision: 0}); @@ -69,5 +66,5 @@ suite(tester => { test(NumberLiteral.run("-01.03"), {value: -1.03, precision: 2}); test(NumberLiteral.run("-1.0300"), {value: -1.03, precision: 4}); test(NumberLiteral.run("-01.0300"), {value: -1.03, precision: 4}); - }; + } }); diff --git a/test/parser.js b/test/parser.js index e1e0bc1..b5e7c89 100644 --- a/test/parser.js +++ b/test/parser.js @@ -6,8 +6,7 @@ import {readdir, readfile, diff, PASS, FAIL} from "./util.js"; const fixtures_dir = process.argv[2]; if (!fixtures_dir) { - console.error( - "Usage: node -r esm parser.js FIXTURE"); + console.error("Usage: node -r esm parser.js FIXTURE"); process.exit(1); } @@ -20,8 +19,7 @@ async function main(fixtures_dir) { fixtures_dir = path.dirname(fixtures_dir); } else { let files = await readdir(fixtures_dir); - var ftls = files.filter( - filename => filename.endsWith(".ftl")); + var ftls = files.filter(filename => filename.endsWith(".ftl")); } // Collect all AssertionErrors. @@ -43,9 +41,7 @@ async function main(fixtures_dir) { continue; } - Resource.run(ftl_source).fold( - assert_equal, - err => assert.fail(err)); + Resource.run(ftl_source).fold(assert_equal, err => assert.fail(err)); function assert_equal(ast) { try { @@ -95,9 +91,7 @@ ${err.message} } function exit_summary(error_count) { - const message = error_count - ? `Tests ${FAIL}: ${error_count}.` - : `All tests ${PASS}.`; + const message = error_count ? `Tests ${FAIL}: ${error_count}.` : `All tests ${PASS}.`; console.log(` ======================================================================== ${message} diff --git a/test/suite.js b/test/suite.js index 6743162..5962164 100644 --- a/test/suite.js +++ b/test/suite.js @@ -2,8 +2,7 @@ import assert from "assert"; import color from "cli-color"; import {diff, PASS, FAIL} from "./util.js"; -export default -function suite(fn) { +export default function suite(fn) { let errors = new Map(); fn(create_tester(errors)); @@ -45,9 +44,7 @@ ${diff(err.expected, err.actual)} } function exit_summary(error_count) { - const message = error_count - ? `Tests ${FAIL}: ${error_count}.` - : `All tests ${PASS}.`; + const message = error_count ? `Tests ${FAIL}: ${error_count}.` : `All tests ${PASS}.`; console.log(` ======================================================================== ${message} From cc9e5a13ac381b9a0dbaf3e3ee4d1c7c9fdaed9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 5 Jun 2019 17:23:51 +0200 Subject: [PATCH 03/36] Add VS Code workspace settings --- .vscode/settings.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f89ed5f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.formatOnSave": true +} From bf7ff796de04dde3502836dc30886bc10f935ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Tue, 28 May 2019 19:29:38 +0200 Subject: [PATCH 04/36] Resolve string variable reference --- resolver/.gitignore | 1 + resolver/ast.ts | 36 ++++++++++++++++++++++++++++++ resolver/mod.ts | 37 +++++++++++++++++++++++++++++++ resolver/resolver.ts | 50 ++++++++++++++++++++++++++++++++++++++++++ resolver/tsconfig.json | 8 +++++++ 5 files changed, 132 insertions(+) create mode 100644 resolver/.gitignore create mode 100644 resolver/ast.ts create mode 100644 resolver/mod.ts create mode 100644 resolver/resolver.ts create mode 100644 resolver/tsconfig.json diff --git a/resolver/.gitignore b/resolver/.gitignore new file mode 100644 index 0000000..a6c7c28 --- /dev/null +++ b/resolver/.gitignore @@ -0,0 +1 @@ +*.js diff --git a/resolver/ast.ts b/resolver/ast.ts new file mode 100644 index 0000000..f0f2307 --- /dev/null +++ b/resolver/ast.ts @@ -0,0 +1,36 @@ +export enum SyntaxNode { + Identifer = "Identifier", + VariableReference = "VariableReference", + TextElement = "TextElement", + Placeable = "Placeable", + Pattern = "Pattern", +} + +export interface ISyntaxNode { + readonly type: SyntaxNode; +} + +export interface IIdentifier extends ISyntaxNode { + readonly type: SyntaxNode.Identifer; + readonly name: string; +} + +export interface IVariableReference extends ISyntaxNode { + readonly type: SyntaxNode.VariableReference; + readonly id: IIdentifier; +} + +export interface ITextElement extends ISyntaxNode { + readonly type: SyntaxNode.TextElement; + readonly value: string; +} + +export interface IPlaceable extends ISyntaxNode { + readonly type: SyntaxNode.Placeable; + readonly expression: IVariableReference; +} + +export interface IPattern extends ISyntaxNode { + readonly type: SyntaxNode.Pattern; + readonly elements: Array; +} diff --git a/resolver/mod.ts b/resolver/mod.ts new file mode 100644 index 0000000..452eb66 --- /dev/null +++ b/resolver/mod.ts @@ -0,0 +1,37 @@ +import * as ast from "./ast"; +import {Resolver} from "./resolver"; + +let message = { + "type": "Message", + "id": { + "type": "Identifer", + "name": "hello" + }, + "value": { + "type": "Pattern", + "elements": [ + { + "type": "TextElement", + "value": "Hello, " + }, + { + "type": "Placeable", + "expression": { + "type": "VariableReference", + "id": { + "type": "Identifier", + "name": "userName" + } + } + } + ] + }, + "attributes": [], + "comment": null +}; + + +let variables = new Map([["userName", "Anne"]]); +let resolver = new Resolver(variables); +let value = resolver.resolve(message.value as ast.IPattern); +console.log(value); diff --git a/resolver/resolver.ts b/resolver/resolver.ts new file mode 100644 index 0000000..8250035 --- /dev/null +++ b/resolver/resolver.ts @@ -0,0 +1,50 @@ +import * as ast from "./ast"; + +export class Resolver { + private readonly variables: Map; + + constructor(variables: Map) { + this.variables = variables; + } + + resolve(node: ast.ISyntaxNode): string { + switch (node.type) { + case ast.SyntaxNode.Identifer: + return this.resolveIdentifier(node as ast.IIdentifier); + case ast.SyntaxNode.VariableReference: + return this.resolveVariableReference(node as ast.IVariableReference); + case ast.SyntaxNode.TextElement: + return this.resolveTextElement(node as ast.ITextElement); + case ast.SyntaxNode.Placeable: + return this.resolvePlaceable(node as ast.IPlaceable); + case ast.SyntaxNode.Pattern: + return this.resolvePattern(node as ast.IPattern); + } + } + + resolveIdentifier(node: ast.IIdentifier): string { + return node.name; + } + + resolveVariableReference(node: ast.IVariableReference): string { + let id = this.resolveIdentifier(node.id); + let value = this.variables.get(id); + if (value !== undefined) { + return value; + } else { + return id; + } + } + + resolveTextElement(node: ast.ITextElement): string { + return node.value; + } + + resolvePlaceable(node: ast.IPlaceable): string { + return this.resolve(node.expression); + } + + resolvePattern(node: ast.IPattern): string { + return node.elements.map(element => this.resolve(element)).join(""); + } +} diff --git a/resolver/tsconfig.json b/resolver/tsconfig.json new file mode 100644 index 0000000..3a56b3b --- /dev/null +++ b/resolver/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true + } +} From 1d2b7cd68b5f14b4b3cbbeb1b7137a6c1ff46997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 29 May 2019 14:21:45 +0200 Subject: [PATCH 05/36] Build TS into dist/ --- resolver/{ => src}/ast.ts | 0 resolver/{mod.ts => src/index.ts} | 0 resolver/{ => src}/resolver.ts | 0 resolver/tsconfig.json | 3 ++- 4 files changed, 2 insertions(+), 1 deletion(-) rename resolver/{ => src}/ast.ts (100%) rename resolver/{mod.ts => src/index.ts} (100%) rename resolver/{ => src}/resolver.ts (100%) diff --git a/resolver/ast.ts b/resolver/src/ast.ts similarity index 100% rename from resolver/ast.ts rename to resolver/src/ast.ts diff --git a/resolver/mod.ts b/resolver/src/index.ts similarity index 100% rename from resolver/mod.ts rename to resolver/src/index.ts diff --git a/resolver/resolver.ts b/resolver/src/resolver.ts similarity index 100% rename from resolver/resolver.ts rename to resolver/src/resolver.ts diff --git a/resolver/tsconfig.json b/resolver/tsconfig.json index 3a56b3b..46062b0 100644 --- a/resolver/tsconfig.json +++ b/resolver/tsconfig.json @@ -3,6 +3,7 @@ "target": "es2018", "module": "commonjs", "strict": true, - "esModuleInterop": true + "esModuleInterop": true, + "outDir": "./dist" } } From 518ae2dda591fa1567fe29a7e7806d3572e0358d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 29 May 2019 20:57:06 +0200 Subject: [PATCH 06/36] IResult --- resolver/src/resolver.ts | 48 ++++++++++++++++++++++++++-------------- resolver/src/result.ts | 48 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 resolver/src/result.ts diff --git a/resolver/src/resolver.ts b/resolver/src/resolver.ts index 8250035..2633028 100644 --- a/resolver/src/resolver.ts +++ b/resolver/src/resolver.ts @@ -1,13 +1,16 @@ import * as ast from "./ast"; +import {IResult, Success, Failure} from "./result"; export class Resolver { private readonly variables: Map; + public errors: Array; constructor(variables: Map) { this.variables = variables; + this.errors = []; } - resolve(node: ast.ISyntaxNode): string { + resolve(node: ast.ISyntaxNode): IResult { switch (node.type) { case ast.SyntaxNode.Identifer: return this.resolveIdentifier(node as ast.IIdentifier); @@ -22,29 +25,40 @@ export class Resolver { } } - resolveIdentifier(node: ast.IIdentifier): string { - return node.name; + resolveIdentifier(node: ast.IIdentifier): IResult { + return new Success(node.name); } - resolveVariableReference(node: ast.IVariableReference): string { - let id = this.resolveIdentifier(node.id); - let value = this.variables.get(id); - if (value !== undefined) { - return value; - } else { - return id; - } - } + resolveVariableReference(node: ast.IVariableReference): IResult { + return this.resolveIdentifier(node.id).then(id => { + if (typeof id === "string") { + let value = this.variables.get(id); + if (value !== undefined) { + return new Success(value); + } else { + this.errors.push("Missing variable"); + return new Failure(`$${id}`); + } + } else { + this.errors.push("Invalid id"); + return new Failure(id); + } + }); + } - resolveTextElement(node: ast.ITextElement): string { - return node.value; + resolveTextElement(node: ast.ITextElement): IResult { + return new Success(node.value); } - resolvePlaceable(node: ast.IPlaceable): string { + resolvePlaceable(node: ast.IPlaceable): IResult { return this.resolve(node.expression); } - resolvePattern(node: ast.IPattern): string { - return node.elements.map(element => this.resolve(element)).join(""); + resolvePattern(node: ast.IPattern): IResult { + return new Success( + node.elements + .map(element => this.resolve(element).fold(value => value, value => `{${value}}`)) + .join("") + ); } } diff --git a/resolver/src/result.ts b/resolver/src/result.ts new file mode 100644 index 0000000..f87a2d9 --- /dev/null +++ b/resolver/src/result.ts @@ -0,0 +1,48 @@ +type Value = number | string | Date; + +export interface IResult { + map(fn: IMapper): IResult; + then(fn: IChainer): IResult; + else(fn: IChainer): IResult; + fold(s: IMapper, f: IMapper): Value; +} + +interface IMapper { + (value: Value): Value; +} + +interface IChainer { + (value: Value): IResult; +} + +export class Success implements IResult { + constructor(private value: Value) {} + map(fn: IMapper): IResult { + return new Success(fn(this.value)); + } + then(fn: IChainer) { + return fn(this.value); + } + else(fn: IChainer) { + return this; + } + fold(s: IMapper, f: IMapper): Value { + return s(this.value); + } +} + +export class Failure implements IResult { + constructor(private value: Value) {} + map(fn: IMapper): IResult { + return this; + } + then(fn: IChainer): IResult { + return this; + } + else(fn: IChainer): IResult { + return fn(this.value); + } + fold(s: IMapper, f: IMapper) { + return f(this.value); + } +} From dbb027cddc31e077b08b400e9fd55b77d8f243e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 29 May 2019 20:58:06 +0200 Subject: [PATCH 07/36] Use Prettier --- resolver/.prettierrc | 6 ++++++ resolver/src/index.ts | 42 +++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 21 deletions(-) create mode 100644 resolver/.prettierrc diff --git a/resolver/.prettierrc b/resolver/.prettierrc new file mode 100644 index 0000000..ac8d76d --- /dev/null +++ b/resolver/.prettierrc @@ -0,0 +1,6 @@ +{ + "bracketSpacing": false, + "trailingComma": "es5", + "printWidth": 100, + "tabWidth": 4 +} diff --git a/resolver/src/index.ts b/resolver/src/index.ts index 452eb66..f450549 100644 --- a/resolver/src/index.ts +++ b/resolver/src/index.ts @@ -2,32 +2,32 @@ import * as ast from "./ast"; import {Resolver} from "./resolver"; let message = { - "type": "Message", - "id": { - "type": "Identifer", - "name": "hello" + type: "Message", + id: { + type: "Identifer", + name: "hello", }, - "value": { - "type": "Pattern", - "elements": [ + value: { + type: "Pattern", + elements: [ { - "type": "TextElement", - "value": "Hello, " + type: "TextElement", + value: "Hello, ", }, { - "type": "Placeable", - "expression": { - "type": "VariableReference", - "id": { - "type": "Identifier", - "name": "userName" - } - } - } - ] + type: "Placeable", + expression: { + type: "VariableReference", + id: { + type: "Identifier", + name: "userName", + }, + }, + }, + ], }, - "attributes": [], - "comment": null + attributes: [], + comment: null, }; From 94f15ee9edaee7cf71e7095f58280950b051d8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 30 May 2019 15:37:30 +0200 Subject: [PATCH 08/36] Rename Resolver to Scope --- resolver/src/index.ts | 7 +++---- resolver/src/{resolver.ts => scope.ts} | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) rename resolver/src/{resolver.ts => scope.ts} (98%) diff --git a/resolver/src/index.ts b/resolver/src/index.ts index f450549..9891975 100644 --- a/resolver/src/index.ts +++ b/resolver/src/index.ts @@ -1,5 +1,5 @@ import * as ast from "./ast"; -import {Resolver} from "./resolver"; +import {Scope} from "./scope"; let message = { type: "Message", @@ -30,8 +30,7 @@ let message = { comment: null, }; - let variables = new Map([["userName", "Anne"]]); -let resolver = new Resolver(variables); -let value = resolver.resolve(message.value as ast.IPattern); +let scope = new Scope(variables); +let value = scope.resolve(message.value as ast.IPattern); console.log(value); diff --git a/resolver/src/resolver.ts b/resolver/src/scope.ts similarity index 98% rename from resolver/src/resolver.ts rename to resolver/src/scope.ts index 2633028..7258b0b 100644 --- a/resolver/src/resolver.ts +++ b/resolver/src/scope.ts @@ -1,7 +1,7 @@ import * as ast from "./ast"; import {IResult, Success, Failure} from "./result"; -export class Resolver { +export class Scope { private readonly variables: Map; public errors: Array; From 23d1a9917587eead15f5d13bb23be2c05909ea24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 30 May 2019 16:52:22 +0200 Subject: [PATCH 09/36] Add the IValue interface --- resolver/src/index.ts | 8 +++++- resolver/src/result.ts | 44 +++++++++++++++----------------- resolver/src/scope.ts | 58 ++++++++++++++++++++---------------------- resolver/src/value.ts | 29 +++++++++++++++++++++ 4 files changed, 84 insertions(+), 55 deletions(-) create mode 100644 resolver/src/value.ts diff --git a/resolver/src/index.ts b/resolver/src/index.ts index 9891975..7000eda 100644 --- a/resolver/src/index.ts +++ b/resolver/src/index.ts @@ -1,5 +1,6 @@ import * as ast from "./ast"; import {Scope} from "./scope"; +import {StringValue} from "./value"; let message = { type: "Message", @@ -30,7 +31,12 @@ let message = { comment: null, }; -let variables = new Map([["userName", "Anne"]]); +let variables = new Map( + Object.entries({ + username: new StringValue("Anne"), + }) +); let scope = new Scope(variables); let value = scope.resolve(message.value as ast.IPattern); console.log(value); +console.log(scope.errors); diff --git a/resolver/src/result.ts b/resolver/src/result.ts index f87a2d9..88c4490 100644 --- a/resolver/src/result.ts +++ b/resolver/src/result.ts @@ -1,48 +1,46 @@ -type Value = number | string | Date; - -export interface IResult { - map(fn: IMapper): IResult; - then(fn: IChainer): IResult; - else(fn: IChainer): IResult; - fold(s: IMapper, f: IMapper): Value; +export interface IResult { + map(fn: IMapper): IResult; + then(fn: IChainer): IResult; + else(fn: IChainer): IResult; + fold(s: IMapper, f: IMapper): T; } -interface IMapper { - (value: Value): Value; +interface IMapper { + (value: T): T; } -interface IChainer { - (value: Value): IResult; +interface IChainer { + (value: T): IResult; } -export class Success implements IResult { - constructor(private value: Value) {} - map(fn: IMapper): IResult { +export class Success implements IResult { + constructor(private value: T) {} + map(fn: IMapper): IResult { return new Success(fn(this.value)); } - then(fn: IChainer) { + then(fn: IChainer) { return fn(this.value); } - else(fn: IChainer) { + else(fn: IChainer) { return this; } - fold(s: IMapper, f: IMapper): Value { + fold(s: IMapper, f: IMapper): T { return s(this.value); } } -export class Failure implements IResult { - constructor(private value: Value) {} - map(fn: IMapper): IResult { +export class Failure implements IResult { + constructor(private value: T) {} + map(fn: IMapper): IResult { return this; } - then(fn: IChainer): IResult { + then(fn: IChainer): IResult { return this; } - else(fn: IChainer): IResult { + else(fn: IChainer): IResult { return fn(this.value); } - fold(s: IMapper, f: IMapper) { + fold(s: IMapper, f: IMapper) { return f(this.value); } } diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index 7258b0b..c60d25f 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -1,19 +1,18 @@ import * as ast from "./ast"; +import {IValue, StringValue} from "./value"; import {IResult, Success, Failure} from "./result"; export class Scope { - private readonly variables: Map; + private readonly variables: Map; public errors: Array; - constructor(variables: Map) { + constructor(variables: Map) { this.variables = variables; this.errors = []; } - resolve(node: ast.ISyntaxNode): IResult { + resolve(node: ast.ISyntaxNode): IResult { switch (node.type) { - case ast.SyntaxNode.Identifer: - return this.resolveIdentifier(node as ast.IIdentifier); case ast.SyntaxNode.VariableReference: return this.resolveVariableReference(node as ast.IVariableReference); case ast.SyntaxNode.TextElement: @@ -22,43 +21,40 @@ export class Scope { return this.resolvePlaceable(node as ast.IPlaceable); case ast.SyntaxNode.Pattern: return this.resolvePattern(node as ast.IPattern); + default: + throw new TypeError("Unresolvable node type."); } } - resolveIdentifier(node: ast.IIdentifier): IResult { - return new Success(node.name); - } - - resolveVariableReference(node: ast.IVariableReference): IResult { - return this.resolveIdentifier(node.id).then(id => { - if (typeof id === "string") { - let value = this.variables.get(id); - if (value !== undefined) { - return new Success(value); - } else { - this.errors.push("Missing variable"); - return new Failure(`$${id}`); - } - } else { - this.errors.push("Invalid id"); - return new Failure(id); - } - }); + resolveVariableReference(node: ast.IVariableReference): IResult { + let value = this.variables.get(node.id.name); + if (value !== undefined) { + return new Success(value); + } else { + this.errors.push("Missing variable"); + return new Failure(new StringValue(`$${node.id.name}`)); + } } - resolveTextElement(node: ast.ITextElement): IResult { - return new Success(node.value); + resolveTextElement(node: ast.ITextElement): IResult { + return new Success(new StringValue(node.value)); } - resolvePlaceable(node: ast.IPlaceable): IResult { + resolvePlaceable(node: ast.IPlaceable): IResult { return this.resolve(node.expression); } - resolvePattern(node: ast.IPattern): IResult { + resolvePattern(node: ast.IPattern): IResult { return new Success( - node.elements - .map(element => this.resolve(element).fold(value => value, value => `{${value}}`)) - .join("") + new StringValue( + node.elements + .map(element => + this.resolve(element) + .fold(value => value, value => new StringValue(`{${value.value}}`)) + .format(this) + ) + .join("") + ) ); } } diff --git a/resolver/src/value.ts b/resolver/src/value.ts new file mode 100644 index 0000000..c90797e --- /dev/null +++ b/resolver/src/value.ts @@ -0,0 +1,29 @@ +import {Scope} from "./scope"; + +export interface IValue { + readonly value: null | string | number; + format(scope: Scope): null | string; +} + +export class NoneValue implements IValue { + value = null; + format(scope: Scope) { + return null; + } +} + +export class StringValue implements IValue { + constructor(readonly value: string) {} + + format(scope: Scope) { + return this.value; + } +} + +export class NumberValue implements IValue { + constructor(readonly value: number) {} + + format(scope: Scope) { + return this.value.toString(); + } +} From 481a09daccde5c0f4fb7da11a19b87553d2d5501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 30 May 2019 17:28:49 +0200 Subject: [PATCH 10/36] Add IMessageReference --- resolver/src/ast.ts | 16 ++++++++++++++- resolver/src/index.ts | 47 ++++++++++++++++++++++++++++++++++++------- resolver/src/scope.ts | 23 +++++++++++++++++++-- 3 files changed, 76 insertions(+), 10 deletions(-) diff --git a/resolver/src/ast.ts b/resolver/src/ast.ts index f0f2307..f8ff3dd 100644 --- a/resolver/src/ast.ts +++ b/resolver/src/ast.ts @@ -1,9 +1,11 @@ export enum SyntaxNode { Identifer = "Identifier", VariableReference = "VariableReference", + MessageReference = "MessageReference", TextElement = "TextElement", Placeable = "Placeable", Pattern = "Pattern", + Message = "Message", } export interface ISyntaxNode { @@ -20,6 +22,12 @@ export interface IVariableReference extends ISyntaxNode { readonly id: IIdentifier; } +export interface IMessageReference extends ISyntaxNode { + readonly type: SyntaxNode.MessageReference; + readonly id: IIdentifier; + readonly attribute: IIdentifier | null; +} + export interface ITextElement extends ISyntaxNode { readonly type: SyntaxNode.TextElement; readonly value: string; @@ -27,10 +35,16 @@ export interface ITextElement extends ISyntaxNode { export interface IPlaceable extends ISyntaxNode { readonly type: SyntaxNode.Placeable; - readonly expression: IVariableReference; + readonly expression: IVariableReference | IMessageReference; } export interface IPattern extends ISyntaxNode { readonly type: SyntaxNode.Pattern; readonly elements: Array; } + +export interface IMessage extends ISyntaxNode { + readonly type: SyntaxNode.Message; + readonly id: IIdentifier; + readonly value: IPattern | null; +} diff --git a/resolver/src/index.ts b/resolver/src/index.ts index 7000eda..bbb01c0 100644 --- a/resolver/src/index.ts +++ b/resolver/src/index.ts @@ -2,7 +2,7 @@ import * as ast from "./ast"; import {Scope} from "./scope"; import {StringValue} from "./value"; -let message = { +let hello = { type: "Message", id: { type: "Identifer", @@ -21,22 +21,55 @@ let message = { type: "VariableReference", id: { type: "Identifier", - name: "userName", + name: "world", }, }, }, ], }, - attributes: [], - comment: null, }; +let exclamation = { + type: "Message", + id: { + type: "Identifier", + name: "exclamation", + }, + value: { + type: "Pattern", + elements: [ + { + type: "Placeable", + expression: { + type: "MessageReference", + id: { + type: "Identifier", + name: "hello", + }, + attribute: null, + }, + }, + { + type: "TextElement", + value: "!", + }, + ], + }, +}; + +let messages = new Map( + Object.entries({ + hello: hello as ast.IMessage, + exclamation: exclamation as ast.IMessage, + }) +); + let variables = new Map( Object.entries({ - username: new StringValue("Anne"), + world: new StringValue("World"), }) ); -let scope = new Scope(variables); -let value = scope.resolve(message.value as ast.IPattern); +let scope = new Scope(messages, variables); +let value = scope.resolve(exclamation.value as ast.IPattern); console.log(value); console.log(scope.errors); diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index c60d25f..ad466fd 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -3,10 +3,12 @@ import {IValue, StringValue} from "./value"; import {IResult, Success, Failure} from "./result"; export class Scope { + private readonly messages: Map; private readonly variables: Map; public errors: Array; - constructor(variables: Map) { + constructor(messages: Map, variables: Map) { + this.messages = messages; this.variables = variables; this.errors = []; } @@ -15,6 +17,8 @@ export class Scope { switch (node.type) { case ast.SyntaxNode.VariableReference: return this.resolveVariableReference(node as ast.IVariableReference); + case ast.SyntaxNode.MessageReference: + return this.resolveMessageReference(node as ast.IMessageReference); case ast.SyntaxNode.TextElement: return this.resolveTextElement(node as ast.ITextElement); case ast.SyntaxNode.Placeable: @@ -31,11 +35,26 @@ export class Scope { if (value !== undefined) { return new Success(value); } else { - this.errors.push("Missing variable"); + this.errors.push(`Unknown variable: $${node.id.name}.`); return new Failure(new StringValue(`$${node.id.name}`)); } } + resolveMessageReference(node: ast.IMessageReference): IResult { + let message = this.messages.get(node.id.name); + if (message !== undefined) { + if (message.value !== null) { + return this.resolve(message.value); + } else { + this.errors.push(`Message ${node.id.name} has a null value.`); + return new Failure(new StringValue(`${node.id.name}`)); + } + } else { + this.errors.push(`Unknown message: ${node.id.name}.`); + return new Failure(new StringValue(`${node.id.name}`)); + } + } + resolveTextElement(node: ast.ITextElement): IResult { return new Success(new StringValue(node.value)); } From d8958b1a991a3b27b4a9ab16c16edb7b3f1e4728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 30 May 2019 17:55:45 +0200 Subject: [PATCH 11/36] Add the runtime Message class --- resolver/src/index.ts | 14 +++++++++----- resolver/src/message.ts | 23 +++++++++++++++++++++++ resolver/src/scope.ts | 12 ++++-------- 3 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 resolver/src/message.ts diff --git a/resolver/src/index.ts b/resolver/src/index.ts index bbb01c0..62e6ab9 100644 --- a/resolver/src/index.ts +++ b/resolver/src/index.ts @@ -1,6 +1,7 @@ import * as ast from "./ast"; import {Scope} from "./scope"; import {StringValue} from "./value"; +import {Message} from "./message"; let hello = { type: "Message", @@ -59,8 +60,8 @@ let exclamation = { let messages = new Map( Object.entries({ - hello: hello as ast.IMessage, - exclamation: exclamation as ast.IMessage, + hello: new Message(hello as ast.IMessage), + exclamation: new Message(exclamation as ast.IMessage), }) ); @@ -70,6 +71,9 @@ let variables = new Map( }) ); let scope = new Scope(messages, variables); -let value = scope.resolve(exclamation.value as ast.IPattern); -console.log(value); -console.log(scope.errors); +let message = messages.get("exclamation"); +if (message !== undefined) { + let value = message.resolveValue(scope); + console.log(value); + console.log(scope.errors); +} diff --git a/resolver/src/message.ts b/resolver/src/message.ts new file mode 100644 index 0000000..1ff21e3 --- /dev/null +++ b/resolver/src/message.ts @@ -0,0 +1,23 @@ +import * as ast from "./ast"; +import {Scope} from "./scope"; +import {Failure, IResult} from "./result"; +import {IValue, StringValue} from "./value"; + +export class Message { + private readonly id: string; + private readonly value: ast.IPattern | null; + + constructor(node: ast.IMessage) { + this.id = node.id.name; + this.value = node.value; + } + + resolveValue(scope: Scope): IResult { + if (this.value !== null) { + return scope.resolve(this.value); + } else { + scope.errors.push(`Message ${this.id} has a null value.`); + return new Failure(new StringValue(this.id)); + } + } +} diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index ad466fd..530f5b2 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -1,13 +1,14 @@ import * as ast from "./ast"; +import {Message} from "./message"; import {IValue, StringValue} from "./value"; import {IResult, Success, Failure} from "./result"; export class Scope { - private readonly messages: Map; + private readonly messages: Map; private readonly variables: Map; public errors: Array; - constructor(messages: Map, variables: Map) { + constructor(messages: Map, variables: Map) { this.messages = messages; this.variables = variables; this.errors = []; @@ -43,12 +44,7 @@ export class Scope { resolveMessageReference(node: ast.IMessageReference): IResult { let message = this.messages.get(node.id.name); if (message !== undefined) { - if (message.value !== null) { - return this.resolve(message.value); - } else { - this.errors.push(`Message ${node.id.name} has a null value.`); - return new Failure(new StringValue(`${node.id.name}`)); - } + return message.resolveValue(this); } else { this.errors.push(`Unknown message: ${node.id.name}.`); return new Failure(new StringValue(`${node.id.name}`)); From 376dd3caabb4a42857309d6e0d989d284ddd5c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 30 May 2019 19:42:19 +0200 Subject: [PATCH 12/36] Move fixtures into a new file --- resolver/src/fixtures.ts | 54 ++++++++++++++++++++++++++++++++++++++ resolver/src/index.ts | 56 +--------------------------------------- 2 files changed, 55 insertions(+), 55 deletions(-) create mode 100644 resolver/src/fixtures.ts diff --git a/resolver/src/fixtures.ts b/resolver/src/fixtures.ts new file mode 100644 index 0000000..1f0e391 --- /dev/null +++ b/resolver/src/fixtures.ts @@ -0,0 +1,54 @@ +export let hello = { + type: "Message", + id: { + type: "Identifer", + name: "hello", + }, + value: { + type: "Pattern", + elements: [ + { + type: "TextElement", + value: "Hello, ", + }, + { + type: "Placeable", + expression: { + type: "VariableReference", + id: { + type: "Identifier", + name: "world", + }, + }, + }, + ], + }, +}; + +export let exclamation = { + type: "Message", + id: { + type: "Identifier", + name: "exclamation", + }, + value: { + type: "Pattern", + elements: [ + { + type: "Placeable", + expression: { + type: "MessageReference", + id: { + type: "Identifier", + name: "hello", + }, + attribute: null, + }, + }, + { + type: "TextElement", + value: "!", + }, + ], + }, +}; diff --git a/resolver/src/index.ts b/resolver/src/index.ts index 62e6ab9..dcb5506 100644 --- a/resolver/src/index.ts +++ b/resolver/src/index.ts @@ -2,61 +2,7 @@ import * as ast from "./ast"; import {Scope} from "./scope"; import {StringValue} from "./value"; import {Message} from "./message"; - -let hello = { - type: "Message", - id: { - type: "Identifer", - name: "hello", - }, - value: { - type: "Pattern", - elements: [ - { - type: "TextElement", - value: "Hello, ", - }, - { - type: "Placeable", - expression: { - type: "VariableReference", - id: { - type: "Identifier", - name: "world", - }, - }, - }, - ], - }, -}; - -let exclamation = { - type: "Message", - id: { - type: "Identifier", - name: "exclamation", - }, - value: { - type: "Pattern", - elements: [ - { - type: "Placeable", - expression: { - type: "MessageReference", - id: { - type: "Identifier", - name: "hello", - }, - attribute: null, - }, - }, - { - type: "TextElement", - value: "!", - }, - ], - }, -}; +import {hello, exclamation} from "./fixtures"; let messages = new Map( Object.entries({ From 596cdddfa57d73a690c7cea9affbe6a7d7e8a8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 30 May 2019 22:47:30 +0200 Subject: [PATCH 13/36] Add ISelectExpression --- resolver/src/ast.ts | 17 +++++++- resolver/src/fixtures.ts | 84 ++++++++++++++++++++++++++++++++++++++++ resolver/src/index.ts | 6 ++- resolver/src/scope.ts | 24 ++++++++++++ 4 files changed, 128 insertions(+), 3 deletions(-) diff --git a/resolver/src/ast.ts b/resolver/src/ast.ts index f8ff3dd..87b60b7 100644 --- a/resolver/src/ast.ts +++ b/resolver/src/ast.ts @@ -2,8 +2,10 @@ export enum SyntaxNode { Identifer = "Identifier", VariableReference = "VariableReference", MessageReference = "MessageReference", + SelectExpression = "SelectExpression", TextElement = "TextElement", Placeable = "Placeable", + Variant = "Variant", Pattern = "Pattern", Message = "Message", } @@ -28,6 +30,12 @@ export interface IMessageReference extends ISyntaxNode { readonly attribute: IIdentifier | null; } +export interface ISelectExpression extends ISyntaxNode { + readonly type: SyntaxNode.SelectExpression; + readonly selector: IVariableReference; + readonly variants: Array; +} + export interface ITextElement extends ISyntaxNode { readonly type: SyntaxNode.TextElement; readonly value: string; @@ -35,7 +43,14 @@ export interface ITextElement extends ISyntaxNode { export interface IPlaceable extends ISyntaxNode { readonly type: SyntaxNode.Placeable; - readonly expression: IVariableReference | IMessageReference; + readonly expression: IVariableReference | IMessageReference | ISelectExpression; +} + +export interface IVariant extends ISyntaxNode { + readonly type: SyntaxNode.Variant; + readonly key: IIdentifier; + readonly value: IPattern; + readonly default: boolean; } export interface IPattern extends ISyntaxNode { diff --git a/resolver/src/fixtures.ts b/resolver/src/fixtures.ts index 1f0e391..56be5da 100644 --- a/resolver/src/fixtures.ts +++ b/resolver/src/fixtures.ts @@ -52,3 +52,87 @@ export let exclamation = { ], }, }; + +export let select = { + type: "Message", + id: { + type: "Identifier", + name: "select", + }, + value: { + type: "Pattern", + elements: [ + { + type: "Placeable", + expression: { + type: "SelectExpression", + selector: { + type: "VariableReference", + id: { + type: "Identifier", + name: "selector", + }, + }, + variants: [ + { + type: "Variant", + key: { + type: "Identifier", + name: "a", + }, + value: { + type: "Pattern", + elements: [ + { + type: "TextElement", + value: "(a) ", + }, + { + type: "Placeable", + expression: { + type: "MessageReference", + id: { + type: "Identifier", + name: "hello", + }, + attribute: null, + }, + }, + ], + }, + default: true, + }, + { + type: "Variant", + key: { + type: "Identifier", + name: "b", + }, + value: { + type: "Pattern", + elements: [ + { + type: "TextElement", + value: "(b) ", + }, + { + type: "Placeable", + expression: { + type: "MessageReference", + id: { + type: "Identifier", + name: "exclamation", + }, + attribute: null, + }, + }, + ], + }, + default: false, + }, + ], + }, + }, + ], + }, +}; diff --git a/resolver/src/index.ts b/resolver/src/index.ts index dcb5506..167d71f 100644 --- a/resolver/src/index.ts +++ b/resolver/src/index.ts @@ -2,22 +2,24 @@ import * as ast from "./ast"; import {Scope} from "./scope"; import {StringValue} from "./value"; import {Message} from "./message"; -import {hello, exclamation} from "./fixtures"; +import {hello, exclamation, select} from "./fixtures"; let messages = new Map( Object.entries({ hello: new Message(hello as ast.IMessage), exclamation: new Message(exclamation as ast.IMessage), + select: new Message(select as ast.IMessage), }) ); let variables = new Map( Object.entries({ world: new StringValue("World"), + selector: new StringValue("b"), }) ); let scope = new Scope(messages, variables); -let message = messages.get("exclamation"); +let message = messages.get("select"); if (message !== undefined) { let value = message.resolveValue(scope); console.log(value); diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index 530f5b2..f2f82d0 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -20,6 +20,8 @@ export class Scope { return this.resolveVariableReference(node as ast.IVariableReference); case ast.SyntaxNode.MessageReference: return this.resolveMessageReference(node as ast.IMessageReference); + case ast.SyntaxNode.SelectExpression: + return this.resolveSelectExpression(node as ast.ISelectExpression); case ast.SyntaxNode.TextElement: return this.resolveTextElement(node as ast.ITextElement); case ast.SyntaxNode.Placeable: @@ -51,6 +53,28 @@ export class Scope { } } + resolveDefaultVariant(node: ast.ISelectExpression): IResult { + for (let variant of node.variants) { + if (variant.default) { + return this.resolve(variant.value); + } + } + throw new RangeError("Missing default variant."); + } + + resolveSelectExpression(node: ast.ISelectExpression): IResult { + return this.resolve(node.selector) + .then(selector => { + for (let variant of node.variants) { + if (variant.key.name === selector.value) { + return this.resolve(variant.value); + } + } + return this.resolveDefaultVariant(node); + }) + .else(_ => this.resolveDefaultVariant(node)); + } + resolveTextElement(node: ast.ITextElement): IResult { return new Success(new StringValue(node.value)); } From 3fadeb13d9d41dbdff22a33da34b50aa1a9099a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 30 May 2019 23:00:31 +0200 Subject: [PATCH 14/36] Remove the I prefix from interfaces --- resolver/src/ast.ts | 64 ++++++++++++++++++++--------------------- resolver/src/index.ts | 6 ++-- resolver/src/message.ts | 10 +++---- resolver/src/result.ts | 36 +++++++++++------------ resolver/src/scope.ts | 48 +++++++++++++++---------------- resolver/src/value.ts | 8 +++--- 6 files changed, 86 insertions(+), 86 deletions(-) diff --git a/resolver/src/ast.ts b/resolver/src/ast.ts index 87b60b7..1e2559b 100644 --- a/resolver/src/ast.ts +++ b/resolver/src/ast.ts @@ -1,4 +1,4 @@ -export enum SyntaxNode { +export enum NodeType { Identifer = "Identifier", VariableReference = "VariableReference", MessageReference = "MessageReference", @@ -10,56 +10,56 @@ export enum SyntaxNode { Message = "Message", } -export interface ISyntaxNode { - readonly type: SyntaxNode; +export interface SyntaxNode { + readonly type: NodeType; } -export interface IIdentifier extends ISyntaxNode { - readonly type: SyntaxNode.Identifer; +export interface Identifier extends SyntaxNode { + readonly type: NodeType.Identifer; readonly name: string; } -export interface IVariableReference extends ISyntaxNode { - readonly type: SyntaxNode.VariableReference; - readonly id: IIdentifier; +export interface VariableReference extends SyntaxNode { + readonly type: NodeType.VariableReference; + readonly id: Identifier; } -export interface IMessageReference extends ISyntaxNode { - readonly type: SyntaxNode.MessageReference; - readonly id: IIdentifier; - readonly attribute: IIdentifier | null; +export interface MessageReference extends SyntaxNode { + readonly type: NodeType.MessageReference; + readonly id: Identifier; + readonly attribute: Identifier | null; } -export interface ISelectExpression extends ISyntaxNode { - readonly type: SyntaxNode.SelectExpression; - readonly selector: IVariableReference; - readonly variants: Array; +export interface SelectExpression extends SyntaxNode { + readonly type: NodeType.SelectExpression; + readonly selector: VariableReference; + readonly variants: Array; } -export interface ITextElement extends ISyntaxNode { - readonly type: SyntaxNode.TextElement; +export interface TextElement extends SyntaxNode { + readonly type: NodeType.TextElement; readonly value: string; } -export interface IPlaceable extends ISyntaxNode { - readonly type: SyntaxNode.Placeable; - readonly expression: IVariableReference | IMessageReference | ISelectExpression; +export interface Placeable extends SyntaxNode { + readonly type: NodeType.Placeable; + readonly expression: VariableReference | MessageReference | SelectExpression; } -export interface IVariant extends ISyntaxNode { - readonly type: SyntaxNode.Variant; - readonly key: IIdentifier; - readonly value: IPattern; +export interface Variant extends SyntaxNode { + readonly type: NodeType.Variant; + readonly key: Identifier; + readonly value: Pattern; readonly default: boolean; } -export interface IPattern extends ISyntaxNode { - readonly type: SyntaxNode.Pattern; - readonly elements: Array; +export interface Pattern extends SyntaxNode { + readonly type: NodeType.Pattern; + readonly elements: Array; } -export interface IMessage extends ISyntaxNode { - readonly type: SyntaxNode.Message; - readonly id: IIdentifier; - readonly value: IPattern | null; +export interface Message extends SyntaxNode { + readonly type: NodeType.Message; + readonly id: Identifier; + readonly value: Pattern | null; } diff --git a/resolver/src/index.ts b/resolver/src/index.ts index 167d71f..955da93 100644 --- a/resolver/src/index.ts +++ b/resolver/src/index.ts @@ -6,9 +6,9 @@ import {hello, exclamation, select} from "./fixtures"; let messages = new Map( Object.entries({ - hello: new Message(hello as ast.IMessage), - exclamation: new Message(exclamation as ast.IMessage), - select: new Message(select as ast.IMessage), + hello: new Message(hello as ast.Message), + exclamation: new Message(exclamation as ast.Message), + select: new Message(select as ast.Message), }) ); diff --git a/resolver/src/message.ts b/resolver/src/message.ts index 1ff21e3..fda0921 100644 --- a/resolver/src/message.ts +++ b/resolver/src/message.ts @@ -1,18 +1,18 @@ import * as ast from "./ast"; import {Scope} from "./scope"; -import {Failure, IResult} from "./result"; -import {IValue, StringValue} from "./value"; +import {Failure, Result} from "./result"; +import {Value, StringValue} from "./value"; export class Message { private readonly id: string; - private readonly value: ast.IPattern | null; + private readonly value: ast.Pattern | null; - constructor(node: ast.IMessage) { + constructor(node: ast.Message) { this.id = node.id.name; this.value = node.value; } - resolveValue(scope: Scope): IResult { + resolveValue(scope: Scope): Result { if (this.value !== null) { return scope.resolve(this.value); } else { diff --git a/resolver/src/result.ts b/resolver/src/result.ts index 88c4490..5941875 100644 --- a/resolver/src/result.ts +++ b/resolver/src/result.ts @@ -1,46 +1,46 @@ -export interface IResult { - map(fn: IMapper): IResult; - then(fn: IChainer): IResult; - else(fn: IChainer): IResult; - fold(s: IMapper, f: IMapper): T; +export interface Result { + map(fn: Mapper): Result; + then(fn: Chainer): Result; + else(fn: Chainer): Result; + fold(s: Mapper, f: Mapper): T; } -interface IMapper { +interface Mapper { (value: T): T; } -interface IChainer { - (value: T): IResult; +interface Chainer { + (value: T): Result; } -export class Success implements IResult { +export class Success implements Result { constructor(private value: T) {} - map(fn: IMapper): IResult { + map(fn: Mapper): Result { return new Success(fn(this.value)); } - then(fn: IChainer) { + then(fn: Chainer) { return fn(this.value); } - else(fn: IChainer) { + else(fn: Chainer) { return this; } - fold(s: IMapper, f: IMapper): T { + fold(s: Mapper, f: Mapper): T { return s(this.value); } } -export class Failure implements IResult { +export class Failure implements Result { constructor(private value: T) {} - map(fn: IMapper): IResult { + map(fn: Mapper): Result { return this; } - then(fn: IChainer): IResult { + then(fn: Chainer): Result { return this; } - else(fn: IChainer): IResult { + else(fn: Chainer): Result { return fn(this.value); } - fold(s: IMapper, f: IMapper) { + fold(s: Mapper, f: Mapper) { return f(this.value); } } diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index f2f82d0..960e4f7 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -1,39 +1,39 @@ import * as ast from "./ast"; import {Message} from "./message"; -import {IValue, StringValue} from "./value"; -import {IResult, Success, Failure} from "./result"; +import {Value, StringValue} from "./value"; +import {Result, Success, Failure} from "./result"; export class Scope { private readonly messages: Map; - private readonly variables: Map; + private readonly variables: Map; public errors: Array; - constructor(messages: Map, variables: Map) { + constructor(messages: Map, variables: Map) { this.messages = messages; this.variables = variables; this.errors = []; } - resolve(node: ast.ISyntaxNode): IResult { + resolve(node: ast.SyntaxNode): Result { switch (node.type) { - case ast.SyntaxNode.VariableReference: - return this.resolveVariableReference(node as ast.IVariableReference); - case ast.SyntaxNode.MessageReference: - return this.resolveMessageReference(node as ast.IMessageReference); - case ast.SyntaxNode.SelectExpression: - return this.resolveSelectExpression(node as ast.ISelectExpression); - case ast.SyntaxNode.TextElement: - return this.resolveTextElement(node as ast.ITextElement); - case ast.SyntaxNode.Placeable: - return this.resolvePlaceable(node as ast.IPlaceable); - case ast.SyntaxNode.Pattern: - return this.resolvePattern(node as ast.IPattern); + case ast.NodeType.VariableReference: + return this.resolveVariableReference(node as ast.VariableReference); + case ast.NodeType.MessageReference: + return this.resolveMessageReference(node as ast.MessageReference); + case ast.NodeType.SelectExpression: + return this.resolveSelectExpression(node as ast.SelectExpression); + case ast.NodeType.TextElement: + return this.resolveTextElement(node as ast.TextElement); + case ast.NodeType.Placeable: + return this.resolvePlaceable(node as ast.Placeable); + case ast.NodeType.Pattern: + return this.resolvePattern(node as ast.Pattern); default: throw new TypeError("Unresolvable node type."); } } - resolveVariableReference(node: ast.IVariableReference): IResult { + resolveVariableReference(node: ast.VariableReference): Result { let value = this.variables.get(node.id.name); if (value !== undefined) { return new Success(value); @@ -43,7 +43,7 @@ export class Scope { } } - resolveMessageReference(node: ast.IMessageReference): IResult { + resolveMessageReference(node: ast.MessageReference): Result { let message = this.messages.get(node.id.name); if (message !== undefined) { return message.resolveValue(this); @@ -53,7 +53,7 @@ export class Scope { } } - resolveDefaultVariant(node: ast.ISelectExpression): IResult { + resolveDefaultVariant(node: ast.SelectExpression): Result { for (let variant of node.variants) { if (variant.default) { return this.resolve(variant.value); @@ -62,7 +62,7 @@ export class Scope { throw new RangeError("Missing default variant."); } - resolveSelectExpression(node: ast.ISelectExpression): IResult { + resolveSelectExpression(node: ast.SelectExpression): Result { return this.resolve(node.selector) .then(selector => { for (let variant of node.variants) { @@ -75,15 +75,15 @@ export class Scope { .else(_ => this.resolveDefaultVariant(node)); } - resolveTextElement(node: ast.ITextElement): IResult { + resolveTextElement(node: ast.TextElement): Result { return new Success(new StringValue(node.value)); } - resolvePlaceable(node: ast.IPlaceable): IResult { + resolvePlaceable(node: ast.Placeable): Result { return this.resolve(node.expression); } - resolvePattern(node: ast.IPattern): IResult { + resolvePattern(node: ast.Pattern): Result { return new Success( new StringValue( node.elements diff --git a/resolver/src/value.ts b/resolver/src/value.ts index c90797e..9f97bc0 100644 --- a/resolver/src/value.ts +++ b/resolver/src/value.ts @@ -1,18 +1,18 @@ import {Scope} from "./scope"; -export interface IValue { +export interface Value { readonly value: null | string | number; format(scope: Scope): null | string; } -export class NoneValue implements IValue { +export class NoneValue implements Value { value = null; format(scope: Scope) { return null; } } -export class StringValue implements IValue { +export class StringValue implements Value { constructor(readonly value: string) {} format(scope: Scope) { @@ -20,7 +20,7 @@ export class StringValue implements IValue { } } -export class NumberValue implements IValue { +export class NumberValue implements Value { constructor(readonly value: number) {} format(scope: Scope) { From 956786b86207604d0a1dc2e7f1609275023bcbd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 30 May 2019 23:08:27 +0200 Subject: [PATCH 15/36] Add PatternElement type alias --- resolver/src/ast.ts | 4 +++- resolver/src/message.ts | 2 +- resolver/src/scope.ts | 29 ++++++++++++----------------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/resolver/src/ast.ts b/resolver/src/ast.ts index 1e2559b..c452f7a 100644 --- a/resolver/src/ast.ts +++ b/resolver/src/ast.ts @@ -46,6 +46,8 @@ export interface Placeable extends SyntaxNode { readonly expression: VariableReference | MessageReference | SelectExpression; } +export type PatternElement = TextElement | Placeable; + export interface Variant extends SyntaxNode { readonly type: NodeType.Variant; readonly key: Identifier; @@ -55,7 +57,7 @@ export interface Variant extends SyntaxNode { export interface Pattern extends SyntaxNode { readonly type: NodeType.Pattern; - readonly elements: Array; + readonly elements: Array; } export interface Message extends SyntaxNode { diff --git a/resolver/src/message.ts b/resolver/src/message.ts index fda0921..c04a9b4 100644 --- a/resolver/src/message.ts +++ b/resolver/src/message.ts @@ -14,7 +14,7 @@ export class Message { resolveValue(scope: Scope): Result { if (this.value !== null) { - return scope.resolve(this.value); + return scope.resolvePattern(this.value); } else { scope.errors.push(`Message ${this.id} has a null value.`); return new Failure(new StringValue(this.id)); diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index 960e4f7..216ac53 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -14,7 +14,7 @@ export class Scope { this.errors = []; } - resolve(node: ast.SyntaxNode): Result { + resolveExpression(node: ast.SyntaxNode): Result { switch (node.type) { case ast.NodeType.VariableReference: return this.resolveVariableReference(node as ast.VariableReference); @@ -22,12 +22,6 @@ export class Scope { return this.resolveMessageReference(node as ast.MessageReference); case ast.NodeType.SelectExpression: return this.resolveSelectExpression(node as ast.SelectExpression); - case ast.NodeType.TextElement: - return this.resolveTextElement(node as ast.TextElement); - case ast.NodeType.Placeable: - return this.resolvePlaceable(node as ast.Placeable); - case ast.NodeType.Pattern: - return this.resolvePattern(node as ast.Pattern); default: throw new TypeError("Unresolvable node type."); } @@ -56,18 +50,18 @@ export class Scope { resolveDefaultVariant(node: ast.SelectExpression): Result { for (let variant of node.variants) { if (variant.default) { - return this.resolve(variant.value); + return this.resolvePattern(variant.value); } } throw new RangeError("Missing default variant."); } resolveSelectExpression(node: ast.SelectExpression): Result { - return this.resolve(node.selector) + return this.resolveExpression(node.selector) .then(selector => { for (let variant of node.variants) { if (variant.key.name === selector.value) { - return this.resolve(variant.value); + return this.resolvePattern(variant.value); } } return this.resolveDefaultVariant(node); @@ -75,12 +69,13 @@ export class Scope { .else(_ => this.resolveDefaultVariant(node)); } - resolveTextElement(node: ast.TextElement): Result { - return new Success(new StringValue(node.value)); - } - - resolvePlaceable(node: ast.Placeable): Result { - return this.resolve(node.expression); + resolvePatternElement(node: ast.PatternElement): Result { + switch (node.type) { + case ast.NodeType.TextElement: + return new Success(new StringValue(node.value)); + case ast.NodeType.Placeable: + return this.resolveExpression(node.expression); + } } resolvePattern(node: ast.Pattern): Result { @@ -88,7 +83,7 @@ export class Scope { new StringValue( node.elements .map(element => - this.resolve(element) + this.resolvePatternElement(element) .fold(value => value, value => new StringValue(`{${value.value}}`)) .format(this) ) From 655701c4d33a16a9ce656c3561d9c36bb4cfadb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 30 May 2019 23:14:55 +0200 Subject: [PATCH 16/36] Add Expression, InlineExpression --- resolver/src/ast.ts | 8 ++++++-- resolver/src/scope.ts | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/resolver/src/ast.ts b/resolver/src/ast.ts index c452f7a..5c2c509 100644 --- a/resolver/src/ast.ts +++ b/resolver/src/ast.ts @@ -32,10 +32,14 @@ export interface MessageReference extends SyntaxNode { export interface SelectExpression extends SyntaxNode { readonly type: NodeType.SelectExpression; - readonly selector: VariableReference; + readonly selector: InlineExpression; readonly variants: Array; } +export type InlineExpression = VariableReference | MessageReference; + +export type Expression = InlineExpression | SelectExpression; + export interface TextElement extends SyntaxNode { readonly type: NodeType.TextElement; readonly value: string; @@ -43,7 +47,7 @@ export interface TextElement extends SyntaxNode { export interface Placeable extends SyntaxNode { readonly type: NodeType.Placeable; - readonly expression: VariableReference | MessageReference | SelectExpression; + readonly expression: Expression; } export type PatternElement = TextElement | Placeable; diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index 216ac53..3857aeb 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -14,7 +14,7 @@ export class Scope { this.errors = []; } - resolveExpression(node: ast.SyntaxNode): Result { + resolveExpression(node: ast.Expression): Result { switch (node.type) { case ast.NodeType.VariableReference: return this.resolveVariableReference(node as ast.VariableReference); @@ -23,7 +23,7 @@ export class Scope { case ast.NodeType.SelectExpression: return this.resolveSelectExpression(node as ast.SelectExpression); default: - throw new TypeError("Unresolvable node type."); + throw new TypeError("Unknown node type."); } } @@ -75,6 +75,8 @@ export class Scope { return new Success(new StringValue(node.value)); case ast.NodeType.Placeable: return this.resolveExpression(node.expression); + default: + throw new TypeError("Unknown node type."); } } From 59d4f7b3b26b8d091b60ae886f2a9621307ed832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Fri, 31 May 2019 08:49:42 +0200 Subject: [PATCH 17/36] Don't use parameter properties in constructors --- resolver/src/result.ts | 10 ++++++++-- resolver/src/value.ts | 15 ++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/resolver/src/result.ts b/resolver/src/result.ts index 5941875..1bdc4db 100644 --- a/resolver/src/result.ts +++ b/resolver/src/result.ts @@ -14,7 +14,10 @@ interface Chainer { } export class Success implements Result { - constructor(private value: T) {} + private readonly value: T; + constructor(value: T) { + this.value = value; + } map(fn: Mapper): Result { return new Success(fn(this.value)); } @@ -30,7 +33,10 @@ export class Success implements Result { } export class Failure implements Result { - constructor(private value: T) {} + private readonly value: T; + constructor(value: T) { + this.value = value; + } map(fn: Mapper): Result { return this; } diff --git a/resolver/src/value.ts b/resolver/src/value.ts index 9f97bc0..75ee5a1 100644 --- a/resolver/src/value.ts +++ b/resolver/src/value.ts @@ -6,14 +6,19 @@ export interface Value { } export class NoneValue implements Value { - value = null; + readonly value = null; + format(scope: Scope) { return null; } } export class StringValue implements Value { - constructor(readonly value: string) {} + readonly value: string; + + constructor(value: string) { + this.value = value; + } format(scope: Scope) { return this.value; @@ -21,7 +26,11 @@ export class StringValue implements Value { } export class NumberValue implements Value { - constructor(readonly value: number) {} + readonly value: number; + + constructor(value: number) { + this.value = value; + } format(scope: Scope) { return this.value.toString(); From 67baf2b3998c340f55fe2a603c4c14feabb5e853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Fri, 31 May 2019 15:54:47 +0200 Subject: [PATCH 18/36] Add Bundle --- resolver/src/bundle.ts | 36 ++++++++++++++++++++++++++++++++++++ resolver/src/index.ts | 25 ++++++++----------------- 2 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 resolver/src/bundle.ts diff --git a/resolver/src/bundle.ts b/resolver/src/bundle.ts new file mode 100644 index 0000000..0d95c87 --- /dev/null +++ b/resolver/src/bundle.ts @@ -0,0 +1,36 @@ +import * as ast from "./ast"; +import {Scope} from "./scope"; +import {Value, NoneValue} from "./value"; +import {Message} from "./message"; +import {hello, exclamation, select} from "./fixtures"; + +export interface Formatted { + readonly value: string | null; + readonly errors: Array; +} + +export class Bundle { + messages = new Map( + Object.entries({ + hello: new Message(hello as ast.Message), + exclamation: new Message(exclamation as ast.Message), + select: new Message(select as ast.Message), + }) + ); + + getMessage(id: string) { + return this.messages.get(id); + } + + private createScope(variables: Map) { + return new Scope(this.messages, variables); + } + + formatValue(message: Message, variables: Map): Formatted { + let scope = this.createScope(variables); + let result = message.resolveValue(scope); + console.log(result); + let value = result.fold(value => value, _ => new NoneValue()).format(scope); + return {value, errors: scope.errors}; + } +} diff --git a/resolver/src/index.ts b/resolver/src/index.ts index 955da93..08f3d65 100644 --- a/resolver/src/index.ts +++ b/resolver/src/index.ts @@ -1,27 +1,18 @@ -import * as ast from "./ast"; -import {Scope} from "./scope"; +import {Bundle} from "./bundle"; import {StringValue} from "./value"; -import {Message} from "./message"; -import {hello, exclamation, select} from "./fixtures"; - -let messages = new Map( - Object.entries({ - hello: new Message(hello as ast.Message), - exclamation: new Message(exclamation as ast.Message), - select: new Message(select as ast.Message), - }) -); let variables = new Map( Object.entries({ - world: new StringValue("World"), + worl: new StringValue("World"), selector: new StringValue("b"), }) ); -let scope = new Scope(messages, variables); -let message = messages.get("select"); + +let bundle = new Bundle(); + +let message = bundle.getMessage("select"); if (message !== undefined) { - let value = message.resolveValue(scope); + let {value, errors} = bundle.formatValue(message, variables); console.log(value); - console.log(scope.errors); + console.log(errors); } From 45b4d6b1a425c40b08194a56f1c777c701f53e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Tue, 4 Jun 2019 10:17:22 +0200 Subject: [PATCH 19/36] Use a tagged union for Result --- resolver/src/result.ts | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/resolver/src/result.ts b/resolver/src/result.ts index 1bdc4db..47ae0e5 100644 --- a/resolver/src/result.ts +++ b/resolver/src/result.ts @@ -1,52 +1,54 @@ -export interface Result { - map(fn: Mapper): Result; - then(fn: Chainer): Result; - else(fn: Chainer): Result; - fold(s: Mapper, f: Mapper): T; +interface IResult { + map(fn: IMapper): IResult; + then(fn: IChainer): IResult; + else(fn: IChainer): IResult; + fold(s: IMapper, f: IMapper): T; } -interface Mapper { +interface IMapper { (value: T): T; } -interface Chainer { +interface IChainer { (value: T): Result; } -export class Success implements Result { +export type Result = Success | Failure; + +export class Success implements IResult { private readonly value: T; constructor(value: T) { this.value = value; } - map(fn: Mapper): Result { + map(fn: IMapper) { return new Success(fn(this.value)); } - then(fn: Chainer) { + then(fn: IChainer) { return fn(this.value); } - else(fn: Chainer) { + else(fn: IChainer) { return this; } - fold(s: Mapper, f: Mapper): T { + fold(s: IMapper, f: IMapper) { return s(this.value); } } -export class Failure implements Result { +export class Failure implements IResult { private readonly value: T; constructor(value: T) { this.value = value; } - map(fn: Mapper): Result { + map(fn: IMapper) { return this; } - then(fn: Chainer): Result { + then(fn: IChainer) { return this; } - else(fn: Chainer): Result { + else(fn: IChainer) { return fn(this.value); } - fold(s: Mapper, f: Mapper) { + fold(s: IMapper, f: IMapper) { return f(this.value); } } From 05f3de524fb2f60ee5808aae890be84fe98f2b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Tue, 4 Jun 2019 10:19:17 +0200 Subject: [PATCH 20/36] Remove Result.map --- resolver/src/result.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/resolver/src/result.ts b/resolver/src/result.ts index 47ae0e5..a3ac144 100644 --- a/resolver/src/result.ts +++ b/resolver/src/result.ts @@ -1,5 +1,4 @@ interface IResult { - map(fn: IMapper): IResult; then(fn: IChainer): IResult; else(fn: IChainer): IResult; fold(s: IMapper, f: IMapper): T; @@ -20,9 +19,6 @@ export class Success implements IResult { constructor(value: T) { this.value = value; } - map(fn: IMapper) { - return new Success(fn(this.value)); - } then(fn: IChainer) { return fn(this.value); } @@ -39,9 +35,6 @@ export class Failure implements IResult { constructor(value: T) { this.value = value; } - map(fn: IMapper) { - return this; - } then(fn: IChainer) { return this; } From 8a5d3519ab61d9b589491a3a4d750890f1d18675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Tue, 4 Jun 2019 10:22:27 +0200 Subject: [PATCH 21/36] Use Rusty names of Result's methods --- resolver/src/bundle.ts | 2 +- resolver/src/result.ts | 22 +++++++++++----------- resolver/src/scope.ts | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/resolver/src/bundle.ts b/resolver/src/bundle.ts index 0d95c87..95eba43 100644 --- a/resolver/src/bundle.ts +++ b/resolver/src/bundle.ts @@ -30,7 +30,7 @@ export class Bundle { let scope = this.createScope(variables); let result = message.resolveValue(scope); console.log(result); - let value = result.fold(value => value, _ => new NoneValue()).format(scope); + let value = result.unwrapOrElse(() => new NoneValue()).format(scope); return {value, errors: scope.errors}; } } diff --git a/resolver/src/result.ts b/resolver/src/result.ts index a3ac144..711f0af 100644 --- a/resolver/src/result.ts +++ b/resolver/src/result.ts @@ -1,7 +1,7 @@ interface IResult { - then(fn: IChainer): IResult; - else(fn: IChainer): IResult; - fold(s: IMapper, f: IMapper): T; + andThen(fn: IChainer): IResult; + orElse(fn: IChainer): IResult; + unwrapOrElse(fn: IMapper): T; } interface IMapper { @@ -19,14 +19,14 @@ export class Success implements IResult { constructor(value: T) { this.value = value; } - then(fn: IChainer) { + andThen(fn: IChainer) { return fn(this.value); } - else(fn: IChainer) { + orElse(fn: IChainer) { return this; } - fold(s: IMapper, f: IMapper) { - return s(this.value); + unwrapOrElse(fn: IMapper) { + return this.value; } } @@ -35,13 +35,13 @@ export class Failure implements IResult { constructor(value: T) { this.value = value; } - then(fn: IChainer) { + andThen(fn: IChainer) { return this; } - else(fn: IChainer) { + orElse(fn: IChainer) { return fn(this.value); } - fold(s: IMapper, f: IMapper) { - return f(this.value); + unwrapOrElse(fn: IMapper) { + return fn(this.value); } } diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index 3857aeb..33d7bbf 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -58,7 +58,7 @@ export class Scope { resolveSelectExpression(node: ast.SelectExpression): Result { return this.resolveExpression(node.selector) - .then(selector => { + .andThen(selector => { for (let variant of node.variants) { if (variant.key.name === selector.value) { return this.resolvePattern(variant.value); @@ -66,7 +66,7 @@ export class Scope { } return this.resolveDefaultVariant(node); }) - .else(_ => this.resolveDefaultVariant(node)); + .orElse(_ => this.resolveDefaultVariant(node)); } resolvePatternElement(node: ast.PatternElement): Result { @@ -86,7 +86,7 @@ export class Scope { node.elements .map(element => this.resolvePatternElement(element) - .fold(value => value, value => new StringValue(`{${value.value}}`)) + .unwrapOrElse(value => new StringValue(`{${value.value}}`)) .format(this) ) .join("") From d0a7fe27908f6532407086f04ddfd59dca1a6572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Tue, 4 Jun 2019 10:58:56 +0200 Subject: [PATCH 22/36] Use inline callback types --- resolver/src/result.ts | 26 +++++++++----------------- resolver/src/scope.ts | 2 +- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/resolver/src/result.ts b/resolver/src/result.ts index 711f0af..9ce843f 100644 --- a/resolver/src/result.ts +++ b/resolver/src/result.ts @@ -1,15 +1,7 @@ interface IResult { - andThen(fn: IChainer): IResult; - orElse(fn: IChainer): IResult; - unwrapOrElse(fn: IMapper): T; -} - -interface IMapper { - (value: T): T; -} - -interface IChainer { - (value: T): Result; + andThen(fn: (value: T) => IResult): IResult; + orElse(fn: (value: T) => IResult): IResult; + unwrapOrElse(fn: (value: T) => T): T; } export type Result = Success | Failure; @@ -19,13 +11,13 @@ export class Success implements IResult { constructor(value: T) { this.value = value; } - andThen(fn: IChainer) { + andThen(fn: (value: T) => Result): Result { return fn(this.value); } - orElse(fn: IChainer) { + orElse(fn: (value: T) => Result): Result { return this; } - unwrapOrElse(fn: IMapper) { + unwrapOrElse(fn: (value: T) => T) { return this.value; } } @@ -35,13 +27,13 @@ export class Failure implements IResult { constructor(value: T) { this.value = value; } - andThen(fn: IChainer) { + andThen(fn: (value: T) => Result): Result { return this; } - orElse(fn: IChainer) { + orElse(fn: (value: T) => Result): Result { return fn(this.value); } - unwrapOrElse(fn: IMapper) { + unwrapOrElse(fn: (value: T) => T) { return fn(this.value); } } diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index 33d7bbf..2e658a0 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -66,7 +66,7 @@ export class Scope { } return this.resolveDefaultVariant(node); }) - .orElse(_ => this.resolveDefaultVariant(node)); + .orElse(() => this.resolveDefaultVariant(node)); } resolvePatternElement(node: ast.PatternElement): Result { From 2d9a3eab6554e4fb666b5f01ee49f223f80adbab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Tue, 4 Jun 2019 14:36:13 +0200 Subject: [PATCH 23/36] Add ScopeError stub --- resolver/src/bundle.ts | 3 ++- resolver/src/error.ts | 16 ++++++++++++++++ resolver/src/message.ts | 3 ++- resolver/src/scope.ts | 7 ++++--- 4 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 resolver/src/error.ts diff --git a/resolver/src/bundle.ts b/resolver/src/bundle.ts index 95eba43..67ddccc 100644 --- a/resolver/src/bundle.ts +++ b/resolver/src/bundle.ts @@ -3,10 +3,11 @@ import {Scope} from "./scope"; import {Value, NoneValue} from "./value"; import {Message} from "./message"; import {hello, exclamation, select} from "./fixtures"; +import {ScopeError} from "./error"; export interface Formatted { readonly value: string | null; - readonly errors: Array; + readonly errors: Array; } export class Bundle { diff --git a/resolver/src/error.ts b/resolver/src/error.ts new file mode 100644 index 0000000..85bc1c9 --- /dev/null +++ b/resolver/src/error.ts @@ -0,0 +1,16 @@ +export enum ErrorKind { + UnknownVariable, + UnknownMessage, + MissingValue, +} + +export class ScopeError extends Error { + public kind: ErrorKind; + public arg: string; + + constructor(kind: ErrorKind, arg: string) { + super(); + this.kind = kind; + this.arg = arg; + } +} diff --git a/resolver/src/message.ts b/resolver/src/message.ts index c04a9b4..e5ac00f 100644 --- a/resolver/src/message.ts +++ b/resolver/src/message.ts @@ -2,6 +2,7 @@ import * as ast from "./ast"; import {Scope} from "./scope"; import {Failure, Result} from "./result"; import {Value, StringValue} from "./value"; +import {ScopeError, ErrorKind} from "./error"; export class Message { private readonly id: string; @@ -16,7 +17,7 @@ export class Message { if (this.value !== null) { return scope.resolvePattern(this.value); } else { - scope.errors.push(`Message ${this.id} has a null value.`); + scope.errors.push(new ScopeError(ErrorKind.MissingValue, this.id)); return new Failure(new StringValue(this.id)); } } diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index 2e658a0..ef525ed 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -2,11 +2,12 @@ import * as ast from "./ast"; import {Message} from "./message"; import {Value, StringValue} from "./value"; import {Result, Success, Failure} from "./result"; +import {ScopeError, ErrorKind} from "./error"; export class Scope { private readonly messages: Map; private readonly variables: Map; - public errors: Array; + public errors: Array; constructor(messages: Map, variables: Map) { this.messages = messages; @@ -32,7 +33,7 @@ export class Scope { if (value !== undefined) { return new Success(value); } else { - this.errors.push(`Unknown variable: $${node.id.name}.`); + this.errors.push(new ScopeError(ErrorKind.UnknownVariable, node.id.name)); return new Failure(new StringValue(`$${node.id.name}`)); } } @@ -42,7 +43,7 @@ export class Scope { if (message !== undefined) { return message.resolveValue(this); } else { - this.errors.push(`Unknown message: ${node.id.name}.`); + this.errors.push(new ScopeError(ErrorKind.UnknownMessage, node.id.name)); return new Failure(new StringValue(`${node.id.name}`)); } } From 138e0c7819f68b7455b55adef03179c6987026e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Tue, 4 Jun 2019 16:17:04 +0200 Subject: [PATCH 24/36] Add a few sample tests --- .vscode/tasks.json | 14 + resolver/package-lock.json | 1051 ++++++++++++++++++++++++++++ resolver/package.json | 19 + resolver/src/bundle.ts | 22 +- resolver/src/index.ts | 20 +- resolver/src/scope.ts | 5 +- resolver/{src => test}/fixtures.ts | 6 + resolver/test/sample_test.ts | 170 +++++ 8 files changed, 1277 insertions(+), 30 deletions(-) create mode 100644 .vscode/tasks.json create mode 100644 resolver/package-lock.json create mode 100644 resolver/package.json rename resolver/{src => test}/fixtures.ts (97%) create mode 100644 resolver/test/sample_test.ts diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..4a0c45d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,14 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "path": "resolver/", + "problemMatcher": [ + "$tsc-watch" + ], + "isBackground": true + } + ] +} diff --git a/resolver/package-lock.json b/resolver/package-lock.json new file mode 100644 index 0000000..1a38f72 --- /dev/null +++ b/resolver/package-lock.json @@ -0,0 +1,1051 @@ +{ + "name": "fluent-reference-resolver", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, + "@types/node": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.4.tgz", + "integrity": "sha512-j8YL2C0fXq7IONwl/Ud5Kt0PeXw22zGERt+HSSnwbKOJVsAGkEz3sFCYwaF9IOuoG1HOtE0vKCj6sXF7Q0+Vaw==", + "dev": true + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "arg": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", + "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", + "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", + "dev": true, + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.2.2", + "yargs-parser": "13.0.0", + "yargs-unparser": "1.5.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "source-map-support": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "ts-node": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.2.0.tgz", + "integrity": "sha512-m8XQwUurkbYqXrKqr3WHCW310utRNvV5OnRVeISeea7LoCWVcdfeB/Ntl8JYWFh+WRoUAdBgESrzKochQt7sMw==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + }, + "dependencies": { + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + } + } + }, + "typescript": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.1.tgz", + "integrity": "sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", + "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + }, + "dependencies": { + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "yn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.0.tgz", + "integrity": "sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg==", + "dev": true + } + } +} diff --git a/resolver/package.json b/resolver/package.json new file mode 100644 index 0000000..0002ba2 --- /dev/null +++ b/resolver/package.json @@ -0,0 +1,19 @@ +{ + "name": "fluent-reference-resolver", + "private": true, + "version": "0.0.1", + "description": "A reference resolver for Fluent AST", + "scripts": { + "watch": "tsc --watch", + "test": "mocha -u tdd -r ts-node/register test/**/*_test.ts" + }, + "author": "Mozilla ", + "license": "Apache-2.0", + "devDependencies": { + "@types/mocha": "^5.2.7", + "@types/node": "^12.0.4", + "mocha": "^6.1.4", + "ts-node": "^8.2.0", + "typescript": "^3.5.1" + } +} diff --git a/resolver/src/bundle.ts b/resolver/src/bundle.ts index 67ddccc..d8cea87 100644 --- a/resolver/src/bundle.ts +++ b/resolver/src/bundle.ts @@ -2,7 +2,6 @@ import * as ast from "./ast"; import {Scope} from "./scope"; import {Value, NoneValue} from "./value"; import {Message} from "./message"; -import {hello, exclamation, select} from "./fixtures"; import {ScopeError} from "./error"; export interface Formatted { @@ -11,13 +10,13 @@ export interface Formatted { } export class Bundle { - messages = new Map( - Object.entries({ - hello: new Message(hello as ast.Message), - exclamation: new Message(exclamation as ast.Message), - select: new Message(select as ast.Message), - }) - ); + public readonly messages: Map = new Map(); + + addResource(resource: Map) { + for (let [id, message] of resource) { + this.messages.set(id, message); + } + } getMessage(id: string) { return this.messages.get(id); @@ -29,9 +28,10 @@ export class Bundle { formatValue(message: Message, variables: Map): Formatted { let scope = this.createScope(variables); - let result = message.resolveValue(scope); - console.log(result); - let value = result.unwrapOrElse(() => new NoneValue()).format(scope); + let value = message + .resolveValue(scope) + .unwrapOrElse(() => new NoneValue()) + .format(scope); return {value, errors: scope.errors}; } } diff --git a/resolver/src/index.ts b/resolver/src/index.ts index 08f3d65..01fad38 100644 --- a/resolver/src/index.ts +++ b/resolver/src/index.ts @@ -1,18 +1,4 @@ -import {Bundle} from "./bundle"; -import {StringValue} from "./value"; +import * as _ast from "./ast"; +export let ast = _ast; -let variables = new Map( - Object.entries({ - worl: new StringValue("World"), - selector: new StringValue("b"), - }) -); - -let bundle = new Bundle(); - -let message = bundle.getMessage("select"); -if (message !== undefined) { - let {value, errors} = bundle.formatValue(message, variables); - console.log(value); - console.log(errors); -} +export {Bundle} from "./bundle"; diff --git a/resolver/src/scope.ts b/resolver/src/scope.ts index ef525ed..78824a7 100644 --- a/resolver/src/scope.ts +++ b/resolver/src/scope.ts @@ -33,8 +33,9 @@ export class Scope { if (value !== undefined) { return new Success(value); } else { - this.errors.push(new ScopeError(ErrorKind.UnknownVariable, node.id.name)); - return new Failure(new StringValue(`$${node.id.name}`)); + let name = `$${node.id.name}`; + this.errors.push(new ScopeError(ErrorKind.UnknownVariable, name)); + return new Failure(new StringValue(name)); } } diff --git a/resolver/src/fixtures.ts b/resolver/test/fixtures.ts similarity index 97% rename from resolver/src/fixtures.ts rename to resolver/test/fixtures.ts index 56be5da..775936d 100644 --- a/resolver/src/fixtures.ts +++ b/resolver/test/fixtures.ts @@ -1,3 +1,4 @@ +// hello = Hello, {$world} export let hello = { type: "Message", id: { @@ -25,6 +26,7 @@ export let hello = { }, }; +// exclamation = {hello}! export let exclamation = { type: "Message", id: { @@ -53,6 +55,10 @@ export let exclamation = { }, }; +// select = {$selector -> +// *[a] (a) {hello} +// [b] (b) {exclamation} +// } export let select = { type: "Message", id: { diff --git a/resolver/test/sample_test.ts b/resolver/test/sample_test.ts new file mode 100644 index 0000000..12aedcb --- /dev/null +++ b/resolver/test/sample_test.ts @@ -0,0 +1,170 @@ +import * as assert from "assert"; +import {hello, exclamation, select} from "./fixtures"; +import * as ast from "../src/ast"; +import {Message} from "../src/message"; +import {Bundle} from "../src/bundle"; +import {StringValue} from "../src/value"; +import {ScopeError, ErrorKind} from "../src/error"; + +let resource = new Map( + Object.entries({ + hello: new Message(hello as ast.Message), + exclamation: new Message(exclamation as ast.Message), + select: new Message(select as ast.Message), + }) +); + +suite("Sample suite", function() { + suiteSetup(function() { + this.bundle = new Bundle(); + this.bundle.addResource(resource); + }); + + test("hello with a variable", function() { + let message = this.bundle.getMessage("hello"); + if (message) { + let {value, errors} = this.bundle.formatValue( + message, + new Map( + Object.entries({ + world: new StringValue("World"), + }) + ) + ); + assert.equal(value, "Hello, World"); + assert.equal(errors.length, 0); + } + }); + + test("hello without a variable", function() { + let message = this.bundle.getMessage("hello"); + if (message) { + let {value, errors} = this.bundle.formatValue(message, new Map()); + assert.equal(value, "Hello, {$world}"); + assert.equal(errors.length, 1); + assert.deepEqual(errors[0], new ScopeError(ErrorKind.UnknownVariable, "$world")); + } + }); + + test("exclamation with a variable", function() { + let message = this.bundle.getMessage("exclamation"); + if (message) { + let {value, errors} = this.bundle.formatValue( + message, + new Map( + Object.entries({ + world: new StringValue("World"), + }) + ) + ); + assert.equal(value, "Hello, World!"); + assert.equal(errors.length, 0); + } + }); + + test("exclamation without a variable", function() { + let message = this.bundle.getMessage("exclamation"); + if (message) { + let {value, errors} = this.bundle.formatValue(message, new Map()); + assert.equal(value, "Hello, {$world}!"); + assert.equal(errors.length, 1); + assert.deepEqual(errors[0], new ScopeError(ErrorKind.UnknownVariable, "$world")); + } + }); + + test("select [a] with a variable", function() { + let message = this.bundle.getMessage("select"); + if (message) { + let {value, errors} = this.bundle.formatValue( + message, + new Map( + Object.entries({ + world: new StringValue("World"), + selector: new StringValue("a"), + }) + ) + ); + assert.equal(value, "(a) Hello, World"); + assert.equal(errors.length, 0); + } + }); + + test("select [a] without a variable", function() { + let message = this.bundle.getMessage("select"); + if (message) { + let {value, errors} = this.bundle.formatValue( + message, + new Map( + Object.entries({ + selector: new StringValue("a"), + }) + ) + ); + assert.equal(value, "(a) Hello, {$world}"); + assert.equal(errors.length, 1); + assert.deepEqual(errors[0], new ScopeError(ErrorKind.UnknownVariable, "$world")); + } + }); + + test("select [b] with a variable", function() { + let message = this.bundle.getMessage("select"); + if (message) { + let {value, errors} = this.bundle.formatValue( + message, + new Map( + Object.entries({ + world: new StringValue("World"), + selector: new StringValue("b"), + }) + ) + ); + assert.equal(value, "(b) Hello, World!"); + assert.equal(errors.length, 0); + } + }); + + test("select [b] without a variable", function() { + let message = this.bundle.getMessage("select"); + if (message) { + let {value, errors} = this.bundle.formatValue( + message, + new Map( + Object.entries({ + selector: new StringValue("b"), + }) + ) + ); + assert.equal(value, "(b) Hello, {$world}!"); + assert.equal(errors.length, 1); + assert.deepEqual(errors[0], new ScopeError(ErrorKind.UnknownVariable, "$world")); + } + }); + + test("select default with a variable", function() { + let message = this.bundle.getMessage("select"); + if (message) { + let {value, errors} = this.bundle.formatValue( + message, + new Map( + Object.entries({ + world: new StringValue("World"), + }) + ) + ); + assert.equal(value, "(a) Hello, World"); + assert.equal(errors.length, 1); + assert.deepEqual(errors[0], new ScopeError(ErrorKind.UnknownVariable, "$selector")); + } + }); + + test("select default without a variable", function() { + let message = this.bundle.getMessage("select"); + if (message) { + let {value, errors} = this.bundle.formatValue(message, new Map()); + assert.equal(value, "(a) Hello, {$world}"); + assert.equal(errors.length, 2); + assert.deepEqual(errors[0], new ScopeError(ErrorKind.UnknownVariable, "$selector")); + assert.deepEqual(errors[1], new ScopeError(ErrorKind.UnknownVariable, "$world")); + } + }); +}); From 1818e8c413f44311a08cf44580caeaa309a4b246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 5 Jun 2019 12:53:15 +0200 Subject: [PATCH 25/36] Add bin/format --- resolver/.gitignore | 3 +- resolver/dist/lib/parser.js | 1137 ++++++++++++++++++++++++++++++++++ resolver/package-lock.json | 6 + resolver/package.json | 1 + resolver/src/ast.ts | 6 + resolver/src/bin/format.ts | 98 +++ resolver/src/bundle.ts | 8 +- resolver/src/index.ts | 4 - resolver/src/lib/parser.d.ts | 10 + resolver/test/sample_test.ts | 14 +- resolver/test/tsconfig.json | 8 + resolver/tsconfig.json | 5 +- 12 files changed, 1279 insertions(+), 21 deletions(-) create mode 100644 resolver/dist/lib/parser.js create mode 100644 resolver/src/bin/format.ts delete mode 100644 resolver/src/index.ts create mode 100644 resolver/src/lib/parser.d.ts create mode 100644 resolver/test/tsconfig.json diff --git a/resolver/.gitignore b/resolver/.gitignore index a6c7c28..657138a 100644 --- a/resolver/.gitignore +++ b/resolver/.gitignore @@ -1 +1,2 @@ -*.js +dist/bin +dist/*.js diff --git a/resolver/dist/lib/parser.js b/resolver/dist/lib/parser.js new file mode 100644 index 0000000..e8b64b0 --- /dev/null +++ b/resolver/dist/lib/parser.js @@ -0,0 +1,1137 @@ +/* eslint-disable */ + +"use strict"; + +Object.defineProperty(exports, "__esModule", {value: true}); + +// Base class for all Fluent AST nodes. +class BaseNode { + constructor() {} +} + +// Base class for AST nodes which can have Spans. +class SyntaxNode extends BaseNode { + addSpan(start, end) { + this.span = new Span(start, end); + } +} + +class Resource extends SyntaxNode { + constructor(body = []) { + super(); + this.type = "Resource"; + this.body = body; + } +} + +// An abstract base class for useful elements of Resource.body. +class Entry extends SyntaxNode {} + +class Message extends Entry { + constructor(id, value = null, attributes = [], comment = null) { + super(); + this.type = "Message"; + this.id = id; + this.value = value; + this.attributes = attributes; + this.comment = comment; + } +} + +class Term extends Entry { + constructor(id, value, attributes = [], comment = null) { + super(); + this.type = "Term"; + this.id = id; + this.value = value; + this.attributes = attributes; + this.comment = comment; + } +} + +class Pattern extends SyntaxNode { + constructor(elements) { + super(); + this.type = "Pattern"; + this.elements = elements; + } +} + +// An abstract base class for elements of Patterns. +class PatternElement extends SyntaxNode {} + +class TextElement extends PatternElement { + constructor(value) { + super(); + this.type = "TextElement"; + this.value = value; + } +} + +class Placeable extends PatternElement { + constructor(expression) { + super(); + this.type = "Placeable"; + this.expression = expression; + } +} + +// An abstract base class for Expressions. +class Expression extends SyntaxNode {} + +// An abstract base class for Literals. +class Literal extends Expression { + constructor(value) { + super(); + // The "value" field contains the exact contents of the literal, + // character-for-character. + this.value = value; + } + + // Implementations are free to decide how they process the raw value. When + // they do, however, they must comply with the behavior of `Literal.parse`. + parse() { + return {value: this.value}; + } +} + +class StringLiteral extends Literal { + constructor(value) { + super(value); + this.type = "StringLiteral"; + } + + parse() { + // Backslash backslash, backslash double quote, uHHHH, UHHHHHH. + const KNOWN_ESCAPES = /(?:\\\\|\\\"|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g; + + function from_escape_sequence(match, codepoint4, codepoint6) { + switch (match) { + case "\\\\": + return "\\"; + case '\\"': + return '"'; + default: + let codepoint = parseInt(codepoint4 || codepoint6, 16); + if (codepoint <= 0xd7ff || 0xe000 <= codepoint) { + // It's a Unicode scalar value. + return String.fromCodePoint(codepoint); + } + // Escape sequences reresenting surrogate code points are + // well-formed but invalid in Fluent. Replace them with U+FFFD + // REPLACEMENT CHARACTER. + return "�"; + } + } + + let value = this.value.replace(KNOWN_ESCAPES, from_escape_sequence); + return {value}; + } +} + +class NumberLiteral extends Literal { + constructor(value) { + super(value); + this.type = "NumberLiteral"; + } + + parse() { + let value = parseFloat(this.value); + let decimal_position = this.value.indexOf("."); + let precision = decimal_position > 0 ? this.value.length - decimal_position - 1 : 0; + return {value, precision}; + } +} + +class MessageReference extends Expression { + constructor(id, attribute) { + super(); + this.type = "MessageReference"; + this.id = id; + this.attribute = attribute; + } +} + +class TermReference extends Expression { + constructor(id, attribute, args) { + super(); + this.type = "TermReference"; + this.id = id; + this.attribute = attribute; + this.arguments = args; + } +} + +class VariableReference extends Expression { + constructor(id) { + super(); + this.type = "VariableReference"; + this.id = id; + } +} + +class FunctionReference extends Expression { + constructor(id, args) { + super(); + this.type = "FunctionReference"; + this.id = id; + this.arguments = args; + } +} + +class SelectExpression extends Expression { + constructor(selector, variants) { + super(); + this.type = "SelectExpression"; + this.selector = selector; + this.variants = variants; + } +} + +class Attribute extends SyntaxNode { + constructor(id, value) { + super(); + this.type = "Attribute"; + this.id = id; + this.value = value; + } +} + +class Variant extends SyntaxNode { + constructor(key, value, def = false) { + super(); + this.type = "Variant"; + this.key = key; + this.value = value; + this.default = def; + } +} + +class CallArguments extends SyntaxNode { + constructor(positional = [], named = []) { + super(); + this.type = "CallArguments"; + this.positional = positional; + this.named = named; + } +} + +class NamedArgument extends SyntaxNode { + constructor(name, value) { + super(); + this.type = "NamedArgument"; + this.name = name; + this.value = value; + } +} + +class Identifier extends SyntaxNode { + constructor(name) { + super(); + this.type = "Identifier"; + this.name = name; + } +} + +class BaseComment extends Entry { + constructor(content) { + super(); + this.type = "BaseComment"; + this.content = content; + } +} + +class Comment extends BaseComment { + constructor(content) { + super(content); + this.type = "Comment"; + } +} + +class GroupComment extends BaseComment { + constructor(content) { + super(content); + this.type = "GroupComment"; + } +} +class ResourceComment extends BaseComment { + constructor(content) { + super(content); + this.type = "ResourceComment"; + } +} + +class Junk extends SyntaxNode { + constructor(content) { + super(); + this.type = "Junk"; + this.annotations = []; + this.content = content; + } + + addAnnotation(annot) { + this.annotations.push(annot); + } +} + +class Span extends BaseNode { + constructor(start, end) { + super(); + this.type = "Span"; + this.start = start; + this.end = end; + } +} + +class Stream { + constructor(iterable, cursor, length) { + this.iterable = iterable; + this.cursor = cursor || 0; + this.length = length === undefined ? iterable.length - this.cursor : length; + } + + // Get the element at the cursor. + head(len = 1) { + if (this.length < 0) { + return undefined; + } + + if (this.length === 0) { + return Symbol.for("eof"); + } + + return this.iterable.slice(this.cursor, this.cursor + len); + } + + // Execute a regex on the iterable. + exec(re) { + // The "u" flag is a feature of ES2015 which makes regexes Unicode-aware. + // See https://mathiasbynens.be/notes/es6-unicode-regex. + // The "y" flag makes the regex sticky. The match must start at the + // offset specified by the regex's lastIndex property. + let sticky = new RegExp(re, "uy"); + sticky.lastIndex = this.cursor; + return sticky.exec(this.iterable); + } + + // Consume the stream by moving the cursor. + move(distance) { + return new Stream(this.iterable, this.cursor + distance, this.length - distance); + } +} + +class Result { + constructor(value, rest) { + this.value = value; + this.rest = rest; + } +} + +class Success extends Result { + map(fn) { + return new Success(fn(this.value), this.rest); + } + bimap(s, f) { + return new Success(s(this.value), this.rest); + } + chain(fn) { + return fn(this.value, this.rest); + } + fold(s, f) { + return s(this.value, this.rest); + } +} + +class Failure extends Result { + map(fn) { + return this; + } + bimap(s, f) { + return new Failure(f(this.value), this.rest); + } + chain(fn) { + return this; + } + fold(s, f) { + return f(this.value, this.rest); + } +} + +class Abstract { + constructor(value) { + this.value = value; + } +} + +class Parser { + constructor(parse) { + this.parse = parse; + } + + run(iterable) { + let stream = iterable instanceof Stream ? iterable : new Stream(iterable); + return this.parse(stream); + } + + get abstract() { + return this.map(value => new Abstract(value)); + } + + map(f) { + return new Parser(stream => this.run(stream).map(f)); + } + + bimap(s, f) { + return new Parser(stream => this.run(stream).bimap(s, f)); + } + + chain(f) { + return new Parser(stream => this.run(stream).chain((value, tail) => f(value).run(tail))); + } + + fold(s, f) { + return new Parser(stream => this.run(stream).fold(s, f)); + } +} + +// Flatten a list up to a given depth. +// This is useful when a parser uses nested sequences and repeats. +const flatten = depth => list => + list.reduce( + (acc, cur) => + acc.concat(!Array.isArray(cur) || depth === 1 ? cur : flatten(depth - 1)(cur)), + [] + ); + +// Mutate an object by merging properties of another object into it. +const mutate = state => obj => Object.assign(obj, state); + +// Join the list of parsed values into a string. +const join = list => list.filter(value => value !== Symbol.for("eof")).join(""); + +// Prune unmatched maybes from a list. +const prune = list => list.filter(value => value !== null); + +// Map a list of {name, value} aliases into an array of values. +const keep_abstract = list => + list.filter(value => value instanceof Abstract).map(({value}) => value); + +// Map a list to the element at the specified index. Useful for parsers which +// define a prefix or a surrounding delimiter. +const element_at = index => list => list[index]; + +function defer(fn) { + // Parsers may be defined as defer(() => parser) to avoid cyclic + // dependecies. + return new Parser(stream => fn().run(stream)); +} + +function string(str) { + return new Parser(stream => + stream.head(str.length) === str + ? new Success(str, stream.move(str.length)) + : new Failure(`${str} not found`, stream) + ); +} + +function regex(re) { + return new Parser(stream => { + const result = stream.exec(re); + + if (result === null) { + return new Failure("regex did not match", stream); + } + + const [match] = result; + + return new Success(match, stream.move(match.length)); + }); +} + +function charset(range) { + return regex(`[${range}]`); +} + +function eof() { + return new Parser(stream => + stream.head() === Symbol.for("eof") + ? new Success(stream.head(), stream.move(1)) + : new Failure("not at EOF", stream) + ); +} + +function lookahead(parser) { + return new Parser(stream => + parser + .run(stream) + .fold(value => new Success(value, stream), value => new Failure(value, stream)) + ); +} + +function not(parser) { + return new Parser(stream => + parser + .run(stream) + .fold( + (value, tail) => new Failure("not failed", stream), + (value, tail) => new Success(null, stream) + ) + ); +} + +function and(...parsers) { + const final = parsers.pop(); + return sequence(...parsers.map(lookahead), final).map(results => results[results.length - 1]); +} + +function either(...parsers) { + return new Parser(stream => { + for (const parser of parsers) { + const result = parser.run(stream); + if (result instanceof Success) { + return result; + } + } + return new Failure("either failed", stream); + }); +} + +function always(value) { + return new Parser(stream => new Success(value, stream)); +} + +function never(value) { + return new Parser(stream => new Failure(value, stream)); +} + +function maybe(parser) { + return new Parser(stream => + parser + .run(stream) + .fold( + (value, tail) => new Success(value, tail), + (value, tail) => new Success(null, stream) + ) + ); +} + +function append(p1, p2) { + return p1.chain(values => p2.map(value => values.concat([value]))); +} + +function sequence(...parsers) { + return parsers.reduce((acc, parser) => append(acc, parser), always([])); +} + +function repeat(parser) { + return new Parser(stream => + parser.run(stream).fold( + (value, tail) => + repeat(parser) + .map(rest => [value].concat(rest)) + .run(tail), + (value, tail) => new Success([], stream) + ) + ); +} + +function repeat1(parser) { + return new Parser(stream => + parser.run(stream).fold( + (value, tail) => + repeat(parser) + .map(rest => [value].concat(rest)) + .run(tail), + (value, tail) => new Failure("repeat1 failed", stream) + ) + ); +} + +/* + * AST Validation + * + * The parse result of the grammar.mjs parser is a well-formed AST which is + * validated according to the rules documented in `spec/valid.md`. + */ + +function list_into(Type) { + switch (Type) { + case Comment: + return ([sigil, content = ""]) => { + switch (sigil) { + case "#": + return always(new Comment(content)); + case "##": + return always(new GroupComment(content)); + case "###": + return always(new ResourceComment(content)); + default: + return never(`Unknown comment sigil: ${sigil}.`); + } + }; + case FunctionReference: + const VALID_FUNCTION_NAME = /^[A-Z][A-Z0-9_-]*$/; + return ([identifier, args]) => { + if (VALID_FUNCTION_NAME.test(identifier.name)) { + return always(new Type(identifier, args)); + } + return never( + `Invalid function name: ${identifier.name}. ` + + "Function names must be all upper-case ASCII letters." + ); + }; + case Pattern: + return elements => + always( + new Pattern( + dedent(elements) + .reduce(join_adjacent(TextElement), []) + .map(trim_text_at_extremes) + .filter(remove_empty_text) + ) + ); + case Resource: + return entries => + always( + new Resource( + entries + .reduce(join_adjacent(Comment, GroupComment, ResourceComment), []) + .reduce(attach_comments, []) + .filter(remove_blank_lines) + ) + ); + case SelectExpression: + return ([selector, variants]) => { + let selector_is_valid = + selector instanceof StringLiteral || + selector instanceof NumberLiteral || + selector instanceof VariableReference || + selector instanceof FunctionReference || + (selector instanceof TermReference && selector.attribute); + if (!selector_is_valid) { + return never(`Invalid selector type: ${selector.type}.`); + } + + return always(new Type(selector, variants)); + }; + default: + return elements => always(new Type(...elements)); + } +} + +function into(Type) { + switch (Type) { + case CallArguments: + return args => { + let positional = []; + let named = new Map(); + for (let arg of args) { + if (arg instanceof NamedArgument) { + let name = arg.name.name; + if (named.has(name)) { + return never("Named arguments must be unique."); + } + named.set(name, arg); + } else if (named.size > 0) { + return never("Positional arguments must not follow " + "named arguments"); + } else { + positional.push(arg); + } + } + return always(new Type(positional, Array.from(named.values()))); + }; + case Placeable: + return expression => { + if (expression instanceof TermReference && expression.attribute) { + return never("Term attributes may not be used as placeables."); + } + return always(new Type(expression)); + }; + default: + return (...args) => always(new Type(...args)); + } +} + +// Create a reducer suitable for joining adjacent nodes of the same type, if +// type is one of types specified. +function join_adjacent(...types) { + return function(acc, cur) { + let prev = acc[acc.length - 1]; + for (let Type of types) { + if (prev instanceof Type && cur instanceof Type) { + // Replace prev with a new node of the same type whose value is + // the sum of prev and cur, and discard cur. + acc[acc.length - 1] = join_of_type(Type, prev, cur); + return acc; + } + } + return acc.concat(cur); + }; +} + +// Join values of two or more nodes of the same type. Return a new node. +function join_of_type(Type, ...elements) { + // TODO Join annotations and spans. + switch (Type) { + case TextElement: + return elements.reduce((a, b) => new Type(a.value + b.value)); + case Comment: + case GroupComment: + case ResourceComment: + return elements.reduce((a, b) => new Type(a.content + "\n" + b.content)); + } +} + +function attach_comments(acc, cur) { + let prev = acc[acc.length - 1]; + if (prev instanceof Comment && (cur instanceof Message || cur instanceof Term)) { + cur.comment = prev; + acc[acc.length - 1] = cur; + return acc; + } else { + return acc.concat(cur); + } +} + +// Remove the largest common indentation from a list of elements of a Pattern. +// The indents are parsed in grammar.mjs and passed to abstract.mjs as string +// primitives along with other PatternElements. +function dedent(elements) { + // Calculate the maximum common indent. + let indents = elements.filter(element => typeof element === "string"); + let common = Math.min(...indents.map(indent => indent.length)); + + function trim_indents(element) { + if (typeof element === "string") { + // Trim the indent and convert it to a proper TextElement. + // It will be joined with its adjacents later on. + return new TextElement(element.slice(common)); + } + return element; + } + + return elements.map(trim_indents); +} + +const LEADING_BLANK_BLOCK = /^\n*/; +const TRAILING_BLANK_INLINE = / *$/; + +function trim_text_at_extremes(element, index, array) { + if (element instanceof TextElement) { + if (index === 0) { + element.value = element.value.replace(LEADING_BLANK_BLOCK, ""); + } + if (index === array.length - 1) { + element.value = element.value.replace(TRAILING_BLANK_INLINE, ""); + } + } + return element; +} + +function remove_empty_text(element) { + return !(element instanceof TextElement) || element.value !== ""; +} + +function remove_blank_lines(element) { + return typeof element !== "string"; +} + +/* ----------------------------------------------------- */ +/* An FTL file defines a Resource consisting of Entries. */ +let Resource$1 = defer(() => + repeat(either(Entry$1, blank_block, Junk$1)).chain(list_into(Resource)) +); + +/* ------------------------------------------------------------------------- */ +/* Entries are the main building blocks of Fluent. They define translations and + * contextual and semantic information about the translations. During the AST + * construction, adjacent comment lines of the same comment type (defined by + * the number of #) are joined together. Single-# comments directly preceding + * Messages and Terms are attached to the Message or Term and are not + * standalone Entries. */ +let Entry$1 = defer(() => + either( + sequence(Message$1, line_end).map(element_at(0)), + sequence(Term$1, line_end).map(element_at(0)), + CommentLine + ) +); + +let Message$1 = defer(() => + sequence( + Identifier$1.abstract, + maybe(blank_inline), + string("="), + maybe(blank_inline), + either( + sequence(Pattern$1.abstract, repeat(Attribute$1).abstract), + sequence(always(null).abstract, repeat1(Attribute$1).abstract) + ) + ) + .map(flatten(1)) + .map(keep_abstract) + .chain(list_into(Message)) +); + +let Term$1 = defer(() => + sequence( + string("-"), + Identifier$1.abstract, + maybe(blank_inline), + string("="), + maybe(blank_inline), + Pattern$1.abstract, + repeat(Attribute$1).abstract + ) + .map(keep_abstract) + .chain(list_into(Term)) +); + +/* -------------------------------------------------------------------------- */ +/* Adjacent comment lines of the same comment type are joined together during + * the AST construction. */ +let CommentLine = defer(() => + sequence( + either(string("###"), string("##"), string("#")).abstract, + maybe(sequence(string(" "), repeat(comment_char).map(join).abstract)), + line_end + ) + .map(flatten(1)) + .map(keep_abstract) + .chain(list_into(Comment)) +); + +let comment_char = defer(() => and(not(line_end), any_char)); + +/* -------------------------------------------------------------------------- */ +/* Junk represents unparsed content. + * + * Junk is parsed line-by-line until a line is found which looks like it might + * be a beginning of a new message, term, or a comment. Any whitespace + * following a broken Entry is also considered part of Junk. + */ +let Junk$1 = defer(() => + sequence( + junk_line, + repeat(and(not(charset("a-zA-Z")), not(string("-")), not(string("#")), junk_line)) + ) + .map(flatten(1)) + .map(join) + .chain(into(Junk)) +); + +let junk_line = sequence(regex(/[^\n]*/), either(string("\u000A"), eof())).map(join); + +/* --------------------------------- */ +/* Attributes of Messages and Terms. */ +let Attribute$1 = defer(() => + sequence( + line_end, + maybe(blank), + string("."), + Identifier$1.abstract, + maybe(blank_inline), + string("="), + maybe(blank_inline), + Pattern$1.abstract + ) + .map(keep_abstract) + .chain(list_into(Attribute)) +); + +/* ---------------------------------------------------------------- */ +/* Patterns are values of Messages, Terms, Attributes and Variants. */ +let Pattern$1 = defer(() => + repeat1(PatternElement$1) + // Flatten block_text and block_placeable which return lists. + .map(flatten(1)) + .chain(list_into(Pattern)) +); + +/* ----------------------------------------------------------------- */ +/* TextElement and Placeable can occur inline or as block. + * Text needs to be indented and start with a non-special character. + * Placeables can start at the beginning of the line or be indented. + * Adjacent TextElements are joined in AST creation. */ + +let PatternElement$1 = defer(() => + either(inline_text, block_text, inline_placeable, block_placeable) +); + +let inline_text = defer(() => + repeat1(text_char) + .map(join) + .chain(into(TextElement)) +); + +let block_text = defer(() => + sequence( + blank_block.chain(into(TextElement)), + blank_inline, + indented_char.chain(into(TextElement)), + maybe(inline_text) + ).map(prune) +); + +let inline_placeable = defer(() => + sequence( + string("{"), + maybe(blank), + either( + // Order matters! + SelectExpression$1, + InlineExpression + ), + maybe(blank), + string("}") + ) + .map(element_at(2)) + .chain(into(Placeable)) +); + +let block_placeable = defer(() => + sequence( + blank_block.chain(into(TextElement)), + // No indent before a placeable counts as 0 in dedention logic. + maybe(blank_inline).map(s => s || ""), + inline_placeable + ) +); + +/* ------------------------------------------------------------------- */ +/* Rules for validating expressions in Placeables and as selectors of + * SelectExpressions are documented in spec/valid.md and enforced in + * syntax/abstract.mjs. */ +let InlineExpression = defer(() => + either( + StringLiteral$1, + NumberLiteral$1, + FunctionReference$1, + MessageReference$1, + TermReference$1, + VariableReference$1, + inline_placeable + ) +); + +/* -------- */ +/* Literals */ +let StringLiteral$1 = defer(() => + sequence(string('"'), repeat(quoted_char), string('"')) + .map(element_at(1)) + .map(join) + .chain(into(StringLiteral)) +); + +let NumberLiteral$1 = defer(() => + sequence(maybe(string("-")), digits, maybe(sequence(string("."), digits))) + .map(flatten(1)) + .map(join) + .chain(into(NumberLiteral)) +); + +/* ------------------ */ +/* Inline Expressions */ +let FunctionReference$1 = defer(() => + sequence(Identifier$1, CallArguments$1).chain(list_into(FunctionReference)) +); + +let MessageReference$1 = defer(() => + sequence(Identifier$1, maybe(AttributeAccessor)).chain(list_into(MessageReference)) +); + +let TermReference$1 = defer(() => + sequence( + string("-"), + Identifier$1.abstract, + maybe(AttributeAccessor).abstract, + maybe(CallArguments$1).abstract + ) + .map(keep_abstract) + .chain(list_into(TermReference)) +); + +let VariableReference$1 = defer(() => + sequence(string("$"), Identifier$1) + .map(element_at(1)) + .chain(into(VariableReference)) +); + +let AttributeAccessor = defer(() => sequence(string("."), Identifier$1)).map(element_at(1)); + +let CallArguments$1 = defer(() => + sequence(maybe(blank), string("("), maybe(blank), argument_list, maybe(blank), string(")")) + .map(element_at(3)) + .chain(into(CallArguments)) +); + +let argument_list = defer(() => + sequence( + repeat(sequence(Argument.abstract, maybe(blank), string(","), maybe(blank))), + maybe(Argument.abstract) + ) + .map(flatten(2)) + .map(keep_abstract) +); + +let Argument = defer(() => either(NamedArgument$1, InlineExpression)); + +let NamedArgument$1 = defer(() => + sequence( + Identifier$1.abstract, + maybe(blank), + string(":"), + maybe(blank), + either(StringLiteral$1, NumberLiteral$1).abstract + ) + .map(keep_abstract) + .chain(list_into(NamedArgument)) +); + +/* ----------------- */ +/* Block Expressions */ +let SelectExpression$1 = defer(() => + sequence( + InlineExpression.abstract, + maybe(blank), + string("->"), + maybe(blank_inline), + variant_list.abstract + ) + .map(keep_abstract) + .chain(list_into(SelectExpression)) +); + +let variant_list = defer(() => + sequence( + repeat(Variant$1).abstract, + DefaultVariant.abstract, + repeat(Variant$1).abstract, + line_end + ) + .map(keep_abstract) + .map(flatten(1)) +); + +let Variant$1 = defer(() => + sequence(line_end, maybe(blank), VariantKey.abstract, maybe(blank_inline), Pattern$1.abstract) + .map(keep_abstract) + .chain(list_into(Variant)) +); + +let DefaultVariant = defer(() => + sequence( + line_end, + maybe(blank), + string("*"), + VariantKey.abstract, + maybe(blank_inline), + Pattern$1.abstract + ) + .map(keep_abstract) + .chain(list_into(Variant)) + .map(mutate({default: true})) +); + +let VariantKey = defer(() => + sequence( + string("["), + maybe(blank), + either(NumberLiteral$1, Identifier$1), + maybe(blank), + string("]") + ).map(element_at(2)) +); + +/* ---------- */ +/* Identifier */ + +let Identifier$1 = sequence(charset("a-zA-Z"), repeat(charset("a-zA-Z0-9_-"))) + .map(flatten(1)) + .map(join) + .chain(into(Identifier)); + +/* -------------------------------------------------------------------------- */ +/* Content Characters + * + * Translation content can be written using any Unicode characters. However, + * some characters are considered special depending on the type of content + * they're in. See text_char and quoted_char for more information. + * + * Some Unicode characters, even if allowed, should be avoided in Fluent + * resources. See spec/recommendations.md. + */ + +let any_char = charset("\\u{0}-\\u{10FFFF}"); + +/* -------------------------------------------------------------------------- */ +/* Text elements + * + * The primary storage for content are text elements. Text elements are not + * delimited with quotes and may span multiple lines as long as all lines are + * indented. The opening brace ({) marks a start of a placeable in the pattern + * and may not be used in text elements verbatim. Due to the indentation + * requirement some text characters may not appear as the first character on a + * new line. + */ + +let special_text_char = either(string("{"), string("}")); + +let text_char = defer(() => and(not(line_end), not(special_text_char), any_char)); + +let indented_char = and(not(string(".")), not(string("*")), not(string("[")), text_char); + +/* -------------------------------------------------------------------------- */ +/* String literals + * + * For special-purpose content, quoted string literals can be used where text + * elements are not a good fit. String literals are delimited with double + * quotes and may not contain line breaks. String literals use the backslash + * (\) as the escape character. The literal double quote can be inserted via + * the \" escape sequence. The literal backslash can be inserted with \\. The + * literal opening brace ({) is allowed in string literals because they may not + * comprise placeables. + */ + +let special_quoted_char = either(string('"'), string("\\")); + +let special_escape = sequence(string("\\"), special_quoted_char).map(join); + +let unicode_escape = either( + sequence(string("\\u"), regex(/[0-9a-fA-F]{4}/)), + sequence(string("\\U"), regex(/[0-9a-fA-F]{6}/)) +).map(join); + +let quoted_char = defer(() => + either(and(not(line_end), not(special_quoted_char), any_char), special_escape, unicode_escape) +); + +/* ------- */ +/* Numbers */ + +let digits = repeat1(charset("0-9")).map(join); + +/* ---------- */ +/* Whitespace */ +let blank_inline = repeat1(string("\u0020")).map(join); + +let line_end = either( + // Normalize CRLF to LF. + string("\u000D\u000A").map(() => "\n"), + string("\u000A"), + eof() +); + +let blank_block = repeat1(sequence(maybe(blank_inline), line_end.abstract)) + .map(flatten(1)) + // Discard the indents and only keep the newlines + // for multiline Patterns. + .map(keep_abstract) + .map(join); + +let blank = repeat1(either(blank_inline, line_end)); + +exports.Entry = Entry$1; +exports.NumberLiteral = NumberLiteral$1; +exports.Resource = Resource$1; +exports.StringLiteral = StringLiteral$1; diff --git a/resolver/package-lock.json b/resolver/package-lock.json index 1a38f72..541c717 100644 --- a/resolver/package-lock.json +++ b/resolver/package-lock.json @@ -4,6 +4,12 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "dev": true + }, "@types/mocha": { "version": "5.2.7", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", diff --git a/resolver/package.json b/resolver/package.json index 0002ba2..c52342f 100644 --- a/resolver/package.json +++ b/resolver/package.json @@ -10,6 +10,7 @@ "author": "Mozilla ", "license": "Apache-2.0", "devDependencies": { + "@types/minimist": "^1.2.0", "@types/mocha": "^5.2.7", "@types/node": "^12.0.4", "mocha": "^6.1.4", diff --git a/resolver/src/ast.ts b/resolver/src/ast.ts index 5c2c509..8a6dcf9 100644 --- a/resolver/src/ast.ts +++ b/resolver/src/ast.ts @@ -8,6 +8,7 @@ export enum NodeType { Variant = "Variant", Pattern = "Pattern", Message = "Message", + Resource = "Resource", } export interface SyntaxNode { @@ -69,3 +70,8 @@ export interface Message extends SyntaxNode { readonly id: Identifier; readonly value: Pattern | null; } + +export interface Resource extends SyntaxNode { + readonly type: NodeType.Message; + readonly body: Array; +} diff --git a/resolver/src/bin/format.ts b/resolver/src/bin/format.ts new file mode 100644 index 0000000..56579bf --- /dev/null +++ b/resolver/src/bin/format.ts @@ -0,0 +1,98 @@ +import fs from "fs"; +import readline from "readline"; +import parseArgs from "minimist"; +import {Resource as ResourceParser} from "../lib/parser"; +import {Resource} from "../ast"; +import {Bundle} from "../bundle"; + +const argv = parseArgs(process.argv.slice(2), { + boolean: ["help"], + alias: { + help: "h", + }, +}); + +if (argv.help) { + exitHelp(0); +} + +const [filePath] = argv._; + +if (filePath === "-") { + parseStdin(); +} else if (filePath) { + parseFile(filePath); +} else { + exitHelp(1); +} + +function exitHelp(exitCode: number) { + console.log(` + Usage: node --experimental-modules parse.mjs [OPTIONS] + + When FILE is "-", read text from stdin. + + Examples: + + node --experimental-modules parse.mjs path/to/file.ftl + cat path/to/file.ftl | node --experimental-modules parse.mjs - + + Options: + + -h, --help Display help and quit. +`); + process.exit(exitCode); +} + +function parseFluent(source: string) { + return ResourceParser.run(source).fold( + resource => resource, + err => { + throw err; + } + ); +} + +function format(resource: Resource) { + let bundle = new Bundle(); + bundle.addResource(resource); + + for (let entry of resource.body) { + let message = bundle.getMessage(entry.id.name); + if (message) { + let {value, errors} = bundle.formatValue(message, new Map()); + console.log(value); + console.log(errors); + } + } +} + +function parseStdin() { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + prompt: "fluent>", + }); + + let lines: Array = []; + + rl.on("line", line => lines.push(line)); + rl.on("close", () => parseFormat(lines.join("\n") + "\n")); +} + +function parseFile(filePath: string) { + fs.readFile(filePath, "utf8", (err: NodeJS.ErrnoException | null, content: string | Buffer) => { + if (err) { + throw err; + } + + if (typeof content === "string") { + parseFormat(content); + } + }); +} + +function parseFormat(source: string) { + let resource = parseFluent(source); + format(resource); +} diff --git a/resolver/src/bundle.ts b/resolver/src/bundle.ts index d8cea87..44bbff0 100644 --- a/resolver/src/bundle.ts +++ b/resolver/src/bundle.ts @@ -1,8 +1,8 @@ -import * as ast from "./ast"; import {Scope} from "./scope"; import {Value, NoneValue} from "./value"; import {Message} from "./message"; import {ScopeError} from "./error"; +import {Resource} from "./ast"; export interface Formatted { readonly value: string | null; @@ -12,9 +12,9 @@ export interface Formatted { export class Bundle { public readonly messages: Map = new Map(); - addResource(resource: Map) { - for (let [id, message] of resource) { - this.messages.set(id, message); + addResource(resource: Resource) { + for (let message of resource.body) { + this.messages.set(message.id.name, new Message(message)); } } diff --git a/resolver/src/index.ts b/resolver/src/index.ts deleted file mode 100644 index 01fad38..0000000 --- a/resolver/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as _ast from "./ast"; -export let ast = _ast; - -export {Bundle} from "./bundle"; diff --git a/resolver/src/lib/parser.d.ts b/resolver/src/lib/parser.d.ts new file mode 100644 index 0000000..20a023a --- /dev/null +++ b/resolver/src/lib/parser.d.ts @@ -0,0 +1,10 @@ +import {Resource} from "../ast"; +interface Result { + fold(s: (value: T) => T, f: (err: E) => never): T; +} + +interface Parser { + run(input: string): Result; +} + +export declare let Resource: Parser; diff --git a/resolver/test/sample_test.ts b/resolver/test/sample_test.ts index 12aedcb..b2f6ffa 100644 --- a/resolver/test/sample_test.ts +++ b/resolver/test/sample_test.ts @@ -1,23 +1,15 @@ import * as assert from "assert"; import {hello, exclamation, select} from "./fixtures"; -import * as ast from "../src/ast"; -import {Message} from "../src/message"; import {Bundle} from "../src/bundle"; import {StringValue} from "../src/value"; import {ScopeError, ErrorKind} from "../src/error"; -let resource = new Map( - Object.entries({ - hello: new Message(hello as ast.Message), - exclamation: new Message(exclamation as ast.Message), - select: new Message(select as ast.Message), - }) -); - suite("Sample suite", function() { suiteSetup(function() { this.bundle = new Bundle(); - this.bundle.addResource(resource); + this.bundle.addResource({ + body: [hello, exclamation, select], + }); }); test("hello with a variable", function() { diff --git a/resolver/test/tsconfig.json b/resolver/test/tsconfig.json new file mode 100644 index 0000000..4a69d9c --- /dev/null +++ b/resolver/test/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + } +} diff --git a/resolver/tsconfig.json b/resolver/tsconfig.json index 46062b0..e02532f 100644 --- a/resolver/tsconfig.json +++ b/resolver/tsconfig.json @@ -5,5 +5,8 @@ "strict": true, "esModuleInterop": true, "outDir": "./dist" - } + }, + "include": [ + "./src/**/*" + ] } From 5c93fe24f93880efc8230e93c6afcf258e18a2f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 5 Jun 2019 14:02:33 +0200 Subject: [PATCH 26/36] Print results as JSON --- resolver/src/bin/format.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/resolver/src/bin/format.ts b/resolver/src/bin/format.ts index 56579bf..74ab2f0 100644 --- a/resolver/src/bin/format.ts +++ b/resolver/src/bin/format.ts @@ -4,6 +4,7 @@ import parseArgs from "minimist"; import {Resource as ResourceParser} from "../lib/parser"; import {Resource} from "../ast"; import {Bundle} from "../bundle"; +import {ErrorKind} from "../error"; const argv = parseArgs(process.argv.slice(2), { boolean: ["help"], @@ -57,14 +58,22 @@ function format(resource: Resource) { let bundle = new Bundle(); bundle.addResource(resource); + let results = []; for (let entry of resource.body) { let message = bundle.getMessage(entry.id.name); if (message) { let {value, errors} = bundle.formatValue(message, new Map()); - console.log(value); - console.log(errors); + results.push({ + value, + errors: errors.map(error => ({ + kind: ErrorKind[error.kind], + arg: error.arg, + })), + }); } } + + console.log(JSON.stringify(results, null, 4)); } function parseStdin() { From 66bd9b364f1fc3c933bbf1252bd887331f1d4768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 5 Jun 2019 14:59:00 +0200 Subject: [PATCH 27/36] Sample test fixtures --- resolver/src/ast.ts | 10 +- resolver/src/bin/format.ts | 110 ++++++++++++------ resolver/src/bin/input.ts | 27 +++++ resolver/src/bundle.ts | 6 +- resolver/test/fixtures/Makefile | 11 ++ resolver/test/fixtures/message_reference.ftl | 4 + resolver/test/fixtures/message_reference.json | 6 + resolver/test/fixtures/variable_reference.ftl | 8 ++ .../test/fixtures/variable_reference.json | 15 +++ 9 files changed, 159 insertions(+), 38 deletions(-) create mode 100644 resolver/src/bin/input.ts create mode 100644 resolver/test/fixtures/Makefile create mode 100644 resolver/test/fixtures/message_reference.ftl create mode 100644 resolver/test/fixtures/message_reference.json create mode 100644 resolver/test/fixtures/variable_reference.ftl create mode 100644 resolver/test/fixtures/variable_reference.json diff --git a/resolver/src/ast.ts b/resolver/src/ast.ts index 8a6dcf9..46b963e 100644 --- a/resolver/src/ast.ts +++ b/resolver/src/ast.ts @@ -8,6 +8,7 @@ export enum NodeType { Variant = "Variant", Pattern = "Pattern", Message = "Message", + GroupComment = "GroupComment", Resource = "Resource", } @@ -71,7 +72,12 @@ export interface Message extends SyntaxNode { readonly value: Pattern | null; } +export interface GroupComment extends SyntaxNode { + readonly type: NodeType.GroupComment; + readonly content: string; +} + export interface Resource extends SyntaxNode { - readonly type: NodeType.Message; - readonly body: Array; + readonly type: NodeType.Resource; + readonly body: Array; } diff --git a/resolver/src/bin/format.ts b/resolver/src/bin/format.ts index 74ab2f0..3f1dd16 100644 --- a/resolver/src/bin/format.ts +++ b/resolver/src/bin/format.ts @@ -1,15 +1,16 @@ -import fs from "fs"; -import readline from "readline"; import parseArgs from "minimist"; +import {fromStdin, fromFile} from "./input"; import {Resource as ResourceParser} from "../lib/parser"; -import {Resource} from "../ast"; +import {Resource, NodeType, GroupComment} from "../ast"; import {Bundle} from "../bundle"; import {ErrorKind} from "../error"; +import {Value, StringValue, NumberValue} from "../value"; const argv = parseArgs(process.argv.slice(2), { - boolean: ["help"], + boolean: ["help", "group"], alias: { help: "h", + group: "g", }, }); @@ -20,32 +21,41 @@ if (argv.help) { const [filePath] = argv._; if (filePath === "-") { - parseStdin(); + fromStdin(print); } else if (filePath) { - parseFile(filePath); + fromFile(filePath, print); } else { exitHelp(1); } function exitHelp(exitCode: number) { console.log(` - Usage: node --experimental-modules parse.mjs [OPTIONS] + Usage: node format.js [OPTIONS] When FILE is "-", read text from stdin. Examples: - node --experimental-modules parse.mjs path/to/file.ftl - cat path/to/file.ftl | node --experimental-modules parse.mjs - + node format.js path/to/file.ftl + cat path/to/file.ftl | node format.js - Options: -h, --help Display help and quit. + -g, --group Treat each group as a separate resource. `); process.exit(exitCode); } -function parseFluent(source: string) { +function print(source: string) { + if (argv.group) { + printGroups(source); + } else { + printAll(source); + } +} + +function parseString(source: string) { return ResourceParser.run(source).fold( resource => resource, err => { @@ -54,15 +64,20 @@ function parseFluent(source: string) { ); } -function format(resource: Resource) { +type Variables = Map; + +function formatResource(resource: Resource, variables: Variables) { let bundle = new Bundle(); bundle.addResource(resource); let results = []; for (let entry of resource.body) { + if (entry.type !== NodeType.Message) { + continue; + } let message = bundle.getMessage(entry.id.name); if (message) { - let {value, errors} = bundle.formatValue(message, new Map()); + let {value, errors} = bundle.formatValue(message, variables); results.push({ value, errors: errors.map(error => ({ @@ -72,36 +87,63 @@ function format(resource: Resource) { }); } } + return results; +} +function printAll(source: string) { + let resource = parseString(source); + let results = formatResource(resource, new Map()); console.log(JSON.stringify(results, null, 4)); } -function parseStdin() { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - prompt: "fluent>", - }); - - let lines: Array = []; - - rl.on("line", line => lines.push(line)); - rl.on("close", () => parseFormat(lines.join("\n") + "\n")); +interface Group { + resource: Resource; + variables: Map; } -function parseFile(filePath: string) { - fs.readFile(filePath, "utf8", (err: NodeJS.ErrnoException | null, content: string | Buffer) => { - if (err) { - throw err; +const RE_VARIABLE = /^\$([a-zA-Z]*): (string|number) = (.*)$/gm; +function parseVariables(comment: GroupComment) { + let variables = new Map(); + let match; + while ((match = RE_VARIABLE.exec(comment.content))) { + let [, name, type, value] = match; + switch (type) { + case "string": + variables.set(name, new StringValue(value)); + break; + case "number": + variables.set(name, new NumberValue(parseFloat(value))); + break; } + } + return variables; +} + +function printGroups(source: string) { + let resource = parseString(source); - if (typeof content === "string") { - parseFormat(content); + let groups: Array = []; + for (let entry of resource.body) { + if (entry.type === NodeType.GroupComment) { + groups.push({ + resource: { + type: NodeType.Resource, + body: [], + }, + variables: parseVariables(entry), + }); + } else if (groups.length > 0) { + let currentGroup = groups[groups.length - 1]; + currentGroup.resource.body.push(entry); } - }); -} + } -function parseFormat(source: string) { - let resource = parseFluent(source); - format(resource); + let results = []; + for (let group of groups) { + let groupResults = formatResource(group.resource, group.variables); + let lastResult = groupResults.pop(); + results.push(lastResult); + } + + console.log(JSON.stringify(results, null, 4)); } diff --git a/resolver/src/bin/input.ts b/resolver/src/bin/input.ts new file mode 100644 index 0000000..0fede02 --- /dev/null +++ b/resolver/src/bin/input.ts @@ -0,0 +1,27 @@ +import fs from "fs"; +import readline from "readline"; + +export function fromStdin(callback: (value: string) => void) { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + prompt: "fluent>", + }); + + let lines: Array = []; + + rl.on("line", line => lines.push(line)); + rl.on("close", () => callback(lines.join("\n") + "\n")); +} + +export function fromFile(filePath: string, callback: (value: string) => void) { + fs.readFile(filePath, "utf8", (err: NodeJS.ErrnoException | null, content: string | Buffer) => { + if (err) { + throw err; + } + + if (typeof content === "string") { + callback(content); + } + }); +} diff --git a/resolver/src/bundle.ts b/resolver/src/bundle.ts index 44bbff0..ff11723 100644 --- a/resolver/src/bundle.ts +++ b/resolver/src/bundle.ts @@ -2,7 +2,7 @@ import {Scope} from "./scope"; import {Value, NoneValue} from "./value"; import {Message} from "./message"; import {ScopeError} from "./error"; -import {Resource} from "./ast"; +import {Resource, NodeType} from "./ast"; export interface Formatted { readonly value: string | null; @@ -14,7 +14,9 @@ export class Bundle { addResource(resource: Resource) { for (let message of resource.body) { - this.messages.set(message.id.name, new Message(message)); + if (message.type === NodeType.Message) { + this.messages.set(message.id.name, new Message(message)); + } } } diff --git a/resolver/test/fixtures/Makefile b/resolver/test/fixtures/Makefile new file mode 100644 index 0000000..5d4a94f --- /dev/null +++ b/resolver/test/fixtures/Makefile @@ -0,0 +1,11 @@ +FTL_FIXTURES := $(wildcard *.ftl) +AST_FIXTURES := $(FTL_FIXTURES:%.ftl=%.json) + +all: $(AST_FIXTURES) + +.PHONY: $(AST_FIXTURES) +$(AST_FIXTURES): %.json: %.ftl + @node ../../dist/bin/format.js --group $< \ + 2> /dev/null \ + 1> $@; + @echo "$< → $@" diff --git a/resolver/test/fixtures/message_reference.ftl b/resolver/test/fixtures/message_reference.ftl new file mode 100644 index 0000000..d2d34f7 --- /dev/null +++ b/resolver/test/fixtures/message_reference.ftl @@ -0,0 +1,4 @@ +## + +foo = Foo +bar = {foo} Bar diff --git a/resolver/test/fixtures/message_reference.json b/resolver/test/fixtures/message_reference.json new file mode 100644 index 0000000..7df6b20 --- /dev/null +++ b/resolver/test/fixtures/message_reference.json @@ -0,0 +1,6 @@ +[ + { + "value": "Foo Bar", + "errors": [] + } +] diff --git a/resolver/test/fixtures/variable_reference.ftl b/resolver/test/fixtures/variable_reference.ftl new file mode 100644 index 0000000..1e8b4e8 --- /dev/null +++ b/resolver/test/fixtures/variable_reference.ftl @@ -0,0 +1,8 @@ +## Unknown variable + +hello = Hello, {$world}! + +## Variable set to a string +## $world: string = World + +hello = Hello, {$world}! diff --git a/resolver/test/fixtures/variable_reference.json b/resolver/test/fixtures/variable_reference.json new file mode 100644 index 0000000..3e5f68d --- /dev/null +++ b/resolver/test/fixtures/variable_reference.json @@ -0,0 +1,15 @@ +[ + { + "value": "Hello, {$world}!", + "errors": [ + { + "kind": "UnknownVariable", + "arg": "$world" + } + ] + }, + { + "value": "Hello, World!", + "errors": [] + } +] From 4bfceffed61e9dac2b5d1dae2f88cd420c21495c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 5 Jun 2019 16:39:49 +0200 Subject: [PATCH 28/36] New folder structure, part 1 --- .vscode/tasks.json | 1 - {resolver/src/bin => lib}/input.ts | 0 package-lock.json | 1283 +++++++++++++++++++++++++--- package.json | 12 +- resolver/dist/lib/parser.js | 1137 ------------------------ resolver/package-lock.json | 1057 ----------------------- resolver/package.json | 20 - resolver/src/bin/format.ts | 14 +- resolver/src/lib/parser.d.ts | 10 - resolver/test/tsconfig.json | 8 - resolver/tsconfig.json | 12 - syntax/grammar.d.ts | 10 + tsconfig.json | 11 + 13 files changed, 1219 insertions(+), 2356 deletions(-) rename {resolver/src/bin => lib}/input.ts (100%) delete mode 100644 resolver/dist/lib/parser.js delete mode 100644 resolver/package-lock.json delete mode 100644 resolver/package.json delete mode 100644 resolver/src/lib/parser.d.ts delete mode 100644 resolver/test/tsconfig.json delete mode 100644 resolver/tsconfig.json create mode 100644 syntax/grammar.d.ts create mode 100644 tsconfig.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4a0c45d..30abe81 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,6 @@ { "type": "npm", "script": "watch", - "path": "resolver/", "problemMatcher": [ "$tsc-watch" ], diff --git a/resolver/src/bin/input.ts b/lib/input.ts similarity index 100% rename from resolver/src/bin/input.ts rename to lib/input.ts diff --git a/package-lock.json b/package-lock.json index 3601eaf..cfc0a38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,12 +4,60 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "dev": true + }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, + "@types/node": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.4.tgz", + "integrity": "sha512-j8YL2C0fXq7IONwl/Ud5Kt0PeXw22zGERt+HSSnwbKOJVsAGkEz3sFCYwaF9IOuoG1HOtE0vKCj6sXF7Q0+Vaw==", + "dev": true + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "arg": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", + "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", @@ -26,12 +74,12 @@ "dev": true }, "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "dev": true, "requires": { - "lodash": "^4.14.0" + "lodash": "^4.17.10" } }, "babylon": { @@ -46,12 +94,6 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base64url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", - "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=", - "dev": true - }, "bash-color": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/bash-color/-/bash-color-0.0.4.tgz", @@ -68,24 +110,96 @@ "concat-map": "0.0.1" } }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "cli-color": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.2.0.tgz", - "integrity": "sha1-OlrnT9drYmevZm5p4q+70B3vNNE=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", + "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", "dev": true, "requires": { "ansi-regex": "^2.1.1", "d": "1", - "es5-ext": "^0.10.12", - "es6-iterator": "2", - "memoizee": "^0.4.3", - "timers-ext": "0.1" + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.14", + "timers-ext": "^0.1.5" + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" } }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, "concat-map": { @@ -94,6 +208,27 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + } + } + }, "d": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", @@ -103,6 +238,36 @@ "es5-ext": "^0.10.9" } }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, "difflib": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", @@ -121,15 +286,55 @@ "wordwrap": ">=0.0.2" } }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "es5-ext": { - "version": "0.10.42", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz", - "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==", + "version": "0.10.50", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz", + "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==", "dev": true, "requires": { "es6-iterator": "~2.0.3", "es6-symbol": "~3.1.1", - "next-tick": "1" + "next-tick": "^1.0.0" } }, "es6-iterator": { @@ -165,11 +370,23 @@ "es6-symbol": "^3.1.1" } }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, "esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, "event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", @@ -180,6 +397,66 @@ "es5-ext": "~0.10.14" } }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "filename-reserved-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz", + "integrity": "sha1-5hz4BfDeHJhFZ9A4bcXfUO5a9+Q=", + "dev": true + }, + "filenamify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.1.tgz", + "integrity": "sha1-qfL/0RxQO+0wABUCknI3jx8TZaU=", + "dev": true, + "requires": { + "filename-reserved-regex": "^1.0.0", + "strip-outer": "^1.0.0", + "trim-repeated": "^1.0.0" + } + }, + "filenamify-url": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/filenamify-url/-/filenamify-url-1.0.0.tgz", + "integrity": "sha1-syvYExnvWGO3MHi+1Q9GpPeXX1A=", + "dev": true, + "requires": { + "filenamify": "^1.0.0", + "humanize-url": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + } + }, "fluent": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/fluent/-/fluent-0.6.4.tgz", @@ -193,9 +470,9 @@ "dev": true }, "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -209,34 +486,40 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "gh-pages": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-1.1.0.tgz", - "integrity": "sha512-ZpDkeOVmIrN5mz+sBWDz5zmTqcbNJzI/updCwEv/7rrSdpTNlj1B5GhBqG7f4Q8p5sJOdnBV0SIqxJrxtZQ9FA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-1.2.0.tgz", + "integrity": "sha512-cGLYAvxtlQ1iTwAS4g7FreZPXoE/g62Fsxln2mmR19mgs4zZI+XJ+wVVUhBFCF/0+Nmvbq+abyTWue1m1BSnmg==", "dev": true, "requires": { - "async": "2.6.0", - "base64url": "^2.0.0", - "commander": "2.11.0", - "fs-extra": "^4.0.2", + "async": "2.6.1", + "commander": "2.15.1", + "filenamify-url": "^1.0.0", + "fs-extra": "^5.0.0", "globby": "^6.1.0", "graceful-fs": "4.1.11", "rimraf": "^2.6.2" - }, - "dependencies": { - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - } } }, "gitbook-cli": { @@ -258,6 +541,12 @@ "user-home": "2.0.0" }, "dependencies": { + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, "fs-extra": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", @@ -283,28 +572,13 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true - }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true - }, - "tmp": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.1" - } } } }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -315,18 +589,74 @@ "path-is-absolute": "^1.0.0" } }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, "heap": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz", "integrity": "sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw=", "dev": true }, + "humanize-url": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/humanize-url/-/humanize-url-1.0.1.tgz", + "integrity": "sha1-9KuZ4NKIF0yk4eUEB8VfuuRk7/8=", + "dev": true, + "requires": { + "normalize-url": "^1.0.0", + "strip-url-auth": "^1.0.0" + } + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -343,16 +673,92 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "json-diff": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/json-diff/-/json-diff-0.5.2.tgz", - "integrity": "sha512-N7oapTQdD4rLMUtA7d1HATCPY/BpHuSNL1mhvIuoS0u5NideDvyR+gB/ntXB7ejFz/LM0XzPLNUJQcC68n5sBw==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/json-diff/-/json-diff-0.5.4.tgz", + "integrity": "sha512-q5Xmx9QXNOzOzIlMoYtLrLiu4Jl/Ce2bn0CNcv54PhyH89CI4GWlGVDye8ei2Ijt9R3U+vsWPsXpLUNob8bs8Q==", "dev": true, "requires": { "cli-color": "~0.1.6", @@ -386,12 +792,40 @@ "graceful-fs": "^4.1.6" } }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", "dev": true }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, "lru-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", @@ -401,22 +835,54 @@ "es5-ext": "~0.10.2" } }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, "memoizee": { - "version": "0.4.12", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.12.tgz", - "integrity": "sha512-sprBu6nwxBWBvBOh5v2jcsGqiGLlL2xr2dLub3vR8dnE8YB17omwtm/0NSHl8jjNbcsJd5GMWJAnTSVe/O0Wfg==", + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", + "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", "dev": true, "requires": { "d": "1", - "es5-ext": "^0.10.30", + "es5-ext": "^0.10.45", "es6-weak-map": "^2.0.2", "event-emitter": "^0.3.5", "is-promise": "^2.1", "lru-queue": "0.1", "next-tick": "1", - "timers-ext": "^0.1.2" + "timers-ext": "^0.1.5" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -426,10 +892,80 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "mocha": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", + "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", + "dev": true, + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.2.2", + "yargs-parser": "13.0.0", + "yargs-unparser": "1.5.0" + }, + "dependencies": { + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true }, "next-tick": { "version": "1.0.0", @@ -437,6 +973,42 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + } + } + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, "npm": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm/-/npm-5.1.0.tgz", @@ -3386,6 +3958,15 @@ } } }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, "npmi": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/npmi/-/npmi-1.0.1.tgz", @@ -5014,12 +5595,46 @@ } } }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5044,12 +5659,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true - }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true } } }, @@ -5059,18 +5668,83 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -5092,41 +5766,257 @@ "pinkie": "^2.0.0" } }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, "prettier": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.1.tgz", "integrity": "sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "q": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", "integrity": "sha1-3QG6ydBtMObyGa7LglPunr3DCPE=", "dev": true }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", + "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "glob": "^7.0.5" + "ansi-regex": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "strip-url-auth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-url-auth/-/strip-url-auth-1.0.1.tgz", + "integrity": "sha1-IrD6OkE4WzO+PzMVUbu4N/oM164=", + "dev": true + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" } }, "timers-ext": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.5.tgz", - "integrity": "sha512-tsEStd7kmACHENhsUPaxb8Jf8/+GZZxyNFQbZD07HQOyooOa6At1rQqjffgvg7n+dxscQa9cjjMdWhJtsP2sxg==", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", "dev": true, "requires": { - "es5-ext": "~0.10.14", + "es5-ext": "~0.10.46", "next-tick": "1" } }, + "tmp": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "ts-node": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.2.0.tgz", + "integrity": "sha512-m8XQwUurkbYqXrKqr3WHCW310utRNvV5OnRVeISeea7LoCWVcdfeB/Ntl8JYWFh+WRoUAdBgESrzKochQt7sMw==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + }, + "dependencies": { + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + } + } + }, + "typescript": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.1.tgz", + "integrity": "sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==", + "dev": true + }, "universalify": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, "user-home": { @@ -5138,17 +6028,206 @@ "os-homedir": "^1.0.0" } }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", "dev": true }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", + "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs-parser": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", + "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + }, + "dependencies": { + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "yn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.0.tgz", + "integrity": "sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg==", + "dev": true } } } diff --git a/package.json b/package.json index e29aa81..9a26903 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "test:ebnf": "node -r esm test/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", "test:fixtures": "node -r esm test/parser.js ./test/fixtures", "test:unit": "node -r esm test/literals.js", - "test": "npm run --silent test:fixtures && npm run --silent test:unit" + "test:resolver": "mocha -u tdd -r ts-node/register ./resolver/test/**/*_test.ts", + "test": "npm run --silent test:fixtures && npm run --silent test:unit", + "watch": "tsc --watch" }, "homepage": "https://projectfluent.org", "repository": { @@ -31,6 +33,9 @@ "url": "https://github.com/projectfluent/fluent/issues" }, "devDependencies": { + "@types/minimist": "^1.2.0", + "@types/mocha": "^5.2.7", + "@types/node": "^12.0.4", "babylon": "^6.18.0", "cli-color": "^1.2.0", "difflib": "^0.2.4", @@ -39,7 +44,10 @@ "gh-pages": "^1.1.0", "gitbook-cli": "^2.3.0", "json-diff": "^0.5.2", - "prettier": "^1.17.1" + "prettier": "^1.17.1", + "mocha": "^6.1.4", + "ts-node": "^8.2.0", + "typescript": "^3.5.1" }, "dependencies": { "esm": "^3.2.25", diff --git a/resolver/dist/lib/parser.js b/resolver/dist/lib/parser.js deleted file mode 100644 index e8b64b0..0000000 --- a/resolver/dist/lib/parser.js +++ /dev/null @@ -1,1137 +0,0 @@ -/* eslint-disable */ - -"use strict"; - -Object.defineProperty(exports, "__esModule", {value: true}); - -// Base class for all Fluent AST nodes. -class BaseNode { - constructor() {} -} - -// Base class for AST nodes which can have Spans. -class SyntaxNode extends BaseNode { - addSpan(start, end) { - this.span = new Span(start, end); - } -} - -class Resource extends SyntaxNode { - constructor(body = []) { - super(); - this.type = "Resource"; - this.body = body; - } -} - -// An abstract base class for useful elements of Resource.body. -class Entry extends SyntaxNode {} - -class Message extends Entry { - constructor(id, value = null, attributes = [], comment = null) { - super(); - this.type = "Message"; - this.id = id; - this.value = value; - this.attributes = attributes; - this.comment = comment; - } -} - -class Term extends Entry { - constructor(id, value, attributes = [], comment = null) { - super(); - this.type = "Term"; - this.id = id; - this.value = value; - this.attributes = attributes; - this.comment = comment; - } -} - -class Pattern extends SyntaxNode { - constructor(elements) { - super(); - this.type = "Pattern"; - this.elements = elements; - } -} - -// An abstract base class for elements of Patterns. -class PatternElement extends SyntaxNode {} - -class TextElement extends PatternElement { - constructor(value) { - super(); - this.type = "TextElement"; - this.value = value; - } -} - -class Placeable extends PatternElement { - constructor(expression) { - super(); - this.type = "Placeable"; - this.expression = expression; - } -} - -// An abstract base class for Expressions. -class Expression extends SyntaxNode {} - -// An abstract base class for Literals. -class Literal extends Expression { - constructor(value) { - super(); - // The "value" field contains the exact contents of the literal, - // character-for-character. - this.value = value; - } - - // Implementations are free to decide how they process the raw value. When - // they do, however, they must comply with the behavior of `Literal.parse`. - parse() { - return {value: this.value}; - } -} - -class StringLiteral extends Literal { - constructor(value) { - super(value); - this.type = "StringLiteral"; - } - - parse() { - // Backslash backslash, backslash double quote, uHHHH, UHHHHHH. - const KNOWN_ESCAPES = /(?:\\\\|\\\"|\\u([0-9a-fA-F]{4})|\\U([0-9a-fA-F]{6}))/g; - - function from_escape_sequence(match, codepoint4, codepoint6) { - switch (match) { - case "\\\\": - return "\\"; - case '\\"': - return '"'; - default: - let codepoint = parseInt(codepoint4 || codepoint6, 16); - if (codepoint <= 0xd7ff || 0xe000 <= codepoint) { - // It's a Unicode scalar value. - return String.fromCodePoint(codepoint); - } - // Escape sequences reresenting surrogate code points are - // well-formed but invalid in Fluent. Replace them with U+FFFD - // REPLACEMENT CHARACTER. - return "�"; - } - } - - let value = this.value.replace(KNOWN_ESCAPES, from_escape_sequence); - return {value}; - } -} - -class NumberLiteral extends Literal { - constructor(value) { - super(value); - this.type = "NumberLiteral"; - } - - parse() { - let value = parseFloat(this.value); - let decimal_position = this.value.indexOf("."); - let precision = decimal_position > 0 ? this.value.length - decimal_position - 1 : 0; - return {value, precision}; - } -} - -class MessageReference extends Expression { - constructor(id, attribute) { - super(); - this.type = "MessageReference"; - this.id = id; - this.attribute = attribute; - } -} - -class TermReference extends Expression { - constructor(id, attribute, args) { - super(); - this.type = "TermReference"; - this.id = id; - this.attribute = attribute; - this.arguments = args; - } -} - -class VariableReference extends Expression { - constructor(id) { - super(); - this.type = "VariableReference"; - this.id = id; - } -} - -class FunctionReference extends Expression { - constructor(id, args) { - super(); - this.type = "FunctionReference"; - this.id = id; - this.arguments = args; - } -} - -class SelectExpression extends Expression { - constructor(selector, variants) { - super(); - this.type = "SelectExpression"; - this.selector = selector; - this.variants = variants; - } -} - -class Attribute extends SyntaxNode { - constructor(id, value) { - super(); - this.type = "Attribute"; - this.id = id; - this.value = value; - } -} - -class Variant extends SyntaxNode { - constructor(key, value, def = false) { - super(); - this.type = "Variant"; - this.key = key; - this.value = value; - this.default = def; - } -} - -class CallArguments extends SyntaxNode { - constructor(positional = [], named = []) { - super(); - this.type = "CallArguments"; - this.positional = positional; - this.named = named; - } -} - -class NamedArgument extends SyntaxNode { - constructor(name, value) { - super(); - this.type = "NamedArgument"; - this.name = name; - this.value = value; - } -} - -class Identifier extends SyntaxNode { - constructor(name) { - super(); - this.type = "Identifier"; - this.name = name; - } -} - -class BaseComment extends Entry { - constructor(content) { - super(); - this.type = "BaseComment"; - this.content = content; - } -} - -class Comment extends BaseComment { - constructor(content) { - super(content); - this.type = "Comment"; - } -} - -class GroupComment extends BaseComment { - constructor(content) { - super(content); - this.type = "GroupComment"; - } -} -class ResourceComment extends BaseComment { - constructor(content) { - super(content); - this.type = "ResourceComment"; - } -} - -class Junk extends SyntaxNode { - constructor(content) { - super(); - this.type = "Junk"; - this.annotations = []; - this.content = content; - } - - addAnnotation(annot) { - this.annotations.push(annot); - } -} - -class Span extends BaseNode { - constructor(start, end) { - super(); - this.type = "Span"; - this.start = start; - this.end = end; - } -} - -class Stream { - constructor(iterable, cursor, length) { - this.iterable = iterable; - this.cursor = cursor || 0; - this.length = length === undefined ? iterable.length - this.cursor : length; - } - - // Get the element at the cursor. - head(len = 1) { - if (this.length < 0) { - return undefined; - } - - if (this.length === 0) { - return Symbol.for("eof"); - } - - return this.iterable.slice(this.cursor, this.cursor + len); - } - - // Execute a regex on the iterable. - exec(re) { - // The "u" flag is a feature of ES2015 which makes regexes Unicode-aware. - // See https://mathiasbynens.be/notes/es6-unicode-regex. - // The "y" flag makes the regex sticky. The match must start at the - // offset specified by the regex's lastIndex property. - let sticky = new RegExp(re, "uy"); - sticky.lastIndex = this.cursor; - return sticky.exec(this.iterable); - } - - // Consume the stream by moving the cursor. - move(distance) { - return new Stream(this.iterable, this.cursor + distance, this.length - distance); - } -} - -class Result { - constructor(value, rest) { - this.value = value; - this.rest = rest; - } -} - -class Success extends Result { - map(fn) { - return new Success(fn(this.value), this.rest); - } - bimap(s, f) { - return new Success(s(this.value), this.rest); - } - chain(fn) { - return fn(this.value, this.rest); - } - fold(s, f) { - return s(this.value, this.rest); - } -} - -class Failure extends Result { - map(fn) { - return this; - } - bimap(s, f) { - return new Failure(f(this.value), this.rest); - } - chain(fn) { - return this; - } - fold(s, f) { - return f(this.value, this.rest); - } -} - -class Abstract { - constructor(value) { - this.value = value; - } -} - -class Parser { - constructor(parse) { - this.parse = parse; - } - - run(iterable) { - let stream = iterable instanceof Stream ? iterable : new Stream(iterable); - return this.parse(stream); - } - - get abstract() { - return this.map(value => new Abstract(value)); - } - - map(f) { - return new Parser(stream => this.run(stream).map(f)); - } - - bimap(s, f) { - return new Parser(stream => this.run(stream).bimap(s, f)); - } - - chain(f) { - return new Parser(stream => this.run(stream).chain((value, tail) => f(value).run(tail))); - } - - fold(s, f) { - return new Parser(stream => this.run(stream).fold(s, f)); - } -} - -// Flatten a list up to a given depth. -// This is useful when a parser uses nested sequences and repeats. -const flatten = depth => list => - list.reduce( - (acc, cur) => - acc.concat(!Array.isArray(cur) || depth === 1 ? cur : flatten(depth - 1)(cur)), - [] - ); - -// Mutate an object by merging properties of another object into it. -const mutate = state => obj => Object.assign(obj, state); - -// Join the list of parsed values into a string. -const join = list => list.filter(value => value !== Symbol.for("eof")).join(""); - -// Prune unmatched maybes from a list. -const prune = list => list.filter(value => value !== null); - -// Map a list of {name, value} aliases into an array of values. -const keep_abstract = list => - list.filter(value => value instanceof Abstract).map(({value}) => value); - -// Map a list to the element at the specified index. Useful for parsers which -// define a prefix or a surrounding delimiter. -const element_at = index => list => list[index]; - -function defer(fn) { - // Parsers may be defined as defer(() => parser) to avoid cyclic - // dependecies. - return new Parser(stream => fn().run(stream)); -} - -function string(str) { - return new Parser(stream => - stream.head(str.length) === str - ? new Success(str, stream.move(str.length)) - : new Failure(`${str} not found`, stream) - ); -} - -function regex(re) { - return new Parser(stream => { - const result = stream.exec(re); - - if (result === null) { - return new Failure("regex did not match", stream); - } - - const [match] = result; - - return new Success(match, stream.move(match.length)); - }); -} - -function charset(range) { - return regex(`[${range}]`); -} - -function eof() { - return new Parser(stream => - stream.head() === Symbol.for("eof") - ? new Success(stream.head(), stream.move(1)) - : new Failure("not at EOF", stream) - ); -} - -function lookahead(parser) { - return new Parser(stream => - parser - .run(stream) - .fold(value => new Success(value, stream), value => new Failure(value, stream)) - ); -} - -function not(parser) { - return new Parser(stream => - parser - .run(stream) - .fold( - (value, tail) => new Failure("not failed", stream), - (value, tail) => new Success(null, stream) - ) - ); -} - -function and(...parsers) { - const final = parsers.pop(); - return sequence(...parsers.map(lookahead), final).map(results => results[results.length - 1]); -} - -function either(...parsers) { - return new Parser(stream => { - for (const parser of parsers) { - const result = parser.run(stream); - if (result instanceof Success) { - return result; - } - } - return new Failure("either failed", stream); - }); -} - -function always(value) { - return new Parser(stream => new Success(value, stream)); -} - -function never(value) { - return new Parser(stream => new Failure(value, stream)); -} - -function maybe(parser) { - return new Parser(stream => - parser - .run(stream) - .fold( - (value, tail) => new Success(value, tail), - (value, tail) => new Success(null, stream) - ) - ); -} - -function append(p1, p2) { - return p1.chain(values => p2.map(value => values.concat([value]))); -} - -function sequence(...parsers) { - return parsers.reduce((acc, parser) => append(acc, parser), always([])); -} - -function repeat(parser) { - return new Parser(stream => - parser.run(stream).fold( - (value, tail) => - repeat(parser) - .map(rest => [value].concat(rest)) - .run(tail), - (value, tail) => new Success([], stream) - ) - ); -} - -function repeat1(parser) { - return new Parser(stream => - parser.run(stream).fold( - (value, tail) => - repeat(parser) - .map(rest => [value].concat(rest)) - .run(tail), - (value, tail) => new Failure("repeat1 failed", stream) - ) - ); -} - -/* - * AST Validation - * - * The parse result of the grammar.mjs parser is a well-formed AST which is - * validated according to the rules documented in `spec/valid.md`. - */ - -function list_into(Type) { - switch (Type) { - case Comment: - return ([sigil, content = ""]) => { - switch (sigil) { - case "#": - return always(new Comment(content)); - case "##": - return always(new GroupComment(content)); - case "###": - return always(new ResourceComment(content)); - default: - return never(`Unknown comment sigil: ${sigil}.`); - } - }; - case FunctionReference: - const VALID_FUNCTION_NAME = /^[A-Z][A-Z0-9_-]*$/; - return ([identifier, args]) => { - if (VALID_FUNCTION_NAME.test(identifier.name)) { - return always(new Type(identifier, args)); - } - return never( - `Invalid function name: ${identifier.name}. ` + - "Function names must be all upper-case ASCII letters." - ); - }; - case Pattern: - return elements => - always( - new Pattern( - dedent(elements) - .reduce(join_adjacent(TextElement), []) - .map(trim_text_at_extremes) - .filter(remove_empty_text) - ) - ); - case Resource: - return entries => - always( - new Resource( - entries - .reduce(join_adjacent(Comment, GroupComment, ResourceComment), []) - .reduce(attach_comments, []) - .filter(remove_blank_lines) - ) - ); - case SelectExpression: - return ([selector, variants]) => { - let selector_is_valid = - selector instanceof StringLiteral || - selector instanceof NumberLiteral || - selector instanceof VariableReference || - selector instanceof FunctionReference || - (selector instanceof TermReference && selector.attribute); - if (!selector_is_valid) { - return never(`Invalid selector type: ${selector.type}.`); - } - - return always(new Type(selector, variants)); - }; - default: - return elements => always(new Type(...elements)); - } -} - -function into(Type) { - switch (Type) { - case CallArguments: - return args => { - let positional = []; - let named = new Map(); - for (let arg of args) { - if (arg instanceof NamedArgument) { - let name = arg.name.name; - if (named.has(name)) { - return never("Named arguments must be unique."); - } - named.set(name, arg); - } else if (named.size > 0) { - return never("Positional arguments must not follow " + "named arguments"); - } else { - positional.push(arg); - } - } - return always(new Type(positional, Array.from(named.values()))); - }; - case Placeable: - return expression => { - if (expression instanceof TermReference && expression.attribute) { - return never("Term attributes may not be used as placeables."); - } - return always(new Type(expression)); - }; - default: - return (...args) => always(new Type(...args)); - } -} - -// Create a reducer suitable for joining adjacent nodes of the same type, if -// type is one of types specified. -function join_adjacent(...types) { - return function(acc, cur) { - let prev = acc[acc.length - 1]; - for (let Type of types) { - if (prev instanceof Type && cur instanceof Type) { - // Replace prev with a new node of the same type whose value is - // the sum of prev and cur, and discard cur. - acc[acc.length - 1] = join_of_type(Type, prev, cur); - return acc; - } - } - return acc.concat(cur); - }; -} - -// Join values of two or more nodes of the same type. Return a new node. -function join_of_type(Type, ...elements) { - // TODO Join annotations and spans. - switch (Type) { - case TextElement: - return elements.reduce((a, b) => new Type(a.value + b.value)); - case Comment: - case GroupComment: - case ResourceComment: - return elements.reduce((a, b) => new Type(a.content + "\n" + b.content)); - } -} - -function attach_comments(acc, cur) { - let prev = acc[acc.length - 1]; - if (prev instanceof Comment && (cur instanceof Message || cur instanceof Term)) { - cur.comment = prev; - acc[acc.length - 1] = cur; - return acc; - } else { - return acc.concat(cur); - } -} - -// Remove the largest common indentation from a list of elements of a Pattern. -// The indents are parsed in grammar.mjs and passed to abstract.mjs as string -// primitives along with other PatternElements. -function dedent(elements) { - // Calculate the maximum common indent. - let indents = elements.filter(element => typeof element === "string"); - let common = Math.min(...indents.map(indent => indent.length)); - - function trim_indents(element) { - if (typeof element === "string") { - // Trim the indent and convert it to a proper TextElement. - // It will be joined with its adjacents later on. - return new TextElement(element.slice(common)); - } - return element; - } - - return elements.map(trim_indents); -} - -const LEADING_BLANK_BLOCK = /^\n*/; -const TRAILING_BLANK_INLINE = / *$/; - -function trim_text_at_extremes(element, index, array) { - if (element instanceof TextElement) { - if (index === 0) { - element.value = element.value.replace(LEADING_BLANK_BLOCK, ""); - } - if (index === array.length - 1) { - element.value = element.value.replace(TRAILING_BLANK_INLINE, ""); - } - } - return element; -} - -function remove_empty_text(element) { - return !(element instanceof TextElement) || element.value !== ""; -} - -function remove_blank_lines(element) { - return typeof element !== "string"; -} - -/* ----------------------------------------------------- */ -/* An FTL file defines a Resource consisting of Entries. */ -let Resource$1 = defer(() => - repeat(either(Entry$1, blank_block, Junk$1)).chain(list_into(Resource)) -); - -/* ------------------------------------------------------------------------- */ -/* Entries are the main building blocks of Fluent. They define translations and - * contextual and semantic information about the translations. During the AST - * construction, adjacent comment lines of the same comment type (defined by - * the number of #) are joined together. Single-# comments directly preceding - * Messages and Terms are attached to the Message or Term and are not - * standalone Entries. */ -let Entry$1 = defer(() => - either( - sequence(Message$1, line_end).map(element_at(0)), - sequence(Term$1, line_end).map(element_at(0)), - CommentLine - ) -); - -let Message$1 = defer(() => - sequence( - Identifier$1.abstract, - maybe(blank_inline), - string("="), - maybe(blank_inline), - either( - sequence(Pattern$1.abstract, repeat(Attribute$1).abstract), - sequence(always(null).abstract, repeat1(Attribute$1).abstract) - ) - ) - .map(flatten(1)) - .map(keep_abstract) - .chain(list_into(Message)) -); - -let Term$1 = defer(() => - sequence( - string("-"), - Identifier$1.abstract, - maybe(blank_inline), - string("="), - maybe(blank_inline), - Pattern$1.abstract, - repeat(Attribute$1).abstract - ) - .map(keep_abstract) - .chain(list_into(Term)) -); - -/* -------------------------------------------------------------------------- */ -/* Adjacent comment lines of the same comment type are joined together during - * the AST construction. */ -let CommentLine = defer(() => - sequence( - either(string("###"), string("##"), string("#")).abstract, - maybe(sequence(string(" "), repeat(comment_char).map(join).abstract)), - line_end - ) - .map(flatten(1)) - .map(keep_abstract) - .chain(list_into(Comment)) -); - -let comment_char = defer(() => and(not(line_end), any_char)); - -/* -------------------------------------------------------------------------- */ -/* Junk represents unparsed content. - * - * Junk is parsed line-by-line until a line is found which looks like it might - * be a beginning of a new message, term, or a comment. Any whitespace - * following a broken Entry is also considered part of Junk. - */ -let Junk$1 = defer(() => - sequence( - junk_line, - repeat(and(not(charset("a-zA-Z")), not(string("-")), not(string("#")), junk_line)) - ) - .map(flatten(1)) - .map(join) - .chain(into(Junk)) -); - -let junk_line = sequence(regex(/[^\n]*/), either(string("\u000A"), eof())).map(join); - -/* --------------------------------- */ -/* Attributes of Messages and Terms. */ -let Attribute$1 = defer(() => - sequence( - line_end, - maybe(blank), - string("."), - Identifier$1.abstract, - maybe(blank_inline), - string("="), - maybe(blank_inline), - Pattern$1.abstract - ) - .map(keep_abstract) - .chain(list_into(Attribute)) -); - -/* ---------------------------------------------------------------- */ -/* Patterns are values of Messages, Terms, Attributes and Variants. */ -let Pattern$1 = defer(() => - repeat1(PatternElement$1) - // Flatten block_text and block_placeable which return lists. - .map(flatten(1)) - .chain(list_into(Pattern)) -); - -/* ----------------------------------------------------------------- */ -/* TextElement and Placeable can occur inline or as block. - * Text needs to be indented and start with a non-special character. - * Placeables can start at the beginning of the line or be indented. - * Adjacent TextElements are joined in AST creation. */ - -let PatternElement$1 = defer(() => - either(inline_text, block_text, inline_placeable, block_placeable) -); - -let inline_text = defer(() => - repeat1(text_char) - .map(join) - .chain(into(TextElement)) -); - -let block_text = defer(() => - sequence( - blank_block.chain(into(TextElement)), - blank_inline, - indented_char.chain(into(TextElement)), - maybe(inline_text) - ).map(prune) -); - -let inline_placeable = defer(() => - sequence( - string("{"), - maybe(blank), - either( - // Order matters! - SelectExpression$1, - InlineExpression - ), - maybe(blank), - string("}") - ) - .map(element_at(2)) - .chain(into(Placeable)) -); - -let block_placeable = defer(() => - sequence( - blank_block.chain(into(TextElement)), - // No indent before a placeable counts as 0 in dedention logic. - maybe(blank_inline).map(s => s || ""), - inline_placeable - ) -); - -/* ------------------------------------------------------------------- */ -/* Rules for validating expressions in Placeables and as selectors of - * SelectExpressions are documented in spec/valid.md and enforced in - * syntax/abstract.mjs. */ -let InlineExpression = defer(() => - either( - StringLiteral$1, - NumberLiteral$1, - FunctionReference$1, - MessageReference$1, - TermReference$1, - VariableReference$1, - inline_placeable - ) -); - -/* -------- */ -/* Literals */ -let StringLiteral$1 = defer(() => - sequence(string('"'), repeat(quoted_char), string('"')) - .map(element_at(1)) - .map(join) - .chain(into(StringLiteral)) -); - -let NumberLiteral$1 = defer(() => - sequence(maybe(string("-")), digits, maybe(sequence(string("."), digits))) - .map(flatten(1)) - .map(join) - .chain(into(NumberLiteral)) -); - -/* ------------------ */ -/* Inline Expressions */ -let FunctionReference$1 = defer(() => - sequence(Identifier$1, CallArguments$1).chain(list_into(FunctionReference)) -); - -let MessageReference$1 = defer(() => - sequence(Identifier$1, maybe(AttributeAccessor)).chain(list_into(MessageReference)) -); - -let TermReference$1 = defer(() => - sequence( - string("-"), - Identifier$1.abstract, - maybe(AttributeAccessor).abstract, - maybe(CallArguments$1).abstract - ) - .map(keep_abstract) - .chain(list_into(TermReference)) -); - -let VariableReference$1 = defer(() => - sequence(string("$"), Identifier$1) - .map(element_at(1)) - .chain(into(VariableReference)) -); - -let AttributeAccessor = defer(() => sequence(string("."), Identifier$1)).map(element_at(1)); - -let CallArguments$1 = defer(() => - sequence(maybe(blank), string("("), maybe(blank), argument_list, maybe(blank), string(")")) - .map(element_at(3)) - .chain(into(CallArguments)) -); - -let argument_list = defer(() => - sequence( - repeat(sequence(Argument.abstract, maybe(blank), string(","), maybe(blank))), - maybe(Argument.abstract) - ) - .map(flatten(2)) - .map(keep_abstract) -); - -let Argument = defer(() => either(NamedArgument$1, InlineExpression)); - -let NamedArgument$1 = defer(() => - sequence( - Identifier$1.abstract, - maybe(blank), - string(":"), - maybe(blank), - either(StringLiteral$1, NumberLiteral$1).abstract - ) - .map(keep_abstract) - .chain(list_into(NamedArgument)) -); - -/* ----------------- */ -/* Block Expressions */ -let SelectExpression$1 = defer(() => - sequence( - InlineExpression.abstract, - maybe(blank), - string("->"), - maybe(blank_inline), - variant_list.abstract - ) - .map(keep_abstract) - .chain(list_into(SelectExpression)) -); - -let variant_list = defer(() => - sequence( - repeat(Variant$1).abstract, - DefaultVariant.abstract, - repeat(Variant$1).abstract, - line_end - ) - .map(keep_abstract) - .map(flatten(1)) -); - -let Variant$1 = defer(() => - sequence(line_end, maybe(blank), VariantKey.abstract, maybe(blank_inline), Pattern$1.abstract) - .map(keep_abstract) - .chain(list_into(Variant)) -); - -let DefaultVariant = defer(() => - sequence( - line_end, - maybe(blank), - string("*"), - VariantKey.abstract, - maybe(blank_inline), - Pattern$1.abstract - ) - .map(keep_abstract) - .chain(list_into(Variant)) - .map(mutate({default: true})) -); - -let VariantKey = defer(() => - sequence( - string("["), - maybe(blank), - either(NumberLiteral$1, Identifier$1), - maybe(blank), - string("]") - ).map(element_at(2)) -); - -/* ---------- */ -/* Identifier */ - -let Identifier$1 = sequence(charset("a-zA-Z"), repeat(charset("a-zA-Z0-9_-"))) - .map(flatten(1)) - .map(join) - .chain(into(Identifier)); - -/* -------------------------------------------------------------------------- */ -/* Content Characters - * - * Translation content can be written using any Unicode characters. However, - * some characters are considered special depending on the type of content - * they're in. See text_char and quoted_char for more information. - * - * Some Unicode characters, even if allowed, should be avoided in Fluent - * resources. See spec/recommendations.md. - */ - -let any_char = charset("\\u{0}-\\u{10FFFF}"); - -/* -------------------------------------------------------------------------- */ -/* Text elements - * - * The primary storage for content are text elements. Text elements are not - * delimited with quotes and may span multiple lines as long as all lines are - * indented. The opening brace ({) marks a start of a placeable in the pattern - * and may not be used in text elements verbatim. Due to the indentation - * requirement some text characters may not appear as the first character on a - * new line. - */ - -let special_text_char = either(string("{"), string("}")); - -let text_char = defer(() => and(not(line_end), not(special_text_char), any_char)); - -let indented_char = and(not(string(".")), not(string("*")), not(string("[")), text_char); - -/* -------------------------------------------------------------------------- */ -/* String literals - * - * For special-purpose content, quoted string literals can be used where text - * elements are not a good fit. String literals are delimited with double - * quotes and may not contain line breaks. String literals use the backslash - * (\) as the escape character. The literal double quote can be inserted via - * the \" escape sequence. The literal backslash can be inserted with \\. The - * literal opening brace ({) is allowed in string literals because they may not - * comprise placeables. - */ - -let special_quoted_char = either(string('"'), string("\\")); - -let special_escape = sequence(string("\\"), special_quoted_char).map(join); - -let unicode_escape = either( - sequence(string("\\u"), regex(/[0-9a-fA-F]{4}/)), - sequence(string("\\U"), regex(/[0-9a-fA-F]{6}/)) -).map(join); - -let quoted_char = defer(() => - either(and(not(line_end), not(special_quoted_char), any_char), special_escape, unicode_escape) -); - -/* ------- */ -/* Numbers */ - -let digits = repeat1(charset("0-9")).map(join); - -/* ---------- */ -/* Whitespace */ -let blank_inline = repeat1(string("\u0020")).map(join); - -let line_end = either( - // Normalize CRLF to LF. - string("\u000D\u000A").map(() => "\n"), - string("\u000A"), - eof() -); - -let blank_block = repeat1(sequence(maybe(blank_inline), line_end.abstract)) - .map(flatten(1)) - // Discard the indents and only keep the newlines - // for multiline Patterns. - .map(keep_abstract) - .map(join); - -let blank = repeat1(either(blank_inline, line_end)); - -exports.Entry = Entry$1; -exports.NumberLiteral = NumberLiteral$1; -exports.Resource = Resource$1; -exports.StringLiteral = StringLiteral$1; diff --git a/resolver/package-lock.json b/resolver/package-lock.json deleted file mode 100644 index 541c717..0000000 --- a/resolver/package-lock.json +++ /dev/null @@ -1,1057 +0,0 @@ -{ - "name": "fluent-reference-resolver", - "version": "0.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@types/minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", - "dev": true - }, - "@types/mocha": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", - "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", - "dev": true - }, - "@types/node": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.4.tgz", - "integrity": "sha512-j8YL2C0fXq7IONwl/Ud5Kt0PeXw22zGERt+HSSnwbKOJVsAGkEz3sFCYwaF9IOuoG1HOtE0vKCj6sXF7Q0+Vaw==", - "dev": true - }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arg": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", - "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "is-buffer": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", - "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==", - "dev": true - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true - }, - "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } - }, - "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", - "dev": true - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "mocha": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", - "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", - "dev": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "2.2.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "ms": "2.1.1", - "node-environment-flags": "1.0.5", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.2.2", - "yargs-parser": "13.0.0", - "yargs-unparser": "1.5.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-environment-flags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", - "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", - "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", - "dev": true - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "source-map-support": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", - "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "ts-node": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.2.0.tgz", - "integrity": "sha512-m8XQwUurkbYqXrKqr3WHCW310utRNvV5OnRVeISeea7LoCWVcdfeB/Ntl8JYWFh+WRoUAdBgESrzKochQt7sMw==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" - }, - "dependencies": { - "diff": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", - "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", - "dev": true - } - } - }, - "typescript": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.1.tgz", - "integrity": "sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yargs": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", - "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", - "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "yargs-unparser": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", - "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", - "dev": true, - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.11", - "yargs": "^12.0.5" - }, - "dependencies": { - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "yn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.0.tgz", - "integrity": "sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg==", - "dev": true - } - } -} diff --git a/resolver/package.json b/resolver/package.json deleted file mode 100644 index c52342f..0000000 --- a/resolver/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "fluent-reference-resolver", - "private": true, - "version": "0.0.1", - "description": "A reference resolver for Fluent AST", - "scripts": { - "watch": "tsc --watch", - "test": "mocha -u tdd -r ts-node/register test/**/*_test.ts" - }, - "author": "Mozilla ", - "license": "Apache-2.0", - "devDependencies": { - "@types/minimist": "^1.2.0", - "@types/mocha": "^5.2.7", - "@types/node": "^12.0.4", - "mocha": "^6.1.4", - "ts-node": "^8.2.0", - "typescript": "^3.5.1" - } -} diff --git a/resolver/src/bin/format.ts b/resolver/src/bin/format.ts index 3f1dd16..085e404 100644 --- a/resolver/src/bin/format.ts +++ b/resolver/src/bin/format.ts @@ -1,10 +1,10 @@ import parseArgs from "minimist"; -import {fromStdin, fromFile} from "./input"; -import {Resource as ResourceParser} from "../lib/parser"; -import {Resource, NodeType, GroupComment} from "../ast"; -import {Bundle} from "../bundle"; -import {ErrorKind} from "../error"; -import {Value, StringValue, NumberValue} from "../value"; +import { fromStdin, fromFile } from "../../../lib/input"; +import { Resource as ResourceParser } from "../../../syntax/grammar"; +import { Resource, NodeType, GroupComment } from "../ast"; +import { Bundle } from "../bundle"; +import { ErrorKind } from "../error"; +import { Value, StringValue, NumberValue } from "../value"; const argv = parseArgs(process.argv.slice(2), { boolean: ["help", "group"], @@ -77,7 +77,7 @@ function formatResource(resource: Resource, variables: Variables) { } let message = bundle.getMessage(entry.id.name); if (message) { - let {value, errors} = bundle.formatValue(message, variables); + let { value, errors } = bundle.formatValue(message, variables); results.push({ value, errors: errors.map(error => ({ diff --git a/resolver/src/lib/parser.d.ts b/resolver/src/lib/parser.d.ts deleted file mode 100644 index 20a023a..0000000 --- a/resolver/src/lib/parser.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {Resource} from "../ast"; -interface Result { - fold(s: (value: T) => T, f: (err: E) => never): T; -} - -interface Parser { - run(input: string): Result; -} - -export declare let Resource: Parser; diff --git a/resolver/test/tsconfig.json b/resolver/test/tsconfig.json deleted file mode 100644 index 4a69d9c..0000000 --- a/resolver/test/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "target": "es2018", - "module": "commonjs", - "strict": true, - "esModuleInterop": true, - } -} diff --git a/resolver/tsconfig.json b/resolver/tsconfig.json deleted file mode 100644 index e02532f..0000000 --- a/resolver/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "target": "es2018", - "module": "commonjs", - "strict": true, - "esModuleInterop": true, - "outDir": "./dist" - }, - "include": [ - "./src/**/*" - ] -} diff --git a/syntax/grammar.d.ts b/syntax/grammar.d.ts new file mode 100644 index 0000000..92d38de --- /dev/null +++ b/syntax/grammar.d.ts @@ -0,0 +1,10 @@ +import { Resource } from "../resolver/src/ast"; +interface Result { + fold(s: (value: T) => T, f: (err: E) => never): T; +} + +interface Parser { + run(input: string): Result; +} + +export declare let Resource: Parser; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..838c563 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "allowJs": true, + "outDir": "./build" + }, + "include": ["./syntax/**/*", "./resolver/**/*", "./bin/**/*", "./lib/**/*"] +} From 21ef0077f9313bc4ff2487b5e657224131b14b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 5 Jun 2019 17:50:19 +0200 Subject: [PATCH 29/36] New folder structure, part 2 --- {resolver/src/bin => bin}/format.ts | 14 +-- package.json | 108 +++++++++--------- resolver/.gitignore | 2 - resolver/.prettierrc | 6 - resolver/{src => }/ast.ts | 0 resolver/{src => }/bundle.ts | 0 resolver/{src => }/error.ts | 0 resolver/{src => }/message.ts | 0 resolver/{src => }/result.ts | 0 resolver/{src => }/scope.ts | 0 resolver/{src => }/value.ts | 0 syntax/grammar.d.ts | 6 +- {resolver/test => test/resolver}/fixtures.ts | 0 .../test => test/resolver}/fixtures/Makefile | 0 .../resolver}/fixtures/message_reference.ftl | 0 .../resolver}/fixtures/message_reference.json | 0 .../resolver}/fixtures/variable_reference.ftl | 0 .../fixtures/variable_reference.json | 0 .../test => test/resolver}/sample_test.ts | 6 +- tsconfig.json | 3 +- 20 files changed, 68 insertions(+), 77 deletions(-) rename {resolver/src/bin => bin}/format.ts (89%) delete mode 100644 resolver/.gitignore delete mode 100644 resolver/.prettierrc rename resolver/{src => }/ast.ts (100%) rename resolver/{src => }/bundle.ts (100%) rename resolver/{src => }/error.ts (100%) rename resolver/{src => }/message.ts (100%) rename resolver/{src => }/result.ts (100%) rename resolver/{src => }/scope.ts (100%) rename resolver/{src => }/value.ts (100%) rename {resolver/test => test/resolver}/fixtures.ts (100%) rename {resolver/test => test/resolver}/fixtures/Makefile (100%) rename {resolver/test => test/resolver}/fixtures/message_reference.ftl (100%) rename {resolver/test => test/resolver}/fixtures/message_reference.json (100%) rename {resolver/test => test/resolver}/fixtures/variable_reference.ftl (100%) rename {resolver/test => test/resolver}/fixtures/variable_reference.json (100%) rename {resolver/test => test/resolver}/sample_test.ts (97%) diff --git a/resolver/src/bin/format.ts b/bin/format.ts similarity index 89% rename from resolver/src/bin/format.ts rename to bin/format.ts index 085e404..4c60b11 100644 --- a/resolver/src/bin/format.ts +++ b/bin/format.ts @@ -1,10 +1,10 @@ import parseArgs from "minimist"; -import { fromStdin, fromFile } from "../../../lib/input"; -import { Resource as ResourceParser } from "../../../syntax/grammar"; -import { Resource, NodeType, GroupComment } from "../ast"; -import { Bundle } from "../bundle"; -import { ErrorKind } from "../error"; -import { Value, StringValue, NumberValue } from "../value"; +import {fromStdin, fromFile} from "../lib/input"; +import {Resource as ResourceParser} from "../syntax/grammar"; +import {Resource, NodeType, GroupComment} from "../resolver/ast"; +import {Bundle} from "../resolver/bundle"; +import {ErrorKind} from "../resolver/error"; +import {Value, StringValue, NumberValue} from "../resolver/value"; const argv = parseArgs(process.argv.slice(2), { boolean: ["help", "group"], @@ -77,7 +77,7 @@ function formatResource(resource: Resource, variables: Variables) { } let message = bundle.getMessage(entry.id.name); if (message) { - let { value, errors } = bundle.formatValue(message, variables); + let {value, errors} = bundle.formatValue(message, variables); results.push({ value, errors: errors.map(error => ({ diff --git a/package.json b/package.json index 9a26903..f142fcc 100644 --- a/package.json +++ b/package.json @@ -1,56 +1,56 @@ { - "name": "fluent-spec", - "description": "Specification and documentation for Fluent", - "version": "1.0.0", - "private": true, - "scripts": { - "bench": "node -r esm --harmony-async-iteration ./test/bench.js ./test/benchmarks/gecko_strings.ftl", - "build:guide": "gitbook build guide build/guide", - "build": "npm run --silent build:guide", - "ci": "npm run --silent lint && npm test && npm run --silent test:ebnf", - "clean": "rm -rf build", - "deploy": "gh-pages -d build", - "generate:ebnf": "node -r esm bin/ebnf.js ./syntax/grammar.js > ./spec/fluent.ebnf", - "generate:fixtures": "make -sC test/fixtures", - "generate": "npm run --silent generate:ebnf && npm run --silent generate:fixtures", - "lint": "prettier --check **/*.js", - "pretty": "prettier --write **/*.js", - "test:ebnf": "node -r esm test/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", - "test:fixtures": "node -r esm test/parser.js ./test/fixtures", - "test:unit": "node -r esm test/literals.js", - "test:resolver": "mocha -u tdd -r ts-node/register ./resolver/test/**/*_test.ts", - "test": "npm run --silent test:fixtures && npm run --silent test:unit", - "watch": "tsc --watch" - }, - "homepage": "https://projectfluent.org", - "repository": { - "type": "git", - "url": "git+https://github.com/projectfluent/fluent.git" - }, - "author": "Mozilla ", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/projectfluent/fluent/issues" - }, - "devDependencies": { - "@types/minimist": "^1.2.0", - "@types/mocha": "^5.2.7", - "@types/node": "^12.0.4", - "babylon": "^6.18.0", - "cli-color": "^1.2.0", - "difflib": "^0.2.4", - "fluent": "^0.6.4", - "fluent-syntax": "^0.7.0", - "gh-pages": "^1.1.0", - "gitbook-cli": "^2.3.0", - "json-diff": "^0.5.2", - "prettier": "^1.17.1", - "mocha": "^6.1.4", - "ts-node": "^8.2.0", - "typescript": "^3.5.1" - }, - "dependencies": { - "esm": "^3.2.25", - "minimist": "^1.2.0" - } + "name": "fluent-spec", + "description": "Specification and documentation for Fluent", + "version": "1.0.0", + "private": true, + "scripts": { + "bench": "node -r esm --harmony-async-iteration ./test/bench.js ./test/benchmarks/gecko_strings.ftl", + "build:guide": "gitbook build guide build/guide", + "build": "npm run --silent build:guide", + "ci": "npm run --silent lint && npm test && npm run --silent test:ebnf", + "clean": "rm -rf build", + "deploy": "gh-pages -d build", + "generate:ebnf": "node -r esm bin/ebnf.js ./syntax/grammar.js > ./spec/fluent.ebnf", + "generate:fixtures": "make -sC test/fixtures", + "generate": "npm run --silent generate:ebnf && npm run --silent generate:fixtures", + "lint": "prettier --check **/*.js", + "pretty": "prettier --write **/*.js", + "test:ebnf": "node -r esm test/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", + "test:fixtures": "node -r esm test/parser.js ./test/fixtures", + "test:unit": "node -r esm test/literals.js", + "test:resolver": "mocha -u tdd -r ts-node/register ./test/resolver/**/*_test.ts", + "test": "npm run --silent test:fixtures && npm run --silent test:unit", + "watch": "tsc --watch" + }, + "homepage": "https://projectfluent.org", + "repository": { + "type": "git", + "url": "git+https://github.com/projectfluent/fluent.git" + }, + "author": "Mozilla ", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/projectfluent/fluent/issues" + }, + "devDependencies": { + "@types/minimist": "^1.2.0", + "@types/mocha": "^5.2.7", + "@types/node": "^12.0.4", + "babylon": "^6.18.0", + "cli-color": "^1.2.0", + "difflib": "^0.2.4", + "fluent": "^0.6.4", + "fluent-syntax": "^0.7.0", + "gh-pages": "^1.1.0", + "gitbook-cli": "^2.3.0", + "json-diff": "^0.5.2", + "prettier": "^1.17.1", + "mocha": "^6.1.4", + "ts-node": "^8.2.0", + "typescript": "^3.5.1" + }, + "dependencies": { + "esm": "^3.2.25", + "minimist": "^1.2.0" + } } diff --git a/resolver/.gitignore b/resolver/.gitignore deleted file mode 100644 index 657138a..0000000 --- a/resolver/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dist/bin -dist/*.js diff --git a/resolver/.prettierrc b/resolver/.prettierrc deleted file mode 100644 index ac8d76d..0000000 --- a/resolver/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "bracketSpacing": false, - "trailingComma": "es5", - "printWidth": 100, - "tabWidth": 4 -} diff --git a/resolver/src/ast.ts b/resolver/ast.ts similarity index 100% rename from resolver/src/ast.ts rename to resolver/ast.ts diff --git a/resolver/src/bundle.ts b/resolver/bundle.ts similarity index 100% rename from resolver/src/bundle.ts rename to resolver/bundle.ts diff --git a/resolver/src/error.ts b/resolver/error.ts similarity index 100% rename from resolver/src/error.ts rename to resolver/error.ts diff --git a/resolver/src/message.ts b/resolver/message.ts similarity index 100% rename from resolver/src/message.ts rename to resolver/message.ts diff --git a/resolver/src/result.ts b/resolver/result.ts similarity index 100% rename from resolver/src/result.ts rename to resolver/result.ts diff --git a/resolver/src/scope.ts b/resolver/scope.ts similarity index 100% rename from resolver/src/scope.ts rename to resolver/scope.ts diff --git a/resolver/src/value.ts b/resolver/value.ts similarity index 100% rename from resolver/src/value.ts rename to resolver/value.ts diff --git a/syntax/grammar.d.ts b/syntax/grammar.d.ts index 92d38de..cb8a63d 100644 --- a/syntax/grammar.d.ts +++ b/syntax/grammar.d.ts @@ -1,10 +1,10 @@ -import { Resource } from "../resolver/src/ast"; +import {Resource} from "../resolver/ast"; interface Result { - fold(s: (value: T) => T, f: (err: E) => never): T; + fold(s: (value: T) => T, f: (err: E) => never): T; } interface Parser { - run(input: string): Result; + run(input: string): Result; } export declare let Resource: Parser; diff --git a/resolver/test/fixtures.ts b/test/resolver/fixtures.ts similarity index 100% rename from resolver/test/fixtures.ts rename to test/resolver/fixtures.ts diff --git a/resolver/test/fixtures/Makefile b/test/resolver/fixtures/Makefile similarity index 100% rename from resolver/test/fixtures/Makefile rename to test/resolver/fixtures/Makefile diff --git a/resolver/test/fixtures/message_reference.ftl b/test/resolver/fixtures/message_reference.ftl similarity index 100% rename from resolver/test/fixtures/message_reference.ftl rename to test/resolver/fixtures/message_reference.ftl diff --git a/resolver/test/fixtures/message_reference.json b/test/resolver/fixtures/message_reference.json similarity index 100% rename from resolver/test/fixtures/message_reference.json rename to test/resolver/fixtures/message_reference.json diff --git a/resolver/test/fixtures/variable_reference.ftl b/test/resolver/fixtures/variable_reference.ftl similarity index 100% rename from resolver/test/fixtures/variable_reference.ftl rename to test/resolver/fixtures/variable_reference.ftl diff --git a/resolver/test/fixtures/variable_reference.json b/test/resolver/fixtures/variable_reference.json similarity index 100% rename from resolver/test/fixtures/variable_reference.json rename to test/resolver/fixtures/variable_reference.json diff --git a/resolver/test/sample_test.ts b/test/resolver/sample_test.ts similarity index 97% rename from resolver/test/sample_test.ts rename to test/resolver/sample_test.ts index b2f6ffa..60e1c2f 100644 --- a/resolver/test/sample_test.ts +++ b/test/resolver/sample_test.ts @@ -1,8 +1,8 @@ import * as assert from "assert"; import {hello, exclamation, select} from "./fixtures"; -import {Bundle} from "../src/bundle"; -import {StringValue} from "../src/value"; -import {ScopeError, ErrorKind} from "../src/error"; +import {Bundle} from "../../resolver/bundle"; +import {StringValue} from "../../resolver/value"; +import {ScopeError, ErrorKind} from "../../resolver/error"; suite("Sample suite", function() { suiteSetup(function() { diff --git a/tsconfig.json b/tsconfig.json index 838c563..26204a8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,5 @@ "esModuleInterop": true, "allowJs": true, "outDir": "./build" - }, - "include": ["./syntax/**/*", "./resolver/**/*", "./bin/**/*", "./lib/**/*"] + } } From bb97aa4e39e25350a6e4083d219aedf1be8d0d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Wed, 5 Jun 2019 17:52:32 +0200 Subject: [PATCH 30/36] Add test:resolver to npm test --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f142fcc..a1b05ba 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "test:fixtures": "node -r esm test/parser.js ./test/fixtures", "test:unit": "node -r esm test/literals.js", "test:resolver": "mocha -u tdd -r ts-node/register ./test/resolver/**/*_test.ts", - "test": "npm run --silent test:fixtures && npm run --silent test:unit", + "test": "npm run --silent test:fixtures && npm run --silent test:unit && npm run --silent test:resolver", "watch": "tsc --watch" }, "homepage": "https://projectfluent.org", From b5acf37913212e1415132b07a2ea764091f8e0ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 6 Jun 2019 10:12:57 +0200 Subject: [PATCH 31/36] test/syntax, test/resolver --- .gitattributes | 4 ++-- package.json | 10 +++++----- test/{ => benchmarks}/bench.js | 5 ++--- test/{ => harness}/suite.js | 1 - test/{ => harness}/util.js | 0 test/resolver/fixtures/Makefile | 4 ++-- test/{ => syntax}/ebnf.js | 4 ++-- test/{ => syntax}/fixtures/Makefile | 4 ++-- test/{ => syntax}/fixtures/any_char.ftl | 0 test/{ => syntax}/fixtures/any_char.json | 0 test/{ => syntax}/fixtures/astral.ftl | 0 test/{ => syntax}/fixtures/astral.json | 0 test/{ => syntax}/fixtures/call_expressions.ftl | 0 test/{ => syntax}/fixtures/call_expressions.json | 0 test/{ => syntax}/fixtures/callee_expressions.ftl | 0 test/{ => syntax}/fixtures/callee_expressions.json | 0 test/{ => syntax}/fixtures/comments.ftl | 0 test/{ => syntax}/fixtures/comments.json | 0 test/{ => syntax}/fixtures/cr.ftl | 0 test/{ => syntax}/fixtures/cr.json | 0 test/{ => syntax}/fixtures/crlf.ftl | 0 test/{ => syntax}/fixtures/crlf.json | 0 test/{ => syntax}/fixtures/eof_comment.ftl | 0 test/{ => syntax}/fixtures/eof_comment.json | 0 test/{ => syntax}/fixtures/eof_empty.ftl | 0 test/{ => syntax}/fixtures/eof_empty.json | 0 test/{ => syntax}/fixtures/eof_id.ftl | 0 test/{ => syntax}/fixtures/eof_id.json | 0 test/{ => syntax}/fixtures/eof_id_equals.ftl | 0 test/{ => syntax}/fixtures/eof_id_equals.json | 0 test/{ => syntax}/fixtures/eof_junk.ftl | 0 test/{ => syntax}/fixtures/eof_junk.json | 0 test/{ => syntax}/fixtures/eof_value.ftl | 0 test/{ => syntax}/fixtures/eof_value.json | 0 test/{ => syntax}/fixtures/escaped_characters.ftl | 0 test/{ => syntax}/fixtures/escaped_characters.json | 0 test/{ => syntax}/fixtures/junk.ftl | 0 test/{ => syntax}/fixtures/junk.json | 0 test/{ => syntax}/fixtures/leading_dots.ftl | 0 test/{ => syntax}/fixtures/leading_dots.json | 0 test/{ => syntax}/fixtures/literal_expressions.ftl | 0 test/{ => syntax}/fixtures/literal_expressions.json | 0 test/{ => syntax}/fixtures/member_expressions.ftl | 0 test/{ => syntax}/fixtures/member_expressions.json | 0 test/{ => syntax}/fixtures/messages.ftl | 0 test/{ => syntax}/fixtures/messages.json | 0 test/{ => syntax}/fixtures/mixed_entries.ftl | 0 test/{ => syntax}/fixtures/mixed_entries.json | 0 test/{ => syntax}/fixtures/multiline_values.ftl | 0 test/{ => syntax}/fixtures/multiline_values.json | 0 test/{ => syntax}/fixtures/numbers.ftl | 0 test/{ => syntax}/fixtures/numbers.json | 0 test/{ => syntax}/fixtures/obsolete.ftl | 0 test/{ => syntax}/fixtures/obsolete.json | 0 test/{ => syntax}/fixtures/placeables.ftl | 0 test/{ => syntax}/fixtures/placeables.json | 0 test/{ => syntax}/fixtures/reference_expressions.ftl | 0 test/{ => syntax}/fixtures/reference_expressions.json | 0 test/{ => syntax}/fixtures/select_expressions.ftl | 0 test/{ => syntax}/fixtures/select_expressions.json | 0 test/{ => syntax}/fixtures/select_indent.ftl | 0 test/{ => syntax}/fixtures/select_indent.json | 0 test/{ => syntax}/fixtures/sparse_entries.ftl | 0 test/{ => syntax}/fixtures/sparse_entries.json | 0 test/{ => syntax}/fixtures/tab.ftl | 0 test/{ => syntax}/fixtures/tab.json | 0 test/{ => syntax}/fixtures/term_parameters.ftl | 0 test/{ => syntax}/fixtures/term_parameters.json | 0 test/{ => syntax}/fixtures/terms.ftl | 0 test/{ => syntax}/fixtures/terms.json | 0 test/{ => syntax}/fixtures/variables.ftl | 0 test/{ => syntax}/fixtures/variables.json | 0 test/{ => syntax}/fixtures/variant_keys.ftl | 0 test/{ => syntax}/fixtures/variant_keys.json | 0 test/{ => syntax}/fixtures/whitespace_in_value.ftl | 0 test/{ => syntax}/fixtures/whitespace_in_value.json | 0 test/{ => syntax}/literals.js | 5 ++--- test/{ => syntax}/parser.js | 4 ++-- 78 files changed, 19 insertions(+), 22 deletions(-) rename test/{ => benchmarks}/bench.js (95%) rename test/{ => harness}/suite.js (97%) rename test/{ => harness}/util.js (100%) rename test/{ => syntax}/ebnf.js (92%) rename test/{ => syntax}/fixtures/Makefile (73%) rename test/{ => syntax}/fixtures/any_char.ftl (100%) rename test/{ => syntax}/fixtures/any_char.json (100%) rename test/{ => syntax}/fixtures/astral.ftl (100%) rename test/{ => syntax}/fixtures/astral.json (100%) rename test/{ => syntax}/fixtures/call_expressions.ftl (100%) rename test/{ => syntax}/fixtures/call_expressions.json (100%) rename test/{ => syntax}/fixtures/callee_expressions.ftl (100%) rename test/{ => syntax}/fixtures/callee_expressions.json (100%) rename test/{ => syntax}/fixtures/comments.ftl (100%) rename test/{ => syntax}/fixtures/comments.json (100%) rename test/{ => syntax}/fixtures/cr.ftl (100%) rename test/{ => syntax}/fixtures/cr.json (100%) rename test/{ => syntax}/fixtures/crlf.ftl (100%) rename test/{ => syntax}/fixtures/crlf.json (100%) rename test/{ => syntax}/fixtures/eof_comment.ftl (100%) rename test/{ => syntax}/fixtures/eof_comment.json (100%) rename test/{ => syntax}/fixtures/eof_empty.ftl (100%) rename test/{ => syntax}/fixtures/eof_empty.json (100%) rename test/{ => syntax}/fixtures/eof_id.ftl (100%) rename test/{ => syntax}/fixtures/eof_id.json (100%) rename test/{ => syntax}/fixtures/eof_id_equals.ftl (100%) rename test/{ => syntax}/fixtures/eof_id_equals.json (100%) rename test/{ => syntax}/fixtures/eof_junk.ftl (100%) rename test/{ => syntax}/fixtures/eof_junk.json (100%) rename test/{ => syntax}/fixtures/eof_value.ftl (100%) rename test/{ => syntax}/fixtures/eof_value.json (100%) rename test/{ => syntax}/fixtures/escaped_characters.ftl (100%) rename test/{ => syntax}/fixtures/escaped_characters.json (100%) rename test/{ => syntax}/fixtures/junk.ftl (100%) rename test/{ => syntax}/fixtures/junk.json (100%) rename test/{ => syntax}/fixtures/leading_dots.ftl (100%) rename test/{ => syntax}/fixtures/leading_dots.json (100%) rename test/{ => syntax}/fixtures/literal_expressions.ftl (100%) rename test/{ => syntax}/fixtures/literal_expressions.json (100%) rename test/{ => syntax}/fixtures/member_expressions.ftl (100%) rename test/{ => syntax}/fixtures/member_expressions.json (100%) rename test/{ => syntax}/fixtures/messages.ftl (100%) rename test/{ => syntax}/fixtures/messages.json (100%) rename test/{ => syntax}/fixtures/mixed_entries.ftl (100%) rename test/{ => syntax}/fixtures/mixed_entries.json (100%) rename test/{ => syntax}/fixtures/multiline_values.ftl (100%) rename test/{ => syntax}/fixtures/multiline_values.json (100%) rename test/{ => syntax}/fixtures/numbers.ftl (100%) rename test/{ => syntax}/fixtures/numbers.json (100%) rename test/{ => syntax}/fixtures/obsolete.ftl (100%) rename test/{ => syntax}/fixtures/obsolete.json (100%) rename test/{ => syntax}/fixtures/placeables.ftl (100%) rename test/{ => syntax}/fixtures/placeables.json (100%) rename test/{ => syntax}/fixtures/reference_expressions.ftl (100%) rename test/{ => syntax}/fixtures/reference_expressions.json (100%) rename test/{ => syntax}/fixtures/select_expressions.ftl (100%) rename test/{ => syntax}/fixtures/select_expressions.json (100%) rename test/{ => syntax}/fixtures/select_indent.ftl (100%) rename test/{ => syntax}/fixtures/select_indent.json (100%) rename test/{ => syntax}/fixtures/sparse_entries.ftl (100%) rename test/{ => syntax}/fixtures/sparse_entries.json (100%) rename test/{ => syntax}/fixtures/tab.ftl (100%) rename test/{ => syntax}/fixtures/tab.json (100%) rename test/{ => syntax}/fixtures/term_parameters.ftl (100%) rename test/{ => syntax}/fixtures/term_parameters.json (100%) rename test/{ => syntax}/fixtures/terms.ftl (100%) rename test/{ => syntax}/fixtures/terms.json (100%) rename test/{ => syntax}/fixtures/variables.ftl (100%) rename test/{ => syntax}/fixtures/variables.json (100%) rename test/{ => syntax}/fixtures/variant_keys.ftl (100%) rename test/{ => syntax}/fixtures/variant_keys.json (100%) rename test/{ => syntax}/fixtures/whitespace_in_value.ftl (100%) rename test/{ => syntax}/fixtures/whitespace_in_value.json (100%) rename test/{ => syntax}/literals.js (95%) rename test/{ => syntax}/parser.js (95%) diff --git a/.gitattributes b/.gitattributes index 4bf2ed7..a19af2b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,3 @@ *.ftl eol=lf -test/fixtures/crlf.ftl eol=crlf -test/fixtures/cr.ftl eol=cr +test/syntax/fixtures/crlf.ftl eol=crlf +test/syntax/fixtures/cr.ftl eol=cr diff --git a/package.json b/package.json index a1b05ba..7c7b5f8 100644 --- a/package.json +++ b/package.json @@ -4,20 +4,20 @@ "version": "1.0.0", "private": true, "scripts": { - "bench": "node -r esm --harmony-async-iteration ./test/bench.js ./test/benchmarks/gecko_strings.ftl", + "bench": "node -r esm --harmony-async-iteration ./test/benchmarks/bench.js ./test/benchmarks/gecko_strings.ftl", "build:guide": "gitbook build guide build/guide", "build": "npm run --silent build:guide", "ci": "npm run --silent lint && npm test && npm run --silent test:ebnf", "clean": "rm -rf build", "deploy": "gh-pages -d build", "generate:ebnf": "node -r esm bin/ebnf.js ./syntax/grammar.js > ./spec/fluent.ebnf", - "generate:fixtures": "make -sC test/fixtures", + "generate:fixtures": "make -sC test/syntax/fixtures && make -sC test/resolver/fixtures", "generate": "npm run --silent generate:ebnf && npm run --silent generate:fixtures", "lint": "prettier --check **/*.js", "pretty": "prettier --write **/*.js", - "test:ebnf": "node -r esm test/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", - "test:fixtures": "node -r esm test/parser.js ./test/fixtures", - "test:unit": "node -r esm test/literals.js", + "test:ebnf": "node -r esm test/syntax/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", + "test:fixtures": "node -r esm test/syntax/parser.js ./test/syntax/fixtures", + "test:unit": "node -r esm test/syntax/literals.js", "test:resolver": "mocha -u tdd -r ts-node/register ./test/resolver/**/*_test.ts", "test": "npm run --silent test:fixtures && npm run --silent test:unit && npm run --silent test:resolver", "watch": "tsc --watch" diff --git a/test/bench.js b/test/benchmarks/bench.js similarity index 95% rename from test/bench.js rename to test/benchmarks/bench.js index ee6e697..18028cb 100644 --- a/test/bench.js +++ b/test/benchmarks/bench.js @@ -1,11 +1,10 @@ -import fs from "fs"; import perf from "perf_hooks"; const {PerformanceObserver, performance} = perf; import {parse} from "fluent-syntax"; import {_parse} from "fluent"; -import {Resource} from "../syntax/grammar.js"; -import {readfile} from "./util.js"; +import {Resource} from "../../syntax/grammar.js"; +import {readfile} from "../harness/util.js"; let args = process.argv.slice(2); diff --git a/test/suite.js b/test/harness/suite.js similarity index 97% rename from test/suite.js rename to test/harness/suite.js index 5962164..8be5e71 100644 --- a/test/suite.js +++ b/test/harness/suite.js @@ -1,5 +1,4 @@ import assert from "assert"; -import color from "cli-color"; import {diff, PASS, FAIL} from "./util.js"; export default function suite(fn) { diff --git a/test/util.js b/test/harness/util.js similarity index 100% rename from test/util.js rename to test/harness/util.js diff --git a/test/resolver/fixtures/Makefile b/test/resolver/fixtures/Makefile index 5d4a94f..8fc4b47 100644 --- a/test/resolver/fixtures/Makefile +++ b/test/resolver/fixtures/Makefile @@ -5,7 +5,7 @@ all: $(AST_FIXTURES) .PHONY: $(AST_FIXTURES) $(AST_FIXTURES): %.json: %.ftl - @node ../../dist/bin/format.js --group $< \ + @node ../../../build/bin/format.js --group $< \ 2> /dev/null \ 1> $@; - @echo "$< → $@" + @echo "resolver/$< → $@" diff --git a/test/ebnf.js b/test/syntax/ebnf.js similarity index 92% rename from test/ebnf.js rename to test/syntax/ebnf.js index cf88861..563b110 100644 --- a/test/ebnf.js +++ b/test/syntax/ebnf.js @@ -1,7 +1,7 @@ import color from "cli-color"; import difflib from "difflib"; -import ebnf from "../lib/ebnf.js"; -import {readfile, PASS, FAIL} from "./util.js"; +import ebnf from "../../lib/ebnf.js"; +import {readfile, PASS, FAIL} from "../harness/util.js"; let args = process.argv.slice(2); diff --git a/test/fixtures/Makefile b/test/syntax/fixtures/Makefile similarity index 73% rename from test/fixtures/Makefile rename to test/syntax/fixtures/Makefile index 3caf947..cc9ccfa 100644 --- a/test/fixtures/Makefile +++ b/test/syntax/fixtures/Makefile @@ -5,7 +5,7 @@ all: $(AST_FIXTURES) .PHONY: $(AST_FIXTURES) $(AST_FIXTURES): %.json: %.ftl - @node -r esm ../../bin/parse.js $< \ + @node -r esm ../../../bin/parse.js $< \ 2> /dev/null \ 1> $@; - @echo "$< → $@" + @echo "syntax/$< → $@" diff --git a/test/fixtures/any_char.ftl b/test/syntax/fixtures/any_char.ftl similarity index 100% rename from test/fixtures/any_char.ftl rename to test/syntax/fixtures/any_char.ftl diff --git a/test/fixtures/any_char.json b/test/syntax/fixtures/any_char.json similarity index 100% rename from test/fixtures/any_char.json rename to test/syntax/fixtures/any_char.json diff --git a/test/fixtures/astral.ftl b/test/syntax/fixtures/astral.ftl similarity index 100% rename from test/fixtures/astral.ftl rename to test/syntax/fixtures/astral.ftl diff --git a/test/fixtures/astral.json b/test/syntax/fixtures/astral.json similarity index 100% rename from test/fixtures/astral.json rename to test/syntax/fixtures/astral.json diff --git a/test/fixtures/call_expressions.ftl b/test/syntax/fixtures/call_expressions.ftl similarity index 100% rename from test/fixtures/call_expressions.ftl rename to test/syntax/fixtures/call_expressions.ftl diff --git a/test/fixtures/call_expressions.json b/test/syntax/fixtures/call_expressions.json similarity index 100% rename from test/fixtures/call_expressions.json rename to test/syntax/fixtures/call_expressions.json diff --git a/test/fixtures/callee_expressions.ftl b/test/syntax/fixtures/callee_expressions.ftl similarity index 100% rename from test/fixtures/callee_expressions.ftl rename to test/syntax/fixtures/callee_expressions.ftl diff --git a/test/fixtures/callee_expressions.json b/test/syntax/fixtures/callee_expressions.json similarity index 100% rename from test/fixtures/callee_expressions.json rename to test/syntax/fixtures/callee_expressions.json diff --git a/test/fixtures/comments.ftl b/test/syntax/fixtures/comments.ftl similarity index 100% rename from test/fixtures/comments.ftl rename to test/syntax/fixtures/comments.ftl diff --git a/test/fixtures/comments.json b/test/syntax/fixtures/comments.json similarity index 100% rename from test/fixtures/comments.json rename to test/syntax/fixtures/comments.json diff --git a/test/fixtures/cr.ftl b/test/syntax/fixtures/cr.ftl similarity index 100% rename from test/fixtures/cr.ftl rename to test/syntax/fixtures/cr.ftl diff --git a/test/fixtures/cr.json b/test/syntax/fixtures/cr.json similarity index 100% rename from test/fixtures/cr.json rename to test/syntax/fixtures/cr.json diff --git a/test/fixtures/crlf.ftl b/test/syntax/fixtures/crlf.ftl similarity index 100% rename from test/fixtures/crlf.ftl rename to test/syntax/fixtures/crlf.ftl diff --git a/test/fixtures/crlf.json b/test/syntax/fixtures/crlf.json similarity index 100% rename from test/fixtures/crlf.json rename to test/syntax/fixtures/crlf.json diff --git a/test/fixtures/eof_comment.ftl b/test/syntax/fixtures/eof_comment.ftl similarity index 100% rename from test/fixtures/eof_comment.ftl rename to test/syntax/fixtures/eof_comment.ftl diff --git a/test/fixtures/eof_comment.json b/test/syntax/fixtures/eof_comment.json similarity index 100% rename from test/fixtures/eof_comment.json rename to test/syntax/fixtures/eof_comment.json diff --git a/test/fixtures/eof_empty.ftl b/test/syntax/fixtures/eof_empty.ftl similarity index 100% rename from test/fixtures/eof_empty.ftl rename to test/syntax/fixtures/eof_empty.ftl diff --git a/test/fixtures/eof_empty.json b/test/syntax/fixtures/eof_empty.json similarity index 100% rename from test/fixtures/eof_empty.json rename to test/syntax/fixtures/eof_empty.json diff --git a/test/fixtures/eof_id.ftl b/test/syntax/fixtures/eof_id.ftl similarity index 100% rename from test/fixtures/eof_id.ftl rename to test/syntax/fixtures/eof_id.ftl diff --git a/test/fixtures/eof_id.json b/test/syntax/fixtures/eof_id.json similarity index 100% rename from test/fixtures/eof_id.json rename to test/syntax/fixtures/eof_id.json diff --git a/test/fixtures/eof_id_equals.ftl b/test/syntax/fixtures/eof_id_equals.ftl similarity index 100% rename from test/fixtures/eof_id_equals.ftl rename to test/syntax/fixtures/eof_id_equals.ftl diff --git a/test/fixtures/eof_id_equals.json b/test/syntax/fixtures/eof_id_equals.json similarity index 100% rename from test/fixtures/eof_id_equals.json rename to test/syntax/fixtures/eof_id_equals.json diff --git a/test/fixtures/eof_junk.ftl b/test/syntax/fixtures/eof_junk.ftl similarity index 100% rename from test/fixtures/eof_junk.ftl rename to test/syntax/fixtures/eof_junk.ftl diff --git a/test/fixtures/eof_junk.json b/test/syntax/fixtures/eof_junk.json similarity index 100% rename from test/fixtures/eof_junk.json rename to test/syntax/fixtures/eof_junk.json diff --git a/test/fixtures/eof_value.ftl b/test/syntax/fixtures/eof_value.ftl similarity index 100% rename from test/fixtures/eof_value.ftl rename to test/syntax/fixtures/eof_value.ftl diff --git a/test/fixtures/eof_value.json b/test/syntax/fixtures/eof_value.json similarity index 100% rename from test/fixtures/eof_value.json rename to test/syntax/fixtures/eof_value.json diff --git a/test/fixtures/escaped_characters.ftl b/test/syntax/fixtures/escaped_characters.ftl similarity index 100% rename from test/fixtures/escaped_characters.ftl rename to test/syntax/fixtures/escaped_characters.ftl diff --git a/test/fixtures/escaped_characters.json b/test/syntax/fixtures/escaped_characters.json similarity index 100% rename from test/fixtures/escaped_characters.json rename to test/syntax/fixtures/escaped_characters.json diff --git a/test/fixtures/junk.ftl b/test/syntax/fixtures/junk.ftl similarity index 100% rename from test/fixtures/junk.ftl rename to test/syntax/fixtures/junk.ftl diff --git a/test/fixtures/junk.json b/test/syntax/fixtures/junk.json similarity index 100% rename from test/fixtures/junk.json rename to test/syntax/fixtures/junk.json diff --git a/test/fixtures/leading_dots.ftl b/test/syntax/fixtures/leading_dots.ftl similarity index 100% rename from test/fixtures/leading_dots.ftl rename to test/syntax/fixtures/leading_dots.ftl diff --git a/test/fixtures/leading_dots.json b/test/syntax/fixtures/leading_dots.json similarity index 100% rename from test/fixtures/leading_dots.json rename to test/syntax/fixtures/leading_dots.json diff --git a/test/fixtures/literal_expressions.ftl b/test/syntax/fixtures/literal_expressions.ftl similarity index 100% rename from test/fixtures/literal_expressions.ftl rename to test/syntax/fixtures/literal_expressions.ftl diff --git a/test/fixtures/literal_expressions.json b/test/syntax/fixtures/literal_expressions.json similarity index 100% rename from test/fixtures/literal_expressions.json rename to test/syntax/fixtures/literal_expressions.json diff --git a/test/fixtures/member_expressions.ftl b/test/syntax/fixtures/member_expressions.ftl similarity index 100% rename from test/fixtures/member_expressions.ftl rename to test/syntax/fixtures/member_expressions.ftl diff --git a/test/fixtures/member_expressions.json b/test/syntax/fixtures/member_expressions.json similarity index 100% rename from test/fixtures/member_expressions.json rename to test/syntax/fixtures/member_expressions.json diff --git a/test/fixtures/messages.ftl b/test/syntax/fixtures/messages.ftl similarity index 100% rename from test/fixtures/messages.ftl rename to test/syntax/fixtures/messages.ftl diff --git a/test/fixtures/messages.json b/test/syntax/fixtures/messages.json similarity index 100% rename from test/fixtures/messages.json rename to test/syntax/fixtures/messages.json diff --git a/test/fixtures/mixed_entries.ftl b/test/syntax/fixtures/mixed_entries.ftl similarity index 100% rename from test/fixtures/mixed_entries.ftl rename to test/syntax/fixtures/mixed_entries.ftl diff --git a/test/fixtures/mixed_entries.json b/test/syntax/fixtures/mixed_entries.json similarity index 100% rename from test/fixtures/mixed_entries.json rename to test/syntax/fixtures/mixed_entries.json diff --git a/test/fixtures/multiline_values.ftl b/test/syntax/fixtures/multiline_values.ftl similarity index 100% rename from test/fixtures/multiline_values.ftl rename to test/syntax/fixtures/multiline_values.ftl diff --git a/test/fixtures/multiline_values.json b/test/syntax/fixtures/multiline_values.json similarity index 100% rename from test/fixtures/multiline_values.json rename to test/syntax/fixtures/multiline_values.json diff --git a/test/fixtures/numbers.ftl b/test/syntax/fixtures/numbers.ftl similarity index 100% rename from test/fixtures/numbers.ftl rename to test/syntax/fixtures/numbers.ftl diff --git a/test/fixtures/numbers.json b/test/syntax/fixtures/numbers.json similarity index 100% rename from test/fixtures/numbers.json rename to test/syntax/fixtures/numbers.json diff --git a/test/fixtures/obsolete.ftl b/test/syntax/fixtures/obsolete.ftl similarity index 100% rename from test/fixtures/obsolete.ftl rename to test/syntax/fixtures/obsolete.ftl diff --git a/test/fixtures/obsolete.json b/test/syntax/fixtures/obsolete.json similarity index 100% rename from test/fixtures/obsolete.json rename to test/syntax/fixtures/obsolete.json diff --git a/test/fixtures/placeables.ftl b/test/syntax/fixtures/placeables.ftl similarity index 100% rename from test/fixtures/placeables.ftl rename to test/syntax/fixtures/placeables.ftl diff --git a/test/fixtures/placeables.json b/test/syntax/fixtures/placeables.json similarity index 100% rename from test/fixtures/placeables.json rename to test/syntax/fixtures/placeables.json diff --git a/test/fixtures/reference_expressions.ftl b/test/syntax/fixtures/reference_expressions.ftl similarity index 100% rename from test/fixtures/reference_expressions.ftl rename to test/syntax/fixtures/reference_expressions.ftl diff --git a/test/fixtures/reference_expressions.json b/test/syntax/fixtures/reference_expressions.json similarity index 100% rename from test/fixtures/reference_expressions.json rename to test/syntax/fixtures/reference_expressions.json diff --git a/test/fixtures/select_expressions.ftl b/test/syntax/fixtures/select_expressions.ftl similarity index 100% rename from test/fixtures/select_expressions.ftl rename to test/syntax/fixtures/select_expressions.ftl diff --git a/test/fixtures/select_expressions.json b/test/syntax/fixtures/select_expressions.json similarity index 100% rename from test/fixtures/select_expressions.json rename to test/syntax/fixtures/select_expressions.json diff --git a/test/fixtures/select_indent.ftl b/test/syntax/fixtures/select_indent.ftl similarity index 100% rename from test/fixtures/select_indent.ftl rename to test/syntax/fixtures/select_indent.ftl diff --git a/test/fixtures/select_indent.json b/test/syntax/fixtures/select_indent.json similarity index 100% rename from test/fixtures/select_indent.json rename to test/syntax/fixtures/select_indent.json diff --git a/test/fixtures/sparse_entries.ftl b/test/syntax/fixtures/sparse_entries.ftl similarity index 100% rename from test/fixtures/sparse_entries.ftl rename to test/syntax/fixtures/sparse_entries.ftl diff --git a/test/fixtures/sparse_entries.json b/test/syntax/fixtures/sparse_entries.json similarity index 100% rename from test/fixtures/sparse_entries.json rename to test/syntax/fixtures/sparse_entries.json diff --git a/test/fixtures/tab.ftl b/test/syntax/fixtures/tab.ftl similarity index 100% rename from test/fixtures/tab.ftl rename to test/syntax/fixtures/tab.ftl diff --git a/test/fixtures/tab.json b/test/syntax/fixtures/tab.json similarity index 100% rename from test/fixtures/tab.json rename to test/syntax/fixtures/tab.json diff --git a/test/fixtures/term_parameters.ftl b/test/syntax/fixtures/term_parameters.ftl similarity index 100% rename from test/fixtures/term_parameters.ftl rename to test/syntax/fixtures/term_parameters.ftl diff --git a/test/fixtures/term_parameters.json b/test/syntax/fixtures/term_parameters.json similarity index 100% rename from test/fixtures/term_parameters.json rename to test/syntax/fixtures/term_parameters.json diff --git a/test/fixtures/terms.ftl b/test/syntax/fixtures/terms.ftl similarity index 100% rename from test/fixtures/terms.ftl rename to test/syntax/fixtures/terms.ftl diff --git a/test/fixtures/terms.json b/test/syntax/fixtures/terms.json similarity index 100% rename from test/fixtures/terms.json rename to test/syntax/fixtures/terms.json diff --git a/test/fixtures/variables.ftl b/test/syntax/fixtures/variables.ftl similarity index 100% rename from test/fixtures/variables.ftl rename to test/syntax/fixtures/variables.ftl diff --git a/test/fixtures/variables.json b/test/syntax/fixtures/variables.json similarity index 100% rename from test/fixtures/variables.json rename to test/syntax/fixtures/variables.json diff --git a/test/fixtures/variant_keys.ftl b/test/syntax/fixtures/variant_keys.ftl similarity index 100% rename from test/fixtures/variant_keys.ftl rename to test/syntax/fixtures/variant_keys.ftl diff --git a/test/fixtures/variant_keys.json b/test/syntax/fixtures/variant_keys.json similarity index 100% rename from test/fixtures/variant_keys.json rename to test/syntax/fixtures/variant_keys.json diff --git a/test/fixtures/whitespace_in_value.ftl b/test/syntax/fixtures/whitespace_in_value.ftl similarity index 100% rename from test/fixtures/whitespace_in_value.ftl rename to test/syntax/fixtures/whitespace_in_value.ftl diff --git a/test/fixtures/whitespace_in_value.json b/test/syntax/fixtures/whitespace_in_value.json similarity index 100% rename from test/fixtures/whitespace_in_value.json rename to test/syntax/fixtures/whitespace_in_value.json diff --git a/test/literals.js b/test/syntax/literals.js similarity index 95% rename from test/literals.js rename to test/syntax/literals.js index 6effe8f..4f47c76 100644 --- a/test/literals.js +++ b/test/syntax/literals.js @@ -1,6 +1,5 @@ -/* eslint quotes: "off" */ -import suite from "./suite.js"; -import {StringLiteral, NumberLiteral} from "../syntax/grammar.js"; +import suite from "../harness/suite.js"; +import {StringLiteral, NumberLiteral} from "../../syntax/grammar.js"; if (process.argv.length > 2) { console.error("Usage: node -r esm literals.js"); diff --git a/test/parser.js b/test/syntax/parser.js similarity index 95% rename from test/parser.js rename to test/syntax/parser.js index b5e7c89..8c87da0 100644 --- a/test/parser.js +++ b/test/syntax/parser.js @@ -1,7 +1,7 @@ import assert from "assert"; import path from "path"; -import {Resource} from "../syntax/grammar.js"; -import {readdir, readfile, diff, PASS, FAIL} from "./util.js"; +import {Resource} from "../../syntax/grammar.js"; +import {readdir, readfile, diff, PASS, FAIL} from "../harness/util.js"; const fixtures_dir = process.argv[2]; From ce26f1d59f8b3740c990ff6ceddfcc17dd3aa643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 6 Jun 2019 10:25:15 +0200 Subject: [PATCH 32/36] Move special EOL attirbutes into test/syntax/fixtures --- .gitattributes | 4 +--- test/syntax/fixtures/.gitattributes | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 test/syntax/fixtures/.gitattributes diff --git a/.gitattributes b/.gitattributes index a19af2b..1e28c29 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1 @@ -*.ftl eol=lf -test/syntax/fixtures/crlf.ftl eol=crlf -test/syntax/fixtures/cr.ftl eol=cr +**.ftl eol=lf diff --git a/test/syntax/fixtures/.gitattributes b/test/syntax/fixtures/.gitattributes new file mode 100644 index 0000000..535a845 --- /dev/null +++ b/test/syntax/fixtures/.gitattributes @@ -0,0 +1,2 @@ +/crlf.ftl eol=crlf +/cr.ftl eol=cr From 593c4c57af501e75c72d0bd18b7945bbc60365c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 6 Jun 2019 12:59:41 +0200 Subject: [PATCH 33/36] Factor out test code for comparing fixtures --- test/harness/fixture.js | 86 +++++++++++++++++++++++++++++++++++ test/syntax/parser.js | 99 ++++------------------------------------- 2 files changed, 95 insertions(+), 90 deletions(-) create mode 100644 test/harness/fixture.js diff --git a/test/harness/fixture.js b/test/harness/fixture.js new file mode 100644 index 0000000..f578fe8 --- /dev/null +++ b/test/harness/fixture.js @@ -0,0 +1,86 @@ +import assert from "assert"; +import path from "path"; +import {readdir, readfile, diff, PASS, FAIL} from "./util.js"; + +export async function test_fixtures(fixtures_dir, compare_fn) { + if (fixtures_dir.endsWith(".ftl")) { + // Actually, this is a filepath, split the path and the dir. + var ftls = [path.basename(fixtures_dir)]; + fixtures_dir = path.dirname(fixtures_dir); + } else { + let files = await readdir(fixtures_dir); + var ftls = files.filter(filename => filename.endsWith(".ftl")); + } + + // Collect all AssertionErrors. + const errors = new Map(); + + // Parse each FTL fixture and compare against the expected AST. + for (const file_name of ftls) { + const ftl_path = path.join(fixtures_dir, file_name); + const res_path = ftl_path.replace(/ftl$/, "json"); + + process.stdout.write(`${ftl_path} `); + + try { + var ftl_source = await readfile(ftl_path); + var expected_result = await readfile(res_path); + } catch (err) { + errors.set(ftl_path, err); + console.log(FAIL); + continue; + } + + try { + compare_fn(ftl_source, expected_result); + console.log(PASS); + } catch (err) { + errors.set(ftl_path, err); + console.log(FAIL); + } + } + + // Print all errors. + for (const [ftl_path, err] of errors) { + if (err instanceof assert.AssertionError) { + print_assert_error(ftl_path, err); + } else { + print_generic_error(ftl_path, err); + } + } + + exit_summary(errors.size); +} + +export function validate_json(actual_ast, expected_serialized) { + const actual_json = JSON.parse(JSON.stringify(actual_ast)); + const expected_json = JSON.parse(expected_serialized); + assert.deepEqual(actual_json, expected_json); +} + +function print_assert_error(ftl_path, err) { + console.log(` +======================================================================== +${FAIL} ${ftl_path} +------------------------------------------------------------------------ +${diff(err.expected, err.actual)} +`); +} + +function print_generic_error(ftl_path, err) { + console.log(` +======================================================================== +${FAIL} ${ftl_path} +------------------------------------------------------------------------ +${err.message} +`); +} + +function exit_summary(error_count) { + const message = error_count ? `Tests ${FAIL}: ${error_count}.` : `All tests ${PASS}.`; + console.log(` +======================================================================== +${message} +`); + process.exit(Number(error_count > 0)); +} diff --git a/test/syntax/parser.js b/test/syntax/parser.js index 8c87da0..c6ae0ea 100644 --- a/test/syntax/parser.js +++ b/test/syntax/parser.js @@ -1,7 +1,6 @@ import assert from "assert"; -import path from "path"; import {Resource} from "../../syntax/grammar.js"; -import {readdir, readfile, diff, PASS, FAIL} from "../harness/util.js"; +import {test_fixtures, validate_json} from "../harness/fixture.js"; const fixtures_dir = process.argv[2]; @@ -10,91 +9,11 @@ if (!fixtures_dir) { process.exit(1); } -main(fixtures_dir); - -async function main(fixtures_dir) { - if (fixtures_dir.endsWith(".ftl")) { - // Actually, this is a filepath, split the path and the dir. - var ftls = [path.basename(fixtures_dir)]; - fixtures_dir = path.dirname(fixtures_dir); - } else { - let files = await readdir(fixtures_dir); - var ftls = files.filter(filename => filename.endsWith(".ftl")); - } - - // Collect all AssertionErrors. - const errors = new Map(); - - // Parse each FTL fixture and compare against the expected AST. - for (const file_name of ftls) { - const ftl_path = path.join(fixtures_dir, file_name); - const ast_path = ftl_path.replace(/ftl$/, "json"); - - process.stdout.write(`${ftl_path} `); - - try { - var ftl_source = await readfile(ftl_path); - var expected_ast = await readfile(ast_path); - } catch (err) { - errors.set(ftl_path, err); - console.log(FAIL); - continue; - } - - Resource.run(ftl_source).fold(assert_equal, err => assert.fail(err)); - - function assert_equal(ast) { - try { - validate(ast, expected_ast); - console.log(PASS); - } catch (err) { - errors.set(ftl_path, err); - console.log(FAIL); - } - } - } - - // Print all errors. - for (const [ftl_path, err] of errors) { - if (err instanceof assert.AssertionError) { - print_assert_error(ftl_path, err); - } else { - print_generic_error(ftl_path, err); - } - } - - exit_summary(errors.size); -} - -function validate(actual_ast, expected_serialized) { - const actual_json = JSON.parse(JSON.stringify(actual_ast)); - const expected_json = JSON.parse(expected_serialized); - assert.deepEqual(actual_json, expected_json); -} - -function print_assert_error(ftl_path, err) { - console.log(` -======================================================================== -${FAIL} ${ftl_path} ------------------------------------------------------------------------- -${diff(err.expected, err.actual)} -`); -} - -function print_generic_error(ftl_path, err) { - console.log(` -======================================================================== -${FAIL} ${ftl_path} ------------------------------------------------------------------------- -${err.message} -`); -} - -function exit_summary(error_count) { - const message = error_count ? `Tests ${FAIL}: ${error_count}.` : `All tests ${PASS}.`; - console.log(` -======================================================================== -${message} -`); - process.exit(Number(error_count > 0)); -} +test_fixtures(fixtures_dir, (ftl_source, expected_ast) => { + Resource.run(ftl_source).fold( + ast => { + validate_json(ast, expected_ast); + }, + err => assert.fail(err) + ); +}); From 267ad11e3dd60b27e7bd6ed5116f45f721889eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 6 Jun 2019 13:00:10 +0200 Subject: [PATCH 34/36] Override file-specific settings in VS Code --- .vscode/settings.json | 8 +++++++- package.json | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f89ed5f..e72c0b3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,9 @@ { - "editor.formatOnSave": true + "editor.formatOnSave": true, + "[javascript]": { + "editor.formatOnSave": true + }, + "[typescript]": { + "editor.formatOnSave": true + } } diff --git a/package.json b/package.json index 7c7b5f8..94af2fe 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "generate:ebnf": "node -r esm bin/ebnf.js ./syntax/grammar.js > ./spec/fluent.ebnf", "generate:fixtures": "make -sC test/syntax/fixtures && make -sC test/resolver/fixtures", "generate": "npm run --silent generate:ebnf && npm run --silent generate:fixtures", - "lint": "prettier --check **/*.js", - "pretty": "prettier --write **/*.js", + "lint": "prettier --check '{bin,lib,resolver,syntax,test}/**/*.js'", + "pretty": "prettier --write '{bin,lib,resolver,syntax,test}/**/*.js'", "test:ebnf": "node -r esm test/syntax/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", "test:fixtures": "node -r esm test/syntax/parser.js ./test/syntax/fixtures", "test:unit": "node -r esm test/syntax/literals.js", From 34c14c38d71a8a68035bacd3f350d950744b1125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 6 Jun 2019 13:14:01 +0200 Subject: [PATCH 35/36] Factor out common parsing and formatting operations to lib/tools --- bin/format.ts | 106 +++----------------------------------------------- lib/tools.ts | 90 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 100 deletions(-) create mode 100644 lib/tools.ts diff --git a/bin/format.ts b/bin/format.ts index 4c60b11..0bcc446 100644 --- a/bin/format.ts +++ b/bin/format.ts @@ -1,10 +1,6 @@ import parseArgs from "minimist"; import {fromStdin, fromFile} from "../lib/input"; -import {Resource as ResourceParser} from "../syntax/grammar"; -import {Resource, NodeType, GroupComment} from "../resolver/ast"; -import {Bundle} from "../resolver/bundle"; -import {ErrorKind} from "../resolver/error"; -import {Value, StringValue, NumberValue} from "../resolver/value"; +import {formatGroups, formatResource, parseString} from "../lib/tools"; const argv = parseArgs(process.argv.slice(2), { boolean: ["help", "group"], @@ -48,102 +44,12 @@ function exitHelp(exitCode: number) { } function print(source: string) { + let resource = parseString(source); if (argv.group) { - printGroups(source); + let results = formatGroups(resource); + console.log(JSON.stringify(results, null, 4)); } else { - printAll(source); - } -} - -function parseString(source: string) { - return ResourceParser.run(source).fold( - resource => resource, - err => { - throw err; - } - ); -} - -type Variables = Map; - -function formatResource(resource: Resource, variables: Variables) { - let bundle = new Bundle(); - bundle.addResource(resource); - - let results = []; - for (let entry of resource.body) { - if (entry.type !== NodeType.Message) { - continue; - } - let message = bundle.getMessage(entry.id.name); - if (message) { - let {value, errors} = bundle.formatValue(message, variables); - results.push({ - value, - errors: errors.map(error => ({ - kind: ErrorKind[error.kind], - arg: error.arg, - })), - }); - } - } - return results; -} - -function printAll(source: string) { - let resource = parseString(source); - let results = formatResource(resource, new Map()); - console.log(JSON.stringify(results, null, 4)); -} - -interface Group { - resource: Resource; - variables: Map; -} - -const RE_VARIABLE = /^\$([a-zA-Z]*): (string|number) = (.*)$/gm; -function parseVariables(comment: GroupComment) { - let variables = new Map(); - let match; - while ((match = RE_VARIABLE.exec(comment.content))) { - let [, name, type, value] = match; - switch (type) { - case "string": - variables.set(name, new StringValue(value)); - break; - case "number": - variables.set(name, new NumberValue(parseFloat(value))); - break; - } - } - return variables; -} - -function printGroups(source: string) { - let resource = parseString(source); - - let groups: Array = []; - for (let entry of resource.body) { - if (entry.type === NodeType.GroupComment) { - groups.push({ - resource: { - type: NodeType.Resource, - body: [], - }, - variables: parseVariables(entry), - }); - } else if (groups.length > 0) { - let currentGroup = groups[groups.length - 1]; - currentGroup.resource.body.push(entry); - } + let results = formatResource(resource, new Map()); + console.log(JSON.stringify(results, null, 4)); } - - let results = []; - for (let group of groups) { - let groupResults = formatResource(group.resource, group.variables); - let lastResult = groupResults.pop(); - results.push(lastResult); - } - - console.log(JSON.stringify(results, null, 4)); } diff --git a/lib/tools.ts b/lib/tools.ts new file mode 100644 index 0000000..3a0be89 --- /dev/null +++ b/lib/tools.ts @@ -0,0 +1,90 @@ +import {Resource as ResourceParser} from "../syntax/grammar"; +import {Resource, NodeType, GroupComment} from "../resolver/ast"; +import {Bundle} from "../resolver/bundle"; +import {ErrorKind} from "../resolver/error"; +import {Value, StringValue, NumberValue} from "../resolver/value"; + +export function parseString(input: string) { + return ResourceParser.run(input).fold( + resource => resource, + err => { + throw err; + } + ); +} + +type Variables = Map; + +export function formatResource(resource: Resource, variables: Variables) { + let bundle = new Bundle(); + bundle.addResource(resource); + + let results = []; + for (let entry of resource.body) { + if (entry.type !== NodeType.Message) { + continue; + } + let message = bundle.getMessage(entry.id.name); + if (message) { + let {value, errors} = bundle.formatValue(message, variables); + results.push({ + value, + errors: errors.map(error => ({ + kind: ErrorKind[error.kind], + arg: error.arg, + })), + }); + } + } + return results; +} + +interface Group { + resource: Resource; + variables: Map; +} + +const RE_VARIABLE = /^\$([a-zA-Z]*): (string|number) = (.*)$/gm; +function parseVariables(comment: GroupComment) { + let variables = new Map(); + let match; + while ((match = RE_VARIABLE.exec(comment.content))) { + let [, name, type, value] = match; + switch (type) { + case "string": + variables.set(name, new StringValue(value)); + break; + case "number": + variables.set(name, new NumberValue(parseFloat(value))); + break; + } + } + return variables; +} + +export function formatGroups(resource: Resource) { + let groups: Array = []; + for (let entry of resource.body) { + if (entry.type === NodeType.GroupComment) { + groups.push({ + resource: { + type: NodeType.Resource, + body: [], + }, + variables: parseVariables(entry), + }); + } else if (groups.length > 0) { + let currentGroup = groups[groups.length - 1]; + currentGroup.resource.body.push(entry); + } + } + + let results = []; + for (let group of groups) { + let groupResults = formatResource(group.resource, group.variables); + let lastResult = groupResults.pop(); + results.push(lastResult); + } + + return results; +} From 08f1568e3861b13ebe440baee83690a1aeae14ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sta=C5=9B=20Ma=C5=82olepszy?= Date: Thu, 6 Jun 2019 13:59:41 +0200 Subject: [PATCH 36/36] Add npm run test:resolver:fixtures --- lib/tools.ts | 16 +++++++++++----- package.json | 19 +++++++++++-------- test/harness/fixture.js | 2 +- .../{fixtures.ts => sample_fixtures.ts} | 0 test/resolver/sample_test.ts | 2 +- test/resolver/test_fixtures.ts | 15 +++++++++++++++ test/syntax/{parser.js => test_fixtures.js} | 6 +++--- 7 files changed, 42 insertions(+), 18 deletions(-) rename test/resolver/{fixtures.ts => sample_fixtures.ts} (100%) create mode 100644 test/resolver/test_fixtures.ts rename test/syntax/{parser.js => test_fixtures.js} (63%) diff --git a/lib/tools.ts b/lib/tools.ts index 3a0be89..470536f 100644 --- a/lib/tools.ts +++ b/lib/tools.ts @@ -14,12 +14,16 @@ export function parseString(input: string) { } type Variables = Map; +type FormatResult = { + value: string | null; + errors: Array<{kind: string; arg: string}>; +}; export function formatResource(resource: Resource, variables: Variables) { let bundle = new Bundle(); bundle.addResource(resource); - let results = []; + let results: Array = []; for (let entry of resource.body) { if (entry.type !== NodeType.Message) { continue; @@ -33,7 +37,7 @@ export function formatResource(resource: Resource, variables: Variables) { kind: ErrorKind[error.kind], arg: error.arg, })), - }); + } as FormatResult); } } return results; @@ -79,11 +83,13 @@ export function formatGroups(resource: Resource) { } } - let results = []; + let results: Array = []; for (let group of groups) { let groupResults = formatResource(group.resource, group.variables); - let lastResult = groupResults.pop(); - results.push(lastResult); + if (groupResults.length > 0) { + let lastResult = groupResults[groupResults.length - 1]; + results.push(lastResult); + } } return results; diff --git a/package.json b/package.json index 94af2fe..2188383 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,10 @@ "private": true, "scripts": { "bench": "node -r esm --harmony-async-iteration ./test/benchmarks/bench.js ./test/benchmarks/gecko_strings.ftl", + "build:impl": "tsc", "build:guide": "gitbook build guide build/guide", - "build": "npm run --silent build:guide", - "ci": "npm run --silent lint && npm test && npm run --silent test:ebnf", + "build": "npm run --silent build:impl && npm run --silent build:guide", + "ci": "npm run --silent lint && npm run --silent build:impl && npm run --silent test:syntax && npm run --silent test:resolver", "clean": "rm -rf build", "deploy": "gh-pages -d build", "generate:ebnf": "node -r esm bin/ebnf.js ./syntax/grammar.js > ./spec/fluent.ebnf", @@ -15,11 +16,14 @@ "generate": "npm run --silent generate:ebnf && npm run --silent generate:fixtures", "lint": "prettier --check '{bin,lib,resolver,syntax,test}/**/*.js'", "pretty": "prettier --write '{bin,lib,resolver,syntax,test}/**/*.js'", - "test:ebnf": "node -r esm test/syntax/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", - "test:fixtures": "node -r esm test/syntax/parser.js ./test/syntax/fixtures", - "test:unit": "node -r esm test/syntax/literals.js", - "test:resolver": "mocha -u tdd -r ts-node/register ./test/resolver/**/*_test.ts", - "test": "npm run --silent test:fixtures && npm run --silent test:unit && npm run --silent test:resolver", + "test:syntax:ebnf": "node -r esm test/syntax/ebnf.js ./syntax/grammar.js ./spec/fluent.ebnf", + "test:syntax:fixtures": "node -r esm test/syntax/test_fixtures.js ./test/syntax/fixtures", + "test:syntax:unit": "node -r esm test/syntax/literals.js", + "test:syntax": "npm run --silent test:syntax:ebnf && npm run --silent test:syntax:fixtures && npm run --silent test:syntax:unit", + "test:resolver:fixtures": "node -r esm ./build/test/resolver/test_fixtures.js ./test/resolver/fixtures", + "test:resolver:unit": "mocha -u tdd ./build/test/resolver/**/*_test.js", + "test:resolver": "npm run --silent test:resolver:fixtures && npm run --silent test:resolver:unit", + "test": "npm run --silent build:impl && npm run --silent test:syntax:fixtures && npm run --silent test:syntax:unit && npm run --silent test:resolver", "watch": "tsc --watch" }, "homepage": "https://projectfluent.org", @@ -46,7 +50,6 @@ "json-diff": "^0.5.2", "prettier": "^1.17.1", "mocha": "^6.1.4", - "ts-node": "^8.2.0", "typescript": "^3.5.1" }, "dependencies": { diff --git a/test/harness/fixture.js b/test/harness/fixture.js index f578fe8..91c154b 100644 --- a/test/harness/fixture.js +++ b/test/harness/fixture.js @@ -52,7 +52,7 @@ export async function test_fixtures(fixtures_dir, compare_fn) { exit_summary(errors.size); } -export function validate_json(actual_ast, expected_serialized) { +export function validate_as_json(actual_ast, expected_serialized) { const actual_json = JSON.parse(JSON.stringify(actual_ast)); const expected_json = JSON.parse(expected_serialized); assert.deepEqual(actual_json, expected_json); diff --git a/test/resolver/fixtures.ts b/test/resolver/sample_fixtures.ts similarity index 100% rename from test/resolver/fixtures.ts rename to test/resolver/sample_fixtures.ts diff --git a/test/resolver/sample_test.ts b/test/resolver/sample_test.ts index 60e1c2f..bed21de 100644 --- a/test/resolver/sample_test.ts +++ b/test/resolver/sample_test.ts @@ -1,5 +1,5 @@ import * as assert from "assert"; -import {hello, exclamation, select} from "./fixtures"; +import {hello, exclamation, select} from "./sample_fixtures"; import {Bundle} from "../../resolver/bundle"; import {StringValue} from "../../resolver/value"; import {ScopeError, ErrorKind} from "../../resolver/error"; diff --git a/test/resolver/test_fixtures.ts b/test/resolver/test_fixtures.ts new file mode 100644 index 0000000..6faae5f --- /dev/null +++ b/test/resolver/test_fixtures.ts @@ -0,0 +1,15 @@ +import {test_fixtures, validate_as_json} from "../harness/fixture.js"; +import {parseString, formatGroups} from "../../lib/tools.js"; + +const fixtures_dir = process.argv[2]; + +if (!fixtures_dir) { + console.error("Usage: node -r esm test_fixtures.js FIXTURE"); + process.exit(1); +} + +test_fixtures(fixtures_dir, (input: string, expected: string) => { + let resource = parseString(input); + let results = formatGroups(resource); + validate_as_json(results, expected); +}); diff --git a/test/syntax/parser.js b/test/syntax/test_fixtures.js similarity index 63% rename from test/syntax/parser.js rename to test/syntax/test_fixtures.js index c6ae0ea..0a26cfd 100644 --- a/test/syntax/parser.js +++ b/test/syntax/test_fixtures.js @@ -1,18 +1,18 @@ import assert from "assert"; import {Resource} from "../../syntax/grammar.js"; -import {test_fixtures, validate_json} from "../harness/fixture.js"; +import {test_fixtures, validate_as_json} from "../harness/fixture.js"; const fixtures_dir = process.argv[2]; if (!fixtures_dir) { - console.error("Usage: node -r esm parser.js FIXTURE"); + console.error("Usage: node -r esm test_fixtures.js FIXTURE"); process.exit(1); } test_fixtures(fixtures_dir, (ftl_source, expected_ast) => { Resource.run(ftl_source).fold( ast => { - validate_json(ast, expected_ast); + validate_as_json(ast, expected_ast); }, err => assert.fail(err) );