consolidate transformer metadata, remove abstract references, make react transformer optional
This commit is contained in:
parent
b495370740
commit
4d965b0f46
@ -9,10 +9,10 @@ export default function (opts, code, callback) {
|
||||
var tokens = [];
|
||||
|
||||
var ast = acorn.parse(code, {
|
||||
allowImportExportEverywhere: opts.allowImportExportEverywhere,
|
||||
allowReturnOutsideFunction: !opts._anal,
|
||||
ecmaVersion: opts.experimental ? 7 : 6,
|
||||
playground: opts.playground,
|
||||
allowImportExportEverywhere: opts.looseModules,
|
||||
allowReturnOutsideFunction: opts.looseModules,
|
||||
transformers: opts.transformers,
|
||||
ecmaVersion: 6,
|
||||
strictMode: opts.strictMode,
|
||||
onComment: comments,
|
||||
locations: true,
|
||||
|
||||
@ -181,10 +181,10 @@ export default class File {
|
||||
each(transform.transformers, function (transformer, key) {
|
||||
var pass = transformers[key] = transformer.buildPass(file);
|
||||
|
||||
if (pass.canRun(file)) {
|
||||
if (pass.canRun) {
|
||||
stack.push(pass);
|
||||
|
||||
if (transformer.secondPass) {
|
||||
if (transformer.metadata.secondPass) {
|
||||
secondaryStack.push(pass);
|
||||
}
|
||||
|
||||
@ -274,7 +274,7 @@ export default class File {
|
||||
this.dynamicImported.push(declar);
|
||||
if (noDefault) this.dynamicImportedNoDefault.push(declar);
|
||||
|
||||
if (this.transformers["es6.modules"].canRun()) {
|
||||
if (this.transformers["es6.modules"].canRun) {
|
||||
this.moduleFormatter.importSpecifier(specifiers[0], declar, this.dynamicImports);
|
||||
} else {
|
||||
this.dynamicImports.push(declar);
|
||||
@ -380,8 +380,19 @@ export default class File {
|
||||
|
||||
var opts = this.opts;
|
||||
|
||||
opts.allowImportExportEverywhere = this.isLoose("es6.modules");
|
||||
opts.strictMode = this.transformers.strict.canRun();
|
||||
//
|
||||
|
||||
var parseOpts = {};
|
||||
|
||||
var transformers = parseOpts.transformers = {};
|
||||
for (var key in this.transformers) {
|
||||
transformers[key] = this.transformers[key].canRun;
|
||||
}
|
||||
|
||||
parseOpts.looseModules = this.isLoose("es6.modules");
|
||||
parseOpts.strictMode = this.transformers.strict.canRun;
|
||||
|
||||
//
|
||||
|
||||
return parse(opts, code, (tree) => {
|
||||
this.transform(tree);
|
||||
@ -413,7 +424,7 @@ export default class File {
|
||||
this.lastStatements = t.getLastStatements(ast.program);
|
||||
|
||||
var modFormatter = this.moduleFormatter = this.getModuleFormatter(this.opts.modules);
|
||||
if (modFormatter.init && this.transformers["es6.modules"].canRun()) {
|
||||
if (modFormatter.init && this.transformers["es6.modules"].canRun) {
|
||||
modFormatter.init();
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,12 @@
|
||||
|
||||
},
|
||||
|
||||
"react": {
|
||||
"description": "",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
|
||||
"highlightErrors": {
|
||||
"description": "ANSI syntax highlight error messages",
|
||||
"type": "boolean",
|
||||
@ -42,13 +48,6 @@
|
||||
"default": false
|
||||
},
|
||||
|
||||
"playground": {
|
||||
"description": "Enable all playground transformers",
|
||||
"shorthand": "p",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
|
||||
"blacklist": {
|
||||
"type": "transformerList",
|
||||
"description": "Blacklist of transformers to NOT use",
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
{
|
||||
"blacklist": ["useStrict", "es6.blockScoping", "regenerator"]
|
||||
"blacklist": ["useStrict", "es6.blockScoping", "regenerator"],
|
||||
"loose": ["es6.modules"]
|
||||
}
|
||||
|
||||
@ -12,10 +12,12 @@ export default class TransformerPass {
|
||||
this.shouldRun = !transformer.check;
|
||||
this.handlers = transformer.handlers;
|
||||
this.file = file;
|
||||
this.ran = false;
|
||||
|
||||
this.canRun = this._canRun();
|
||||
this.ran = false;
|
||||
}
|
||||
|
||||
canRun(): boolean {
|
||||
_canRun(): boolean {
|
||||
var transformer = this.transformer;
|
||||
|
||||
var opts = this.file.opts;
|
||||
@ -32,14 +34,14 @@ export default class TransformerPass {
|
||||
var whitelist = opts.whitelist;
|
||||
if (whitelist.length) return includes(whitelist, key);
|
||||
|
||||
// experimental
|
||||
if (transformer.experimental && opts.experimental) return true;
|
||||
// react
|
||||
if (transformer.metadata.react && opts.react) return true;
|
||||
|
||||
// playground
|
||||
if (transformer.playground && opts.playground) return true;
|
||||
// experimental
|
||||
if (transformer.metadata.experimental && opts.experimental) return true;
|
||||
|
||||
// optional
|
||||
if (transformer.optional && !includes(opts.optional, key)) return false;
|
||||
if (transformer.metadata.optional && !includes(opts.optional, key)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -23,15 +23,11 @@ export default class Transformer {
|
||||
};
|
||||
|
||||
this.manipulateOptions = take("manipulateOptions");
|
||||
this.metadata = take("metadata") || {};
|
||||
this.check = take("check");
|
||||
this.post = take("post");
|
||||
this.pre = take("pre");
|
||||
|
||||
this.experimental = !!take("experimental");
|
||||
this.playground = !!take("playground");
|
||||
this.secondPass = !!take("secondPass");
|
||||
this.optional = !!take("optional");
|
||||
|
||||
this.handlers = this.normalize(transformer);
|
||||
this.opts ||= {};
|
||||
this.key = transformerKey;
|
||||
|
||||
@ -26,7 +26,9 @@ var visitor = {
|
||||
}
|
||||
};
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
export function BlockStatement(node, parent, scope, file) {
|
||||
var letRefs = node._letReferences;
|
||||
|
||||
@ -44,7 +44,7 @@ export function check(node) {
|
||||
export function VariableDeclaration(node, parent, scope, file) {
|
||||
if (!isLet(node, parent)) return;
|
||||
|
||||
if (isLetInitable(node) && file.transformers["es6.blockScopingTDZ"].canRun()) {
|
||||
if (isLetInitable(node) && file.transformers["es6.blockScopingTDZ"].canRun) {
|
||||
var nodes = [node];
|
||||
|
||||
for (var i = 0; i < node.declarations.length; i++) {
|
||||
|
||||
@ -62,7 +62,7 @@ exports.Function = function (node, parent, scope, file) {
|
||||
param.traverse(iifeVisitor, state);
|
||||
}
|
||||
|
||||
if (file.transformers["es6.blockScopingTDZ"].canRun() && param.isIdentifier()) {
|
||||
if (file.transformers["es6.blockScopingTDZ"].canRun && param.isIdentifier()) {
|
||||
pushDefNode(param.node, t.identifier("undefined"), i);
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
export function UnaryExpression(node, parent, scope, file) {
|
||||
this.skip();
|
||||
|
||||
@ -1,112 +0,0 @@
|
||||
// https://github.com/zenparsing/es-abstract-refs
|
||||
|
||||
import * as util from "../../../util";
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var experimental = true;
|
||||
export var optional = true;
|
||||
|
||||
var container = function (parent, call, ret, file) {
|
||||
if (t.isExpressionStatement(parent) && !file.isConsequenceExpressionStatement(parent)) {
|
||||
// we don't need to worry about return values
|
||||
return call;
|
||||
} else {
|
||||
var exprs = [];
|
||||
if (t.isSequenceExpression(call)) {
|
||||
exprs = call.expressions;
|
||||
} else {
|
||||
exprs.push(call);
|
||||
}
|
||||
exprs.push(ret);
|
||||
return t.sequenceExpression(exprs);
|
||||
}
|
||||
};
|
||||
|
||||
export function AssignmentExpression(node, parent, scope, file) {
|
||||
var left = node.left;
|
||||
if (!t.isVirtualPropertyExpression(left)) return;
|
||||
|
||||
var value = node.right;
|
||||
var temp;
|
||||
|
||||
// we need to return `node.right`
|
||||
if (!t.isExpressionStatement(parent)) {
|
||||
temp = scope.generateTempBasedOnNode(node.right);
|
||||
if (temp) value = temp;
|
||||
}
|
||||
|
||||
if (node.operator !== "=") {
|
||||
value = t.binaryExpression(
|
||||
node.operator[0],
|
||||
util.template("abstract-expression-get", {
|
||||
PROPERTY: node.property,
|
||||
OBJECT: node.object
|
||||
}),
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
var call = util.template("abstract-expression-set", {
|
||||
PROPERTY: left.property,
|
||||
OBJECT: left.object,
|
||||
VALUE: value
|
||||
});
|
||||
|
||||
if (temp) {
|
||||
call = t.sequenceExpression([
|
||||
t.assignmentExpression("=", temp, node.right),
|
||||
call
|
||||
]);
|
||||
}
|
||||
|
||||
return container(parent, call, value, file);
|
||||
}
|
||||
|
||||
export function UnaryExpression(node, parent, scope, file) {
|
||||
var arg = node.argument;
|
||||
if (!t.isVirtualPropertyExpression(arg)) return;
|
||||
if (node.operator !== "delete") return;
|
||||
|
||||
var call = util.template("abstract-expression-delete", {
|
||||
PROPERTY: arg.property,
|
||||
OBJECT: arg.object
|
||||
});
|
||||
|
||||
return container(parent, call, t.literal(true), file);
|
||||
}
|
||||
|
||||
export function CallExpression(node, parent, scope) {
|
||||
var callee = node.callee;
|
||||
if (!t.isVirtualPropertyExpression(callee)) return;
|
||||
|
||||
var temp = scope.generateTempBasedOnNode(callee.object);
|
||||
|
||||
var call = util.template("abstract-expression-call", {
|
||||
PROPERTY: callee.property,
|
||||
OBJECT: temp || callee.object
|
||||
});
|
||||
|
||||
call.arguments = call.arguments.concat(node.arguments);
|
||||
|
||||
if (temp) {
|
||||
return t.sequenceExpression([
|
||||
t.assignmentExpression("=", temp, callee.object),
|
||||
call
|
||||
]);
|
||||
} else {
|
||||
return call;
|
||||
}
|
||||
}
|
||||
|
||||
export function VirtualPropertyExpression(node) {
|
||||
return util.template("abstract-expression-get", {
|
||||
PROPERTY: node.property,
|
||||
OBJECT: node.object
|
||||
});
|
||||
}
|
||||
|
||||
export function PrivateDeclaration(node) {
|
||||
return t.variableDeclaration("const", node.declarations.map(function (id) {
|
||||
return t.variableDeclarator(id, t.newExpression(t.identifier("WeakMap"), []));
|
||||
}));
|
||||
}
|
||||
@ -3,8 +3,10 @@ import traverse from "../../../traversal";
|
||||
import * as util from "../../../util";
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var experimental = true;
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
experimental: true,
|
||||
optional: true
|
||||
};
|
||||
|
||||
export function ComprehensionExpression(node, parent, scope, file) {
|
||||
var callback = array;
|
||||
|
||||
@ -3,8 +3,9 @@
|
||||
import build from "../../helpers/build-binary-assignment-operator-transformer";
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var experimental = true;
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
var MATH_POW = t.memberExpression(t.identifier("Math"), t.identifier("pow"));
|
||||
|
||||
|
||||
@ -2,8 +2,11 @@
|
||||
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var experimental = true;
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
experimental: true,
|
||||
optional: true,
|
||||
react: true
|
||||
};
|
||||
|
||||
export function manipulateOptions(opts) {
|
||||
if (opts.whitelist.length) opts.whitelist.push("es6.destructuring");
|
||||
|
||||
@ -44,7 +44,6 @@ export default {
|
||||
|
||||
"es6.regex.sticky": require("./es6/regex.sticky"),
|
||||
"es6.regex.unicode": require("./es6/regex.unicode"),
|
||||
"es7.abstractReferences": require("./es7/abstract-references"),
|
||||
|
||||
"es6.constants": require("./es6/constants"),
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import * as strict from "../../helpers/strict";
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var secondPass = true;
|
||||
export var metadata = {
|
||||
secondPass: true
|
||||
};
|
||||
|
||||
export function BlockStatement(node, parent, scope, file) {
|
||||
if (!node._declarations) return;
|
||||
|
||||
@ -5,7 +5,7 @@ export function Program(program, parent, scope, file) {
|
||||
program.body = file.dynamicImports.concat(program.body);
|
||||
});
|
||||
|
||||
if (!file.transformers["es6.modules"].canRun()) return;
|
||||
if (!file.transformers["es6.modules"].canRun) return;
|
||||
|
||||
if (file.moduleFormatter.transform) {
|
||||
file.moduleFormatter.transform(program);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import * as t from "../../../types";
|
||||
|
||||
export function Program(program, parent, scope, file) {
|
||||
if (file.transformers.strict.canRun()) {
|
||||
if (file.transformers.strict.canRun) {
|
||||
var directive = file.get("existingStrictDirective");
|
||||
|
||||
if (!directive) {
|
||||
|
||||
@ -2,7 +2,9 @@ import remapAsyncToGenerator from "../../helpers/remap-async-to-generator";
|
||||
|
||||
export { manipulateOptions } from "./bluebird-coroutines";
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
exports.Function = function (node, parent, scope, file) {
|
||||
if (!node.async || node.generator) return;
|
||||
|
||||
@ -6,7 +6,9 @@ export function manipulateOptions(opts) {
|
||||
opts.blacklist.push("regenerator");
|
||||
}
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
exports.Function = function (node, parent, scope, file) {
|
||||
if (!node.async || node.generator) return;
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var metadata = {
|
||||
react: true
|
||||
};
|
||||
|
||||
export function Flow(node) {
|
||||
this.remove();
|
||||
}
|
||||
|
||||
@ -5,7 +5,10 @@ export function manipulateOptions(opts) {
|
||||
opts.blacklist.push("react");
|
||||
}
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true,
|
||||
react: true
|
||||
};
|
||||
|
||||
require("../../helpers/build-react-transformer")(exports, {
|
||||
pre(state) {
|
||||
|
||||
@ -3,6 +3,10 @@ import * as t from "../../../types";
|
||||
|
||||
var JSX_ANNOTATION_REGEX = /^\*\s*@jsx\s+([^\s]+)/;
|
||||
|
||||
export var metadata = {
|
||||
react: true
|
||||
};
|
||||
|
||||
export function Program(node, parent, scope, file) {
|
||||
var id = "React.createElement";
|
||||
|
||||
|
||||
@ -69,7 +69,10 @@ var astVisitor = {
|
||||
}
|
||||
};
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true,
|
||||
react: true
|
||||
};
|
||||
|
||||
export function manipulateOptions(opts) {
|
||||
if (opts.whitelist.length) opts.whitelist.push("es6.modules");
|
||||
|
||||
@ -14,8 +14,10 @@ function buildDefaultsCallExpression(expr, ref, file) {
|
||||
return t.expressionStatement(t.callExpression(file.addHelper("defaults"), [ref, expr.right]));
|
||||
}
|
||||
|
||||
export var secondPass = true;
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
secondPass: true,
|
||||
optional: true
|
||||
};
|
||||
|
||||
export function AssignmentExpression(node, parent, scope, file) {
|
||||
if (!isProtoAssignmentExpression(node)) return;
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true,
|
||||
react: true
|
||||
};
|
||||
|
||||
export function Identifier(node, parent) {
|
||||
if (node.name === "undefined" && this.isReferenced()) {
|
||||
|
||||
@ -17,7 +17,9 @@ function toStatements(node) {
|
||||
return node;
|
||||
}
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
export function ConditionalExpression(node, parent, scope) {
|
||||
var evaluateTest = this.get("test").evaluateTruthy();
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
var match = t.buildMatchMemberExpression("process.env");
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
export function Expression(node, parent, scope) {
|
||||
var res = this.evaluate();
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
export function CallExpression(node, parent) {
|
||||
if (this.get("callee").matchesPattern("console", true)) {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import * as t from "../../../types";
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
export function ExpressionStatement(node) {
|
||||
if (this.get("expression").isIdentifier({ name: "debugger" })) {
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import levenshtein from "leven";
|
||||
import * as messages from "../../../messages";
|
||||
|
||||
export var optional = true;
|
||||
export var metadata = {
|
||||
optional: true
|
||||
};
|
||||
|
||||
export function Identifier(node, parent, scope, file) {
|
||||
if (!this.isReferenced()) return;
|
||||
|
||||
@ -78,7 +78,7 @@ var templateVisitor = {
|
||||
return nodes[node.name];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
@ -109,7 +109,7 @@ export function template(name: string, nodes?: Array<Object>, keepExpression?: b
|
||||
}
|
||||
|
||||
export function parseTemplate(loc: string, code: string): Object {
|
||||
var ast = parse({ filename: loc }, code).program;
|
||||
var ast = parse({ filename: loc, looseModules: true }, code).program;
|
||||
ast = traverse.removeProperties(ast);
|
||||
return ast;
|
||||
}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
foo::bar();
|
||||
foo::bar("arg");
|
||||
|
||||
var test = "test";
|
||||
test::bar();
|
||||
test::bar("arg");
|
||||
@ -1,10 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var _foo, _foo2;
|
||||
|
||||
_foo = foo, bar[Symbol.referenceGet](_foo).call(_foo);
|
||||
_foo2 = foo, bar[Symbol.referenceGet](_foo2).call(_foo2, "arg");
|
||||
|
||||
var test = "test";
|
||||
bar[Symbol.referenceGet](test).call(test);
|
||||
bar[Symbol.referenceGet](test).call(test, "arg");
|
||||
@ -1,3 +0,0 @@
|
||||
delete foo::bar;
|
||||
|
||||
if (delete foo::bar) {}
|
||||
@ -1,5 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
bar[Symbol.referenceDelete](foo);
|
||||
|
||||
if ((bar[Symbol.referenceDelete](foo), true)) {}
|
||||
@ -1 +0,0 @@
|
||||
foo::bar;
|
||||
@ -1,3 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
bar[Symbol.referenceGet](foo);
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"experimental": true
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
private A;
|
||||
private B, C;
|
||||
|
||||
class D {
|
||||
private E;
|
||||
private F, G;
|
||||
}
|
||||
|
||||
var H = class {
|
||||
private I;
|
||||
private J, K;
|
||||
};
|
||||
@ -1,30 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
|
||||
|
||||
var A = new WeakMap();
|
||||
var B = new WeakMap(),
|
||||
C = new WeakMap();
|
||||
|
||||
var D = (function () {
|
||||
var F = new WeakMap(),
|
||||
G = new WeakMap();
|
||||
var E = new WeakMap();
|
||||
|
||||
function D() {
|
||||
_classCallCheck(this, D);
|
||||
}
|
||||
|
||||
return D;
|
||||
})();
|
||||
|
||||
var H = (function () {
|
||||
var _class = function H() {
|
||||
_classCallCheck(this, _class);
|
||||
};
|
||||
|
||||
var J = new WeakMap(),
|
||||
K = new WeakMap();
|
||||
var I = new WeakMap();
|
||||
return _class;
|
||||
})();
|
||||
@ -1,3 +0,0 @@
|
||||
var baz = "foo";
|
||||
foo::bar = baz;
|
||||
if (foo::bar = baz) {}
|
||||
@ -1,5 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var baz = "foo";
|
||||
bar[Symbol.referenceSet](foo, baz);
|
||||
if ((bar[Symbol.referenceSet](foo, baz), baz)) {}
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"experimental": true
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"react": true,
|
||||
"experimental": true,
|
||||
"whitelist": ["flow"],
|
||||
"noCheckAst": true
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"react": true,
|
||||
"blacklist": "strict",
|
||||
"optional": "reactCompat"
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
{
|
||||
"react": true,
|
||||
"blacklist": ["strict"]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user