Skip to content

Instantly share code, notes, and snippets.

@stasm
Created April 21, 2020 13:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stasm/c4e167711cb2678a014445d82ae7e92c to your computer and use it in GitHub Desktop.
Save stasm/c4e167711cb2678a014445d82ae7e92c to your computer and use it in GitHub Desktop.
@fluent compat vs ES2018
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
// &amp;, &#0038;, &#x0026;.
- 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
// &amp;, &#0038;, &#x0026;.
-
- 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