Created
April 21, 2020 13:40
-
-
Save stasm/c4e167711cb2678a014445d82ae7e92c to your computer and use it in GitHub Desktop.
@fluent compat vs ES2018
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff -wru compat/fluent-bundle.js es2018/fluent-bundle.js | |
--- compat/fluent-bundle.js 2020-04-21 14:45:46.219383600 +0200 | |
+++ es2018/fluent-bundle.js 2020-04-21 14:46:02.403757100 +0200 | |
@@ -8,190 +8,6 @@ | |
})(this, function (exports) { | |
"use strict"; | |
- function _defineProperty(obj, key, value) { | |
- if (key in obj) { | |
- Object.defineProperty(obj, key, { | |
- value: value, | |
- enumerable: true, | |
- configurable: true, | |
- writable: true, | |
- }); | |
- } else { | |
- obj[key] = value; | |
- } | |
- | |
- return obj; | |
- } | |
- | |
- function ownKeys(object, enumerableOnly) { | |
- var keys = Object.keys(object); | |
- | |
- if (Object.getOwnPropertySymbols) { | |
- var symbols = Object.getOwnPropertySymbols(object); | |
- if (enumerableOnly) | |
- symbols = symbols.filter(function (sym) { | |
- return Object.getOwnPropertyDescriptor(object, sym).enumerable; | |
- }); | |
- keys.push.apply(keys, symbols); | |
- } | |
- | |
- return keys; | |
- } | |
- | |
- function _objectSpread2(target) { | |
- for (var i = 1; i < arguments.length; i++) { | |
- var source = arguments[i] != null ? arguments[i] : {}; | |
- | |
- if (i % 2) { | |
- ownKeys(Object(source), true).forEach(function (key) { | |
- _defineProperty(target, key, source[key]); | |
- }); | |
- } else if (Object.getOwnPropertyDescriptors) { | |
- Object.defineProperties( | |
- target, | |
- Object.getOwnPropertyDescriptors(source) | |
- ); | |
- } else { | |
- ownKeys(Object(source)).forEach(function (key) { | |
- Object.defineProperty( | |
- target, | |
- key, | |
- Object.getOwnPropertyDescriptor(source, key) | |
- ); | |
- }); | |
- } | |
- } | |
- | |
- return target; | |
- } | |
- | |
- function _slicedToArray(arr, i) { | |
- return ( | |
- _arrayWithHoles(arr) || | |
- _iterableToArrayLimit(arr, i) || | |
- _unsupportedIterableToArray(arr, i) || | |
- _nonIterableRest() | |
- ); | |
- } | |
- | |
- function _arrayWithHoles(arr) { | |
- if (Array.isArray(arr)) return arr; | |
- } | |
- | |
- function _iterableToArrayLimit(arr, i) { | |
- if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) | |
- return; | |
- var _arr = []; | |
- var _n = true; | |
- var _d = false; | |
- var _e = undefined; | |
- | |
- try { | |
- for ( | |
- var _i = arr[Symbol.iterator](), _s; | |
- !(_n = (_s = _i.next()).done); | |
- _n = true | |
- ) { | |
- _arr.push(_s.value); | |
- | |
- if (i && _arr.length === i) break; | |
- } | |
- } catch (err) { | |
- _d = true; | |
- _e = err; | |
- } finally { | |
- try { | |
- if (!_n && _i["return"] != null) _i["return"](); | |
- } finally { | |
- if (_d) throw _e; | |
- } | |
- } | |
- | |
- return _arr; | |
- } | |
- | |
- function _unsupportedIterableToArray(o, minLen) { | |
- if (!o) return; | |
- if (typeof o === "string") return _arrayLikeToArray(o, minLen); | |
- var n = Object.prototype.toString.call(o).slice(8, -1); | |
- if (n === "Object" && o.constructor) n = o.constructor.name; | |
- if (n === "Map" || n === "Set") return Array.from(n); | |
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) | |
- return _arrayLikeToArray(o, minLen); | |
- } | |
- | |
- function _arrayLikeToArray(arr, len) { | |
- if (len == null || len > arr.length) len = arr.length; | |
- | |
- for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | |
- | |
- return arr2; | |
- } | |
- | |
- function _nonIterableRest() { | |
- throw new TypeError( | |
- "Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
- function _createForOfIteratorHelper(o) { | |
- if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { | |
- if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { | |
- var i = 0; | |
- | |
- var F = function () {}; | |
- | |
- return { | |
- s: F, | |
- n: function () { | |
- if (i >= o.length) | |
- return { | |
- done: true, | |
- }; | |
- return { | |
- done: false, | |
- value: o[i++], | |
- }; | |
- }, | |
- e: function (e) { | |
- throw e; | |
- }, | |
- f: F, | |
- }; | |
- } | |
- | |
- throw new TypeError( | |
- "Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
- var it, | |
- normalCompletion = true, | |
- didErr = false, | |
- err; | |
- return { | |
- s: function () { | |
- it = o[Symbol.iterator](); | |
- }, | |
- n: function () { | |
- var step = it.next(); | |
- normalCompletion = step.done; | |
- return step; | |
- }, | |
- e: function (e) { | |
- didErr = true; | |
- err = e; | |
- }, | |
- f: function () { | |
- try { | |
- if (!normalCompletion && it.return != null) it.return(); | |
- } finally { | |
- if (didErr) throw err; | |
- } | |
- }, | |
- }; | |
- } | |
- | |
/** | |
* The `FluentType` class is the base of Fluent's type system. | |
* | |
@@ -211,7 +27,6 @@ | |
/** | |
* Unwrap the raw value stored by this `FluentType`. | |
*/ | |
- | |
valueOf() { | |
return this.value; | |
} | |
@@ -219,25 +34,19 @@ | |
/** | |
* A `FluentType` representing no correct value. | |
*/ | |
- | |
class FluentNone extends FluentType { | |
/** | |
* Create an instance of `FluentNone` with an optional fallback value. | |
* @param value The fallback value of this `FluentNone`. | |
*/ | |
- constructor() { | |
- var value = | |
- arguments.length > 0 && arguments[0] !== undefined | |
- ? arguments[0] | |
- : "???"; | |
+ constructor(value = "???") { | |
super(value); | |
} | |
/** | |
* Format this `FluentNone` to the fallback string. | |
*/ | |
- | |
toString(scope) { | |
- return "{".concat(this.value, "}"); | |
+ return `{${this.value}}`; | |
} | |
} | |
/** | |
@@ -247,7 +56,6 @@ | |
* represents. It may also store an option bag of options which will be passed | |
* to `Intl.NumerFormat` when the `FluentNumber` is formatted to a string. | |
*/ | |
- | |
class FluentNumber extends FluentType { | |
/** | |
* Create an instance of `FluentNumber` with options to the | |
@@ -256,19 +64,16 @@ | |
* @param value The number value of this `FluentNumber`. | |
* @param opts Options which will be passed to `Intl.NumberFormat`. | |
*/ | |
- constructor(value) { | |
- var opts = | |
- arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | |
+ constructor(value, opts = {}) { | |
super(value); | |
this.opts = opts; | |
} | |
/** | |
* Format this `FluentNumber` to a string. | |
*/ | |
- | |
toString(scope) { | |
try { | |
- var nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts); | |
+ const nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts); | |
return nf.format(this.value); | |
} catch (err) { | |
scope.reportError(err); | |
@@ -284,7 +89,6 @@ | |
* option bag of options which will be passed to `Intl.DateTimeFormat` when the | |
* `FluentDateTime` is formatted to a string. | |
*/ | |
- | |
class FluentDateTime extends FluentType { | |
/** | |
* Create an instance of `FluentDateTime` with options to the | |
@@ -293,19 +97,16 @@ | |
* @param value The number value of this `FluentDateTime`, in milliseconds. | |
* @param opts Options which will be passed to `Intl.DateTimeFormat`. | |
*/ | |
- constructor(value) { | |
- var opts = | |
- arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | |
+ constructor(value, opts = {}) { | |
super(value); | |
this.opts = opts; | |
} | |
/** | |
* Format this `FluentDateTime` to a string. | |
*/ | |
- | |
toString(scope) { | |
try { | |
- var dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts); | |
+ const dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts); | |
return dtf.format(this.value); | |
} catch (err) { | |
scope.reportError(err); | |
@@ -314,20 +115,21 @@ | |
} | |
} | |
+ /* global Intl */ | |
+ // The maximum number of placeables which can be expanded in a single call to | |
// `formatPattern`. The limit protects against the Billion Laughs and Quadratic | |
// Blowup attacks. See https://msdn.microsoft.com/en-us/magazine/ee335713.aspx. | |
- | |
- var MAX_PLACEABLES = 100; // Unicode bidi isolation characters. | |
- | |
- var FSI = "\u2068"; | |
- var PDI = "\u2069"; // Helper: match a variant key to the given selector. | |
- | |
+ const MAX_PLACEABLES = 100; | |
+ // Unicode bidi isolation characters. | |
+ const FSI = "\u2068"; | |
+ const PDI = "\u2069"; | |
+ // Helper: match a variant key to the given selector. | |
function match(scope, selector, key) { | |
if (key === selector) { | |
// Both are strings. | |
return true; | |
- } // XXX Consider comparing options too, e.g. minimumFractionDigits. | |
- | |
+ } | |
+ // XXX Consider comparing options too, e.g. minimumFractionDigits. | |
if ( | |
key instanceof FluentNumber && | |
selector instanceof FluentNumber && | |
@@ -335,98 +137,69 @@ | |
) { | |
return true; | |
} | |
- | |
if (selector instanceof FluentNumber && typeof key === "string") { | |
- var category = scope | |
+ let category = scope | |
.memoizeIntlObject(Intl.PluralRules, selector.opts) | |
.select(selector.value); | |
- | |
if (key === category) { | |
return true; | |
} | |
} | |
- | |
return false; | |
- } // Helper: resolve the default variant from a list of variants. | |
- | |
+ } | |
+ // Helper: resolve the default variant from a list of variants. | |
function getDefault(scope, variants, star) { | |
if (variants[star]) { | |
return resolvePattern(scope, variants[star].value); | |
} | |
- | |
scope.reportError(new RangeError("No default")); | |
return new FluentNone(); | |
- } // Helper: resolve arguments to a call expression. | |
- | |
+ } | |
+ // Helper: resolve arguments to a call expression. | |
function getArguments(scope, args) { | |
- var positional = []; | |
- var named = Object.create(null); | |
- | |
- var _iterator = _createForOfIteratorHelper(args), | |
- _step; | |
- | |
- try { | |
- for (_iterator.s(); !(_step = _iterator.n()).done; ) { | |
- var arg = _step.value; | |
- | |
+ const positional = []; | |
+ const named = Object.create(null); | |
+ for (const arg of args) { | |
if (arg.type === "narg") { | |
named[arg.name] = resolveExpression(scope, arg.value); | |
} else { | |
positional.push(resolveExpression(scope, arg)); | |
} | |
} | |
- } catch (err) { | |
- _iterator.e(err); | |
- } finally { | |
- _iterator.f(); | |
+ return { positional, named }; | |
} | |
- | |
- return { | |
- positional, | |
- named, | |
- }; | |
- } // Resolve an expression to a Fluent type. | |
- | |
+ // Resolve an expression to a Fluent type. | |
function resolveExpression(scope, expr) { | |
switch (expr.type) { | |
case "str": | |
return expr.value; | |
- | |
case "num": | |
return new FluentNumber(expr.value, { | |
minimumFractionDigits: expr.precision, | |
}); | |
- | |
case "var": | |
return resolveVariableReference(scope, expr); | |
- | |
case "mesg": | |
return resolveMessageReference(scope, expr); | |
- | |
case "term": | |
return resolveTermReference(scope, expr); | |
- | |
case "func": | |
return resolveFunctionReference(scope, expr); | |
- | |
case "select": | |
return resolveSelectExpression(scope, expr); | |
- | |
default: | |
return new FluentNone(); | |
} | |
- } // Resolve a reference to a variable. | |
- | |
- function resolveVariableReference(scope, _ref) { | |
- var name = _ref.name; | |
- var arg; | |
- | |
+ } | |
+ // Resolve a reference to a variable. | |
+ function resolveVariableReference(scope, { name }) { | |
+ let arg; | |
if (scope.params) { | |
// We're inside a TermReference. It's OK to reference undefined parameters. | |
if (Object.prototype.hasOwnProperty.call(scope.params, name)) { | |
arg = scope.params[name]; | |
} else { | |
- return new FluentNone("$".concat(name)); | |
+ return new FluentNone(`$${name}`); | |
} | |
} else if ( | |
scope.args && | |
@@ -436,232 +209,160 @@ | |
// variables references produce ReferenceErrors. | |
arg = scope.args[name]; | |
} else { | |
- scope.reportError(new ReferenceError("Unknown variable: $".concat(name))); | |
- return new FluentNone("$".concat(name)); | |
- } // Return early if the argument already is an instance of FluentType. | |
- | |
+ scope.reportError(new ReferenceError(`Unknown variable: $${name}`)); | |
+ return new FluentNone(`$${name}`); | |
+ } | |
+ // Return early if the argument already is an instance of FluentType. | |
if (arg instanceof FluentType) { | |
return arg; | |
- } // Convert the argument to a Fluent type. | |
- | |
+ } | |
+ // Convert the argument to a Fluent type. | |
switch (typeof arg) { | |
case "string": | |
return arg; | |
- | |
case "number": | |
return new FluentNumber(arg); | |
- | |
case "object": | |
if (arg instanceof Date) { | |
return new FluentDateTime(arg.getTime()); | |
} | |
- | |
// eslint-disable-next-line no-fallthrough | |
- | |
default: | |
scope.reportError( | |
- new TypeError( | |
- "Variable type not supported: $" | |
- .concat(name, ", ") | |
- .concat(typeof arg) | |
- ) | |
+ new TypeError(`Variable type not supported: $${name}, ${typeof arg}`) | |
); | |
- return new FluentNone("$".concat(name)); | |
+ return new FluentNone(`$${name}`); | |
} | |
- } // Resolve a reference to another message. | |
- | |
- function resolveMessageReference(scope, _ref2) { | |
- var name = _ref2.name, | |
- attr = _ref2.attr; | |
- | |
- var message = scope.bundle._messages.get(name); | |
- | |
+ } | |
+ // Resolve a reference to another message. | |
+ function resolveMessageReference(scope, { name, attr }) { | |
+ const message = scope.bundle._messages.get(name); | |
if (!message) { | |
- scope.reportError(new ReferenceError("Unknown message: ".concat(name))); | |
+ scope.reportError(new ReferenceError(`Unknown message: ${name}`)); | |
return new FluentNone(name); | |
} | |
- | |
if (attr) { | |
- var attribute = message.attributes[attr]; | |
- | |
+ const attribute = message.attributes[attr]; | |
if (attribute) { | |
return resolvePattern(scope, attribute); | |
} | |
- | |
- scope.reportError(new ReferenceError("Unknown attribute: ".concat(attr))); | |
- return new FluentNone("".concat(name, ".").concat(attr)); | |
+ scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`)); | |
+ return new FluentNone(`${name}.${attr}`); | |
} | |
- | |
if (message.value) { | |
return resolvePattern(scope, message.value); | |
} | |
- | |
- scope.reportError(new ReferenceError("No value: ".concat(name))); | |
+ scope.reportError(new ReferenceError(`No value: ${name}`)); | |
return new FluentNone(name); | |
- } // Resolve a call to a Term with key-value arguments. | |
- | |
- function resolveTermReference(scope, _ref3) { | |
- var name = _ref3.name, | |
- attr = _ref3.attr, | |
- args = _ref3.args; | |
- var id = "-".concat(name); | |
- | |
- var term = scope.bundle._terms.get(id); | |
- | |
+ } | |
+ // Resolve a call to a Term with key-value arguments. | |
+ function resolveTermReference(scope, { name, attr, args }) { | |
+ const id = `-${name}`; | |
+ const term = scope.bundle._terms.get(id); | |
if (!term) { | |
- scope.reportError(new ReferenceError("Unknown term: ".concat(id))); | |
+ scope.reportError(new ReferenceError(`Unknown term: ${id}`)); | |
return new FluentNone(id); | |
} | |
- | |
if (attr) { | |
- var attribute = term.attributes[attr]; | |
- | |
+ const attribute = term.attributes[attr]; | |
if (attribute) { | |
// Every TermReference has its own variables. | |
scope.params = getArguments(scope, args).named; | |
- | |
- var _resolved = resolvePattern(scope, attribute); | |
- | |
+ const resolved = resolvePattern(scope, attribute); | |
scope.params = null; | |
- return _resolved; | |
+ return resolved; | |
} | |
- | |
- scope.reportError(new ReferenceError("Unknown attribute: ".concat(attr))); | |
- return new FluentNone("".concat(id, ".").concat(attr)); | |
+ scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`)); | |
+ return new FluentNone(`${id}.${attr}`); | |
} | |
- | |
scope.params = getArguments(scope, args).named; | |
- var resolved = resolvePattern(scope, term.value); | |
+ const resolved = resolvePattern(scope, term.value); | |
scope.params = null; | |
return resolved; | |
- } // Resolve a call to a Function with positional and key-value arguments. | |
- | |
- function resolveFunctionReference(scope, _ref4) { | |
- var name = _ref4.name, | |
- args = _ref4.args; | |
+ } | |
+ // Resolve a call to a Function with positional and key-value arguments. | |
+ function resolveFunctionReference(scope, { name, args }) { | |
// Some functions are built-in. Others may be provided by the runtime via | |
// the `FluentBundle` constructor. | |
- var func = scope.bundle._functions[name]; | |
- | |
+ let func = scope.bundle._functions[name]; | |
if (!func) { | |
- scope.reportError( | |
- new ReferenceError("Unknown function: ".concat(name, "()")) | |
- ); | |
- return new FluentNone("".concat(name, "()")); | |
+ scope.reportError(new ReferenceError(`Unknown function: ${name}()`)); | |
+ return new FluentNone(`${name}()`); | |
} | |
- | |
if (typeof func !== "function") { | |
- scope.reportError( | |
- new TypeError("Function ".concat(name, "() is not callable")) | |
- ); | |
- return new FluentNone("".concat(name, "()")); | |
+ scope.reportError(new TypeError(`Function ${name}() is not callable`)); | |
+ return new FluentNone(`${name}()`); | |
} | |
- | |
try { | |
- var resolved = getArguments(scope, args); | |
+ let resolved = getArguments(scope, args); | |
return func(resolved.positional, resolved.named); | |
} catch (err) { | |
scope.reportError(err); | |
- return new FluentNone("".concat(name, "()")); | |
+ return new FluentNone(`${name}()`); | |
} | |
- } // Resolve a select expression to the member object. | |
- | |
- function resolveSelectExpression(scope, _ref5) { | |
- var selector = _ref5.selector, | |
- variants = _ref5.variants, | |
- star = _ref5.star; | |
- var sel = resolveExpression(scope, selector); | |
- | |
+ } | |
+ // Resolve a select expression to the member object. | |
+ function resolveSelectExpression(scope, { selector, variants, star }) { | |
+ let sel = resolveExpression(scope, selector); | |
if (sel instanceof FluentNone) { | |
return getDefault(scope, variants, star); | |
- } // Match the selector against keys of each variant, in order. | |
- | |
- var _iterator2 = _createForOfIteratorHelper(variants), | |
- _step2; | |
- | |
- try { | |
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done; ) { | |
- var variant = _step2.value; | |
- var key = resolveExpression(scope, variant.key); | |
- | |
+ } | |
+ // Match the selector against keys of each variant, in order. | |
+ for (const variant of variants) { | |
+ const key = resolveExpression(scope, variant.key); | |
if (match(scope, sel, key)) { | |
return resolvePattern(scope, variant.value); | |
} | |
} | |
- } catch (err) { | |
- _iterator2.e(err); | |
- } finally { | |
- _iterator2.f(); | |
- } | |
- | |
return getDefault(scope, variants, star); | |
- } // Resolve a pattern (a complex string with placeables). | |
- | |
+ } | |
+ // Resolve a pattern (a complex string with placeables). | |
function resolveComplexPattern(scope, ptn) { | |
if (scope.dirty.has(ptn)) { | |
scope.reportError(new RangeError("Cyclic reference")); | |
return new FluentNone(); | |
- } // Tag the pattern as dirty for the purpose of the current resolution. | |
- | |
+ } | |
+ // Tag the pattern as dirty for the purpose of the current resolution. | |
scope.dirty.add(ptn); | |
- var result = []; // Wrap interpolations with Directional Isolate Formatting characters | |
+ const result = []; | |
+ // Wrap interpolations with Directional Isolate Formatting characters | |
// only when the pattern has more than one element. | |
- | |
- var useIsolating = scope.bundle._useIsolating && ptn.length > 1; | |
- | |
- var _iterator3 = _createForOfIteratorHelper(ptn), | |
- _step3; | |
- | |
- try { | |
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done; ) { | |
- var elem = _step3.value; | |
- | |
+ const useIsolating = scope.bundle._useIsolating && ptn.length > 1; | |
+ for (const elem of ptn) { | |
if (typeof elem === "string") { | |
result.push(scope.bundle._transform(elem)); | |
continue; | |
} | |
- | |
scope.placeables++; | |
- | |
if (scope.placeables > MAX_PLACEABLES) { | |
- scope.dirty.delete(ptn); // This is a fatal error which causes the resolver to instantly bail out | |
+ scope.dirty.delete(ptn); | |
+ // This is a fatal error which causes the resolver to instantly bail out | |
// on this pattern. The length check protects against excessive memory | |
// usage, and throwing protects against eating up the CPU when long | |
// placeables are deeply nested. | |
- | |
throw new RangeError( | |
- "Too many placeables expanded: ".concat(scope.placeables, ", ") + | |
- "max allowed is ".concat(MAX_PLACEABLES) | |
+ `Too many placeables expanded: ${scope.placeables}, ` + | |
+ `max allowed is ${MAX_PLACEABLES}` | |
); | |
} | |
- | |
if (useIsolating) { | |
result.push(FSI); | |
} | |
- | |
result.push(resolveExpression(scope, elem).toString(scope)); | |
- | |
if (useIsolating) { | |
result.push(PDI); | |
} | |
} | |
- } catch (err) { | |
- _iterator3.e(err); | |
- } finally { | |
- _iterator3.f(); | |
- } | |
- | |
scope.dirty.delete(ptn); | |
return result.join(""); | |
- } // Resolve a simple or a complex Pattern to a FluentString (which is really the | |
+ } | |
+ // Resolve a simple or a complex Pattern to a FluentString (which is really the | |
// string primitive). | |
- | |
function resolvePattern(scope, value) { | |
// Resolve a simple pattern. | |
if (typeof value === "string") { | |
return scope.bundle._transform(value); | |
} | |
- | |
return resolveComplexPattern(scope, value); | |
} | |
@@ -671,65 +372,56 @@ | |
* Used to detect and prevent cyclic resolutions. */ | |
this.dirty = new WeakSet(); | |
/** A dict of parameters passed to a TermReference. */ | |
- | |
this.params = null; | |
/** The running count of placeables resolved so far. Used to detect the | |
* Billion Laughs and Quadratic Blowup attacks. */ | |
- | |
this.placeables = 0; | |
this.bundle = bundle; | |
this.errors = errors; | |
this.args = args; | |
} | |
- | |
reportError(error) { | |
if (!this.errors) { | |
throw error; | |
} | |
- | |
this.errors.push(error); | |
} | |
- | |
memoizeIntlObject(ctor, opts) { | |
- var cache = this.bundle._intls.get(ctor); | |
- | |
+ let cache = this.bundle._intls.get(ctor); | |
if (!cache) { | |
cache = {}; | |
- | |
this.bundle._intls.set(ctor, cache); | |
} | |
- | |
- var id = JSON.stringify(opts); | |
- | |
+ let id = JSON.stringify(opts); | |
if (!cache[id]) { | |
cache[id] = new ctor(this.bundle.locales, opts); | |
} | |
- | |
return cache[id]; | |
} | |
} | |
+ /** | |
+ * @overview | |
+ * | |
+ * The FTL resolver ships with a number of functions built-in. | |
+ * | |
+ * Each function take two arguments: | |
+ * - args - an array of positional args | |
+ * - opts - an object of key-value args | |
+ * | |
+ * Arguments to functions are guaranteed to already be instances of | |
+ * `FluentValue`. Functions must return `FluentValues` as well. | |
+ */ | |
function values(opts, allowed) { | |
- var unwrapped = Object.create(null); | |
- | |
- for ( | |
- var _i = 0, _Object$entries = Object.entries(opts); | |
- _i < _Object$entries.length; | |
- _i++ | |
- ) { | |
- var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), | |
- name = _Object$entries$_i[0], | |
- opt = _Object$entries$_i[1]; | |
- | |
+ const unwrapped = Object.create(null); | |
+ for (const [name, opt] of Object.entries(opts)) { | |
if (allowed.includes(name)) { | |
unwrapped[name] = opt.valueOf(); | |
} | |
} | |
- | |
return unwrapped; | |
} | |
- | |
- var NUMBER_ALLOWED = [ | |
+ const NUMBER_ALLOWED = [ | |
"unitDisplay", | |
"currencyDisplay", | |
"useGrouping", | |
@@ -767,24 +459,20 @@ | |
* @param args The positional arguments passed to this `NUMBER()`. | |
* @param opts The named argments passed to this `NUMBER()`. | |
*/ | |
- | |
function NUMBER(args, opts) { | |
- var arg = args[0]; | |
- | |
+ let arg = args[0]; | |
if (arg instanceof FluentNone) { | |
- return new FluentNone("NUMBER(".concat(arg.valueOf(), ")")); | |
+ return new FluentNone(`NUMBER(${arg.valueOf()})`); | |
} | |
- | |
if (arg instanceof FluentNumber || arg instanceof FluentDateTime) { | |
- return new FluentNumber( | |
- arg.valueOf(), | |
- _objectSpread2({}, arg.opts, {}, values(opts, NUMBER_ALLOWED)) | |
- ); | |
+ return new FluentNumber(arg.valueOf(), { | |
+ ...arg.opts, | |
+ ...values(opts, NUMBER_ALLOWED), | |
+ }); | |
} | |
- | |
throw new TypeError("Invalid argument to NUMBER"); | |
} | |
- var DATETIME_ALLOWED = [ | |
+ const DATETIME_ALLOWED = [ | |
"dateStyle", | |
"timeStyle", | |
"fractionalSecondDigits", | |
@@ -834,21 +522,17 @@ | |
* @param args The positional arguments passed to this `DATETIME()`. | |
* @param opts The named argments passed to this `DATETIME()`. | |
*/ | |
- | |
function DATETIME(args, opts) { | |
- var arg = args[0]; | |
- | |
+ let arg = args[0]; | |
if (arg instanceof FluentNone) { | |
- return new FluentNone("DATETIME(".concat(arg.valueOf(), ")")); | |
+ return new FluentNone(`DATETIME(${arg.valueOf()})`); | |
} | |
- | |
if (arg instanceof FluentNumber || arg instanceof FluentDateTime) { | |
- return new FluentDateTime( | |
- arg.valueOf(), | |
- _objectSpread2({}, arg.opts, {}, values(opts, DATETIME_ALLOWED)) | |
- ); | |
+ return new FluentDateTime(arg.valueOf(), { | |
+ ...arg.opts, | |
+ ...values(opts, DATETIME_ALLOWED), | |
+ }); | |
} | |
- | |
throw new TypeError("Invalid argument to DATETIME"); | |
} | |
@@ -856,7 +540,6 @@ | |
* Message bundles are single-language stores of translation resources. They are | |
* responsible for formatting message values and attributes to strings. | |
*/ | |
- | |
class FluentBundle { | |
/** | |
* Create an instance of `FluentBundle`. | |
@@ -887,28 +570,19 @@ | |
* | |
* - `transform` - a function used to transform string parts of patterns. | |
*/ | |
- constructor(locales) { | |
- var _ref = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : {}, | |
- functions = _ref.functions, | |
- _ref$useIsolating = _ref.useIsolating, | |
- useIsolating = _ref$useIsolating === void 0 ? true : _ref$useIsolating, | |
- _ref$transform = _ref.transform, | |
- transform = _ref$transform === void 0 ? (v) => v : _ref$transform; | |
- | |
+ constructor( | |
+ locales, | |
+ { functions, useIsolating = true, transform = (v) => v } = {} | |
+ ) { | |
this._terms = new Map(); | |
this._messages = new Map(); | |
this._intls = new WeakMap(); | |
this.locales = Array.isArray(locales) ? locales : [locales]; | |
- this._functions = _objectSpread2( | |
- { | |
+ this._functions = { | |
NUMBER, | |
DATETIME, | |
- }, | |
- functions | |
- ); | |
+ ...functions, | |
+ }; | |
this._useIsolating = useIsolating; | |
this._transform = transform; | |
} | |
@@ -917,7 +591,6 @@ | |
* | |
* @param id - The identifier of the message to check. | |
*/ | |
- | |
hasMessage(id) { | |
return this._messages.has(id); | |
} | |
@@ -930,7 +603,6 @@ | |
* | |
* @param id - The identifier of the message to check. | |
*/ | |
- | |
getMessage(id) { | |
return this._messages.get(id); | |
} | |
@@ -952,51 +624,32 @@ | |
* @param res - FluentResource object. | |
* @param options | |
*/ | |
- | |
- addResource(res) { | |
- var _ref2 = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : {}, | |
- _ref2$allowOverrides = _ref2.allowOverrides, | |
- allowOverrides = | |
- _ref2$allowOverrides === void 0 ? false : _ref2$allowOverrides; | |
- | |
- var errors = []; | |
- | |
- for (var i = 0; i < res.body.length; i++) { | |
- var entry = res.body[i]; | |
- | |
+ addResource(res, { allowOverrides = false } = {}) { | |
+ const errors = []; | |
+ for (let i = 0; i < res.body.length; i++) { | |
+ let entry = res.body[i]; | |
if (entry.id.startsWith("-")) { | |
// Identifiers starting with a dash (-) define terms. Terms are private | |
// and cannot be retrieved from FluentBundle. | |
if (allowOverrides === false && this._terms.has(entry.id)) { | |
errors.push( | |
- new Error( | |
- 'Attempt to override an existing term: "'.concat(entry.id, '"') | |
- ) | |
+ new Error(`Attempt to override an existing term: "${entry.id}"`) | |
); | |
continue; | |
} | |
- | |
this._terms.set(entry.id, entry); | |
} else { | |
if (allowOverrides === false && this._messages.has(entry.id)) { | |
errors.push( | |
new Error( | |
- 'Attempt to override an existing message: "'.concat( | |
- entry.id, | |
- '"' | |
- ) | |
+ `Attempt to override an existing message: "${entry.id}"` | |
) | |
); | |
continue; | |
} | |
- | |
this._messages.set(entry.id, entry); | |
} | |
} | |
- | |
return errors; | |
} | |
/** | |
@@ -1026,34 +679,22 @@ | |
* | |
* If `errors` is omitted, the first encountered error will be thrown. | |
*/ | |
- | |
- formatPattern(pattern) { | |
- var args = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : null; | |
- var errors = | |
- arguments.length > 2 && arguments[2] !== undefined | |
- ? arguments[2] | |
- : null; | |
- | |
+ formatPattern(pattern, args = null, errors = null) { | |
// Resolve a simple pattern without creating a scope. No error handling is | |
// required; by definition simple patterns don't have placeables. | |
if (typeof pattern === "string") { | |
return this._transform(pattern); | |
- } // Resolve a complex pattern. | |
- | |
- var scope = new Scope(this, errors, args); | |
- | |
+ } | |
+ // Resolve a complex pattern. | |
+ let scope = new Scope(this, errors, args); | |
try { | |
- var value = resolveComplexPattern(scope, pattern); | |
+ let value = resolveComplexPattern(scope, pattern); | |
return value.toString(scope); | |
} catch (err) { | |
if (scope.errors) { | |
scope.errors.push(err); | |
return new FluentNone().toString(scope); | |
} | |
- | |
throw err; | |
} | |
} | |
@@ -1061,65 +702,61 @@ | |
// This regex is used to iterate through the beginnings of messages and terms. | |
// With the /m flag, the ^ matches at the beginning of every line. | |
- var RE_MESSAGE_START = /^(-?[a-zA-Z][\w-]*) *= */gm; // Both Attributes and Variants are parsed in while loops. These regexes are | |
+ const RE_MESSAGE_START = /^(-?[a-zA-Z][\w-]*) *= */gm; | |
+ // Both Attributes and Variants are parsed in while loops. These regexes are | |
// used to break out of them. | |
- | |
- var RE_ATTRIBUTE_START = /\.([a-zA-Z][\w-]*) *= */y; | |
- var RE_VARIANT_START = /\*?\[/y; | |
- var RE_NUMBER_LITERAL = /(-?[0-9]+(?:\.([0-9]+))?)/y; | |
- var RE_IDENTIFIER = /([a-zA-Z][\w-]*)/y; | |
- var RE_REFERENCE = /([$-])?([a-zA-Z][\w-]*)(?:\.([a-zA-Z][\w-]*))?/y; | |
- var RE_FUNCTION_NAME = /^[A-Z][A-Z0-9_-]*$/; // A "run" is a sequence of text or string literal characters which don't | |
+ const RE_ATTRIBUTE_START = /\.([a-zA-Z][\w-]*) *= */y; | |
+ const RE_VARIANT_START = /\*?\[/y; | |
+ const RE_NUMBER_LITERAL = /(-?[0-9]+(?:\.([0-9]+))?)/y; | |
+ const RE_IDENTIFIER = /([a-zA-Z][\w-]*)/y; | |
+ const RE_REFERENCE = /([$-])?([a-zA-Z][\w-]*)(?:\.([a-zA-Z][\w-]*))?/y; | |
+ const RE_FUNCTION_NAME = /^[A-Z][A-Z0-9_-]*$/; | |
+ // A "run" is a sequence of text or string literal characters which don't | |
// require any special handling. For TextElements such special characters are: { | |
// (starts a placeable), and line breaks which require additional logic to check | |
// if the next line is indented. For StringLiterals they are: \ (starts an | |
// escape sequence), " (ends the literal), and line breaks which are not allowed | |
// in StringLiterals. Note that string runs may be empty; text runs may not. | |
- | |
- var RE_TEXT_RUN = /([^{}\n\r]+)/y; | |
- var RE_STRING_RUN = /([^\\"\n\r]*)/y; // Escape sequences. | |
- | |
- var RE_STRING_ESCAPE = /\\([\\"])/y; | |
- var RE_UNICODE_ESCAPE = /\\u([a-fA-F0-9]{4})|\\U([a-fA-F0-9]{6})/y; // Used for trimming TextElements and indents. | |
- | |
- var RE_LEADING_NEWLINES = /^\n+/; | |
- var RE_TRAILING_SPACES = / +$/; // Used in makeIndent to strip spaces from blank lines and normalize CRLF to LF. | |
- | |
- var RE_BLANK_LINES = / *\r?\n/g; // Used in makeIndent to measure the indentation. | |
- | |
- var RE_INDENT = /( *)$/; // Common tokens. | |
- | |
- var TOKEN_BRACE_OPEN = /{\s*/y; | |
- var TOKEN_BRACE_CLOSE = /\s*}/y; | |
- var TOKEN_BRACKET_OPEN = /\[\s*/y; | |
- var TOKEN_BRACKET_CLOSE = /\s*] */y; | |
- var TOKEN_PAREN_OPEN = /\s*\(\s*/y; | |
- var TOKEN_ARROW = /\s*->\s*/y; | |
- var TOKEN_COLON = /\s*:\s*/y; // Note the optional comma. As a deviation from the Fluent EBNF, the parser | |
+ const RE_TEXT_RUN = /([^{}\n\r]+)/y; | |
+ const RE_STRING_RUN = /([^\\"\n\r]*)/y; | |
+ // Escape sequences. | |
+ const RE_STRING_ESCAPE = /\\([\\"])/y; | |
+ const RE_UNICODE_ESCAPE = /\\u([a-fA-F0-9]{4})|\\U([a-fA-F0-9]{6})/y; | |
+ // Used for trimming TextElements and indents. | |
+ const RE_LEADING_NEWLINES = /^\n+/; | |
+ const RE_TRAILING_SPACES = / +$/; | |
+ // Used in makeIndent to strip spaces from blank lines and normalize CRLF to LF. | |
+ const RE_BLANK_LINES = / *\r?\n/g; | |
+ // Used in makeIndent to measure the indentation. | |
+ const RE_INDENT = /( *)$/; | |
+ // Common tokens. | |
+ const TOKEN_BRACE_OPEN = /{\s*/y; | |
+ const TOKEN_BRACE_CLOSE = /\s*}/y; | |
+ const TOKEN_BRACKET_OPEN = /\[\s*/y; | |
+ const TOKEN_BRACKET_CLOSE = /\s*] */y; | |
+ const TOKEN_PAREN_OPEN = /\s*\(\s*/y; | |
+ const TOKEN_ARROW = /\s*->\s*/y; | |
+ const TOKEN_COLON = /\s*:\s*/y; | |
+ // Note the optional comma. As a deviation from the Fluent EBNF, the parser | |
// doesn't enforce commas between call arguments. | |
- | |
- var TOKEN_COMMA = /\s*,?\s*/y; | |
- var TOKEN_BLANK = /\s+/y; | |
+ const TOKEN_COMMA = /\s*,?\s*/y; | |
+ const TOKEN_BLANK = /\s+/y; | |
/** | |
* Fluent Resource is a structure storing parsed localization entries. | |
*/ | |
- | |
class FluentResource { | |
constructor(source) { | |
this.body = []; | |
RE_MESSAGE_START.lastIndex = 0; | |
- var cursor = 0; // Iterate over the beginnings of messages and terms to efficiently skip | |
+ let cursor = 0; | |
+ // Iterate over the beginnings of messages and terms to efficiently skip | |
// comments and recover from errors. | |
- | |
while (true) { | |
- var next = RE_MESSAGE_START.exec(source); | |
- | |
+ let next = RE_MESSAGE_START.exec(source); | |
if (next === null) { | |
break; | |
} | |
- | |
cursor = RE_MESSAGE_START.lastIndex; | |
- | |
try { | |
this.body.push(parseMessage(next[1])); | |
} catch (err) { | |
@@ -1128,10 +765,10 @@ | |
// beginning of the next message or term. | |
continue; | |
} | |
- | |
throw err; | |
} | |
- } // The parser implementation is inlined below for performance reasons, | |
+ } | |
+ // The parser implementation is inlined below for performance reasons, | |
// as well as for convenience of accessing `source` and `cursor`. | |
// The parser focuses on minimizing the number of false negatives at the | |
// expense of increasing the risk of false positives. In other words, it | |
@@ -1144,173 +781,130 @@ | |
// The parser makes an extensive use of sticky regexes which can be anchored | |
// to any offset of the source string without slicing it. Errors are thrown | |
// to bail out of parsing of ill-formed messages. | |
- | |
function test(re) { | |
re.lastIndex = cursor; | |
return re.test(source); | |
- } // Advance the cursor by the char if it matches. May be used as a predicate | |
+ } | |
+ // Advance the cursor by the char if it matches. May be used as a predicate | |
// (was the match found?) or, if errorClass is passed, as an assertion. | |
- | |
function consumeChar(char, errorClass) { | |
if (source[cursor] === char) { | |
cursor++; | |
return true; | |
} | |
- | |
if (errorClass) { | |
- throw new errorClass("Expected ".concat(char)); | |
+ throw new errorClass(`Expected ${char}`); | |
} | |
- | |
return false; | |
- } // Advance the cursor by the token if it matches. May be used as a predicate | |
+ } | |
+ // Advance the cursor by the token if it matches. May be used as a predicate | |
// (was the match found?) or, if errorClass is passed, as an assertion. | |
- | |
function consumeToken(re, errorClass) { | |
if (test(re)) { | |
cursor = re.lastIndex; | |
return true; | |
} | |
- | |
if (errorClass) { | |
- throw new errorClass("Expected ".concat(re.toString())); | |
+ throw new errorClass(`Expected ${re.toString()}`); | |
} | |
- | |
return false; | |
- } // Execute a regex, advance the cursor, and return all capture groups. | |
- | |
+ } | |
+ // Execute a regex, advance the cursor, and return all capture groups. | |
function match(re) { | |
re.lastIndex = cursor; | |
- var result = re.exec(source); | |
- | |
+ let result = re.exec(source); | |
if (result === null) { | |
- throw new SyntaxError("Expected ".concat(re.toString())); | |
+ throw new SyntaxError(`Expected ${re.toString()}`); | |
} | |
- | |
cursor = re.lastIndex; | |
return result; | |
- } // Execute a regex, advance the cursor, and return the capture group. | |
- | |
+ } | |
+ // Execute a regex, advance the cursor, and return the capture group. | |
function match1(re) { | |
return match(re)[1]; | |
} | |
- | |
function parseMessage(id) { | |
- var value = parsePattern(); | |
- var attributes = parseAttributes(); | |
- | |
+ let value = parsePattern(); | |
+ let attributes = parseAttributes(); | |
if (value === null && Object.keys(attributes).length === 0) { | |
throw new SyntaxError("Expected message value or attributes"); | |
} | |
- | |
- return { | |
- id, | |
- value, | |
- attributes, | |
- }; | |
+ return { id, value, attributes }; | |
} | |
- | |
function parseAttributes() { | |
- var attrs = Object.create(null); | |
- | |
+ let attrs = Object.create(null); | |
while (test(RE_ATTRIBUTE_START)) { | |
- var name = match1(RE_ATTRIBUTE_START); | |
- var value = parsePattern(); | |
- | |
+ let name = match1(RE_ATTRIBUTE_START); | |
+ let value = parsePattern(); | |
if (value === null) { | |
throw new SyntaxError("Expected attribute value"); | |
} | |
- | |
attrs[name] = value; | |
} | |
- | |
return attrs; | |
} | |
- | |
function parsePattern() { | |
- var first; // First try to parse any simple text on the same line as the id. | |
- | |
+ let first; | |
+ // First try to parse any simple text on the same line as the id. | |
if (test(RE_TEXT_RUN)) { | |
first = match1(RE_TEXT_RUN); | |
- } // If there's a placeable on the first line, parse a complex pattern. | |
- | |
+ } | |
+ // If there's a placeable on the first line, parse a complex pattern. | |
if (source[cursor] === "{" || source[cursor] === "}") { | |
// Re-use the text parsed above, if possible. | |
return parsePatternElements(first ? [first] : [], Infinity); | |
- } // RE_TEXT_VALUE stops at newlines. Only continue parsing the pattern if | |
+ } | |
+ // RE_TEXT_VALUE stops at newlines. Only continue parsing the pattern if | |
// what comes after the newline is indented. | |
- | |
- var indent = parseIndent(); | |
- | |
+ let indent = parseIndent(); | |
if (indent) { | |
if (first) { | |
// If there's text on the first line, the blank block is part of the | |
// translation content in its entirety. | |
return parsePatternElements([first, indent], indent.length); | |
- } // Otherwise, we're dealing with a block pattern, i.e. a pattern which | |
+ } | |
+ // Otherwise, we're dealing with a block pattern, i.e. a pattern which | |
// starts on a new line. Discrad the leading newlines but keep the | |
// inline indent; it will be used by the dedentation logic. | |
- | |
indent.value = trim(indent.value, RE_LEADING_NEWLINES); | |
return parsePatternElements([indent], indent.length); | |
} | |
- | |
if (first) { | |
// It was just a simple inline text after all. | |
return trim(first, RE_TRAILING_SPACES); | |
} | |
- | |
return null; | |
- } // Parse a complex pattern as an array of elements. | |
- | |
- function parsePatternElements() { | |
- var elements = | |
- arguments.length > 0 && arguments[0] !== undefined | |
- ? arguments[0] | |
- : []; | |
- var commonIndent = arguments.length > 1 ? arguments[1] : undefined; | |
- | |
+ } | |
+ // Parse a complex pattern as an array of elements. | |
+ function parsePatternElements(elements = [], commonIndent) { | |
while (true) { | |
if (test(RE_TEXT_RUN)) { | |
elements.push(match1(RE_TEXT_RUN)); | |
continue; | |
} | |
- | |
if (source[cursor] === "{") { | |
elements.push(parsePlaceable()); | |
continue; | |
} | |
- | |
if (source[cursor] === "}") { | |
throw new SyntaxError("Unbalanced closing brace"); | |
} | |
- | |
- var indent = parseIndent(); | |
- | |
+ let indent = parseIndent(); | |
if (indent) { | |
elements.push(indent); | |
commonIndent = Math.min(commonIndent, indent.length); | |
continue; | |
} | |
- | |
break; | |
} | |
- | |
- var lastIndex = elements.length - 1; | |
- var lastElement = elements[lastIndex]; // Trim the trailing spaces in the last element if it's a TextElement. | |
- | |
+ let lastIndex = elements.length - 1; | |
+ let lastElement = elements[lastIndex]; | |
+ // Trim the trailing spaces in the last element if it's a TextElement. | |
if (typeof lastElement === "string") { | |
elements[lastIndex] = trim(lastElement, RE_TRAILING_SPACES); | |
} | |
- | |
- var baked = []; | |
- | |
- var _iterator = _createForOfIteratorHelper(elements), | |
- _step; | |
- | |
- try { | |
- for (_iterator.s(); !(_step = _iterator.n()).done; ) { | |
- var element = _step.value; | |
- | |
+ let baked = []; | |
+ for (let element of elements) { | |
if (element instanceof Indent) { | |
// Dedent indented lines by the maximum common indent. | |
element = element.value.slice( | |
@@ -1318,88 +912,50 @@ | |
element.value.length - commonIndent | |
); | |
} | |
- | |
if (element) { | |
baked.push(element); | |
} | |
} | |
- } catch (err) { | |
- _iterator.e(err); | |
- } finally { | |
- _iterator.f(); | |
- } | |
- | |
return baked; | |
} | |
- | |
function parsePlaceable() { | |
consumeToken(TOKEN_BRACE_OPEN, SyntaxError); | |
- var selector = parseInlineExpression(); | |
- | |
+ let selector = parseInlineExpression(); | |
if (consumeToken(TOKEN_BRACE_CLOSE)) { | |
return selector; | |
} | |
- | |
if (consumeToken(TOKEN_ARROW)) { | |
- var variants = parseVariants(); | |
+ let variants = parseVariants(); | |
consumeToken(TOKEN_BRACE_CLOSE, SyntaxError); | |
- return _objectSpread2( | |
- { | |
+ return { | |
type: "select", | |
selector, | |
- }, | |
- variants | |
- ); | |
+ ...variants, | |
+ }; | |
} | |
- | |
throw new SyntaxError("Unclosed placeable"); | |
} | |
- | |
function parseInlineExpression() { | |
if (source[cursor] === "{") { | |
// It's a nested placeable. | |
return parsePlaceable(); | |
} | |
- | |
if (test(RE_REFERENCE)) { | |
- var _match = match(RE_REFERENCE), | |
- _match2 = _slicedToArray(_match, 4), | |
- sigil = _match2[1], | |
- name = _match2[2], | |
- _match2$ = _match2[3], | |
- attr = _match2$ === void 0 ? null : _match2$; | |
- | |
+ let [, sigil, name, attr = null] = match(RE_REFERENCE); | |
if (sigil === "$") { | |
- return { | |
- type: "var", | |
- name, | |
- }; | |
+ return { type: "var", name }; | |
} | |
- | |
if (consumeToken(TOKEN_PAREN_OPEN)) { | |
- var args = parseArguments(); | |
- | |
+ let args = parseArguments(); | |
if (sigil === "-") { | |
// A parameterized term: -term(...). | |
- return { | |
- type: "term", | |
- name, | |
- attr, | |
- args, | |
- }; | |
+ return { type: "term", name, attr, args }; | |
} | |
- | |
if (RE_FUNCTION_NAME.test(name)) { | |
- return { | |
- type: "func", | |
- name, | |
- args, | |
- }; | |
+ return { type: "func", name, args }; | |
} | |
- | |
throw new SyntaxError("Function names must be all upper-case"); | |
} | |
- | |
if (sigil === "-") { | |
// A non-parameterized term: -term. | |
return { | |
@@ -1409,45 +965,30 @@ | |
args: [], | |
}; | |
} | |
- | |
- return { | |
- type: "mesg", | |
- name, | |
- attr, | |
- }; | |
+ return { type: "mesg", name, attr }; | |
} | |
- | |
return parseLiteral(); | |
} | |
- | |
function parseArguments() { | |
- var args = []; | |
- | |
+ let args = []; | |
while (true) { | |
switch (source[cursor]) { | |
- case ")": | |
- // End of the argument list. | |
+ case ")": // End of the argument list. | |
cursor++; | |
return args; | |
- | |
- case undefined: | |
- // EOF | |
+ case undefined: // EOF | |
throw new SyntaxError("Unclosed argument list"); | |
} | |
- | |
- args.push(parseArgument()); // Commas between arguments are treated as whitespace. | |
- | |
+ args.push(parseArgument()); | |
+ // Commas between arguments are treated as whitespace. | |
consumeToken(TOKEN_COMMA); | |
} | |
} | |
- | |
function parseArgument() { | |
- var expr = parseInlineExpression(); | |
- | |
+ let expr = parseInlineExpression(); | |
if (expr.type !== "mesg") { | |
return expr; | |
} | |
- | |
if (consumeToken(TOKEN_COLON)) { | |
// The reference is the beginning of a named argument. | |
return { | |
@@ -1455,52 +996,36 @@ | |
name: expr.name, | |
value: parseLiteral(), | |
}; | |
- } // It's a regular message reference. | |
- | |
+ } | |
+ // It's a regular message reference. | |
return expr; | |
} | |
- | |
function parseVariants() { | |
- var variants = []; | |
- var count = 0; | |
- var star; | |
- | |
+ let variants = []; | |
+ let count = 0; | |
+ let star; | |
while (test(RE_VARIANT_START)) { | |
if (consumeChar("*")) { | |
star = count; | |
} | |
- | |
- var key = parseVariantKey(); | |
- var value = parsePattern(); | |
- | |
+ let key = parseVariantKey(); | |
+ let value = parsePattern(); | |
if (value === null) { | |
throw new SyntaxError("Expected variant value"); | |
} | |
- | |
- variants[count++] = { | |
- key, | |
- value, | |
- }; | |
+ variants[count++] = { key, value }; | |
} | |
- | |
if (count === 0) { | |
return null; | |
} | |
- | |
if (star === undefined) { | |
throw new SyntaxError("Expected default variant"); | |
} | |
- | |
- return { | |
- variants, | |
- star, | |
- }; | |
+ return { variants, star }; | |
} | |
- | |
function parseVariantKey() { | |
consumeToken(TOKEN_BRACKET_OPEN, SyntaxError); | |
- var key; | |
- | |
+ let key; | |
if (test(RE_NUMBER_LITERAL)) { | |
key = parseNumberLiteral(); | |
} else { | |
@@ -1509,128 +1034,104 @@ | |
value: match1(RE_IDENTIFIER), | |
}; | |
} | |
- | |
consumeToken(TOKEN_BRACKET_CLOSE, SyntaxError); | |
return key; | |
} | |
- | |
function parseLiteral() { | |
if (test(RE_NUMBER_LITERAL)) { | |
return parseNumberLiteral(); | |
} | |
- | |
if (source[cursor] === '"') { | |
return parseStringLiteral(); | |
} | |
- | |
throw new SyntaxError("Invalid expression"); | |
} | |
- | |
function parseNumberLiteral() { | |
- var _match3 = match(RE_NUMBER_LITERAL), | |
- _match4 = _slicedToArray(_match3, 3), | |
- value = _match4[1], | |
- _match4$ = _match4[2], | |
- fraction = _match4$ === void 0 ? "" : _match4$; | |
- | |
- var precision = fraction.length; | |
+ let [, value, fraction = ""] = match(RE_NUMBER_LITERAL); | |
+ let precision = fraction.length; | |
return { | |
type: "num", | |
value: parseFloat(value), | |
precision, | |
}; | |
} | |
- | |
function parseStringLiteral() { | |
consumeChar('"', SyntaxError); | |
- var value = ""; | |
- | |
+ let value = ""; | |
while (true) { | |
value += match1(RE_STRING_RUN); | |
- | |
if (source[cursor] === "\\") { | |
value += parseEscapeSequence(); | |
continue; | |
} | |
- | |
if (consumeChar('"')) { | |
- return { | |
- type: "str", | |
- value, | |
- }; | |
- } // We've reached an EOL of EOF. | |
- | |
+ return { type: "str", value }; | |
+ } | |
+ // We've reached an EOL of EOF. | |
throw new SyntaxError("Unclosed string literal"); | |
} | |
- } // Unescape known escape sequences. | |
- | |
+ } | |
+ // Unescape known escape sequences. | |
function parseEscapeSequence() { | |
if (test(RE_STRING_ESCAPE)) { | |
return match1(RE_STRING_ESCAPE); | |
} | |
- | |
if (test(RE_UNICODE_ESCAPE)) { | |
- var _match5 = match(RE_UNICODE_ESCAPE), | |
- _match6 = _slicedToArray(_match5, 3), | |
- codepoint4 = _match6[1], | |
- codepoint6 = _match6[2]; | |
- | |
- var codepoint = parseInt(codepoint4 || codepoint6, 16); | |
- return codepoint <= 0xd7ff || 0xe000 <= codepoint // It's a Unicode scalar value. | |
- ? String.fromCodePoint(codepoint) // Lonely surrogates can cause trouble when the parsing result is | |
- : // saved using UTF-8. Use U+FFFD REPLACEMENT CHARACTER instead. | |
+ let [, codepoint4, codepoint6] = match(RE_UNICODE_ESCAPE); | |
+ let codepoint = parseInt(codepoint4 || codepoint6, 16); | |
+ return codepoint <= 0xd7ff || 0xe000 <= codepoint | |
+ ? // It's a Unicode scalar value. | |
+ String.fromCodePoint(codepoint) | |
+ : // Lonely surrogates can cause trouble when the parsing result is | |
+ // saved using UTF-8. Use U+FFFD REPLACEMENT CHARACTER instead. | |
"�"; | |
} | |
- | |
throw new SyntaxError("Unknown escape sequence"); | |
- } // Parse blank space. Return it if it looks like indent before a pattern | |
+ } | |
+ // Parse blank space. Return it if it looks like indent before a pattern | |
// line. Skip it othwerwise. | |
- | |
function parseIndent() { | |
- var start = cursor; | |
- consumeToken(TOKEN_BLANK); // Check the first non-blank character after the indent. | |
- | |
+ let start = cursor; | |
+ consumeToken(TOKEN_BLANK); | |
+ // Check the first non-blank character after the indent. | |
switch (source[cursor]) { | |
case ".": | |
case "[": | |
case "*": | |
case "}": | |
- case undefined: | |
- // EOF | |
+ case undefined: // EOF | |
// A special character. End the Pattern. | |
return false; | |
- | |
case "{": | |
// Placeables don't require indentation (in EBNF: block-placeable). | |
// Continue the Pattern. | |
return makeIndent(source.slice(start, cursor)); | |
- } // If the first character on the line is not one of the special characters | |
+ } | |
+ // If the first character on the line is not one of the special characters | |
// listed above, it's a regular text character. Check if there's at least | |
// one space of indent before it. | |
- | |
if (source[cursor - 1] === " ") { | |
// It's an indented text character (in EBNF: indented-char). Continue | |
// the Pattern. | |
return makeIndent(source.slice(start, cursor)); | |
- } // A not-indented text character is likely the identifier of the next | |
+ } | |
+ // A not-indented text character is likely the identifier of the next | |
// message. End the Pattern. | |
- | |
return false; | |
- } // Trim blanks in text according to the given regex. | |
- | |
+ } | |
+ // Trim blanks in text according to the given regex. | |
function trim(text, re) { | |
return text.replace(re, ""); | |
- } // Normalize a blank block and extract the indent details. | |
- | |
+ } | |
+ // Normalize a blank block and extract the indent details. | |
function makeIndent(blank) { | |
- var value = blank.replace(RE_BLANK_LINES, "\n"); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
- | |
- var length = RE_INDENT.exec(blank)[1].length; | |
+ let value = blank.replace(RE_BLANK_LINES, "\n"); | |
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
+ let length = RE_INDENT.exec(blank)[1].length; | |
return new Indent(value, length); | |
} | |
} | |
} | |
- | |
class Indent { | |
constructor(value, length) { | |
this.value = value; | |
diff -wru compat/fluent-dedent.js es2018/fluent-dedent.js | |
--- compat/fluent-dedent.js 2020-04-21 14:45:54.662890200 +0200 | |
+++ es2018/fluent-dedent.js 2020-04-21 14:46:02.430757800 +0200 | |
@@ -9,7 +9,7 @@ | |
"use strict"; | |
// A blank line may contain spaces and tabs. | |
- var RE_BLANK = /^[ \t]*$/; | |
+ const RE_BLANK = /^[ \t]*$/; | |
/** | |
* Template literal tag for dedenting Fluent code. | |
* | |
@@ -21,38 +21,21 @@ | |
* @param strings | |
* @param values | |
*/ | |
- | |
- function ftl(strings) { | |
- for ( | |
- var _len = arguments.length, | |
- values = new Array(_len > 1 ? _len - 1 : 0), | |
- _key = 1; | |
- _key < _len; | |
- _key++ | |
- ) { | |
- values[_key - 1] = arguments[_key]; | |
- } | |
- | |
- var code = strings.reduce((acc, cur) => acc + values.shift() + cur); | |
- var lines = code.split("\n"); | |
- var first = lines.shift(); | |
- | |
+ function ftl(strings, ...values) { | |
+ let code = strings.reduce((acc, cur) => acc + values.shift() + cur); | |
+ let lines = code.split("\n"); | |
+ let first = lines.shift(); | |
if (first === undefined || !RE_BLANK.test(first)) { | |
throw new RangeError("Content must start on a new line."); | |
} | |
- | |
- var commonIndent = lines.pop(); | |
- | |
+ let commonIndent = lines.pop(); | |
if (commonIndent === undefined || !RE_BLANK.test(commonIndent)) { | |
throw new RangeError("Closing delimiter must appear on a new line."); | |
} | |
- | |
- var dedented = []; | |
- | |
- for (var i = 0; i < lines.length; i++) { | |
- var line = lines[i]; | |
- var lineIndent = line.slice(0, commonIndent.length); | |
- | |
+ let dedented = []; | |
+ for (let i = 0; i < lines.length; i++) { | |
+ let line = lines[i]; | |
+ let lineIndent = line.slice(0, commonIndent.length); | |
if (lineIndent.length === 0) { | |
// Empty blank lines are preserved even if technically they are not | |
// indented at all. This also short-circuits the dedentation logic when | |
@@ -60,17 +43,13 @@ | |
dedented.push(line); | |
continue; | |
} | |
- | |
if (lineIndent !== commonIndent) { | |
// The indentation of the line must match commonIndent exacty. | |
- throw new RangeError( | |
- "Insufficient indentation in line ".concat(i + 1, ".") | |
- ); | |
- } // Strip commonIndent. | |
- | |
+ throw new RangeError(`Insufficient indentation in line ${i + 1}.`); | |
+ } | |
+ // Strip commonIndent. | |
dedented.push(line.slice(commonIndent.length)); | |
} | |
- | |
return dedented.join("\n"); | |
} | |
diff -wru compat/fluent-dom.js es2018/fluent-dom.js | |
--- compat/fluent-dom.js 2020-04-21 14:45:54.820891000 +0200 | |
+++ es2018/fluent-dom.js 2020-04-21 14:46:02.561792500 +0200 | |
@@ -9,201 +9,11 @@ | |
})(this, function (exports, cachedIterable) { | |
"use strict"; | |
- function _asyncIterator(iterable) { | |
- var method; | |
- | |
- if (typeof Symbol !== "undefined") { | |
- if (Symbol.asyncIterator) { | |
- method = iterable[Symbol.asyncIterator]; | |
- if (method != null) return method.call(iterable); | |
- } | |
- | |
- if (Symbol.iterator) { | |
- method = iterable[Symbol.iterator]; | |
- if (method != null) return method.call(iterable); | |
- } | |
- } | |
- | |
- throw new TypeError("Object is not async iterable"); | |
- } | |
- | |
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { | |
- try { | |
- var info = gen[key](arg); | |
- var value = info.value; | |
- } catch (error) { | |
- reject(error); | |
- return; | |
- } | |
- | |
- if (info.done) { | |
- resolve(value); | |
- } else { | |
- Promise.resolve(value).then(_next, _throw); | |
- } | |
- } | |
- | |
- function _asyncToGenerator(fn) { | |
- return function () { | |
- var self = this, | |
- args = arguments; | |
- return new Promise(function (resolve, reject) { | |
- var gen = fn.apply(self, args); | |
- | |
- function _next(value) { | |
- asyncGeneratorStep( | |
- gen, | |
- resolve, | |
- reject, | |
- _next, | |
- _throw, | |
- "next", | |
- value | |
- ); | |
- } | |
- | |
- function _throw(err) { | |
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); | |
- } | |
- | |
- _next(undefined); | |
- }); | |
- }; | |
- } | |
- | |
- function _slicedToArray(arr, i) { | |
- return ( | |
- _arrayWithHoles(arr) || | |
- _iterableToArrayLimit(arr, i) || | |
- _unsupportedIterableToArray(arr, i) || | |
- _nonIterableRest() | |
- ); | |
- } | |
- | |
- function _arrayWithHoles(arr) { | |
- if (Array.isArray(arr)) return arr; | |
- } | |
- | |
- function _iterableToArrayLimit(arr, i) { | |
- if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) | |
- return; | |
- var _arr = []; | |
- var _n = true; | |
- var _d = false; | |
- var _e = undefined; | |
- | |
- try { | |
- for ( | |
- var _i = arr[Symbol.iterator](), _s; | |
- !(_n = (_s = _i.next()).done); | |
- _n = true | |
- ) { | |
- _arr.push(_s.value); | |
- | |
- if (i && _arr.length === i) break; | |
- } | |
- } catch (err) { | |
- _d = true; | |
- _e = err; | |
- } finally { | |
- try { | |
- if (!_n && _i["return"] != null) _i["return"](); | |
- } finally { | |
- if (_d) throw _e; | |
- } | |
- } | |
- | |
- return _arr; | |
- } | |
- | |
- function _unsupportedIterableToArray(o, minLen) { | |
- if (!o) return; | |
- if (typeof o === "string") return _arrayLikeToArray(o, minLen); | |
- var n = Object.prototype.toString.call(o).slice(8, -1); | |
- if (n === "Object" && o.constructor) n = o.constructor.name; | |
- if (n === "Map" || n === "Set") return Array.from(n); | |
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) | |
- return _arrayLikeToArray(o, minLen); | |
- } | |
- | |
- function _arrayLikeToArray(arr, len) { | |
- if (len == null || len > arr.length) len = arr.length; | |
- | |
- for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | |
- | |
- return arr2; | |
- } | |
- | |
- function _nonIterableRest() { | |
- throw new TypeError( | |
- "Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
- function _createForOfIteratorHelper(o) { | |
- if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { | |
- if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { | |
- var i = 0; | |
- | |
- var F = function () {}; | |
- | |
- return { | |
- s: F, | |
- n: function () { | |
- if (i >= o.length) | |
- return { | |
- done: true, | |
- }; | |
- return { | |
- done: false, | |
- value: o[i++], | |
- }; | |
- }, | |
- e: function (e) { | |
- throw e; | |
- }, | |
- f: F, | |
- }; | |
- } | |
- | |
- throw new TypeError( | |
- "Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
- var it, | |
- normalCompletion = true, | |
- didErr = false, | |
- err; | |
- return { | |
- s: function () { | |
- it = o[Symbol.iterator](); | |
- }, | |
- n: function () { | |
- var step = it.next(); | |
- normalCompletion = step.done; | |
- return step; | |
- }, | |
- e: function (e) { | |
- didErr = true; | |
- err = e; | |
- }, | |
- f: function () { | |
- try { | |
- if (!normalCompletion && it.return != null) it.return(); | |
- } finally { | |
- if (didErr) throw err; | |
- } | |
- }, | |
- }; | |
- } | |
- | |
/* eslint no-console: ["error", {allow: ["warn"]}] */ | |
- | |
/* global console */ | |
// Match the opening angle bracket (<) in HTML tags, and HTML entities like | |
// &, &, &. | |
- var reOverlay = /<|&#?\w+;/; | |
+ const reOverlay = /<|&#?\w+;/; | |
/** | |
* Elements allowed in translations even if they are not present in the source | |
* HTML. They are text-level elements as defined by the HTML5 spec: | |
@@ -212,8 +22,7 @@ | |
* - a - because we don't allow href on it anyways, | |
* - ruby, rt, rp - because we don't allow nested elements to be inserted. | |
*/ | |
- | |
- var TEXT_LEVEL_ELEMENTS = { | |
+ const TEXT_LEVEL_ELEMENTS = { | |
"http://www.w3.org/1999/xhtml": [ | |
"em", | |
"strong", | |
@@ -242,7 +51,7 @@ | |
"wbr", | |
], | |
}; | |
- var LOCALIZABLE_ATTRIBUTES = { | |
+ const LOCALIZABLE_ATTRIBUTES = { | |
"http://www.w3.org/1999/xhtml": { | |
global: ["title", "aria-label", "aria-valuetext"], | |
a: ["download"], | |
@@ -286,10 +95,8 @@ | |
* @param {Object} translation | |
* @private | |
*/ | |
- | |
function translateElement(element, translation) { | |
- var value = translation.value; | |
- | |
+ const { value } = translation; | |
if (typeof value === "string") { | |
if ( | |
element.localName === "title" && | |
@@ -303,17 +110,17 @@ | |
} else { | |
// Else parse the translation's HTML using an inert template element, | |
// sanitize it and replace the element's content. | |
- var templateElement = element.ownerDocument.createElementNS( | |
+ const templateElement = element.ownerDocument.createElementNS( | |
"http://www.w3.org/1999/xhtml", | |
"template" | |
); | |
templateElement.innerHTML = value; | |
overlayChildNodes(templateElement.content, element); | |
} | |
- } // Even if the translation doesn't define any localizable attributes, run | |
+ } | |
+ // Even if the translation doesn't define any localizable attributes, run | |
// overlayAttributes to remove any localizable attributes set by previous | |
// translations. | |
- | |
overlayAttributes(translation, element); | |
} | |
/** | |
@@ -326,79 +133,45 @@ | |
* @param {Element} toElement - The target of the overlay. | |
* @private | |
*/ | |
- | |
function overlayChildNodes(fromFragment, toElement) { | |
- var _iterator = _createForOfIteratorHelper(fromFragment.childNodes), | |
- _step; | |
- | |
- try { | |
- for (_iterator.s(); !(_step = _iterator.n()).done; ) { | |
- var childNode = _step.value; | |
- | |
+ for (const childNode of fromFragment.childNodes) { | |
if (childNode.nodeType === childNode.TEXT_NODE) { | |
// Keep the translated text node. | |
continue; | |
} | |
- | |
if (childNode.hasAttribute("data-l10n-name")) { | |
- var sanitized = getNodeForNamedElement(toElement, childNode); | |
+ const sanitized = getNodeForNamedElement(toElement, childNode); | |
fromFragment.replaceChild(sanitized, childNode); | |
continue; | |
} | |
- | |
if (isElementAllowed(childNode)) { | |
- var _sanitized = createSanitizedElement(childNode); | |
- | |
- fromFragment.replaceChild(_sanitized, childNode); | |
+ const sanitized = createSanitizedElement(childNode); | |
+ fromFragment.replaceChild(sanitized, childNode); | |
continue; | |
} | |
- | |
console.warn( | |
- 'An element of forbidden type "'.concat( | |
- childNode.localName, | |
- '" was found in ' | |
- ) + | |
+ `An element of forbidden type "${childNode.localName}" was found in ` + | |
"the translation. Only safe text-level elements and elements with " + | |
"data-l10n-name are allowed." | |
- ); // If all else fails, replace the element with its text content. | |
- | |
+ ); | |
+ // If all else fails, replace the element with its text content. | |
fromFragment.replaceChild( | |
createTextNodeFromTextContent(childNode), | |
childNode | |
); | |
} | |
- } catch (err) { | |
- _iterator.e(err); | |
- } finally { | |
- _iterator.f(); | |
- } | |
- | |
toElement.textContent = ""; | |
toElement.appendChild(fromFragment); | |
} | |
- | |
function hasAttribute(attributes, name) { | |
if (!attributes) { | |
return false; | |
} | |
- | |
- var _iterator2 = _createForOfIteratorHelper(attributes), | |
- _step2; | |
- | |
- try { | |
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done; ) { | |
- var attr = _step2.value; | |
- | |
+ for (let attr of attributes) { | |
if (attr.name === name) { | |
return true; | |
} | |
} | |
- } catch (err) { | |
- _iterator2.e(err); | |
- } finally { | |
- _iterator2.f(); | |
- } | |
- | |
return false; | |
} | |
/** | |
@@ -411,49 +184,36 @@ | |
* @param {Element} toElement - The target of the overlay. | |
* @private | |
*/ | |
- | |
function overlayAttributes(fromElement, toElement) { | |
- var explicitlyAllowed = toElement.hasAttribute("data-l10n-attrs") | |
+ const explicitlyAllowed = toElement.hasAttribute("data-l10n-attrs") | |
? toElement | |
.getAttribute("data-l10n-attrs") | |
.split(",") | |
.map((i) => i.trim()) | |
- : null; // Remove existing localizable attributes if they | |
+ : null; | |
+ // Remove existing localizable attributes if they | |
// will not be used in the new translation. | |
- | |
- for ( | |
- var _i = 0, _Array$from = Array.from(toElement.attributes); | |
- _i < _Array$from.length; | |
- _i++ | |
- ) { | |
- var attr = _Array$from[_i]; | |
- | |
+ for (const attr of Array.from(toElement.attributes)) { | |
if ( | |
isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed) && | |
!hasAttribute(fromElement.attributes, attr.name) | |
) { | |
toElement.removeAttribute(attr.name); | |
} | |
- } // fromElement might be a {value, attributes} object as returned by | |
+ } | |
+ // fromElement might be a {value, attributes} object as returned by | |
// Localization.messageFromBundle. In which case attributes may be null to | |
// save GC cycles. | |
- | |
if (!fromElement.attributes) { | |
return; | |
- } // Set localizable attributes. | |
- | |
- for ( | |
- var _i2 = 0, _Array$from2 = Array.from(fromElement.attributes); | |
- _i2 < _Array$from2.length; | |
- _i2++ | |
- ) { | |
- var _attr = _Array$from2[_i2]; | |
- | |
+ } | |
+ // Set localizable attributes. | |
+ for (const attr of Array.from(fromElement.attributes)) { | |
if ( | |
- isAttrNameLocalizable(_attr.name, toElement, explicitlyAllowed) && | |
- toElement.getAttribute(_attr.name) !== _attr.value | |
+ isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed) && | |
+ toElement.getAttribute(attr.name) !== attr.value | |
) { | |
- toElement.setAttribute(_attr.name, _attr.value); | |
+ toElement.setAttribute(attr.name, attr.value); | |
} | |
} | |
} | |
@@ -469,45 +229,36 @@ | |
* @returns {Element} | |
* @private | |
*/ | |
- | |
function getNodeForNamedElement(sourceElement, translatedChild) { | |
- var childName = translatedChild.getAttribute("data-l10n-name"); | |
- var sourceChild = sourceElement.querySelector( | |
- '[data-l10n-name="'.concat(childName, '"]') | |
+ const childName = translatedChild.getAttribute("data-l10n-name"); | |
+ const sourceChild = sourceElement.querySelector( | |
+ `[data-l10n-name="${childName}"]` | |
); | |
- | |
if (!sourceChild) { | |
console.warn( | |
- 'An element named "'.concat(childName, "\" wasn't found in the source.") | |
+ `An element named "${childName}" wasn't found in the source.` | |
); | |
return createTextNodeFromTextContent(translatedChild); | |
} | |
- | |
if (sourceChild.localName !== translatedChild.localName) { | |
console.warn( | |
- 'An element named "'.concat( | |
- childName, | |
- '" was found in the translation ' | |
- ) + | |
- "but its type ".concat( | |
- translatedChild.localName, | |
- " didn't match the " | |
- ) + | |
- "element found in the source (".concat(sourceChild.localName, ").") | |
+ `An element named "${childName}" was found in the translation ` + | |
+ `but its type ${translatedChild.localName} didn't match the ` + | |
+ `element found in the source (${sourceChild.localName}).` | |
); | |
return createTextNodeFromTextContent(translatedChild); | |
- } // Remove it from sourceElement so that the translation cannot use | |
+ } | |
+ // Remove it from sourceElement so that the translation cannot use | |
// the same reference name again. | |
- | |
- sourceElement.removeChild(sourceChild); // We can't currently guarantee that a translation won't remove | |
+ sourceElement.removeChild(sourceChild); | |
+ // We can't currently guarantee that a translation won't remove | |
// sourceChild from the element completely, which could break the app if | |
// it relies on an event handler attached to the sourceChild. Let's make | |
// this limitation explicit for now by breaking the identitiy of the | |
// sourceChild by cloning it. This will destroy all event handlers | |
// attached to sourceChild via addEventListener and via on<name> | |
// properties. | |
- | |
- var clone = sourceChild.cloneNode(false); | |
+ const clone = sourceChild.cloneNode(false); | |
return shallowPopulateUsing(translatedChild, clone); | |
} | |
/** | |
@@ -520,11 +271,10 @@ | |
* @returns {Element} | |
* @private | |
*/ | |
- | |
function createSanitizedElement(element) { | |
// Start with an empty element of the same type to remove nested children | |
// and non-localizable attributes defined by the translation. | |
- var clone = element.ownerDocument.createElement(element.localName); | |
+ const clone = element.ownerDocument.createElement(element.localName); | |
return shallowPopulateUsing(element, clone); | |
} | |
/** | |
@@ -534,7 +284,6 @@ | |
* @returns {Node} | |
* @private | |
*/ | |
- | |
function createTextNodeFromTextContent(element) { | |
return element.ownerDocument.createTextNode(element.textContent); | |
} | |
@@ -548,9 +297,8 @@ | |
* @returns {boolean} | |
* @private | |
*/ | |
- | |
function isElementAllowed(element) { | |
- var allowed = TEXT_LEVEL_ELEMENTS[element.namespaceURI]; | |
+ const allowed = TEXT_LEVEL_ELEMENTS[element.namespaceURI]; | |
return allowed && allowed.includes(element.localName); | |
} | |
/** | |
@@ -569,48 +317,39 @@ | |
* @returns {boolean} | |
* @private | |
*/ | |
- | |
- function isAttrNameLocalizable(name, element) { | |
- var explicitlyAllowed = | |
- arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; | |
- | |
+ function isAttrNameLocalizable(name, element, explicitlyAllowed = null) { | |
if (explicitlyAllowed && explicitlyAllowed.includes(name)) { | |
return true; | |
} | |
- | |
- var allowed = LOCALIZABLE_ATTRIBUTES[element.namespaceURI]; | |
- | |
+ const allowed = LOCALIZABLE_ATTRIBUTES[element.namespaceURI]; | |
if (!allowed) { | |
return false; | |
} | |
- | |
- var attrName = name.toLowerCase(); | |
- var elemName = element.localName; // Is it a globally safe attribute? | |
- | |
+ const attrName = name.toLowerCase(); | |
+ const elemName = element.localName; | |
+ // Is it a globally safe attribute? | |
if (allowed.global.includes(attrName)) { | |
return true; | |
- } // Are there no allowed attributes for this element? | |
- | |
+ } | |
+ // Are there no allowed attributes for this element? | |
if (!allowed[elemName]) { | |
return false; | |
- } // Is it allowed on this element? | |
- | |
+ } | |
+ // Is it allowed on this element? | |
if (allowed[elemName].includes(attrName)) { | |
return true; | |
- } // Special case for value on HTML inputs with type button, reset, submit | |
- | |
+ } | |
+ // Special case for value on HTML inputs with type button, reset, submit | |
if ( | |
element.namespaceURI === "http://www.w3.org/1999/xhtml" && | |
elemName === "input" && | |
attrName === "value" | |
) { | |
- var type = element.type.toLowerCase(); | |
- | |
+ const type = element.type.toLowerCase(); | |
if (type === "submit" || type === "button" || type === "reset") { | |
return true; | |
} | |
} | |
- | |
return false; | |
} | |
/** | |
@@ -621,20 +360,19 @@ | |
* @returns {Element} | |
* @private | |
*/ | |
- | |
function shallowPopulateUsing(fromElement, toElement) { | |
toElement.textContent = fromElement.textContent; | |
overlayAttributes(fromElement, toElement); | |
return toElement; | |
} | |
+ /* eslint no-console: ["error", { allow: ["warn", "error"] }] */ | |
/** | |
* The `Localization` class is a central high-level API for vanilla | |
* JavaScript use of Fluent. | |
* It combines language negotiation, FluentBundle and I/O to | |
* provide a scriptable API to format translations. | |
*/ | |
- | |
class Localization { | |
/** | |
* @param {Array<String>} resourceIds - List of resource IDs | |
@@ -643,25 +381,16 @@ | |
* | |
* @returns {Localization} | |
*/ | |
- constructor() { | |
- var resourceIds = | |
- arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; | |
- var generateBundles = arguments.length > 1 ? arguments[1] : undefined; | |
+ constructor(resourceIds = [], generateBundles) { | |
this.resourceIds = resourceIds; | |
this.generateBundles = generateBundles; | |
this.onChange(true); | |
} | |
- | |
- addResourceIds(resourceIds) { | |
- var eager = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : false; | |
+ addResourceIds(resourceIds, eager = false) { | |
this.resourceIds.push(...resourceIds); | |
this.onChange(eager); | |
return this.resourceIds.length; | |
} | |
- | |
removeResourceIds(resourceIds) { | |
this.resourceIds = this.resourceIds.filter( | |
(r) => !resourceIds.includes(r) | |
@@ -681,71 +410,28 @@ | |
* @returns {Promise<Array<string|Object>>} | |
* @private | |
*/ | |
- | |
- formatWithFallback(keys, method) { | |
- var _this = this; | |
- | |
- return _asyncToGenerator(function* () { | |
- var translations = []; | |
- var hasAtLeastOneBundle = false; | |
- var _iteratorNormalCompletion = true; | |
- var _didIteratorError = false; | |
- | |
- var _iteratorError; | |
- | |
- try { | |
- for ( | |
- var _iterator = _asyncIterator(_this.bundles), _step, _value; | |
- (_step = yield _iterator.next()), | |
- (_iteratorNormalCompletion = _step.done), | |
- (_value = yield _step.value), | |
- !_iteratorNormalCompletion; | |
- _iteratorNormalCompletion = true | |
- ) { | |
- var bundle = _value; | |
+ async formatWithFallback(keys, method) { | |
+ const translations = []; | |
+ let hasAtLeastOneBundle = false; | |
+ for await (const bundle of this.bundles) { | |
hasAtLeastOneBundle = true; | |
- var missingIds = keysFromBundle(method, bundle, keys, translations); | |
- | |
+ const missingIds = keysFromBundle(method, bundle, keys, translations); | |
if (missingIds.size === 0) { | |
break; | |
} | |
- | |
if (typeof console !== "undefined") { | |
- var locale = bundle.locales[0]; | |
- var ids = Array.from(missingIds).join(", "); | |
- console.warn( | |
- "[fluent] Missing translations in " | |
- .concat(locale, ": ") | |
- .concat(ids) | |
- ); | |
- } | |
- } | |
- } catch (err) { | |
- _didIteratorError = true; | |
- _iteratorError = err; | |
- } finally { | |
- try { | |
- if (!_iteratorNormalCompletion && _iterator.return != null) { | |
- yield _iterator.return(); | |
- } | |
- } finally { | |
- if (_didIteratorError) { | |
- throw _iteratorError; | |
+ const locale = bundle.locales[0]; | |
+ const ids = Array.from(missingIds).join(", "); | |
+ console.warn(`[fluent] Missing translations in ${locale}: ${ids}`); | |
} | |
} | |
- } | |
- | |
if (!hasAtLeastOneBundle && typeof console !== "undefined") { | |
// eslint-disable-next-line max-len | |
- console.warn( | |
- "[fluent] Request for keys failed because no resource bundles got generated.\n keys: " | |
- .concat(JSON.stringify(keys), ".\n resourceIds: ") | |
- .concat(JSON.stringify(_this.resourceIds), ".") | |
- ); | |
+ console.warn(`[fluent] Request for keys failed because no resource bundles got generated. | |
+ keys: ${JSON.stringify(keys)}. | |
+ resourceIds: ${JSON.stringify(this.resourceIds)}.`); | |
} | |
- | |
return translations; | |
- })(); | |
} | |
/** | |
* Format translations into {value, attributes} objects. | |
@@ -773,7 +459,6 @@ | |
* @returns {Promise<Array<{value: string, attributes: Object}>>} | |
* @private | |
*/ | |
- | |
formatMessages(keys) { | |
return this.formatWithFallback(keys, messageFromBundle); | |
} | |
@@ -796,7 +481,6 @@ | |
* @param {Array<Object>} keys | |
* @returns {Promise<Array<string>>} | |
*/ | |
- | |
formatValues(keys) { | |
return this.formatWithFallback(keys, valueFromBundle); | |
} | |
@@ -822,24 +506,10 @@ | |
* @param {Object} [args] - Optional external arguments | |
* @returns {Promise<string>} | |
*/ | |
- | |
- formatValue(id, args) { | |
- var _this2 = this; | |
- | |
- return _asyncToGenerator(function* () { | |
- var _yield$_this2$formatV = yield _this2.formatValues([ | |
- { | |
- id, | |
- args, | |
- }, | |
- ]), | |
- _yield$_this2$formatV2 = _slicedToArray(_yield$_this2$formatV, 1), | |
- val = _yield$_this2$formatV2[0]; | |
- | |
+ async formatValue(id, args) { | |
+ const [val] = await this.formatValues([{ id, args }]); | |
return val; | |
- })(); | |
} | |
- | |
handleEvent() { | |
this.onChange(); | |
} | |
@@ -847,16 +517,10 @@ | |
* This method should be called when there's a reason to believe | |
* that language negotiation or available resources changed. | |
*/ | |
- | |
- onChange() { | |
- var eager = | |
- arguments.length > 0 && arguments[0] !== undefined | |
- ? arguments[0] | |
- : false; | |
+ onChange(eager = false) { | |
this.bundles = cachedIterable.CachedAsyncIterable.from( | |
this.generateBundles(this.resourceIds) | |
); | |
- | |
if (eager) { | |
this.bundles.touchNext(2); | |
} | |
@@ -877,12 +541,10 @@ | |
* @returns {string|null} | |
* @private | |
*/ | |
- | |
function valueFromBundle(bundle, errors, message, args) { | |
if (message.value) { | |
return bundle.formatPattern(message.value, args, errors); | |
} | |
- | |
return null; | |
} | |
/** | |
@@ -901,48 +563,26 @@ | |
* @returns {Object} | |
* @private | |
*/ | |
- | |
function messageFromBundle(bundle, errors, message, args) { | |
- var formatted = { | |
+ const formatted = { | |
value: null, | |
attributes: null, | |
}; | |
- | |
if (message.value) { | |
formatted.value = bundle.formatPattern(message.value, args, errors); | |
} | |
- | |
- var attrNames = Object.keys(message.attributes); | |
- | |
+ let attrNames = Object.keys(message.attributes); | |
if (attrNames.length > 0) { | |
formatted.attributes = new Array(attrNames.length); | |
- | |
- var _iterator2 = _createForOfIteratorHelper(attrNames.entries()), | |
- _step2; | |
- | |
- try { | |
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done; ) { | |
- var _step2$value = _slicedToArray(_step2.value, 2), | |
- i = _step2$value[0], | |
- name = _step2$value[1]; | |
- | |
- var value = bundle.formatPattern( | |
+ for (let [i, name] of attrNames.entries()) { | |
+ let value = bundle.formatPattern( | |
message.attributes[name], | |
args, | |
errors | |
); | |
- formatted.attributes[i] = { | |
- name, | |
- value, | |
- }; | |
- } | |
- } catch (err) { | |
- _iterator2.e(err); | |
- } finally { | |
- _iterator2.f(); | |
+ formatted.attributes[i] = { name, value }; | |
} | |
} | |
- | |
return formatted; | |
} | |
/** | |
@@ -977,33 +617,23 @@ | |
* @returns {Set<string>} | |
* @private | |
*/ | |
- | |
function keysFromBundle(method, bundle, keys, translations) { | |
- var messageErrors = []; | |
- var missingIds = new Set(); | |
- keys.forEach((_ref, i) => { | |
- var id = _ref.id, | |
- args = _ref.args; | |
- | |
+ const messageErrors = []; | |
+ const missingIds = new Set(); | |
+ keys.forEach(({ id, args }, i) => { | |
if (translations[i] !== undefined) { | |
return; | |
} | |
- | |
- var message = bundle.getMessage(id); | |
- | |
+ let message = bundle.getMessage(id); | |
if (message) { | |
messageErrors.length = 0; | |
translations[i] = method(bundle, messageErrors, message, args); | |
- | |
if (messageErrors.length > 0 && typeof console !== "undefined") { | |
- var locale = bundle.locales[0]; | |
- var errors = messageErrors.join(", "); // eslint-disable-next-line max-len | |
- | |
+ const locale = bundle.locales[0]; | |
+ const errors = messageErrors.join(", "); | |
+ // eslint-disable-next-line max-len | |
console.warn( | |
- "[fluent][resolver] errors in " | |
- .concat(locale, "/") | |
- .concat(id, ": ") | |
- .concat(errors, ".") | |
+ `[fluent][resolver] errors in ${locale}/${id}: ${errors}.` | |
); | |
} | |
} else { | |
@@ -1013,9 +643,9 @@ | |
return missingIds; | |
} | |
- var L10NID_ATTR_NAME = "data-l10n-id"; | |
- var L10NARGS_ATTR_NAME = "data-l10n-args"; | |
- var L10N_ELEMENT_QUERY = "[".concat(L10NID_ATTR_NAME, "]"); | |
+ const L10NID_ATTR_NAME = "data-l10n-id"; | |
+ const L10NARGS_ATTR_NAME = "data-l10n-args"; | |
+ const L10N_ELEMENT_QUERY = `[${L10NID_ATTR_NAME}]`; | |
/** | |
* The `DOMLocalization` class is responsible for fetching resources and | |
* formatting translations. | |
@@ -1024,7 +654,6 @@ | |
* formatting of translations and methods for observing DOM | |
* trees with a `MutationObserver`. | |
*/ | |
- | |
class DOMLocalization extends Localization { | |
/** | |
* @param {Array<String>} resourceIds - List of resource IDs | |
@@ -1033,12 +662,12 @@ | |
* @returns {DOMLocalization} | |
*/ | |
constructor(resourceIds, generateBundles) { | |
- super(resourceIds, generateBundles); // A Set of DOM trees observed by the `MutationObserver`. | |
- | |
- this.roots = new Set(); // requestAnimationFrame handler. | |
- | |
- this.pendingrAF = null; // list of elements pending for translation. | |
- | |
+ super(resourceIds, generateBundles); | |
+ // A Set of DOM trees observed by the `MutationObserver`. | |
+ this.roots = new Set(); | |
+ // requestAnimationFrame handler. | |
+ this.pendingrAF = null; | |
+ // list of elements pending for translation. | |
this.pendingElements = new Set(); | |
this.windowElement = null; | |
this.mutationObserver = null; | |
@@ -1050,14 +679,8 @@ | |
attributeFilter: [L10NID_ATTR_NAME, L10NARGS_ATTR_NAME], | |
}; | |
} | |
- | |
- onChange() { | |
- var eager = | |
- arguments.length > 0 && arguments[0] !== undefined | |
- ? arguments[0] | |
- : false; | |
+ onChange(eager = false) { | |
super.onChange(eager); | |
- | |
if (this.roots) { | |
this.translateRoots(); | |
} | |
@@ -1097,16 +720,13 @@ | |
* @param {Object<string, string>} args - KVP list of l10n arguments | |
* @returns {Element} | |
*/ | |
- | |
setAttributes(element, id, args) { | |
element.setAttribute(L10NID_ATTR_NAME, id); | |
- | |
if (args) { | |
element.setAttribute(L10NARGS_ATTR_NAME, JSON.stringify(args)); | |
} else { | |
element.removeAttribute(L10NARGS_ATTR_NAME); | |
} | |
- | |
return element; | |
} | |
/** | |
@@ -1122,7 +742,6 @@ | |
* @param {Element} element - HTML element | |
* @returns {{id: string, args: Object}} | |
*/ | |
- | |
getAttributes(element) { | |
return { | |
id: element.getAttribute(L10NID_ATTR_NAME), | |
@@ -1137,15 +756,8 @@ | |
* | |
* @param {Element} newRoot - Root to observe. | |
*/ | |
- | |
connectRoot(newRoot) { | |
- var _iterator = _createForOfIteratorHelper(this.roots), | |
- _step; | |
- | |
- try { | |
- for (_iterator.s(); !(_step = _iterator.n()).done; ) { | |
- var root = _step.value; | |
- | |
+ for (const root of this.roots) { | |
if ( | |
root === newRoot || | |
root.contains(newRoot) || | |
@@ -1156,17 +768,10 @@ | |
); | |
} | |
} | |
- } catch (err) { | |
- _iterator.e(err); | |
- } finally { | |
- _iterator.f(); | |
- } | |
- | |
if (this.windowElement) { | |
if (this.windowElement !== newRoot.ownerDocument.defaultView) { | |
- throw new Error( | |
- "Cannot connect a root:\n DOMLocalization already has a root from a different window." | |
- ); | |
+ throw new Error(`Cannot connect a root: | |
+ DOMLocalization already has a root from a different window.`); | |
} | |
} else { | |
this.windowElement = newRoot.ownerDocument.defaultView; | |
@@ -1174,7 +779,6 @@ | |
(mutations) => this.translateMutations(mutations) | |
); | |
} | |
- | |
this.roots.add(newRoot); | |
this.mutationObserver.observe(newRoot, this.observerConfig); | |
} | |
@@ -1190,20 +794,18 @@ | |
* @param {Element} root - Root to disconnect. | |
* @returns {boolean} | |
*/ | |
- | |
disconnectRoot(root) { | |
- this.roots.delete(root); // Pause the mutation observer to stop observing `root`. | |
- | |
+ this.roots.delete(root); | |
+ // Pause the mutation observer to stop observing `root`. | |
this.pauseObserving(); | |
- | |
if (this.roots.size === 0) { | |
this.mutationObserver = null; | |
this.windowElement = null; | |
this.pendingrAF = null; | |
this.pendingElements.clear(); | |
return true; | |
- } // Resume observing all other roots. | |
- | |
+ } | |
+ // Resume observing all other roots. | |
this.resumeObserving(); | |
return false; | |
} | |
@@ -1212,9 +814,8 @@ | |
* | |
* @returns {Promise} | |
*/ | |
- | |
translateRoots() { | |
- var roots = Array.from(this.roots); | |
+ const roots = Array.from(this.roots); | |
return Promise.all(roots.map((root) => this.translateFragment(root))); | |
} | |
/** | |
@@ -1222,12 +823,10 @@ | |
* | |
* @private | |
*/ | |
- | |
pauseObserving() { | |
if (!this.mutationObserver) { | |
return; | |
} | |
- | |
this.translateMutations(this.mutationObserver.takeRecords()); | |
this.mutationObserver.disconnect(); | |
} | |
@@ -1236,98 +835,44 @@ | |
* | |
* @private | |
*/ | |
- | |
resumeObserving() { | |
if (!this.mutationObserver) { | |
return; | |
} | |
- | |
- var _iterator2 = _createForOfIteratorHelper(this.roots), | |
- _step2; | |
- | |
- try { | |
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done; ) { | |
- var root = _step2.value; | |
+ for (const root of this.roots) { | |
this.mutationObserver.observe(root, this.observerConfig); | |
} | |
- } catch (err) { | |
- _iterator2.e(err); | |
- } finally { | |
- _iterator2.f(); | |
- } | |
} | |
/** | |
* Translate mutations detected by the `MutationObserver`. | |
* | |
* @private | |
*/ | |
- | |
translateMutations(mutations) { | |
- var _iterator3 = _createForOfIteratorHelper(mutations), | |
- _step3; | |
- | |
- try { | |
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done; ) { | |
- var mutation = _step3.value; | |
- | |
+ for (const mutation of mutations) { | |
switch (mutation.type) { | |
case "attributes": | |
if (mutation.target.hasAttribute("data-l10n-id")) { | |
this.pendingElements.add(mutation.target); | |
} | |
- | |
break; | |
- | |
case "childList": | |
- var _iterator4 = _createForOfIteratorHelper(mutation.addedNodes), | |
- _step4; | |
- | |
- try { | |
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done; ) { | |
- var addedNode = _step4.value; | |
- | |
+ for (const addedNode of mutation.addedNodes) { | |
if (addedNode.nodeType === addedNode.ELEMENT_NODE) { | |
if (addedNode.childElementCount) { | |
- var _iterator5 = _createForOfIteratorHelper( | |
- this.getTranslatables(addedNode) | |
- ), | |
- _step5; | |
- | |
- try { | |
- for ( | |
- _iterator5.s(); | |
- !(_step5 = _iterator5.n()).done; | |
- | |
- ) { | |
- var element = _step5.value; | |
+ for (const element of this.getTranslatables(addedNode)) { | |
this.pendingElements.add(element); | |
} | |
- } catch (err) { | |
- _iterator5.e(err); | |
- } finally { | |
- _iterator5.f(); | |
- } | |
} else if (addedNode.hasAttribute(L10NID_ATTR_NAME)) { | |
this.pendingElements.add(addedNode); | |
} | |
} | |
} | |
- } catch (err) { | |
- _iterator4.e(err); | |
- } finally { | |
- _iterator4.f(); | |
- } | |
- | |
break; | |
} | |
- } // This fragment allows us to coalesce all pending translations | |
- // into a single requestAnimationFrame. | |
- } catch (err) { | |
- _iterator3.e(err); | |
- } finally { | |
- _iterator3.f(); | |
} | |
- | |
+ // This fragment allows us to coalesce all pending translations | |
+ // into a single requestAnimationFrame. | |
if (this.pendingElements.size > 0) { | |
if (this.pendingrAF === null) { | |
this.pendingrAF = this.windowElement.requestAnimationFrame(() => { | |
@@ -1351,7 +896,6 @@ | |
* @param {DOMFragment} frag - Element or DocumentFragment to be translated | |
* @returns {Promise} | |
*/ | |
- | |
translateFragment(frag) { | |
return this.translateElements(this.getTranslatables(frag)); | |
} | |
@@ -1368,19 +912,13 @@ | |
* @param {Array<Element>} elements - List of elements to be translated | |
* @returns {Promise} | |
*/ | |
- | |
- translateElements(elements) { | |
- var _this = this; | |
- | |
- return _asyncToGenerator(function* () { | |
+ async translateElements(elements) { | |
if (!elements.length) { | |
return undefined; | |
} | |
- | |
- var keys = elements.map(_this.getKeysForElement); | |
- var translations = yield _this.formatMessages(keys); | |
- return _this.applyTranslations(elements, translations); | |
- })(); | |
+ const keys = elements.map(this.getKeysForElement); | |
+ const translations = await this.formatMessages(keys); | |
+ return this.applyTranslations(elements, translations); | |
} | |
/** | |
* Applies translations onto elements. | |
@@ -1389,16 +927,13 @@ | |
* @param {Array<Object>} translations | |
* @private | |
*/ | |
- | |
applyTranslations(elements, translations) { | |
this.pauseObserving(); | |
- | |
- for (var i = 0; i < elements.length; i++) { | |
+ for (let i = 0; i < elements.length; i++) { | |
if (translations[i] !== undefined) { | |
translateElement(elements[i], translations[i]); | |
} | |
} | |
- | |
this.resumeObserving(); | |
} | |
/** | |
@@ -1408,17 +943,14 @@ | |
* @returns {Array<Element>} | |
* @private | |
*/ | |
- | |
getTranslatables(element) { | |
- var nodes = Array.from(element.querySelectorAll(L10N_ELEMENT_QUERY)); | |
- | |
+ const nodes = Array.from(element.querySelectorAll(L10N_ELEMENT_QUERY)); | |
if ( | |
typeof element.hasAttribute === "function" && | |
element.hasAttribute(L10NID_ATTR_NAME) | |
) { | |
nodes.push(element); | |
} | |
- | |
return nodes; | |
} | |
/** | |
@@ -1429,7 +961,6 @@ | |
* @returns {Object} | |
* @private | |
*/ | |
- | |
getKeysForElement(element) { | |
return { | |
id: element.getAttribute(L10NID_ATTR_NAME), | |
diff -wru compat/fluent-langneg.js es2018/fluent-langneg.js | |
--- compat/fluent-langneg.js 2020-04-21 14:45:54.900896700 +0200 | |
+++ es2018/fluent-langneg.js 2020-04-21 14:46:02.627792200 +0200 | |
@@ -8,133 +8,6 @@ | |
})(this, function (exports) { | |
"use strict"; | |
- function _slicedToArray(arr, i) { | |
- return ( | |
- _arrayWithHoles(arr) || | |
- _iterableToArrayLimit(arr, i) || | |
- _unsupportedIterableToArray(arr, i) || | |
- _nonIterableRest() | |
- ); | |
- } | |
- | |
- function _arrayWithHoles(arr) { | |
- if (Array.isArray(arr)) return arr; | |
- } | |
- | |
- function _iterableToArrayLimit(arr, i) { | |
- if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) | |
- return; | |
- var _arr = []; | |
- var _n = true; | |
- var _d = false; | |
- var _e = undefined; | |
- | |
- try { | |
- for ( | |
- var _i = arr[Symbol.iterator](), _s; | |
- !(_n = (_s = _i.next()).done); | |
- _n = true | |
- ) { | |
- _arr.push(_s.value); | |
- | |
- if (i && _arr.length === i) break; | |
- } | |
- } catch (err) { | |
- _d = true; | |
- _e = err; | |
- } finally { | |
- try { | |
- if (!_n && _i["return"] != null) _i["return"](); | |
- } finally { | |
- if (_d) throw _e; | |
- } | |
- } | |
- | |
- return _arr; | |
- } | |
- | |
- function _unsupportedIterableToArray(o, minLen) { | |
- if (!o) return; | |
- if (typeof o === "string") return _arrayLikeToArray(o, minLen); | |
- var n = Object.prototype.toString.call(o).slice(8, -1); | |
- if (n === "Object" && o.constructor) n = o.constructor.name; | |
- if (n === "Map" || n === "Set") return Array.from(n); | |
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) | |
- return _arrayLikeToArray(o, minLen); | |
- } | |
- | |
- function _arrayLikeToArray(arr, len) { | |
- if (len == null || len > arr.length) len = arr.length; | |
- | |
- for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | |
- | |
- return arr2; | |
- } | |
- | |
- function _nonIterableRest() { | |
- throw new TypeError( | |
- "Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
- function _createForOfIteratorHelper(o) { | |
- if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { | |
- if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { | |
- var i = 0; | |
- | |
- var F = function () {}; | |
- | |
- return { | |
- s: F, | |
- n: function () { | |
- if (i >= o.length) | |
- return { | |
- done: true, | |
- }; | |
- return { | |
- done: false, | |
- value: o[i++], | |
- }; | |
- }, | |
- e: function (e) { | |
- throw e; | |
- }, | |
- f: F, | |
- }; | |
- } | |
- | |
- throw new TypeError( | |
- "Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
- var it, | |
- normalCompletion = true, | |
- didErr = false, | |
- err; | |
- return { | |
- s: function () { | |
- it = o[Symbol.iterator](); | |
- }, | |
- n: function () { | |
- var step = it.next(); | |
- normalCompletion = step.done; | |
- return step; | |
- }, | |
- e: function (e) { | |
- didErr = true; | |
- err = e; | |
- }, | |
- f: function () { | |
- try { | |
- if (!normalCompletion && it.return != null) it.return(); | |
- } finally { | |
- if (didErr) throw err; | |
- } | |
- }, | |
- }; | |
- } | |
- | |
/** | |
* Below is a manually a list of likely subtags corresponding to Unicode | |
* CLDR likelySubtags list. | |
@@ -144,8 +17,7 @@ | |
* | |
* This version of the list is based on CLDR 30.0.3. | |
*/ | |
- | |
- var likelySubtagsMin = { | |
+ const likelySubtagsMin = { | |
ar: "ar-arab-eg", | |
"az-arab": "az-arab-ir", | |
"az-ir": "az-arab-ir", | |
@@ -168,7 +40,7 @@ | |
"zh-gb": "zh-hant-gb", | |
"zh-us": "zh-hant-us", | |
}; | |
- var regionMatchingLangs = [ | |
+ const regionMatchingLangs = [ | |
"az", | |
"bg", | |
"cs", | |
@@ -189,21 +61,19 @@ | |
if (Object.prototype.hasOwnProperty.call(likelySubtagsMin, loc)) { | |
return new Locale(likelySubtagsMin[loc]); | |
} | |
- | |
- var locale = new Locale(loc); | |
- | |
+ const locale = new Locale(loc); | |
if (locale.language && regionMatchingLangs.includes(locale.language)) { | |
locale.region = locale.language.toUpperCase(); | |
return locale; | |
} | |
- | |
return null; | |
} | |
- var languageCodeRe = "([a-z]{2,3}|\\*)"; | |
- var scriptCodeRe = "(?:-([a-z]{4}|\\*))"; | |
- var regionCodeRe = "(?:-([a-z]{2}|\\*))"; | |
- var variantCodeRe = "(?:-(([0-9][a-z0-9]{3}|[a-z0-9]{5,8})|\\*))"; | |
+ /* eslint no-magic-numbers: 0 */ | |
+ const languageCodeRe = "([a-z]{2,3}|\\*)"; | |
+ const scriptCodeRe = "(?:-([a-z]{4}|\\*))"; | |
+ const regionCodeRe = "(?:-([a-z]{2}|\\*))"; | |
+ const variantCodeRe = "(?:-(([0-9][a-z0-9]{3}|[a-z0-9]{5,8})|\\*))"; | |
/** | |
* Regular expression splitting locale id into four pieces: | |
* | |
@@ -216,13 +86,8 @@ | |
* | |
* It can also accept a range `*` character on any position. | |
*/ | |
- | |
- var localeRe = new RegExp( | |
- "^" | |
- .concat(languageCodeRe) | |
- .concat(scriptCodeRe, "?") | |
- .concat(regionCodeRe, "?") | |
- .concat(variantCodeRe, "?$"), | |
+ const localeRe = new RegExp( | |
+ `^${languageCodeRe}${scriptCodeRe}?${regionCodeRe}?${variantCodeRe}?$`, | |
"i" | |
); | |
class Locale { | |
@@ -236,35 +101,24 @@ | |
* properly parsed as `en-*-US-*`. | |
*/ | |
constructor(locale) { | |
- var result = localeRe.exec(locale.replace(/_/g, "-")); | |
- | |
+ const result = localeRe.exec(locale.replace(/_/g, "-")); | |
if (!result) { | |
this.isWellFormed = false; | |
return; | |
} | |
- | |
- var _result = _slicedToArray(result, 5), | |
- language = _result[1], | |
- script = _result[2], | |
- region = _result[3], | |
- variant = _result[4]; | |
- | |
+ let [, language, script, region, variant] = result; | |
if (language) { | |
this.language = language.toLowerCase(); | |
} | |
- | |
if (script) { | |
this.script = script[0].toUpperCase() + script.slice(1); | |
} | |
- | |
if (region) { | |
this.region = region.toUpperCase(); | |
} | |
- | |
this.variant = variant; | |
this.isWellFormed = true; | |
} | |
- | |
isEqual(other) { | |
return ( | |
this.language === other.language && | |
@@ -273,16 +127,7 @@ | |
this.variant === other.variant | |
); | |
} | |
- | |
- matches(other) { | |
- var thisRange = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : false; | |
- var otherRange = | |
- arguments.length > 2 && arguments[2] !== undefined | |
- ? arguments[2] | |
- : false; | |
+ matches(other, thisRange = false, otherRange = false) { | |
return ( | |
(this.language === other.language || | |
(thisRange && this.language === undefined) || | |
@@ -298,24 +143,19 @@ | |
(otherRange && other.variant === undefined)) | |
); | |
} | |
- | |
toString() { | |
return [this.language, this.script, this.region, this.variant] | |
.filter((part) => part !== undefined) | |
.join("-"); | |
} | |
- | |
clearVariants() { | |
this.variant = undefined; | |
} | |
- | |
clearRegion() { | |
this.region = undefined; | |
} | |
- | |
addLikelySubtags() { | |
- var newLocale = getLikelySubtagsMin(this.toString().toLowerCase()); | |
- | |
+ const newLocale = getLikelySubtagsMin(this.toString().toLowerCase()); | |
if (newLocale) { | |
this.language = newLocale.language; | |
this.script = newLocale.script; | |
@@ -323,11 +163,11 @@ | |
this.variant = newLocale.variant; | |
return true; | |
} | |
- | |
return false; | |
} | |
} | |
+ /* eslint no-magic-numbers: 0 */ | |
/** | |
* Negotiates the languages between the list of requested locales against | |
* a list of available locales. | |
@@ -398,54 +238,27 @@ | |
* ignoring script ranges. That means that `sr-Cyrl` will never match | |
* against `sr-Latn`. | |
*/ | |
- | |
function filterMatches(requestedLocales, availableLocales, strategy) { | |
- var supportedLocales = new Set(); | |
- var availableLocalesMap = new Map(); | |
- | |
- var _iterator = _createForOfIteratorHelper(availableLocales), | |
- _step; | |
- | |
- try { | |
- for (_iterator.s(); !(_step = _iterator.n()).done; ) { | |
- var locale = _step.value; | |
- var newLocale = new Locale(locale); | |
- | |
+ const supportedLocales = new Set(); | |
+ const availableLocalesMap = new Map(); | |
+ for (let locale of availableLocales) { | |
+ let newLocale = new Locale(locale); | |
if (newLocale.isWellFormed) { | |
availableLocalesMap.set(locale, new Locale(locale)); | |
} | |
} | |
- } catch (err) { | |
- _iterator.e(err); | |
- } finally { | |
- _iterator.f(); | |
- } | |
- | |
- var _iterator2 = _createForOfIteratorHelper(requestedLocales), | |
- _step2; | |
- | |
- try { | |
- outer: for (_iterator2.s(); !(_step2 = _iterator2.n()).done; ) { | |
- var reqLocStr = _step2.value; | |
- var reqLocStrLC = reqLocStr.toLowerCase(); | |
- var requestedLocale = new Locale(reqLocStrLC); | |
- | |
+ outer: for (const reqLocStr of requestedLocales) { | |
+ const reqLocStrLC = reqLocStr.toLowerCase(); | |
+ const requestedLocale = new Locale(reqLocStrLC); | |
if (requestedLocale.language === undefined) { | |
continue; | |
- } // 1) Attempt to make an exact match | |
+ } | |
+ // 1) Attempt to make an exact match | |
// Example: `en-US` === `en-US` | |
- | |
- var _iterator3 = _createForOfIteratorHelper(availableLocalesMap.keys()), | |
- _step3; | |
- | |
- try { | |
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done; ) { | |
- var _key2 = _step3.value; | |
- | |
- if (reqLocStrLC === _key2.toLowerCase()) { | |
- supportedLocales.add(_key2); | |
- availableLocalesMap.delete(_key2); | |
- | |
+ for (const key of availableLocalesMap.keys()) { | |
+ if (reqLocStrLC === key.toLowerCase()) { | |
+ supportedLocales.add(key); | |
+ availableLocalesMap.delete(key); | |
if (strategy === "lookup") { | |
return Array.from(supportedLocales); | |
} else if (strategy === "filtering") { | |
@@ -454,30 +267,14 @@ | |
continue outer; | |
} | |
} | |
- } // 2) Attempt to match against the available range | |
+ } | |
+ // 2) Attempt to match against the available range | |
// This turns `en` into `en-*-*-*` and `en-US` into `en-*-US-*` | |
// Example: ['en-US'] * ['en'] = ['en'] | |
- } catch (err) { | |
- _iterator3.e(err); | |
- } finally { | |
- _iterator3.f(); | |
- } | |
- | |
- var _iterator4 = _createForOfIteratorHelper( | |
- availableLocalesMap.entries() | |
- ), | |
- _step4; | |
- | |
- try { | |
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done; ) { | |
- var _step4$value = _slicedToArray(_step4.value, 2), | |
- _key3 = _step4$value[0], | |
- _availableLocale2 = _step4$value[1]; | |
- | |
- if (_availableLocale2.matches(requestedLocale, true, false)) { | |
- supportedLocales.add(_key3); | |
- availableLocalesMap.delete(_key3); | |
- | |
+ for (const [key, availableLocale] of availableLocalesMap.entries()) { | |
+ if (availableLocale.matches(requestedLocale, true, false)) { | |
+ supportedLocales.add(key); | |
+ availableLocalesMap.delete(key); | |
if (strategy === "lookup") { | |
return Array.from(supportedLocales); | |
} else if (strategy === "filtering") { | |
@@ -486,32 +283,16 @@ | |
continue outer; | |
} | |
} | |
- } // 3) Attempt to retrieve a maximal version of the requested locale ID | |
+ } | |
+ // 3) Attempt to retrieve a maximal version of the requested locale ID | |
// If data is available, it'll expand `en` into `en-Latn-US` and | |
// `zh` into `zh-Hans-CN`. | |
// Example: ['en'] * ['en-GB', 'en-US'] = ['en-US'] | |
- } catch (err) { | |
- _iterator4.e(err); | |
- } finally { | |
- _iterator4.f(); | |
- } | |
- | |
if (requestedLocale.addLikelySubtags()) { | |
- var _iterator5 = _createForOfIteratorHelper( | |
- availableLocalesMap.entries() | |
- ), | |
- _step5; | |
- | |
- try { | |
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done; ) { | |
- var _step5$value = _slicedToArray(_step5.value, 2), | |
- key = _step5$value[0], | |
- availableLocale = _step5$value[1]; | |
- | |
+ for (const [key, availableLocale] of availableLocalesMap.entries()) { | |
if (availableLocale.matches(requestedLocale, true, false)) { | |
supportedLocales.add(key); | |
availableLocalesMap.delete(key); | |
- | |
if (strategy === "lookup") { | |
return Array.from(supportedLocales); | |
} else if (strategy === "filtering") { | |
@@ -521,31 +302,14 @@ | |
} | |
} | |
} | |
- } catch (err) { | |
- _iterator5.e(err); | |
- } finally { | |
- _iterator5.f(); | |
} | |
- } // 4) Attempt to look up for a different variant for the same locale ID | |
+ // 4) Attempt to look up for a different variant for the same locale ID | |
// Example: ['en-US-mac'] * ['en-US-win'] = ['en-US-win'] | |
- | |
requestedLocale.clearVariants(); | |
- | |
- var _iterator6 = _createForOfIteratorHelper( | |
- availableLocalesMap.entries() | |
- ), | |
- _step6; | |
- | |
- try { | |
- for (_iterator6.s(); !(_step6 = _iterator6.n()).done; ) { | |
- var _step6$value = _slicedToArray(_step6.value, 2), | |
- _key4 = _step6$value[0], | |
- _availableLocale3 = _step6$value[1]; | |
- | |
- if (_availableLocale3.matches(requestedLocale, true, true)) { | |
- supportedLocales.add(_key4); | |
- availableLocalesMap.delete(_key4); | |
- | |
+ for (const [key, availableLocale] of availableLocalesMap.entries()) { | |
+ if (availableLocale.matches(requestedLocale, true, true)) { | |
+ supportedLocales.add(key); | |
+ availableLocalesMap.delete(key); | |
if (strategy === "lookup") { | |
return Array.from(supportedLocales); | |
} else if (strategy === "filtering") { | |
@@ -554,36 +318,19 @@ | |
continue outer; | |
} | |
} | |
- } // 5) Attempt to match against the likely subtag without region | |
+ } | |
+ // 5) Attempt to match against the likely subtag without region | |
// In the example below, addLikelySubtags will turn | |
// `zh-Hant` into `zh-Hant-TW` giving `zh-TW` priority match | |
// over `zh-CN`. | |
// | |
// Example: ['zh-Hant-HK'] * ['zh-TW', 'zh-CN'] = ['zh-TW'] | |
- } catch (err) { | |
- _iterator6.e(err); | |
- } finally { | |
- _iterator6.f(); | |
- } | |
- | |
requestedLocale.clearRegion(); | |
- | |
if (requestedLocale.addLikelySubtags()) { | |
- var _iterator7 = _createForOfIteratorHelper( | |
- availableLocalesMap.entries() | |
- ), | |
- _step7; | |
- | |
- try { | |
- for (_iterator7.s(); !(_step7 = _iterator7.n()).done; ) { | |
- var _step7$value = _slicedToArray(_step7.value, 2), | |
- _key = _step7$value[0], | |
- _availableLocale = _step7$value[1]; | |
- | |
- if (_availableLocale.matches(requestedLocale, true, false)) { | |
- supportedLocales.add(_key); | |
- availableLocalesMap.delete(_key); | |
- | |
+ for (const [key, availableLocale] of availableLocalesMap.entries()) { | |
+ if (availableLocale.matches(requestedLocale, true, false)) { | |
+ supportedLocales.add(key); | |
+ availableLocalesMap.delete(key); | |
if (strategy === "lookup") { | |
return Array.from(supportedLocales); | |
} else if (strategy === "filtering") { | |
@@ -593,31 +340,14 @@ | |
} | |
} | |
} | |
- } catch (err) { | |
- _iterator7.e(err); | |
- } finally { | |
- _iterator7.f(); | |
} | |
- } // 6) Attempt to look up for a different region for the same locale ID | |
+ // 6) Attempt to look up for a different region for the same locale ID | |
// Example: ['en-US'] * ['en-AU'] = ['en-AU'] | |
- | |
requestedLocale.clearRegion(); | |
- | |
- var _iterator8 = _createForOfIteratorHelper( | |
- availableLocalesMap.entries() | |
- ), | |
- _step8; | |
- | |
- try { | |
- for (_iterator8.s(); !(_step8 = _iterator8.n()).done; ) { | |
- var _step8$value = _slicedToArray(_step8.value, 2), | |
- _key5 = _step8$value[0], | |
- _availableLocale4 = _step8$value[1]; | |
- | |
- if (_availableLocale4.matches(requestedLocale, true, true)) { | |
- supportedLocales.add(_key5); | |
- availableLocalesMap.delete(_key5); | |
- | |
+ for (const [key, availableLocale] of availableLocalesMap.entries()) { | |
+ if (availableLocale.matches(requestedLocale, true, true)) { | |
+ supportedLocales.add(key); | |
+ availableLocalesMap.delete(key); | |
if (strategy === "lookup") { | |
return Array.from(supportedLocales); | |
} else if (strategy === "filtering") { | |
@@ -627,18 +357,7 @@ | |
} | |
} | |
} | |
- } catch (err) { | |
- _iterator8.e(err); | |
- } finally { | |
- _iterator8.f(); | |
} | |
- } | |
- } catch (err) { | |
- _iterator2.e(err); | |
- } finally { | |
- _iterator2.f(); | |
- } | |
- | |
return Array.from(supportedLocales); | |
} | |
@@ -685,46 +404,36 @@ | |
* | |
* This strategy requires defaultLocale option to be set. | |
*/ | |
- | |
- function negotiateLanguages(requestedLocales, availableLocales) { | |
- var _ref = | |
- arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, | |
- _ref$strategy = _ref.strategy, | |
- strategy = _ref$strategy === void 0 ? "filtering" : _ref$strategy, | |
- defaultLocale = _ref.defaultLocale; | |
- | |
- var supportedLocales = filterMatches( | |
+ function negotiateLanguages( | |
+ requestedLocales, | |
+ availableLocales, | |
+ { strategy = "filtering", defaultLocale } = {} | |
+ ) { | |
+ const supportedLocales = filterMatches( | |
Array.from(Object(requestedLocales)).map(String), | |
Array.from(Object(availableLocales)).map(String), | |
strategy | |
); | |
- | |
if (strategy === "lookup") { | |
if (defaultLocale === undefined) { | |
throw new Error( | |
"defaultLocale cannot be undefined for strategy `lookup`" | |
); | |
} | |
- | |
if (supportedLocales.length === 0) { | |
supportedLocales.push(defaultLocale); | |
} | |
} else if (defaultLocale && !supportedLocales.includes(defaultLocale)) { | |
supportedLocales.push(defaultLocale); | |
} | |
- | |
return supportedLocales; | |
} | |
- function acceptedLanguages() { | |
- var str = | |
- arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ""; | |
- | |
+ function acceptedLanguages(str = "") { | |
if (typeof str !== "string") { | |
throw new TypeError("Argument must be a string"); | |
} | |
- | |
- var tokens = str.split(",").map((t) => t.trim()); | |
+ const tokens = str.split(",").map((t) => t.trim()); | |
return tokens.filter((t) => t !== "").map((t) => t.split(";")[0]); | |
} | |
diff -wru compat/fluent-react.js es2018/fluent-react.js | |
--- compat/fluent-react.js 2020-04-21 14:45:54.966892600 +0200 | |
+++ es2018/fluent-react.js 2020-04-21 14:46:02.667791000 +0200 | |
@@ -3,16 +3,16 @@ | |
typeof exports === "object" && typeof module !== "undefined" | |
? factory( | |
exports, | |
- require("@fluent/sequence/compat"), | |
- require("cached-iterable/compat"), | |
+ require("@fluent/sequence"), | |
+ require("cached-iterable"), | |
require("react"), | |
require("prop-types") | |
) | |
: typeof define === "function" && define.amd | |
? define("@fluent/react", [ | |
"exports", | |
- "@fluent/sequence/compat", | |
- "cached-iterable/compat", | |
+ "@fluent/sequence", | |
+ "cached-iterable", | |
"react", | |
"prop-types", | |
], factory) | |
@@ -24,7 +24,7 @@ | |
global.React, | |
global.PropTypes | |
)); | |
-})(this, function (exports, compat, compat$1, react, PropTypes) { | |
+})(this, function (exports, sequence, cachedIterable, react, PropTypes) { | |
"use strict"; | |
PropTypes = | |
@@ -33,11 +33,11 @@ | |
: PropTypes; | |
/* eslint-env browser */ | |
- var cachedParseMarkup; // We use a function creator to make the reference to `document` lazy. At the | |
+ let cachedParseMarkup; | |
+ // We use a function creator to make the reference to `document` lazy. At the | |
// same time, it's eager enough to throw in <LocalizationProvider> as soon as | |
// it's first mounted which reduces the risk of this error making it to the | |
// runtime without developers noticing it in development. | |
- | |
function createParseMarkup() { | |
if (typeof document === "undefined") { | |
// We can't use <template> to sanitize translations. | |
@@ -47,16 +47,13 @@ | |
"https://github.com/projectfluent/fluent.js/wiki/React-Overlays." | |
); | |
} | |
- | |
if (!cachedParseMarkup) { | |
- var template = document.createElement("template"); | |
- | |
+ const template = document.createElement("template"); | |
cachedParseMarkup = function parseMarkup(str) { | |
template.innerHTML = str; | |
return Array.from(template.content.childNodes); | |
}; | |
} | |
- | |
return cachedParseMarkup; | |
} | |
@@ -71,54 +68,39 @@ | |
* The `ReactLocalization` class instances are exposed to `Localized` elements | |
* via the `LocalizationProvider` component. | |
*/ | |
- | |
class ReactLocalization { | |
- constructor(bundles) { | |
- var parseMarkup = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : createParseMarkup(); | |
- this.bundles = compat$1.CachedSyncIterable.from(bundles); | |
+ constructor(bundles, parseMarkup = createParseMarkup()) { | |
+ this.bundles = cachedIterable.CachedSyncIterable.from(bundles); | |
this.parseMarkup = parseMarkup; | |
} | |
- | |
getBundle(id) { | |
- return compat.mapBundleSync(this.bundles, id); | |
+ return sequence.mapBundleSync(this.bundles, id); | |
} | |
- | |
getString(id, args, fallback) { | |
- var bundle = this.getBundle(id); | |
- | |
+ const bundle = this.getBundle(id); | |
if (bundle) { | |
- var msg = bundle.getMessage(id); | |
- | |
+ const msg = bundle.getMessage(id); | |
if (msg && msg.value) { | |
- var errors = []; | |
- var value = bundle.formatPattern(msg.value, args, errors); | |
- | |
- for (var _i = 0, _errors = errors; _i < _errors.length; _i++) { | |
- var error = _errors[_i]; | |
+ let errors = []; | |
+ let value = bundle.formatPattern(msg.value, args, errors); | |
+ for (let error of errors) { | |
this.reportError(error); | |
} | |
- | |
return value; | |
} | |
} | |
- | |
return fallback || id; | |
- } // XXX Control this via a prop passed to the LocalizationProvider. | |
+ } | |
+ // XXX Control this via a prop passed to the LocalizationProvider. | |
// See https://github.com/projectfluent/fluent.js/issues/411. | |
- | |
reportError(error) { | |
/* global console */ | |
// eslint-disable-next-line no-console | |
- console.warn( | |
- "[@fluent/react] ".concat(error.name, ": ").concat(error.message) | |
- ); | |
+ console.warn(`[@fluent/react] ${error.name}: ${error.message}`); | |
} | |
} | |
- var FluentContext = react.createContext(new ReactLocalization([], null)); | |
+ let FluentContext = react.createContext(new ReactLocalization([], null)); | |
/* | |
* The Provider component for the `ReactLocalization` class. | |
@@ -136,7 +118,6 @@ | |
* `l10n` prop. This instance will be made available to `Localized` components | |
* under the provider. | |
*/ | |
- | |
function LocalizationProvider(props) { | |
return react.createElement( | |
FluentContext.Provider, | |
@@ -151,155 +132,16 @@ | |
l10n: PropTypes.instanceOf(ReactLocalization).isRequired, | |
}; | |
- function _defineProperty(obj, key, value) { | |
- if (key in obj) { | |
- Object.defineProperty(obj, key, { | |
- value: value, | |
- enumerable: true, | |
- configurable: true, | |
- writable: true, | |
- }); | |
- } else { | |
- obj[key] = value; | |
- } | |
- | |
- return obj; | |
- } | |
- | |
- function ownKeys(object, enumerableOnly) { | |
- var keys = Object.keys(object); | |
- | |
- if (Object.getOwnPropertySymbols) { | |
- var symbols = Object.getOwnPropertySymbols(object); | |
- if (enumerableOnly) | |
- symbols = symbols.filter(function (sym) { | |
- return Object.getOwnPropertyDescriptor(object, sym).enumerable; | |
- }); | |
- keys.push.apply(keys, symbols); | |
- } | |
- | |
- return keys; | |
- } | |
- | |
- function _objectSpread2(target) { | |
- for (var i = 1; i < arguments.length; i++) { | |
- var source = arguments[i] != null ? arguments[i] : {}; | |
- | |
- if (i % 2) { | |
- ownKeys(Object(source), true).forEach(function (key) { | |
- _defineProperty(target, key, source[key]); | |
- }); | |
- } else if (Object.getOwnPropertyDescriptors) { | |
- Object.defineProperties( | |
- target, | |
- Object.getOwnPropertyDescriptors(source) | |
- ); | |
- } else { | |
- ownKeys(Object(source)).forEach(function (key) { | |
- Object.defineProperty( | |
- target, | |
- key, | |
- Object.getOwnPropertyDescriptor(source, key) | |
- ); | |
- }); | |
- } | |
- } | |
- | |
- return target; | |
- } | |
- | |
- function _slicedToArray(arr, i) { | |
- return ( | |
- _arrayWithHoles(arr) || | |
- _iterableToArrayLimit(arr, i) || | |
- _unsupportedIterableToArray(arr, i) || | |
- _nonIterableRest() | |
- ); | |
- } | |
- | |
- function _arrayWithHoles(arr) { | |
- if (Array.isArray(arr)) return arr; | |
- } | |
- | |
- function _iterableToArrayLimit(arr, i) { | |
- if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) | |
- return; | |
- var _arr = []; | |
- var _n = true; | |
- var _d = false; | |
- var _e = undefined; | |
- | |
- try { | |
- for ( | |
- var _i = arr[Symbol.iterator](), _s; | |
- !(_n = (_s = _i.next()).done); | |
- _n = true | |
- ) { | |
- _arr.push(_s.value); | |
- | |
- if (i && _arr.length === i) break; | |
- } | |
- } catch (err) { | |
- _d = true; | |
- _e = err; | |
- } finally { | |
- try { | |
- if (!_n && _i["return"] != null) _i["return"](); | |
- } finally { | |
- if (_d) throw _e; | |
- } | |
- } | |
- | |
- return _arr; | |
- } | |
- | |
- function _unsupportedIterableToArray(o, minLen) { | |
- if (!o) return; | |
- if (typeof o === "string") return _arrayLikeToArray(o, minLen); | |
- var n = Object.prototype.toString.call(o).slice(8, -1); | |
- if (n === "Object" && o.constructor) n = o.constructor.name; | |
- if (n === "Map" || n === "Set") return Array.from(n); | |
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) | |
- return _arrayLikeToArray(o, minLen); | |
- } | |
- | |
- function _arrayLikeToArray(arr, len) { | |
- if (len == null || len > arr.length) len = arr.length; | |
- | |
- for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | |
- | |
- return arr2; | |
- } | |
- | |
- function _nonIterableRest() { | |
- throw new TypeError( | |
- "Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
function withLocalization(Inner) { | |
function WithLocalization(props) { | |
- var l10n = react.useContext(FluentContext); // Re-bind getString to trigger a re-render of Inner. | |
- | |
- var getString = l10n.getString.bind(l10n); | |
- return react.createElement( | |
- Inner, | |
- _objectSpread2( | |
- { | |
- getString, | |
- }, | |
- props | |
- ) | |
- ); | |
+ const l10n = react.useContext(FluentContext); | |
+ // Re-bind getString to trigger a re-render of Inner. | |
+ const getString = l10n.getString.bind(l10n); | |
+ return react.createElement(Inner, { getString, ...props }); | |
} | |
- | |
- WithLocalization.displayName = "WithLocalization(".concat( | |
- displayName(Inner), | |
- ")" | |
- ); | |
+ WithLocalization.displayName = `WithLocalization(${displayName(Inner)})`; | |
return WithLocalization; | |
} | |
- | |
function displayName(component) { | |
return component.displayName || component.name || "Component"; | |
} | |
@@ -310,8 +152,10 @@ | |
* This source code is licensed under the MIT license found in the | |
* LICENSE file in this directory. | |
*/ | |
+ | |
// For HTML, certain tags should omit their close tag. We keep a whitelist for | |
// those special-case tags. | |
+ | |
var omittedCloseTags = { | |
area: true, | |
base: true, | |
@@ -327,21 +171,28 @@ | |
param: true, | |
source: true, | |
track: true, | |
- wbr: true, // NOTE: menuitem's close tag should be omitted, but that causes problems. | |
+ wbr: true, | |
+ // NOTE: menuitem's close tag should be omitted, but that causes problems. | |
}; | |
+ /** | |
+ * Copyright (c) 2013-present, Facebook, Inc. | |
+ * | |
+ * This source code is licensed under the MIT license found in the | |
+ * LICENSE file in this directory. | |
+ */ | |
+ | |
+ // For HTML, certain tags cannot have children. This has the same purpose as | |
// `omittedCloseTags` except that `menuitem` should still have its closing tag. | |
- var voidElementTags = _objectSpread2( | |
- { | |
+ var voidElementTags = { | |
menuitem: true, | |
- }, | |
- omittedCloseTags | |
- ); | |
+ ...omittedCloseTags, | |
+ }; | |
+ // Match the opening angle bracket (<) in HTML tags, and HTML entities like | |
// &, &, &. | |
- | |
- var reMarkup = /<|&#?\w+;/; | |
+ const reMarkup = /<|&#?\w+;/; | |
/* | |
* The `Localized` class renders its child with translated props and children. | |
* | |
@@ -364,96 +215,51 @@ | |
* translation is available. It also makes it easy to grep for strings in the | |
* source code. | |
*/ | |
- | |
function Localized(props) { | |
- var id = props.id, | |
- attrs = props.attrs, | |
- vars = props.vars, | |
- elems = props.elems, | |
- _props$children = props.children, | |
- child = _props$children === void 0 ? null : _props$children; | |
- var l10n = react.useContext(FluentContext); // Validate that the child element isn't an array | |
- | |
+ const { id, attrs, vars, elems, children: child = null } = props; | |
+ const l10n = react.useContext(FluentContext); | |
+ // Validate that the child element isn't an array | |
if (Array.isArray(child)) { | |
throw new Error( | |
"<Localized/> expected to receive a single " + "React node child" | |
); | |
} | |
- | |
if (!l10n) { | |
// Use the wrapped component as fallback. | |
return react.createElement(react.Fragment, null, child); | |
} | |
- | |
- var bundle = l10n.getBundle(id); | |
- | |
+ const bundle = l10n.getBundle(id); | |
if (bundle === null) { | |
// Use the wrapped component as fallback. | |
return react.createElement(react.Fragment, null, child); | |
- } // l10n.getBundle makes the bundle.hasMessage check which ensures that | |
+ } | |
+ // l10n.getBundle makes the bundle.hasMessage check which ensures that | |
// bundle.getMessage returns an existing message. | |
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
- | |
- var msg = bundle.getMessage(id); | |
- var errors = []; // Check if the child inside <Localized> is a valid element -- if not, then | |
+ const msg = bundle.getMessage(id); | |
+ let errors = []; | |
+ // Check if the child inside <Localized> is a valid element -- if not, then | |
// it's either null or a simple fallback string. No need to localize the | |
// attributes. | |
- | |
if (!react.isValidElement(child)) { | |
if (msg.value) { | |
// Replace the fallback string with the message value; | |
- var value = bundle.formatPattern(msg.value, vars, errors); | |
- var _iteratorNormalCompletion = true; | |
- var _didIteratorError = false; | |
- var _iteratorError = undefined; | |
- | |
- try { | |
- for ( | |
- var _iterator = errors[Symbol.iterator](), _step; | |
- !(_iteratorNormalCompletion = (_step = _iterator.next()).done); | |
- _iteratorNormalCompletion = true | |
- ) { | |
- var error = _step.value; | |
+ let value = bundle.formatPattern(msg.value, vars, errors); | |
+ for (let error of errors) { | |
l10n.reportError(error); | |
} | |
- } catch (err) { | |
- _didIteratorError = true; | |
- _iteratorError = err; | |
- } finally { | |
- try { | |
- if (!_iteratorNormalCompletion && _iterator.return != null) { | |
- _iterator.return(); | |
- } | |
- } finally { | |
- if (_didIteratorError) { | |
- throw _iteratorError; | |
- } | |
- } | |
- } | |
- | |
return react.createElement(react.Fragment, null, value); | |
} | |
- | |
return react.createElement(react.Fragment, null, child); | |
} | |
- | |
- var localizedProps; // The default is to forbid all message attributes. If the attrs prop exists | |
+ let localizedProps; | |
+ // The default is to forbid all message attributes. If the attrs prop exists | |
// on the Localized instance, only set message attributes which have been | |
// explicitly allowed by the developer. | |
- | |
if (attrs && msg.attributes) { | |
localizedProps = {}; | |
errors = []; | |
- | |
- for ( | |
- var _i = 0, _Object$entries = Object.entries(attrs); | |
- _i < _Object$entries.length; | |
- _i++ | |
- ) { | |
- var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), | |
- name = _Object$entries$_i[0], | |
- allowed = _Object$entries$_i[1]; | |
- | |
+ for (const [name, allowed] of Object.entries(attrs)) { | |
if (allowed && name in msg.attributes) { | |
localizedProps[name] = bundle.formatPattern( | |
msg.attributes[name], | |
@@ -462,89 +268,71 @@ | |
); | |
} | |
} | |
- | |
- for (var _i2 = 0, _errors = errors; _i2 < _errors.length; _i2++) { | |
- var _error = _errors[_i2]; | |
- l10n.reportError(_error); | |
+ for (let error of errors) { | |
+ l10n.reportError(error); | |
+ } | |
} | |
- } // If the wrapped component is a known void element, explicitly dismiss the | |
+ // If the wrapped component is a known void element, explicitly dismiss the | |
// message value and do not pass it to cloneElement in order to avoid the | |
// "void element tags must neither have `children` nor use | |
// `dangerouslySetInnerHTML`" error. | |
- | |
if (child.type in voidElementTags) { | |
return react.cloneElement(child, localizedProps); | |
- } // If the message has a null value, we're only interested in its attributes. | |
+ } | |
+ // If the message has a null value, we're only interested in its attributes. | |
// Do not pass the null value to cloneElement as it would nuke all children | |
// of the wrapped component. | |
- | |
if (msg.value === null) { | |
return react.cloneElement(child, localizedProps); | |
} | |
- | |
errors = []; | |
- var messageValue = bundle.formatPattern(msg.value, vars, errors); | |
- | |
- for (var _i3 = 0, _errors2 = errors; _i3 < _errors2.length; _i3++) { | |
- var _error2 = _errors2[_i3]; | |
- l10n.reportError(_error2); | |
- } // If the message value doesn't contain any markup nor any HTML entities, | |
+ const messageValue = bundle.formatPattern(msg.value, vars, errors); | |
+ for (let error of errors) { | |
+ l10n.reportError(error); | |
+ } | |
+ // If the message value doesn't contain any markup nor any HTML entities, | |
// insert it as the only child of the wrapped component. | |
- | |
if (!reMarkup.test(messageValue) || l10n.parseMarkup === null) { | |
return react.cloneElement(child, localizedProps, messageValue); | |
} | |
- | |
- var elemsLower; | |
- | |
+ let elemsLower; | |
if (elems) { | |
elemsLower = {}; | |
- | |
- for ( | |
- var _i4 = 0, _Object$entries2 = Object.entries(elems); | |
- _i4 < _Object$entries2.length; | |
- _i4++ | |
- ) { | |
- var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i4], 2), | |
- _name = _Object$entries2$_i[0], | |
- elem = _Object$entries2$_i[1]; | |
- | |
- elemsLower[_name.toLowerCase()] = elem; | |
+ for (let [name, elem] of Object.entries(elems)) { | |
+ elemsLower[name.toLowerCase()] = elem; | |
+ } | |
} | |
- } // If the message contains markup, parse it and try to match the children | |
+ // If the message contains markup, parse it and try to match the children | |
// found in the translation with the props passed to this Localized. | |
- | |
- var translationNodes = l10n.parseMarkup(messageValue); | |
- var translatedChildren = translationNodes.map((childNode) => { | |
+ const translationNodes = l10n.parseMarkup(messageValue); | |
+ const translatedChildren = translationNodes.map((childNode) => { | |
if (childNode.nodeName === "#text") { | |
return childNode.textContent; | |
} | |
- | |
- var childName = childNode.nodeName.toLowerCase(); // If the child is not expected just take its textContent. | |
- | |
+ const childName = childNode.nodeName.toLowerCase(); | |
+ // If the child is not expected just take its textContent. | |
if ( | |
!elemsLower || | |
!Object.prototype.hasOwnProperty.call(elemsLower, childName) | |
) { | |
return childNode.textContent; | |
} | |
- | |
- var sourceChild = elemsLower[childName]; // Ignore elems which are not valid React elements. | |
- | |
+ const sourceChild = elemsLower[childName]; | |
+ // Ignore elems which are not valid React elements. | |
if (!react.isValidElement(sourceChild)) { | |
return childNode.textContent; | |
- } // If the element passed in the elems prop is a known void element, | |
+ } | |
+ // If the element passed in the elems prop is a known void element, | |
// explicitly dismiss any textContent which might have accidentally been | |
// defined in the translation to prevent the "void element tags must not | |
// have children" error. | |
- | |
if (sourceChild.type in voidElementTags) { | |
return sourceChild; | |
- } // TODO Protect contents of elements wrapped in <Localized> | |
+ } | |
+ // TODO Protect contents of elements wrapped in <Localized> | |
// https://github.com/projectfluent/fluent.js/issues/184 | |
// TODO Control localizable attributes on elements passed as props | |
// https://github.com/projectfluent/fluent.js/issues/185 | |
- | |
return react.cloneElement(sourceChild, undefined, childNode.textContent); | |
}); | |
return react.cloneElement(child, localizedProps, ...translatedChildren); | |
diff -wru compat/fluent-sequence.js es2018/fluent-sequence.js | |
--- compat/fluent-sequence.js 2020-04-21 14:45:54.997932100 +0200 | |
+++ es2018/fluent-sequence.js 2020-04-21 14:46:02.685791800 +0200 | |
@@ -8,195 +8,6 @@ | |
})(this, function (exports) { | |
"use strict"; | |
- function _asyncIterator(iterable) { | |
- var method; | |
- | |
- if (typeof Symbol !== "undefined") { | |
- if (Symbol.asyncIterator) { | |
- method = iterable[Symbol.asyncIterator]; | |
- if (method != null) return method.call(iterable); | |
- } | |
- | |
- if (Symbol.iterator) { | |
- method = iterable[Symbol.iterator]; | |
- if (method != null) return method.call(iterable); | |
- } | |
- } | |
- | |
- throw new TypeError("Object is not async iterable"); | |
- } | |
- | |
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { | |
- try { | |
- var info = gen[key](arg); | |
- var value = info.value; | |
- } catch (error) { | |
- reject(error); | |
- return; | |
- } | |
- | |
- if (info.done) { | |
- resolve(value); | |
- } else { | |
- Promise.resolve(value).then(_next, _throw); | |
- } | |
- } | |
- | |
- function _asyncToGenerator(fn) { | |
- return function () { | |
- var self = this, | |
- args = arguments; | |
- return new Promise(function (resolve, reject) { | |
- var gen = fn.apply(self, args); | |
- | |
- function _next(value) { | |
- asyncGeneratorStep( | |
- gen, | |
- resolve, | |
- reject, | |
- _next, | |
- _throw, | |
- "next", | |
- value | |
- ); | |
- } | |
- | |
- function _throw(err) { | |
- asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); | |
- } | |
- | |
- _next(undefined); | |
- }); | |
- }; | |
- } | |
- | |
- function _slicedToArray(arr, i) { | |
- return ( | |
- _arrayWithHoles(arr) || | |
- _iterableToArrayLimit(arr, i) || | |
- _unsupportedIterableToArray(arr, i) || | |
- _nonIterableRest() | |
- ); | |
- } | |
- | |
- function _arrayWithHoles(arr) { | |
- if (Array.isArray(arr)) return arr; | |
- } | |
- | |
- function _iterableToArrayLimit(arr, i) { | |
- if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) | |
- return; | |
- var _arr = []; | |
- var _n = true; | |
- var _d = false; | |
- var _e = undefined; | |
- | |
- try { | |
- for ( | |
- var _i = arr[Symbol.iterator](), _s; | |
- !(_n = (_s = _i.next()).done); | |
- _n = true | |
- ) { | |
- _arr.push(_s.value); | |
- | |
- if (i && _arr.length === i) break; | |
- } | |
- } catch (err) { | |
- _d = true; | |
- _e = err; | |
- } finally { | |
- try { | |
- if (!_n && _i["return"] != null) _i["return"](); | |
- } finally { | |
- if (_d) throw _e; | |
- } | |
- } | |
- | |
- return _arr; | |
- } | |
- | |
- function _unsupportedIterableToArray(o, minLen) { | |
- if (!o) return; | |
- if (typeof o === "string") return _arrayLikeToArray(o, minLen); | |
- var n = Object.prototype.toString.call(o).slice(8, -1); | |
- if (n === "Object" && o.constructor) n = o.constructor.name; | |
- if (n === "Map" || n === "Set") return Array.from(n); | |
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) | |
- return _arrayLikeToArray(o, minLen); | |
- } | |
- | |
- function _arrayLikeToArray(arr, len) { | |
- if (len == null || len > arr.length) len = arr.length; | |
- | |
- for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | |
- | |
- return arr2; | |
- } | |
- | |
- function _nonIterableRest() { | |
- throw new TypeError( | |
- "Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
- function _createForOfIteratorHelper(o) { | |
- if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { | |
- if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { | |
- var i = 0; | |
- | |
- var F = function () {}; | |
- | |
- return { | |
- s: F, | |
- n: function () { | |
- if (i >= o.length) | |
- return { | |
- done: true, | |
- }; | |
- return { | |
- done: false, | |
- value: o[i++], | |
- }; | |
- }, | |
- e: function (e) { | |
- throw e; | |
- }, | |
- f: F, | |
- }; | |
- } | |
- | |
- throw new TypeError( | |
- "Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
- var it, | |
- normalCompletion = true, | |
- didErr = false, | |
- err; | |
- return { | |
- s: function () { | |
- it = o[Symbol.iterator](); | |
- }, | |
- n: function () { | |
- var step = it.next(); | |
- normalCompletion = step.done; | |
- return step; | |
- }, | |
- e: function (e) { | |
- didErr = true; | |
- err = e; | |
- }, | |
- f: function () { | |
- try { | |
- if (!normalCompletion && it.return != null) it.return(); | |
- } finally { | |
- if (didErr) throw err; | |
- } | |
- }, | |
- }; | |
- } | |
- | |
/* | |
* Synchronously map an identifier or an array of identifiers to the best | |
* `FluentBundle` instance(s). | |
@@ -208,31 +19,17 @@ | |
if (!Array.isArray(ids)) { | |
return getBundleForId(bundles, ids); | |
} | |
- | |
return ids.map((id) => getBundleForId(bundles, id)); | |
} | |
/* | |
* Find the best `FluentBundle` with the translation for `id`. | |
*/ | |
- | |
function getBundleForId(bundles, id) { | |
- var _iterator = _createForOfIteratorHelper(bundles), | |
- _step; | |
- | |
- try { | |
- for (_iterator.s(); !(_step = _iterator.n()).done; ) { | |
- var bundle = _step.value; | |
- | |
+ for (const bundle of bundles) { | |
if (bundle.hasMessage(id)) { | |
return bundle; | |
} | |
} | |
- } catch (err) { | |
- _iterator.e(err); | |
- } finally { | |
- _iterator.f(); | |
- } | |
- | |
return null; | |
} | |
@@ -243,111 +40,30 @@ | |
* @param bundles - An iterable of bundles to sift through. | |
* @param ids - An id or ids to map. | |
*/ | |
- function mapBundleAsync(_x, _x2) { | |
- return _mapBundleAsync.apply(this, arguments); | |
- } | |
- | |
- function _mapBundleAsync() { | |
- _mapBundleAsync = _asyncToGenerator(function* (bundles, ids) { | |
+ async function mapBundleAsync(bundles, ids) { | |
if (!Array.isArray(ids)) { | |
- var _iteratorNormalCompletion = true; | |
- var _didIteratorError = false; | |
- | |
- var _iteratorError; | |
- | |
- try { | |
- for ( | |
- var _iterator = _asyncIterator(bundles), _step, _value; | |
- (_step = yield _iterator.next()), | |
- (_iteratorNormalCompletion = _step.done), | |
- (_value = yield _step.value), | |
- !_iteratorNormalCompletion; | |
- _iteratorNormalCompletion = true | |
- ) { | |
- var bundle = _value; | |
- | |
+ for await (const bundle of bundles) { | |
if (bundle.hasMessage(ids)) { | |
return bundle; | |
} | |
} | |
- } catch (err) { | |
- _didIteratorError = true; | |
- _iteratorError = err; | |
- } finally { | |
- try { | |
- if (!_iteratorNormalCompletion && _iterator.return != null) { | |
- yield _iterator.return(); | |
- } | |
- } finally { | |
- if (_didIteratorError) { | |
- throw _iteratorError; | |
- } | |
- } | |
- } | |
- | |
return null; | |
} | |
- | |
- var foundBundles = new Array(ids.length).fill(null); | |
- var remainingCount = ids.length; | |
- var _iteratorNormalCompletion2 = true; | |
- var _didIteratorError2 = false; | |
- | |
- var _iteratorError2; | |
- | |
- try { | |
- for ( | |
- var _iterator2 = _asyncIterator(bundles), _step2, _value2; | |
- (_step2 = yield _iterator2.next()), | |
- (_iteratorNormalCompletion2 = _step2.done), | |
- (_value2 = yield _step2.value), | |
- !_iteratorNormalCompletion2; | |
- _iteratorNormalCompletion2 = true | |
- ) { | |
- var _bundle = _value2; | |
- | |
- var _iterator3 = _createForOfIteratorHelper(ids.entries()), | |
- _step3; | |
- | |
- try { | |
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done; ) { | |
- var _step3$value = _slicedToArray(_step3.value, 2), | |
- index = _step3$value[0], | |
- id = _step3$value[1]; | |
- | |
- if (!foundBundles[index] && _bundle.hasMessage(id)) { | |
- foundBundles[index] = _bundle; | |
+ const foundBundles = new Array(ids.length).fill(null); | |
+ let remainingCount = ids.length; | |
+ for await (const bundle of bundles) { | |
+ for (const [index, id] of ids.entries()) { | |
+ if (!foundBundles[index] && bundle.hasMessage(id)) { | |
+ foundBundles[index] = bundle; | |
remainingCount--; | |
- } // Return early when all ids have been mapped to bundles. | |
- | |
+ } | |
+ // Return early when all ids have been mapped to bundles. | |
if (remainingCount === 0) { | |
return foundBundles; | |
} | |
} | |
- } catch (err) { | |
- _iterator3.e(err); | |
- } finally { | |
- _iterator3.f(); | |
- } | |
- } | |
- } catch (err) { | |
- _didIteratorError2 = true; | |
- _iteratorError2 = err; | |
- } finally { | |
- try { | |
- if (!_iteratorNormalCompletion2 && _iterator2.return != null) { | |
- yield _iterator2.return(); | |
- } | |
- } finally { | |
- if (_didIteratorError2) { | |
- throw _iteratorError2; | |
- } | |
} | |
- } | |
- | |
return foundBundles; | |
- }); | |
- return _mapBundleAsync.apply(this, arguments); | |
} | |
exports.mapBundleAsync = mapBundleAsync; | |
diff -wru compat/fluent-syntax.js es2018/fluent-syntax.js | |
--- compat/fluent-syntax.js 2020-04-21 14:45:55.183892800 +0200 | |
+++ es2018/fluent-syntax.js 2020-04-21 14:46:02.907757700 +0200 | |
@@ -8,133 +8,6 @@ | |
})(this, function (exports) { | |
"use strict"; | |
- function _slicedToArray(arr, i) { | |
- return ( | |
- _arrayWithHoles(arr) || | |
- _iterableToArrayLimit(arr, i) || | |
- _unsupportedIterableToArray(arr, i) || | |
- _nonIterableRest() | |
- ); | |
- } | |
- | |
- function _arrayWithHoles(arr) { | |
- if (Array.isArray(arr)) return arr; | |
- } | |
- | |
- function _iterableToArrayLimit(arr, i) { | |
- if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) | |
- return; | |
- var _arr = []; | |
- var _n = true; | |
- var _d = false; | |
- var _e = undefined; | |
- | |
- try { | |
- for ( | |
- var _i = arr[Symbol.iterator](), _s; | |
- !(_n = (_s = _i.next()).done); | |
- _n = true | |
- ) { | |
- _arr.push(_s.value); | |
- | |
- if (i && _arr.length === i) break; | |
- } | |
- } catch (err) { | |
- _d = true; | |
- _e = err; | |
- } finally { | |
- try { | |
- if (!_n && _i["return"] != null) _i["return"](); | |
- } finally { | |
- if (_d) throw _e; | |
- } | |
- } | |
- | |
- return _arr; | |
- } | |
- | |
- function _unsupportedIterableToArray(o, minLen) { | |
- if (!o) return; | |
- if (typeof o === "string") return _arrayLikeToArray(o, minLen); | |
- var n = Object.prototype.toString.call(o).slice(8, -1); | |
- if (n === "Object" && o.constructor) n = o.constructor.name; | |
- if (n === "Map" || n === "Set") return Array.from(n); | |
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) | |
- return _arrayLikeToArray(o, minLen); | |
- } | |
- | |
- function _arrayLikeToArray(arr, len) { | |
- if (len == null || len > arr.length) len = arr.length; | |
- | |
- for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | |
- | |
- return arr2; | |
- } | |
- | |
- function _nonIterableRest() { | |
- throw new TypeError( | |
- "Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
- function _createForOfIteratorHelper(o) { | |
- if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { | |
- if (Array.isArray(o) || (o = _unsupportedIterableToArray(o))) { | |
- var i = 0; | |
- | |
- var F = function () {}; | |
- | |
- return { | |
- s: F, | |
- n: function () { | |
- if (i >= o.length) | |
- return { | |
- done: true, | |
- }; | |
- return { | |
- done: false, | |
- value: o[i++], | |
- }; | |
- }, | |
- e: function (e) { | |
- throw e; | |
- }, | |
- f: F, | |
- }; | |
- } | |
- | |
- throw new TypeError( | |
- "Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." | |
- ); | |
- } | |
- | |
- var it, | |
- normalCompletion = true, | |
- didErr = false, | |
- err; | |
- return { | |
- s: function () { | |
- it = o[Symbol.iterator](); | |
- }, | |
- n: function () { | |
- var step = it.next(); | |
- normalCompletion = step.done; | |
- return step; | |
- }, | |
- e: function (e) { | |
- didErr = true; | |
- err = e; | |
- }, | |
- f: function () { | |
- try { | |
- if (!normalCompletion && it.return != null) it.return(); | |
- } finally { | |
- if (didErr) throw err; | |
- } | |
- }, | |
- }; | |
- } | |
- | |
/* | |
* Base class for all Fluent AST nodes. | |
* | |
@@ -146,60 +19,32 @@ | |
constructor() { | |
this.type = "BaseNode"; | |
} | |
- | |
- equals(other) { | |
- var ignoredFields = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : ["span"]; | |
- var thisKeys = new Set(Object.keys(this)); | |
- var otherKeys = new Set(Object.keys(other)); | |
- | |
+ equals(other, ignoredFields = ["span"]) { | |
+ const thisKeys = new Set(Object.keys(this)); | |
+ const otherKeys = new Set(Object.keys(other)); | |
if (ignoredFields) { | |
- var _iterator = _createForOfIteratorHelper(ignoredFields), | |
- _step; | |
- | |
- try { | |
- for (_iterator.s(); !(_step = _iterator.n()).done; ) { | |
- var fieldName = _step.value; | |
+ for (const fieldName of ignoredFields) { | |
thisKeys.delete(fieldName); | |
otherKeys.delete(fieldName); | |
} | |
- } catch (err) { | |
- _iterator.e(err); | |
- } finally { | |
- _iterator.f(); | |
- } | |
} | |
- | |
if (thisKeys.size !== otherKeys.size) { | |
return false; | |
} | |
- | |
- var _iterator2 = _createForOfIteratorHelper(thisKeys), | |
- _step2; | |
- | |
- try { | |
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done; ) { | |
- var _fieldName = _step2.value; | |
- | |
- if (!otherKeys.has(_fieldName)) { | |
+ for (const fieldName of thisKeys) { | |
+ if (!otherKeys.has(fieldName)) { | |
return false; | |
} | |
- | |
- var thisVal = this[_fieldName]; | |
- var otherVal = other[_fieldName]; | |
- | |
+ const thisVal = this[fieldName]; | |
+ const otherVal = other[fieldName]; | |
if (typeof thisVal !== typeof otherVal) { | |
return false; | |
} | |
- | |
if (thisVal instanceof Array && otherVal instanceof Array) { | |
if (thisVal.length !== otherVal.length) { | |
return false; | |
} | |
- | |
- for (var i = 0; i < thisVal.length; ++i) { | |
+ for (let i = 0; i < thisVal.length; ++i) { | |
if (!scalarsEqual(thisVal[i], otherVal[i], ignoredFields)) { | |
return false; | |
} | |
@@ -208,68 +53,45 @@ | |
return false; | |
} | |
} | |
- } catch (err) { | |
- _iterator2.e(err); | |
- } finally { | |
- _iterator2.f(); | |
- } | |
- | |
return true; | |
} | |
- | |
clone() { | |
function visit(value) { | |
if (value instanceof BaseNode) { | |
return value.clone(); | |
} | |
- | |
if (Array.isArray(value)) { | |
return value.map(visit); | |
} | |
- | |
return value; | |
} | |
- | |
- var clone = Object.create(this.constructor.prototype); | |
- | |
- for ( | |
- var _i = 0, _Object$keys = Object.keys(this); | |
- _i < _Object$keys.length; | |
- _i++ | |
- ) { | |
- var prop = _Object$keys[_i]; | |
+ const clone = Object.create(this.constructor.prototype); | |
+ for (const prop of Object.keys(this)) { | |
clone[prop] = visit(this[prop]); | |
} | |
- | |
return clone; | |
} | |
} | |
- | |
function scalarsEqual(thisVal, otherVal, ignoredFields) { | |
if (thisVal instanceof BaseNode && otherVal instanceof BaseNode) { | |
return thisVal.equals(otherVal, ignoredFields); | |
} | |
- | |
return thisVal === otherVal; | |
} | |
/* | |
* Base class for AST nodes which can have Spans. | |
*/ | |
- | |
class SyntaxNode extends BaseNode { | |
constructor() { | |
super(...arguments); | |
this.type = "SyntaxNode"; | |
} | |
- | |
addSpan(start, end) { | |
this.span = new Span(start, end); | |
} | |
} | |
class Resource extends SyntaxNode { | |
- constructor() { | |
- var body = | |
- arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; | |
+ constructor(body = []) { | |
super(); | |
this.type = "Resource"; | |
this.body = body; | |
@@ -278,7 +100,6 @@ | |
/* | |
* An abstract base class for useful elements of Resource.body. | |
*/ | |
- | |
class Entry extends SyntaxNode { | |
constructor() { | |
super(...arguments); | |
@@ -286,17 +107,7 @@ | |
} | |
} | |
class Message extends Entry { | |
- constructor(id) { | |
- var value = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : null; | |
- var attributes = | |
- arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; | |
- var comment = | |
- arguments.length > 3 && arguments[3] !== undefined | |
- ? arguments[3] | |
- : null; | |
+ constructor(id, value = null, attributes = [], comment = null) { | |
super(); | |
this.type = "Message"; | |
this.id = id; | |
@@ -306,13 +117,7 @@ | |
} | |
} | |
class Term extends Entry { | |
- constructor(id, value) { | |
- var attributes = | |
- arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; | |
- var comment = | |
- arguments.length > 3 && arguments[3] !== undefined | |
- ? arguments[3] | |
- : null; | |
+ constructor(id, value, attributes = [], comment = null) { | |
super(); | |
this.type = "Term"; | |
this.id = id; | |
@@ -331,7 +136,6 @@ | |
/* | |
* An abstract base class for elements of Patterns. | |
*/ | |
- | |
class PatternElement extends SyntaxNode { | |
constructor() { | |
super(...arguments); | |
@@ -355,20 +159,19 @@ | |
/* | |
* An abstract base class for expressions. | |
*/ | |
- | |
class Expression extends SyntaxNode { | |
constructor() { | |
super(...arguments); | |
this.type = "Expression"; | |
} | |
- } // An abstract base class for Literals. | |
- | |
+ } | |
+ // An abstract base class for Literals. | |
class Literal extends Expression { | |
constructor(value) { | |
super(); | |
- this.type = "Literal"; // The "value" field contains the exact contents of the literal, | |
+ this.type = "Literal"; | |
+ // The "value" field contains the exact contents of the literal, | |
// character-for-character. | |
- | |
this.value = value; | |
} | |
} | |
@@ -377,38 +180,30 @@ | |
super(...arguments); | |
this.type = "StringLiteral"; | |
} | |
- | |
parse() { | |
// Backslash backslash, backslash double quote, uHHHH, UHHHHHH. | |
- var 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 fromEscapeSequence(match, codepoint4, codepoint6) { | |
switch (match) { | |
case "\\\\": | |
return "\\"; | |
- | |
case '\\"': | |
return '"'; | |
- | |
default: { | |
- var codepoint = parseInt(codepoint4 || codepoint6, 16); | |
- | |
+ 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 | |
+ } | |
+ // Escape sequences reresenting surrogate code points are | |
// well-formed but invalid in Fluent. Replace them with U+FFFD | |
// REPLACEMENT CHARACTER. | |
- | |
return "�"; | |
} | |
} | |
} | |
- | |
- var value = this.value.replace(KNOWN_ESCAPES, fromEscapeSequence); | |
- return { | |
- value, | |
- }; | |
+ let value = this.value.replace(KNOWN_ESCAPES, fromEscapeSequence); | |
+ return { value }; | |
} | |
} | |
class NumberLiteral extends Literal { | |
@@ -416,23 +211,15 @@ | |
super(...arguments); | |
this.type = "NumberLiteral"; | |
} | |
- | |
parse() { | |
- var value = parseFloat(this.value); | |
- var decimalPos = this.value.indexOf("."); | |
- var precision = decimalPos > 0 ? this.value.length - decimalPos - 1 : 0; | |
- return { | |
- value, | |
- precision, | |
- }; | |
+ let value = parseFloat(this.value); | |
+ let decimalPos = this.value.indexOf("."); | |
+ let precision = decimalPos > 0 ? this.value.length - decimalPos - 1 : 0; | |
+ return { value, precision }; | |
} | |
} | |
class MessageReference extends Expression { | |
- constructor(id) { | |
- var attribute = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : null; | |
+ constructor(id, attribute = null) { | |
super(); | |
this.type = "MessageReference"; | |
this.id = id; | |
@@ -440,15 +227,7 @@ | |
} | |
} | |
class TermReference extends Expression { | |
- constructor(id) { | |
- var attribute = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : null; | |
- var args = | |
- arguments.length > 2 && arguments[2] !== undefined | |
- ? arguments[2] | |
- : null; | |
+ constructor(id, attribute = null, args = null) { | |
super(); | |
this.type = "TermReference"; | |
this.id = id; | |
@@ -480,11 +259,7 @@ | |
} | |
} | |
class CallArguments extends SyntaxNode { | |
- constructor() { | |
- var positional = | |
- arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; | |
- var named = | |
- arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; | |
+ constructor(positional = [], named = []) { | |
super(); | |
this.type = "CallArguments"; | |
this.positional = positional; | |
@@ -555,7 +330,6 @@ | |
this.annotations = []; | |
this.content = content; | |
} | |
- | |
addAnnotation(annotation) { | |
this.annotations.push(annotation); | |
} | |
@@ -569,10 +343,7 @@ | |
} | |
} | |
class Annotation extends SyntaxNode { | |
- constructor(code) { | |
- var args = | |
- arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; | |
- var message = arguments.length > 2 ? arguments[2] : undefined; | |
+ constructor(code, args = [], message) { | |
super(); | |
this.type = "Annotation"; | |
this.code = code; | |
@@ -582,139 +353,84 @@ | |
} | |
class ParseError extends Error { | |
- constructor(code) { | |
+ constructor(code, ...args) { | |
super(); | |
this.code = code; | |
- | |
- for ( | |
- var _len = arguments.length, | |
- args = new Array(_len > 1 ? _len - 1 : 0), | |
- _key = 1; | |
- _key < _len; | |
- _key++ | |
- ) { | |
- args[_key - 1] = arguments[_key]; | |
- } | |
- | |
this.args = args; | |
this.message = getErrorMessage(code, args); | |
} | |
} | |
/* eslint-disable complexity */ | |
- | |
function getErrorMessage(code, args) { | |
switch (code) { | |
case "E0001": | |
return "Generic error"; | |
- | |
case "E0002": | |
return "Expected an entry start"; | |
- | |
case "E0003": { | |
- var _args = _slicedToArray(args, 1), | |
- token = _args[0]; | |
- | |
- return 'Expected token: "'.concat(token, '"'); | |
+ const [token] = args; | |
+ return `Expected token: "${token}"`; | |
} | |
- | |
case "E0004": { | |
- var _args2 = _slicedToArray(args, 1), | |
- range = _args2[0]; | |
- | |
- return 'Expected a character from range: "'.concat(range, '"'); | |
+ const [range] = args; | |
+ return `Expected a character from range: "${range}"`; | |
} | |
- | |
case "E0005": { | |
- var _args3 = _slicedToArray(args, 1), | |
- id = _args3[0]; | |
- | |
- return 'Expected message "'.concat( | |
- id, | |
- '" to have a value or attributes' | |
- ); | |
+ const [id] = args; | |
+ return `Expected message "${id}" to have a value or attributes`; | |
} | |
- | |
case "E0006": { | |
- var _args4 = _slicedToArray(args, 1), | |
- _id = _args4[0]; | |
- | |
- return 'Expected term "-'.concat(_id, '" to have a value'); | |
+ const [id] = args; | |
+ return `Expected term "-${id}" to have a value`; | |
} | |
- | |
case "E0007": | |
return "Keyword cannot end with a whitespace"; | |
- | |
case "E0008": | |
return "The callee has to be an upper-case identifier or a term"; | |
- | |
case "E0009": | |
return "The argument name has to be a simple identifier"; | |
- | |
case "E0010": | |
return "Expected one of the variants to be marked as default (*)"; | |
- | |
case "E0011": | |
return 'Expected at least one variant after "->"'; | |
- | |
case "E0012": | |
return "Expected value"; | |
- | |
case "E0013": | |
return "Expected variant key"; | |
- | |
case "E0014": | |
return "Expected literal"; | |
- | |
case "E0015": | |
return "Only one variant can be marked as default (*)"; | |
- | |
case "E0016": | |
return "Message references cannot be used as selectors"; | |
- | |
case "E0017": | |
return "Terms cannot be used as selectors"; | |
- | |
case "E0018": | |
return "Attributes of messages cannot be used as selectors"; | |
- | |
case "E0019": | |
return "Attributes of terms cannot be used as placeables"; | |
- | |
case "E0020": | |
return "Unterminated string expression"; | |
- | |
case "E0021": | |
return "Positional arguments must not follow named arguments"; | |
- | |
case "E0022": | |
return "Named arguments must be unique"; | |
- | |
case "E0024": | |
return "Cannot access variants of a message."; | |
- | |
case "E0025": { | |
- var _args5 = _slicedToArray(args, 1), | |
- char = _args5[0]; | |
- | |
- return "Unknown escape sequence: \\".concat(char, "."); | |
+ const [char] = args; | |
+ return `Unknown escape sequence: \\${char}.`; | |
} | |
- | |
case "E0026": { | |
- var _args6 = _slicedToArray(args, 1), | |
- sequence = _args6[0]; | |
- | |
- return "Invalid Unicode escape sequence: ".concat(sequence, "."); | |
+ const [sequence] = args; | |
+ return `Invalid Unicode escape sequence: ${sequence}.`; | |
} | |
- | |
case "E0027": | |
return "Unbalanced closing brace in TextElement."; | |
- | |
case "E0028": | |
return "Expected an inline expression"; | |
- | |
case "E0029": | |
return "Expected simple expression as selector"; | |
- | |
default: | |
return code; | |
} | |
@@ -727,7 +443,6 @@ | |
this.index = 0; | |
this.peekOffset = 0; | |
} | |
- | |
charAt(offset) { | |
// When the cursor is at CRLF, return LF but don't move the cursor. | |
// The cursor still points to the EOL position, which in this case is the | |
@@ -736,32 +451,26 @@ | |
if (this.string[offset] === "\r" && this.string[offset + 1] === "\n") { | |
return "\n"; | |
} | |
- | |
return this.string[offset]; | |
} | |
- | |
currentChar() { | |
return this.charAt(this.index); | |
} | |
- | |
currentPeek() { | |
return this.charAt(this.index + this.peekOffset); | |
} | |
- | |
next() { | |
- this.peekOffset = 0; // Skip over the CRLF as if it was a single character. | |
- | |
+ this.peekOffset = 0; | |
+ // Skip over the CRLF as if it was a single character. | |
if ( | |
this.string[this.index] === "\r" && | |
this.string[this.index + 1] === "\n" | |
) { | |
this.index++; | |
} | |
- | |
this.index++; | |
return this.string[this.index]; | |
} | |
- | |
peek() { | |
// Skip over the CRLF as if it was a single character. | |
if ( | |
@@ -770,280 +479,221 @@ | |
) { | |
this.peekOffset++; | |
} | |
- | |
this.peekOffset++; | |
return this.string[this.index + this.peekOffset]; | |
} | |
- | |
- resetPeek() { | |
- var offset = | |
- arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; | |
+ resetPeek(offset = 0) { | |
this.peekOffset = offset; | |
} | |
- | |
skipToPeek() { | |
this.index += this.peekOffset; | |
this.peekOffset = 0; | |
} | |
} | |
- var EOL = "\n"; | |
- var EOF = undefined; | |
- var SPECIAL_LINE_START_CHARS = ["}", ".", "[", "*"]; | |
+ const EOL = "\n"; | |
+ const EOF = undefined; | |
+ const SPECIAL_LINE_START_CHARS = ["}", ".", "[", "*"]; | |
class FluentParserStream extends ParserStream { | |
peekBlankInline() { | |
- var start = this.index + this.peekOffset; | |
- | |
+ const start = this.index + this.peekOffset; | |
while (this.currentPeek() === " ") { | |
this.peek(); | |
} | |
- | |
return this.string.slice(start, this.index + this.peekOffset); | |
} | |
- | |
skipBlankInline() { | |
- var blank = this.peekBlankInline(); | |
+ const blank = this.peekBlankInline(); | |
this.skipToPeek(); | |
return blank; | |
} | |
- | |
peekBlankBlock() { | |
- var blank = ""; | |
- | |
+ let blank = ""; | |
while (true) { | |
- var lineStart = this.peekOffset; | |
+ const lineStart = this.peekOffset; | |
this.peekBlankInline(); | |
- | |
if (this.currentPeek() === EOL) { | |
blank += EOL; | |
this.peek(); | |
continue; | |
} | |
- | |
if (this.currentPeek() === EOF) { | |
// Treat the blank line at EOF as a blank block. | |
return blank; | |
- } // Any other char; reset to column 1 on this line. | |
- | |
+ } | |
+ // Any other char; reset to column 1 on this line. | |
this.resetPeek(lineStart); | |
return blank; | |
} | |
} | |
- | |
skipBlankBlock() { | |
- var blank = this.peekBlankBlock(); | |
+ const blank = this.peekBlankBlock(); | |
this.skipToPeek(); | |
return blank; | |
} | |
- | |
peekBlank() { | |
while (this.currentPeek() === " " || this.currentPeek() === EOL) { | |
this.peek(); | |
} | |
} | |
- | |
skipBlank() { | |
this.peekBlank(); | |
this.skipToPeek(); | |
} | |
- | |
expectChar(ch) { | |
if (this.currentChar() === ch) { | |
this.next(); | |
return; | |
} | |
- | |
throw new ParseError("E0003", ch); | |
} | |
- | |
expectLineEnd() { | |
if (this.currentChar() === EOF) { | |
// EOF is a valid line end in Fluent. | |
return; | |
} | |
- | |
if (this.currentChar() === EOL) { | |
this.next(); | |
return; | |
- } // Unicode Character 'SYMBOL FOR NEWLINE' (U+2424) | |
- | |
+ } | |
+ // Unicode Character 'SYMBOL FOR NEWLINE' (U+2424) | |
throw new ParseError("E0003", "\u2424"); | |
} | |
- | |
takeChar(f) { | |
- var ch = this.currentChar(); | |
- | |
+ const ch = this.currentChar(); | |
if (ch === EOF) { | |
return EOF; | |
} | |
- | |
if (f(ch)) { | |
this.next(); | |
return ch; | |
} | |
- | |
return null; | |
} | |
- | |
isCharIdStart(ch) { | |
if (ch === EOF) { | |
return false; | |
} | |
- | |
- var cc = ch.charCodeAt(0); | |
+ const cc = ch.charCodeAt(0); | |
return ( | |
(cc >= 97 && cc <= 122) || // a-z | |
(cc >= 65 && cc <= 90) | |
); // A-Z | |
} | |
- | |
isIdentifierStart() { | |
return this.isCharIdStart(this.currentPeek()); | |
} | |
- | |
isNumberStart() { | |
- var ch = this.currentChar() === "-" ? this.peek() : this.currentChar(); | |
- | |
+ const ch = this.currentChar() === "-" ? this.peek() : this.currentChar(); | |
if (ch === EOF) { | |
this.resetPeek(); | |
return false; | |
} | |
- | |
- var cc = ch.charCodeAt(0); | |
- var isDigit = cc >= 48 && cc <= 57; // 0-9 | |
- | |
+ const cc = ch.charCodeAt(0); | |
+ const isDigit = cc >= 48 && cc <= 57; // 0-9 | |
this.resetPeek(); | |
return isDigit; | |
} | |
- | |
isCharPatternContinuation(ch) { | |
if (ch === EOF) { | |
return false; | |
} | |
- | |
return !SPECIAL_LINE_START_CHARS.includes(ch); | |
} | |
- | |
isValueStart() { | |
// Inline Patterns may start with any char. | |
- var ch = this.currentPeek(); | |
+ const ch = this.currentPeek(); | |
return ch !== EOL && ch !== EOF; | |
} | |
- | |
isValueContinuation() { | |
- var column1 = this.peekOffset; | |
+ const column1 = this.peekOffset; | |
this.peekBlankInline(); | |
- | |
if (this.currentPeek() === "{") { | |
this.resetPeek(column1); | |
return true; | |
} | |
- | |
if (this.peekOffset - column1 === 0) { | |
return false; | |
} | |
- | |
if (this.isCharPatternContinuation(this.currentPeek())) { | |
this.resetPeek(column1); | |
return true; | |
} | |
- | |
return false; | |
- } // -1 - any | |
+ } | |
+ // -1 - any | |
// 0 - comment | |
// 1 - group comment | |
// 2 - resource comment | |
- | |
- isNextLineComment() { | |
- var level = | |
- arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : -1; | |
- | |
+ isNextLineComment(level = -1) { | |
if (this.currentChar() !== EOL) { | |
return false; | |
} | |
- | |
- var i = 0; | |
- | |
+ let i = 0; | |
while (i <= level || (level === -1 && i < 3)) { | |
if (this.peek() !== "#") { | |
if (i <= level && level !== -1) { | |
this.resetPeek(); | |
return false; | |
} | |
- | |
break; | |
} | |
- | |
i++; | |
- } // The first char after #, ## or ###. | |
- | |
- var ch = this.peek(); | |
- | |
+ } | |
+ // The first char after #, ## or ###. | |
+ const ch = this.peek(); | |
if (ch === " " || ch === EOL) { | |
this.resetPeek(); | |
return true; | |
} | |
- | |
this.resetPeek(); | |
return false; | |
} | |
- | |
isVariantStart() { | |
- var currentPeekOffset = this.peekOffset; | |
- | |
+ const currentPeekOffset = this.peekOffset; | |
if (this.currentPeek() === "*") { | |
this.peek(); | |
} | |
- | |
if (this.currentPeek() === "[") { | |
this.resetPeek(currentPeekOffset); | |
return true; | |
} | |
- | |
this.resetPeek(currentPeekOffset); | |
return false; | |
} | |
- | |
isAttributeStart() { | |
return this.currentPeek() === "."; | |
} | |
- | |
skipToNextEntryStart(junkStart) { | |
- var lastNewline = this.string.lastIndexOf(EOL, this.index); | |
- | |
+ let lastNewline = this.string.lastIndexOf(EOL, this.index); | |
if (junkStart < lastNewline) { | |
// Last seen newline is _after_ the junk start. It's safe to rewind | |
// without the risk of resuming at the same broken entry. | |
this.index = lastNewline; | |
} | |
- | |
while (this.currentChar()) { | |
// We're only interested in beginnings of line. | |
if (this.currentChar() !== EOL) { | |
this.next(); | |
continue; | |
- } // Break if the first char in this line looks like an entry start. | |
- | |
- var first = this.next(); | |
- | |
+ } | |
+ // Break if the first char in this line looks like an entry start. | |
+ const first = this.next(); | |
if (this.isCharIdStart(first) || first === "-" || first === "#") { | |
break; | |
} | |
} | |
} | |
- | |
takeIDStart() { | |
if (this.isCharIdStart(this.currentChar())) { | |
- var ret = this.currentChar(); | |
+ const ret = this.currentChar(); | |
this.next(); | |
return ret; | |
} | |
- | |
throw new ParseError("E0004", "a-zA-Z"); | |
} | |
- | |
takeIDChar() { | |
- var closure = (ch) => { | |
- var cc = ch.charCodeAt(0); | |
+ const closure = (ch) => { | |
+ const cc = ch.charCodeAt(0); | |
return ( | |
(cc >= 97 && cc <= 122) || // a-z | |
(cc >= 65 && cc <= 90) || // A-Z | |
@@ -1052,78 +702,52 @@ | |
cc === 45 | |
); // _- | |
}; | |
- | |
return this.takeChar(closure); | |
} | |
- | |
takeDigit() { | |
- var closure = (ch) => { | |
- var cc = ch.charCodeAt(0); | |
+ const closure = (ch) => { | |
+ const cc = ch.charCodeAt(0); | |
return cc >= 48 && cc <= 57; // 0-9 | |
}; | |
- | |
return this.takeChar(closure); | |
} | |
- | |
takeHexDigit() { | |
- var closure = (ch) => { | |
- var cc = ch.charCodeAt(0); | |
+ const closure = (ch) => { | |
+ const cc = ch.charCodeAt(0); | |
return ( | |
(cc >= 48 && cc <= 57) || // 0-9 | |
(cc >= 65 && cc <= 70) || // A-F | |
(cc >= 97 && cc <= 102) | |
); // a-f | |
}; | |
- | |
return this.takeChar(closure); | |
} | |
} | |
- var trailingWSRe = /[ \t\n\r]+$/; | |
- | |
+ /* eslint no-magic-numbers: [0] */ | |
+ const trailingWSRe = /[ \t\n\r]+$/; | |
function withSpan(fn) { | |
- return function (ps) { | |
- for ( | |
- var _len = arguments.length, | |
- args = new Array(_len > 1 ? _len - 1 : 0), | |
- _key = 1; | |
- _key < _len; | |
- _key++ | |
- ) { | |
- args[_key - 1] = arguments[_key]; | |
- } | |
- | |
+ return function (ps, ...args) { | |
if (!this.withSpans) { | |
return fn.call(this, ps, ...args); | |
} | |
- | |
- var start = ps.index; | |
- var node = fn.call(this, ps, ...args); // Don't re-add the span if the node already has it. This may happen when | |
+ const start = ps.index; | |
+ const node = fn.call(this, ps, ...args); | |
+ // Don't re-add the span if the node already has it. This may happen when | |
// one decorated function calls another decorated function. | |
- | |
if (node.span) { | |
return node; | |
} | |
- | |
- var end = ps.index; | |
+ const end = ps.index; | |
node.addSpan(start, end); | |
return node; | |
}; | |
} | |
- | |
class FluentParser { | |
- constructor() { | |
- var _ref = | |
- arguments.length > 0 && arguments[0] !== undefined | |
- ? arguments[0] | |
- : {}, | |
- _ref$withSpans = _ref.withSpans, | |
- withSpans = _ref$withSpans === void 0 ? true : _ref$withSpans; | |
- | |
- this.withSpans = withSpans; // Poor man's decorators. | |
- | |
+ constructor({ withSpans = true } = {}) { | |
+ this.withSpans = withSpans; | |
+ // Poor man's decorators. | |
/* eslint-disable @typescript-eslint/unbound-method */ | |
- | |
this.getComment = withSpan(this.getComment); | |
this.getMessage = withSpan(this.getMessage); | |
this.getTerm = withSpan(this.getTerm); | |
@@ -1143,21 +767,19 @@ | |
this.getComment = withSpan(this.getComment); | |
/* eslint-enable @typescript-eslint/unbound-method */ | |
} | |
- | |
parse(source) { | |
- var ps = new FluentParserStream(source); | |
+ const ps = new FluentParserStream(source); | |
ps.skipBlankBlock(); | |
- var entries = []; | |
- var lastComment = null; | |
- | |
+ const entries = []; | |
+ let lastComment = null; | |
while (ps.currentChar()) { | |
- var entry = this.getEntryOrJunk(ps); | |
- var blankLines = ps.skipBlankBlock(); // Regular Comments require special logic. Comments may be attached to | |
+ const entry = this.getEntryOrJunk(ps); | |
+ const blankLines = ps.skipBlankBlock(); | |
+ // Regular Comments require special logic. Comments may be attached to | |
// Messages or Terms if they are followed immediately by them. However | |
// they should parse as standalone when they're followed by Junk. | |
// Consequently, we only attach Comments once we know that the Message | |
// or the Term parsed successfully. | |
- | |
if ( | |
entry instanceof Comment && | |
blankLines.length === 0 && | |
@@ -1167,31 +789,26 @@ | |
lastComment = entry; | |
continue; | |
} | |
- | |
if (lastComment) { | |
if (entry instanceof Message || entry instanceof Term) { | |
entry.comment = lastComment; | |
- | |
if (this.withSpans) { | |
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
entry.span.start = entry.comment.span.start; | |
} | |
} else { | |
entries.push(lastComment); | |
- } // In either case, the stashed comment has been dealt with; clear it. | |
- | |
+ } | |
+ // In either case, the stashed comment has been dealt with; clear it. | |
lastComment = null; | |
- } // No special logic for other types of entries. | |
- | |
+ } | |
+ // No special logic for other types of entries. | |
entries.push(entry); | |
} | |
- | |
- var res = new Resource(entries); | |
- | |
+ const res = new Resource(entries); | |
if (this.withSpans) { | |
res.addSpan(0, ps.index); | |
} | |
- | |
return res; | |
} | |
/* | |
@@ -1203,104 +820,82 @@ | |
* Preceding comments are ignored unless they contain syntax errors | |
* themselves, in which case Junk for the invalid comment is returned. | |
*/ | |
- | |
parseEntry(source) { | |
- var ps = new FluentParserStream(source); | |
+ const ps = new FluentParserStream(source); | |
ps.skipBlankBlock(); | |
- | |
while (ps.currentChar() === "#") { | |
- var skipped = this.getEntryOrJunk(ps); | |
- | |
+ const skipped = this.getEntryOrJunk(ps); | |
if (skipped instanceof Junk) { | |
// Don't skip Junk comments. | |
return skipped; | |
} | |
- | |
ps.skipBlankBlock(); | |
} | |
- | |
return this.getEntryOrJunk(ps); | |
} | |
- | |
getEntryOrJunk(ps) { | |
- var entryStartPos = ps.index; | |
- | |
+ const entryStartPos = ps.index; | |
try { | |
- var entry = this.getEntry(ps); | |
+ const entry = this.getEntry(ps); | |
ps.expectLineEnd(); | |
return entry; | |
} catch (err) { | |
if (!(err instanceof ParseError)) { | |
throw err; | |
} | |
- | |
- var errorIndex = ps.index; | |
+ let errorIndex = ps.index; | |
ps.skipToNextEntryStart(entryStartPos); | |
- var nextEntryStart = ps.index; | |
- | |
+ const nextEntryStart = ps.index; | |
if (nextEntryStart < errorIndex) { | |
// The position of the error must be inside of the Junk's span. | |
errorIndex = nextEntryStart; | |
- } // Create a Junk instance | |
- | |
- var slice = ps.string.substring(entryStartPos, nextEntryStart); | |
- var junk = new Junk(slice); | |
- | |
+ } | |
+ // Create a Junk instance | |
+ const slice = ps.string.substring(entryStartPos, nextEntryStart); | |
+ const junk = new Junk(slice); | |
if (this.withSpans) { | |
junk.addSpan(entryStartPos, nextEntryStart); | |
} | |
- | |
- var annot = new Annotation(err.code, err.args, err.message); | |
+ const annot = new Annotation(err.code, err.args, err.message); | |
annot.addSpan(errorIndex, errorIndex); | |
junk.addAnnotation(annot); | |
return junk; | |
} | |
} | |
- | |
getEntry(ps) { | |
if (ps.currentChar() === "#") { | |
return this.getComment(ps); | |
} | |
- | |
if (ps.currentChar() === "-") { | |
return this.getTerm(ps); | |
} | |
- | |
if (ps.isIdentifierStart()) { | |
return this.getMessage(ps); | |
} | |
- | |
throw new ParseError("E0002"); | |
} | |
- | |
getComment(ps) { | |
// 0 - comment | |
// 1 - group comment | |
// 2 - resource comment | |
- var level = -1; | |
- var content = ""; | |
- | |
+ let level = -1; | |
+ let content = ""; | |
while (true) { | |
- var i = -1; | |
- | |
+ let i = -1; | |
while (ps.currentChar() === "#" && i < (level === -1 ? 2 : level)) { | |
ps.next(); | |
i++; | |
} | |
- | |
if (level === -1) { | |
level = i; | |
} | |
- | |
if (ps.currentChar() !== EOL) { | |
ps.expectChar(" "); | |
- var ch = void 0; | |
- | |
+ let ch; | |
while ((ch = ps.takeChar((x) => x !== EOL))) { | |
content += ch; | |
} | |
} | |
- | |
if (ps.isNextLineComment(level)) { | |
content += ps.currentChar(); | |
ps.next(); | |
@@ -1308,439 +903,330 @@ | |
break; | |
} | |
} | |
- | |
- var Comment$1; | |
- | |
+ let Comment$1; | |
switch (level) { | |
case 0: | |
Comment$1 = Comment; | |
break; | |
- | |
case 1: | |
Comment$1 = GroupComment; | |
break; | |
- | |
default: | |
Comment$1 = ResourceComment; | |
} | |
- | |
return new Comment$1(content); | |
} | |
- | |
getMessage(ps) { | |
- var id = this.getIdentifier(ps); | |
+ const id = this.getIdentifier(ps); | |
ps.skipBlankInline(); | |
ps.expectChar("="); | |
- var value = this.maybeGetPattern(ps); | |
- var attrs = this.getAttributes(ps); | |
- | |
+ const value = this.maybeGetPattern(ps); | |
+ const attrs = this.getAttributes(ps); | |
if (value === null && attrs.length === 0) { | |
throw new ParseError("E0005", id.name); | |
} | |
- | |
return new Message(id, value, attrs); | |
} | |
- | |
getTerm(ps) { | |
ps.expectChar("-"); | |
- var id = this.getIdentifier(ps); | |
+ const id = this.getIdentifier(ps); | |
ps.skipBlankInline(); | |
ps.expectChar("="); | |
- var value = this.maybeGetPattern(ps); | |
- | |
+ const value = this.maybeGetPattern(ps); | |
if (value === null) { | |
throw new ParseError("E0006", id.name); | |
} | |
- | |
- var attrs = this.getAttributes(ps); | |
+ const attrs = this.getAttributes(ps); | |
return new Term(id, value, attrs); | |
} | |
- | |
getAttribute(ps) { | |
ps.expectChar("."); | |
- var key = this.getIdentifier(ps); | |
+ const key = this.getIdentifier(ps); | |
ps.skipBlankInline(); | |
ps.expectChar("="); | |
- var value = this.maybeGetPattern(ps); | |
- | |
+ const value = this.maybeGetPattern(ps); | |
if (value === null) { | |
throw new ParseError("E0012"); | |
} | |
- | |
return new Attribute(key, value); | |
} | |
- | |
getAttributes(ps) { | |
- var attrs = []; | |
+ const attrs = []; | |
ps.peekBlank(); | |
- | |
while (ps.isAttributeStart()) { | |
ps.skipToPeek(); | |
- var attr = this.getAttribute(ps); | |
+ const attr = this.getAttribute(ps); | |
attrs.push(attr); | |
ps.peekBlank(); | |
} | |
- | |
return attrs; | |
} | |
- | |
getIdentifier(ps) { | |
- var name = ps.takeIDStart(); | |
- var ch; | |
- | |
+ let name = ps.takeIDStart(); | |
+ let ch; | |
while ((ch = ps.takeIDChar())) { | |
name += ch; | |
} | |
- | |
return new Identifier(name); | |
} | |
- | |
getVariantKey(ps) { | |
- var ch = ps.currentChar(); | |
- | |
+ const ch = ps.currentChar(); | |
if (ch === EOF) { | |
throw new ParseError("E0013"); | |
} | |
- | |
- var cc = ch.charCodeAt(0); | |
- | |
+ const cc = ch.charCodeAt(0); | |
if ((cc >= 48 && cc <= 57) || cc === 45) { | |
// 0-9, - | |
return this.getNumber(ps); | |
} | |
- | |
return this.getIdentifier(ps); | |
} | |
- | |
- getVariant(ps) { | |
- var hasDefault = | |
- arguments.length > 1 && arguments[1] !== undefined | |
- ? arguments[1] | |
- : false; | |
- var defaultIndex = false; | |
- | |
+ getVariant(ps, hasDefault = false) { | |
+ let defaultIndex = false; | |
if (ps.currentChar() === "*") { | |
if (hasDefault) { | |
throw new ParseError("E0015"); | |
} | |
- | |
ps.next(); | |
defaultIndex = true; | |
} | |
- | |
ps.expectChar("["); | |
ps.skipBlank(); | |
- var key = this.getVariantKey(ps); | |
+ const key = this.getVariantKey(ps); | |
ps.skipBlank(); | |
ps.expectChar("]"); | |
- var value = this.maybeGetPattern(ps); | |
- | |
+ const value = this.maybeGetPattern(ps); | |
if (value === null) { | |
throw new ParseError("E0012"); | |
} | |
- | |
return new Variant(key, value, defaultIndex); | |
} | |
- | |
getVariants(ps) { | |
- var variants = []; | |
- var hasDefault = false; | |
+ const variants = []; | |
+ let hasDefault = false; | |
ps.skipBlank(); | |
- | |
while (ps.isVariantStart()) { | |
- var variant = this.getVariant(ps, hasDefault); | |
- | |
+ const variant = this.getVariant(ps, hasDefault); | |
if (variant.default) { | |
hasDefault = true; | |
} | |
- | |
variants.push(variant); | |
ps.expectLineEnd(); | |
ps.skipBlank(); | |
} | |
- | |
if (variants.length === 0) { | |
throw new ParseError("E0011"); | |
} | |
- | |
if (!hasDefault) { | |
throw new ParseError("E0010"); | |
} | |
- | |
return variants; | |
} | |
- | |
getDigits(ps) { | |
- var num = ""; | |
- var ch; | |
- | |
+ let num = ""; | |
+ let ch; | |
while ((ch = ps.takeDigit())) { | |
num += ch; | |
} | |
- | |
if (num.length === 0) { | |
throw new ParseError("E0004", "0-9"); | |
} | |
- | |
return num; | |
} | |
- | |
getNumber(ps) { | |
- var value = ""; | |
- | |
+ let value = ""; | |
if (ps.currentChar() === "-") { | |
ps.next(); | |
- value += "-".concat(this.getDigits(ps)); | |
+ value += `-${this.getDigits(ps)}`; | |
} else { | |
value += this.getDigits(ps); | |
} | |
- | |
if (ps.currentChar() === ".") { | |
ps.next(); | |
- value += ".".concat(this.getDigits(ps)); | |
+ value += `.${this.getDigits(ps)}`; | |
} | |
- | |
return new NumberLiteral(value); | |
- } // maybeGetPattern distinguishes between patterns which start on the same line | |
+ } | |
+ // maybeGetPattern distinguishes between patterns which start on the same line | |
// as the identifier (a.k.a. inline signleline patterns and inline multiline | |
// patterns) and patterns which start on a new line (a.k.a. block multiline | |
// patterns). The distinction is important for the dedentation logic: the | |
// indent of the first line of a block pattern must be taken into account when | |
// calculating the maximum common indent. | |
- | |
maybeGetPattern(ps) { | |
ps.peekBlankInline(); | |
- | |
if (ps.isValueStart()) { | |
ps.skipToPeek(); | |
return this.getPattern(ps, false); | |
} | |
- | |
ps.peekBlankBlock(); | |
- | |
if (ps.isValueContinuation()) { | |
ps.skipToPeek(); | |
return this.getPattern(ps, true); | |
} | |
- | |
return null; | |
} | |
- | |
getPattern(ps, isBlock) { | |
- var elements = []; | |
- var commonIndentLength; | |
- | |
+ const elements = []; | |
+ let commonIndentLength; | |
if (isBlock) { | |
// A block pattern is a pattern which starts on a new line. Store and | |
// measure the indent of this first line for the dedentation logic. | |
- var blankStart = ps.index; | |
- var firstIndent = ps.skipBlankInline(); | |
+ const blankStart = ps.index; | |
+ const firstIndent = ps.skipBlankInline(); | |
elements.push(this.getIndent(ps, firstIndent, blankStart)); | |
commonIndentLength = firstIndent.length; | |
} else { | |
commonIndentLength = Infinity; | |
} | |
- | |
- var ch; | |
- | |
+ let ch; | |
elements: while ((ch = ps.currentChar())) { | |
switch (ch) { | |
case EOL: { | |
- var _blankStart = ps.index; | |
- var blankLines = ps.peekBlankBlock(); | |
- | |
+ const blankStart = ps.index; | |
+ const blankLines = ps.peekBlankBlock(); | |
if (ps.isValueContinuation()) { | |
ps.skipToPeek(); | |
- var indent = ps.skipBlankInline(); | |
+ const indent = ps.skipBlankInline(); | |
commonIndentLength = Math.min(commonIndentLength, indent.length); | |
elements.push( | |
- this.getIndent(ps, blankLines + indent, _blankStart) | |
+ this.getIndent(ps, blankLines + indent, blankStart) | |
); | |
continue elements; | |
- } // The end condition for getPattern's while loop is a newline | |
+ } | |
+ // The end condition for getPattern's while loop is a newline | |
// which is not followed by a valid pattern continuation. | |
- | |
ps.resetPeek(); | |
break elements; | |
} | |
- | |
case "{": | |
elements.push(this.getPlaceable(ps)); | |
continue elements; | |
- | |
case "}": | |
throw new ParseError("E0027"); | |
- | |
default: | |
elements.push(this.getTextElement(ps)); | |
} | |
} | |
- | |
- var dedented = this.dedent(elements, commonIndentLength); | |
+ const dedented = this.dedent(elements, commonIndentLength); | |
return new Pattern(dedented); | |
- } // Create a token representing an indent. It's not part of the AST and it will | |
+ } | |
+ // Create a token representing an indent. It's not part of the AST and it will | |
// be trimmed and merged into adjacent TextElements, or turned into a new | |
// TextElement, if it's surrounded by two Placeables. | |
- | |
getIndent(ps, value, start) { | |
return new Indent(value, start, ps.index); | |
- } // Dedent a list of elements by removing the maximum common indent from the | |
+ } | |
+ // Dedent a list of elements by removing the maximum common indent from the | |
// beginning of text lines. The common indent is calculated in getPattern. | |
- | |
dedent(elements, commonIndent) { | |
- var trimmed = []; | |
- | |
- var _iterator = _createForOfIteratorHelper(elements), | |
- _step; | |
- | |
- try { | |
- for (_iterator.s(); !(_step = _iterator.n()).done; ) { | |
- var element = _step.value; | |
- | |
+ const trimmed = []; | |
+ for (let element of elements) { | |
if (element instanceof Placeable) { | |
trimmed.push(element); | |
continue; | |
} | |
- | |
if (element instanceof Indent) { | |
// Strip common indent. | |
element.value = element.value.slice( | |
0, | |
element.value.length - commonIndent | |
); | |
- | |
if (element.value.length === 0) { | |
continue; | |
} | |
} | |
- | |
- var prev = trimmed[trimmed.length - 1]; | |
- | |
+ let prev = trimmed[trimmed.length - 1]; | |
if (prev && prev instanceof TextElement) { | |
// Join adjacent TextElements by replacing them with their sum. | |
- var sum = new TextElement(prev.value + element.value); | |
- | |
+ const sum = new TextElement(prev.value + element.value); | |
if (this.withSpans) { | |
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
sum.addSpan(prev.span.start, element.span.end); | |
} | |
- | |
trimmed[trimmed.length - 1] = sum; | |
continue; | |
} | |
- | |
if (element instanceof Indent) { | |
// If the indent hasn't been merged into a preceding TextElement, | |
// convert it into a new TextElement. | |
- var textElement = new TextElement(element.value); | |
- | |
+ const textElement = new TextElement(element.value); | |
if (this.withSpans) { | |
textElement.addSpan(element.span.start, element.span.end); | |
} | |
- | |
element = textElement; | |
} | |
- | |
trimmed.push(element); | |
- } // Trim trailing whitespace from the Pattern. | |
- } catch (err) { | |
- _iterator.e(err); | |
- } finally { | |
- _iterator.f(); | |
} | |
- | |
- var lastElement = trimmed[trimmed.length - 1]; | |
- | |
+ // Trim trailing whitespace from the Pattern. | |
+ const lastElement = trimmed[trimmed.length - 1]; | |
if (lastElement instanceof TextElement) { | |
lastElement.value = lastElement.value.replace(trailingWSRe, ""); | |
- | |
if (lastElement.value.length === 0) { | |
trimmed.pop(); | |
} | |
} | |
- | |
return trimmed; | |
} | |
- | |
getTextElement(ps) { | |
- var buffer = ""; | |
- var ch; | |
- | |
+ let buffer = ""; | |
+ let ch; | |
while ((ch = ps.currentChar())) { | |
if (ch === "{" || ch === "}") { | |
return new TextElement(buffer); | |
} | |
- | |
if (ch === EOL) { | |
return new TextElement(buffer); | |
} | |
- | |
buffer += ch; | |
ps.next(); | |
} | |
- | |
return new TextElement(buffer); | |
} | |
- | |
getEscapeSequence(ps) { | |
- var next = ps.currentChar(); | |
- | |
+ const next = ps.currentChar(); | |
switch (next) { | |
case "\\": | |
case '"': | |
ps.next(); | |
- return "\\".concat(next); | |
- | |
+ return `\\${next}`; | |
case "u": | |
return this.getUnicodeEscapeSequence(ps, next, 4); | |
- | |
case "U": | |
return this.getUnicodeEscapeSequence(ps, next, 6); | |
- | |
default: | |
throw new ParseError("E0025", next); | |
} | |
} | |
- | |
getUnicodeEscapeSequence(ps, u, digits) { | |
ps.expectChar(u); | |
- var sequence = ""; | |
- | |
- for (var i = 0; i < digits; i++) { | |
- var ch = ps.takeHexDigit(); | |
- | |
+ let sequence = ""; | |
+ for (let i = 0; i < digits; i++) { | |
+ const ch = ps.takeHexDigit(); | |
if (!ch) { | |
- throw new ParseError( | |
- "E0026", | |
- "\\".concat(u).concat(sequence).concat(ps.currentChar()) | |
- ); | |
+ throw new ParseError("E0026", `\\${u}${sequence}${ps.currentChar()}`); | |
} | |
- | |
sequence += ch; | |
} | |
- | |
- return "\\".concat(u).concat(sequence); | |
+ return `\\${u}${sequence}`; | |
} | |
- | |
getPlaceable(ps) { | |
ps.expectChar("{"); | |
ps.skipBlank(); | |
- var expression = this.getExpression(ps); | |
+ const expression = this.getExpression(ps); | |
ps.expectChar("}"); | |
return new Placeable(expression); | |
} | |
- | |
getExpression(ps) { | |
- var selector = this.getInlineExpression(ps); | |
+ const selector = this.getInlineExpression(ps); | |
ps.skipBlank(); | |
- | |
if (ps.currentChar() === "-") { | |
if (ps.peek() !== ">") { | |
ps.resetPeek(); | |
return selector; | |
- } // Validate selector expression according to | |
+ } | |
+ // Validate selector expression according to | |
// abstract.js in the Fluent specification | |
- | |
if (selector instanceof MessageReference) { | |
if (selector.attribute === null) { | |
throw new ParseError("E0016"); | |
@@ -1754,132 +1240,99 @@ | |
} else if (selector instanceof Placeable) { | |
throw new ParseError("E0029"); | |
} | |
- | |
ps.next(); | |
ps.next(); | |
ps.skipBlankInline(); | |
ps.expectLineEnd(); | |
- var variants = this.getVariants(ps); | |
+ const variants = this.getVariants(ps); | |
return new SelectExpression(selector, variants); | |
} | |
- | |
if (selector instanceof TermReference && selector.attribute !== null) { | |
throw new ParseError("E0019"); | |
} | |
- | |
return selector; | |
} | |
- | |
getInlineExpression(ps) { | |
if (ps.currentChar() === "{") { | |
return this.getPlaceable(ps); | |
} | |
- | |
if (ps.isNumberStart()) { | |
return this.getNumber(ps); | |
} | |
- | |
if (ps.currentChar() === '"') { | |
return this.getString(ps); | |
} | |
- | |
if (ps.currentChar() === "$") { | |
ps.next(); | |
- var id = this.getIdentifier(ps); | |
+ const id = this.getIdentifier(ps); | |
return new VariableReference(id); | |
} | |
- | |
if (ps.currentChar() === "-") { | |
ps.next(); | |
- | |
- var _id = this.getIdentifier(ps); | |
- | |
- var attr; | |
- | |
+ const id = this.getIdentifier(ps); | |
+ let attr; | |
if (ps.currentChar() === ".") { | |
ps.next(); | |
attr = this.getIdentifier(ps); | |
} | |
- | |
- var args; | |
+ let args; | |
ps.peekBlank(); | |
- | |
if (ps.currentPeek() === "(") { | |
ps.skipToPeek(); | |
args = this.getCallArguments(ps); | |
} | |
- | |
- return new TermReference(_id, attr, args); | |
+ return new TermReference(id, attr, args); | |
} | |
- | |
if (ps.isIdentifierStart()) { | |
- var _id2 = this.getIdentifier(ps); | |
- | |
+ const id = this.getIdentifier(ps); | |
ps.peekBlank(); | |
- | |
if (ps.currentPeek() === "(") { | |
// It's a Function. Ensure it's all upper-case. | |
- if (!/^[A-Z][A-Z0-9_-]*$/.test(_id2.name)) { | |
+ if (!/^[A-Z][A-Z0-9_-]*$/.test(id.name)) { | |
throw new ParseError("E0008"); | |
} | |
- | |
ps.skipToPeek(); | |
- | |
- var _args = this.getCallArguments(ps); | |
- | |
- return new FunctionReference(_id2, _args); | |
+ let args = this.getCallArguments(ps); | |
+ return new FunctionReference(id, args); | |
} | |
- | |
- var _attr; | |
- | |
+ let attr; | |
if (ps.currentChar() === ".") { | |
ps.next(); | |
- _attr = this.getIdentifier(ps); | |
+ attr = this.getIdentifier(ps); | |
} | |
- | |
- return new MessageReference(_id2, _attr); | |
+ return new MessageReference(id, attr); | |
} | |
- | |
throw new ParseError("E0028"); | |
} | |
- | |
getCallArgument(ps) { | |
- var exp = this.getInlineExpression(ps); | |
+ const exp = this.getInlineExpression(ps); | |
ps.skipBlank(); | |
- | |
if (ps.currentChar() !== ":") { | |
return exp; | |
} | |
- | |
if (exp instanceof MessageReference && exp.attribute === null) { | |
ps.next(); | |
ps.skipBlank(); | |
- var value = this.getLiteral(ps); | |
+ const value = this.getLiteral(ps); | |
return new NamedArgument(exp.id, value); | |
} | |
- | |
throw new ParseError("E0009"); | |
} | |
- | |
getCallArguments(ps) { | |
- var positional = []; | |
- var named = []; | |
- var argumentNames = new Set(); | |
+ const positional = []; | |
+ const named = []; | |
+ const argumentNames = new Set(); | |
ps.expectChar("("); | |
ps.skipBlank(); | |
- | |
while (true) { | |
if (ps.currentChar() === ")") { | |
break; | |
} | |
- | |
- var arg = this.getCallArgument(ps); | |
- | |
+ const arg = this.getCallArgument(ps); | |
if (arg instanceof NamedArgument) { | |
if (argumentNames.has(arg.name.name)) { | |
throw new ParseError("E0022"); | |
} | |
- | |
named.push(arg); | |
argumentNames.add(arg.name.name); | |
} else if (argumentNames.size > 0) { | |
@@ -1887,27 +1340,21 @@ | |
} else { | |
positional.push(arg); | |
} | |
- | |
ps.skipBlank(); | |
- | |
if (ps.currentChar() === ",") { | |
ps.next(); | |
ps.skipBlank(); | |
continue; | |
} | |
- | |
break; | |
} | |
- | |
ps.expectChar(")"); | |
return new CallArguments(positional, named); | |
} | |
- | |
getString(ps) { | |
ps.expectChar('"'); | |
- var value = ""; | |
- var ch; | |
- | |
+ let value = ""; | |
+ let ch; | |
while ((ch = ps.takeChar((x) => x !== '"' && x !== EOL))) { | |
if (ch === "\\") { | |
value += this.getEscapeSequence(ps); | |
@@ -1915,28 +1362,22 @@ | |
value += ch; | |
} | |
} | |
- | |
if (ps.currentChar() === EOL) { | |
throw new ParseError("E0020"); | |
} | |
- | |
ps.expectChar('"'); | |
return new StringLiteral(value); | |
} | |
- | |
getLiteral(ps) { | |
if (ps.isNumberStart()) { | |
return this.getNumber(ps); | |
} | |
- | |
if (ps.currentChar() === '"') { | |
return this.getString(ps); | |
} | |
- | |
throw new ParseError("E0014"); | |
} | |
} | |
- | |
class Indent { | |
constructor(value, start, end) { | |
this.type = "Indent"; | |
@@ -1948,335 +1389,211 @@ | |
function indent(content) { | |
return content.split("\n").join("\n "); | |
} | |
- | |
function includesNewLine(elem) { | |
return elem instanceof TextElement && elem.value.includes("\n"); | |
} | |
- | |
function isSelectExpr(elem) { | |
return ( | |
elem instanceof Placeable && elem.expression instanceof SelectExpression | |
); | |
- } // Bit masks representing the state of the serializer. | |
- | |
- var HAS_ENTRIES = 1; | |
+ } | |
+ // Bit masks representing the state of the serializer. | |
+ const HAS_ENTRIES = 1; | |
class FluentSerializer { | |
- constructor() { | |
- var _ref = | |
- arguments.length > 0 && arguments[0] !== undefined | |
- ? arguments[0] | |
- : {}, | |
- _ref$withJunk = _ref.withJunk, | |
- withJunk = _ref$withJunk === void 0 ? false : _ref$withJunk; | |
- | |
+ constructor({ withJunk = false } = {}) { | |
this.withJunk = withJunk; | |
} | |
- | |
serialize(resource) { | |
if (!(resource instanceof Resource)) { | |
- throw new Error("Unknown resource type: ".concat(resource)); | |
+ throw new Error(`Unknown resource type: ${resource}`); | |
} | |
- | |
- var state = 0; | |
- var parts = []; | |
- | |
- var _iterator = _createForOfIteratorHelper(resource.body), | |
- _step; | |
- | |
- try { | |
- for (_iterator.s(); !(_step = _iterator.n()).done; ) { | |
- var entry = _step.value; | |
- | |
+ let state = 0; | |
+ const parts = []; | |
+ for (const entry of resource.body) { | |
if (!(entry instanceof Junk) || this.withJunk) { | |
parts.push(this.serializeEntry(entry, state)); | |
- | |
if (!(state & HAS_ENTRIES)) { | |
state |= HAS_ENTRIES; | |
} | |
} | |
} | |
- } catch (err) { | |
- _iterator.e(err); | |
- } finally { | |
- _iterator.f(); | |
- } | |
- | |
return parts.join(""); | |
} | |
- | |
- serializeEntry(entry) { | |
- var state = | |
- arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; | |
- | |
+ serializeEntry(entry, state = 0) { | |
if (entry instanceof Message) { | |
return serializeMessage(entry); | |
} | |
- | |
if (entry instanceof Term) { | |
return serializeTerm(entry); | |
} | |
- | |
if (entry instanceof Comment) { | |
if (state & HAS_ENTRIES) { | |
- return "\n".concat(serializeComment(entry, "#"), "\n"); | |
+ return `\n${serializeComment(entry, "#")}\n`; | |
} | |
- | |
- return "".concat(serializeComment(entry, "#"), "\n"); | |
+ return `${serializeComment(entry, "#")}\n`; | |
} | |
- | |
if (entry instanceof GroupComment) { | |
if (state & HAS_ENTRIES) { | |
- return "\n".concat(serializeComment(entry, "##"), "\n"); | |
+ return `\n${serializeComment(entry, "##")}\n`; | |
} | |
- | |
- return "".concat(serializeComment(entry, "##"), "\n"); | |
+ return `${serializeComment(entry, "##")}\n`; | |
} | |
- | |
if (entry instanceof ResourceComment) { | |
if (state & HAS_ENTRIES) { | |
- return "\n".concat(serializeComment(entry, "###"), "\n"); | |
+ return `\n${serializeComment(entry, "###")}\n`; | |
} | |
- | |
- return "".concat(serializeComment(entry, "###"), "\n"); | |
+ return `${serializeComment(entry, "###")}\n`; | |
} | |
- | |
if (entry instanceof Junk) { | |
return serializeJunk(entry); | |
} | |
- | |
- throw new Error("Unknown entry type: ".concat(entry)); | |
+ throw new Error(`Unknown entry type: ${entry}`); | |
} | |
} | |
- | |
- function serializeComment(comment) { | |
- var prefix = | |
- arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "#"; | |
- var prefixed = comment.content | |
+ function serializeComment(comment, prefix = "#") { | |
+ const prefixed = comment.content | |
.split("\n") | |
- .map((line) => | |
- line.length ? "".concat(prefix, " ").concat(line) : prefix | |
- ) | |
- .join("\n"); // Add the trailing newline. | |
- | |
- return "".concat(prefixed, "\n"); | |
+ .map((line) => (line.length ? `${prefix} ${line}` : prefix)) | |
+ .join("\n"); | |
+ // Add the trailing newline. | |
+ return `${prefixed}\n`; | |
} | |
- | |
function serializeJunk(junk) { | |
return junk.content; | |
} | |
- | |
function serializeMessage(message) { | |
- var parts = []; | |
- | |
+ const parts = []; | |
if (message.comment) { | |
parts.push(serializeComment(message.comment)); | |
} | |
- | |
- parts.push("".concat(message.id.name, " =")); | |
- | |
+ parts.push(`${message.id.name} =`); | |
if (message.value) { | |
parts.push(serializePattern(message.value)); | |
} | |
- | |
- var _iterator2 = _createForOfIteratorHelper(message.attributes), | |
- _step2; | |
- | |
- try { | |
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done; ) { | |
- var attribute = _step2.value; | |
+ for (const attribute of message.attributes) { | |
parts.push(serializeAttribute(attribute)); | |
} | |
- } catch (err) { | |
- _iterator2.e(err); | |
- } finally { | |
- _iterator2.f(); | |
- } | |
- | |
parts.push("\n"); | |
return parts.join(""); | |
} | |
- | |
function serializeTerm(term) { | |
- var parts = []; | |
- | |
+ const parts = []; | |
if (term.comment) { | |
parts.push(serializeComment(term.comment)); | |
} | |
- | |
- parts.push("-".concat(term.id.name, " =")); | |
+ parts.push(`-${term.id.name} =`); | |
parts.push(serializePattern(term.value)); | |
- | |
- var _iterator3 = _createForOfIteratorHelper(term.attributes), | |
- _step3; | |
- | |
- try { | |
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done; ) { | |
- var attribute = _step3.value; | |
+ for (const attribute of term.attributes) { | |
parts.push(serializeAttribute(attribute)); | |
} | |
- } catch (err) { | |
- _iterator3.e(err); | |
- } finally { | |
- _iterator3.f(); | |
- } | |
- | |
parts.push("\n"); | |
return parts.join(""); | |
} | |
- | |
function serializeAttribute(attribute) { | |
- var value = indent(serializePattern(attribute.value)); | |
- return "\n .".concat(attribute.id.name, " =").concat(value); | |
+ const value = indent(serializePattern(attribute.value)); | |
+ return `\n .${attribute.id.name} =${value}`; | |
} | |
- | |
function serializePattern(pattern) { | |
- var content = pattern.elements.map(serializeElement).join(""); | |
- var startOnNewLine = | |
+ const content = pattern.elements.map(serializeElement).join(""); | |
+ const startOnNewLine = | |
pattern.elements.some(isSelectExpr) || | |
pattern.elements.some(includesNewLine); | |
- | |
if (startOnNewLine) { | |
- return "\n ".concat(indent(content)); | |
+ return `\n ${indent(content)}`; | |
} | |
- | |
- return " ".concat(content); | |
+ return ` ${content}`; | |
} | |
- | |
function serializeElement(element) { | |
if (element instanceof TextElement) { | |
return element.value; | |
} | |
- | |
if (element instanceof Placeable) { | |
return serializePlaceable(element); | |
} | |
- | |
- throw new Error("Unknown element type: ".concat(element)); | |
+ throw new Error(`Unknown element type: ${element}`); | |
} | |
- | |
function serializePlaceable(placeable) { | |
- var expr = placeable.expression; | |
- | |
+ const expr = placeable.expression; | |
if (expr instanceof Placeable) { | |
- return "{".concat(serializePlaceable(expr), "}"); | |
+ return `{${serializePlaceable(expr)}}`; | |
} | |
- | |
if (expr instanceof SelectExpression) { | |
// Special-case select expression to control the whitespace around the | |
// opening and the closing brace. | |
- return "{ ".concat(serializeExpression(expr), "}"); | |
+ return `{ ${serializeExpression(expr)}}`; | |
} | |
- | |
- return "{ ".concat(serializeExpression(expr), " }"); | |
+ return `{ ${serializeExpression(expr)} }`; | |
} | |
- | |
function serializeExpression(expr) { | |
if (expr instanceof StringLiteral) { | |
- return '"'.concat(expr.value, '"'); | |
+ return `"${expr.value}"`; | |
} | |
- | |
if (expr instanceof NumberLiteral) { | |
return expr.value; | |
} | |
- | |
if (expr instanceof VariableReference) { | |
- return "$".concat(expr.id.name); | |
+ return `$${expr.id.name}`; | |
} | |
- | |
if (expr instanceof TermReference) { | |
- var out = "-".concat(expr.id.name); | |
- | |
+ let out = `-${expr.id.name}`; | |
if (expr.attribute) { | |
- out += ".".concat(expr.attribute.name); | |
+ out += `.${expr.attribute.name}`; | |
} | |
- | |
if (expr.arguments) { | |
out += serializeCallArguments(expr.arguments); | |
} | |
- | |
return out; | |
} | |
- | |
if (expr instanceof MessageReference) { | |
- var _out = expr.id.name; | |
- | |
+ let out = expr.id.name; | |
if (expr.attribute) { | |
- _out += ".".concat(expr.attribute.name); | |
+ out += `.${expr.attribute.name}`; | |
} | |
- | |
- return _out; | |
+ return out; | |
} | |
- | |
if (expr instanceof FunctionReference) { | |
- return "" | |
- .concat(expr.id.name) | |
- .concat(serializeCallArguments(expr.arguments)); | |
+ return `${expr.id.name}${serializeCallArguments(expr.arguments)}`; | |
} | |
- | |
if (expr instanceof SelectExpression) { | |
- var _out2 = "".concat(serializeExpression(expr.selector), " ->"); | |
- | |
- var _iterator4 = _createForOfIteratorHelper(expr.variants), | |
- _step4; | |
- | |
- try { | |
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done; ) { | |
- var variant = _step4.value; | |
- _out2 += serializeVariant(variant); | |
+ let out = `${serializeExpression(expr.selector)} ->`; | |
+ for (let variant of expr.variants) { | |
+ out += serializeVariant(variant); | |
} | |
- } catch (err) { | |
- _iterator4.e(err); | |
- } finally { | |
- _iterator4.f(); | |
+ return `${out}\n`; | |
} | |
- | |
- return "".concat(_out2, "\n"); | |
- } | |
- | |
if (expr instanceof Placeable) { | |
return serializePlaceable(expr); | |
} | |
- | |
- throw new Error("Unknown expression type: ".concat(expr)); | |
+ throw new Error(`Unknown expression type: ${expr}`); | |
} | |
- | |
function serializeVariant(variant) { | |
- var key = serializeVariantKey(variant.key); | |
- var value = indent(serializePattern(variant.value)); | |
- | |
+ const key = serializeVariantKey(variant.key); | |
+ const value = indent(serializePattern(variant.value)); | |
if (variant.default) { | |
- return "\n *[".concat(key, "]").concat(value); | |
+ return `\n *[${key}]${value}`; | |
} | |
- | |
- return "\n [".concat(key, "]").concat(value); | |
+ return `\n [${key}]${value}`; | |
} | |
- | |
function serializeCallArguments(expr) { | |
- var positional = expr.positional.map(serializeExpression).join(", "); | |
- var named = expr.named.map(serializeNamedArgument).join(", "); | |
- | |
+ const positional = expr.positional.map(serializeExpression).join(", "); | |
+ const named = expr.named.map(serializeNamedArgument).join(", "); | |
if (expr.positional.length > 0 && expr.named.length > 0) { | |
- return "(".concat(positional, ", ").concat(named, ")"); | |
+ return `(${positional}, ${named})`; | |
} | |
- | |
- return "(".concat(positional || named, ")"); | |
+ return `(${positional || named})`; | |
} | |
- | |
function serializeNamedArgument(arg) { | |
- var value = serializeExpression(arg.value); | |
- return "".concat(arg.name.name, ": ").concat(value); | |
+ const value = serializeExpression(arg.value); | |
+ return `${arg.name.name}: ${value}`; | |
} | |
- | |
function serializeVariantKey(key) { | |
if (key instanceof Identifier) { | |
return key.name; | |
} | |
- | |
if (key instanceof NumberLiteral) { | |
return key.value; | |
} | |
- | |
- throw new Error("Unknown variant key type: ".concat(key)); | |
+ throw new Error(`Unknown variant key type: ${key}`); | |
} | |
/** | |
@@ -2293,43 +1610,24 @@ | |
* (this: Visitor, node: BaseNode): void; | |
* } | |
*/ | |
- | |
class Visitor { | |
visit(node) { | |
- var visit = this["visit".concat(node.type)]; | |
- | |
+ let visit = this[`visit${node.type}`]; | |
if (typeof visit === "function") { | |
visit.call(this, node); | |
} else { | |
this.genericVisit(node); | |
} | |
} | |
- | |
genericVisit(node) { | |
- for ( | |
- var _i = 0, _Object$keys = Object.keys(node); | |
- _i < _Object$keys.length; | |
- _i++ | |
- ) { | |
- var key = _Object$keys[_i]; | |
- var prop = node[key]; | |
- | |
+ for (const key of Object.keys(node)) { | |
+ let prop = node[key]; | |
if (prop instanceof BaseNode) { | |
this.visit(prop); | |
} else if (Array.isArray(prop)) { | |
- var _iterator = _createForOfIteratorHelper(prop), | |
- _step; | |
- | |
- try { | |
- for (_iterator.s(); !(_step = _iterator.n()).done; ) { | |
- var element = _step.value; | |
+ for (let element of prop) { | |
this.visit(element); | |
} | |
- } catch (err) { | |
- _iterator.e(err); | |
- } finally { | |
- _iterator.f(); | |
- } | |
} | |
} | |
} | |
@@ -2351,71 +1649,45 @@ | |
* The returned node wili replace the original one in the AST. Return | |
* `undefined` to remove the node instead. | |
*/ | |
- | |
class Transformer extends Visitor { | |
visit(node) { | |
- var visit = this["visit".concat(node.type)]; | |
- | |
+ let visit = this[`visit${node.type}`]; | |
if (typeof visit === "function") { | |
return visit.call(this, node); | |
} | |
- | |
return this.genericVisit(node); | |
} | |
- | |
genericVisit(node) { | |
- for ( | |
- var _i2 = 0, _Object$keys2 = Object.keys(node); | |
- _i2 < _Object$keys2.length; | |
- _i2++ | |
- ) { | |
- var key = _Object$keys2[_i2]; | |
- var prop = node[key]; | |
- | |
+ for (const key of Object.keys(node)) { | |
+ let prop = node[key]; | |
if (prop instanceof BaseNode) { | |
- var newVal = this.visit(prop); | |
- | |
+ let newVal = this.visit(prop); | |
if (newVal === undefined) { | |
delete node[key]; | |
} else { | |
node[key] = newVal; | |
} | |
} else if (Array.isArray(prop)) { | |
- var newVals = []; | |
- | |
- var _iterator2 = _createForOfIteratorHelper(prop), | |
- _step2; | |
- | |
- try { | |
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done; ) { | |
- var element = _step2.value; | |
- | |
- var _newVal = this.visit(element); | |
- | |
- if (_newVal !== undefined) { | |
- newVals.push(_newVal); | |
- } | |
+ let newVals = []; | |
+ for (let element of prop) { | |
+ let newVal = this.visit(element); | |
+ if (newVal !== undefined) { | |
+ newVals.push(newVal); | |
} | |
- } catch (err) { | |
- _iterator2.e(err); | |
- } finally { | |
- _iterator2.f(); | |
} | |
- | |
node[key] = newVals; | |
} | |
} | |
- | |
return node; | |
} | |
} | |
function parse(source, opts) { | |
- var parser = new FluentParser(opts); | |
+ const parser = new FluentParser(opts); | |
return parser.parse(source); | |
} | |
function serialize(resource, opts) { | |
- var serializer = new FluentSerializer(opts); | |
+ const serializer = new FluentSerializer(opts); | |
return serializer.serialize(resource); | |
} | |
function lineOffset(source, pos) { | |
@@ -2426,13 +1698,13 @@ | |
// Find the last line break starting backwards from the index just before | |
// pos. This allows us to correctly handle ths case where the character at | |
// pos is a line break as well. | |
- var fromIndex = pos - 1; | |
- var prevLineBreak = source.lastIndexOf("\n", fromIndex); // pos is a position in the first line of source. | |
- | |
+ const fromIndex = pos - 1; | |
+ const prevLineBreak = source.lastIndexOf("\n", fromIndex); | |
+ // pos is a position in the first line of source. | |
if (prevLineBreak === -1) { | |
return pos; | |
- } // Subtracting two offsets gives length; subtract 1 to get the offset. | |
- | |
+ } | |
+ // Subtracting two offsets gives length; subtract 1 to get the offset. | |
return pos - prevLineBreak - 1; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment