You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

319 lines
45 KiB

2 years ago
'use strict';var _slicedToArray = function () {function sliceIterator(arr, i) {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"]) _i["return"]();} finally {if (_d) throw _e;}}return _arr;}return function (arr, i) {if (Array.isArray(arr)) {return arr;} else if (Symbol.iterator in Object(arr)) {return sliceIterator(arr, i);} else {throw new TypeError("Invalid attempt to destructure non-iterable instance");}};}();var _resolve = require('eslint-module-utils/resolve');var _resolve2 = _interopRequireDefault(_resolve);
var _docsUrl = require('../docsUrl');var _docsUrl2 = _interopRequireDefault(_docsUrl);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { 'default': obj };}function _toConsumableArray(arr) {if (Array.isArray(arr)) {for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {arr2[i] = arr[i];}return arr2;} else {return Array.from(arr);}}function _toArray(arr) {return Array.isArray(arr) ? arr : Array.from(arr);}
function checkImports(imported, context) {var _iteratorNormalCompletion = true;var _didIteratorError = false;var _iteratorError = undefined;try {
for (var _iterator = imported.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {var _ref = _step.value;var _ref2 = _slicedToArray(_ref, 2);var _module = _ref2[0];var nodes = _ref2[1];
if (nodes.length > 1) {
var message = '\'' + String(_module) + '\' imported multiple times.';var _nodes = _toArray(
nodes),first = _nodes[0],rest = _nodes.slice(1);
var sourceCode = context.getSourceCode();
var fix = getFix(first, rest, sourceCode);
context.report({
node: first.source,
message: message,
fix: fix // Attach the autofix (if any) to the first import.
});var _iteratorNormalCompletion2 = true;var _didIteratorError2 = false;var _iteratorError2 = undefined;try {
for (var _iterator2 = rest[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {var node = _step2.value;
context.report({
node: node.source,
message: message });
}} catch (err) {_didIteratorError2 = true;_iteratorError2 = err;} finally {try {if (!_iteratorNormalCompletion2 && _iterator2['return']) {_iterator2['return']();}} finally {if (_didIteratorError2) {throw _iteratorError2;}}}
}
}} catch (err) {_didIteratorError = true;_iteratorError = err;} finally {try {if (!_iteratorNormalCompletion && _iterator['return']) {_iterator['return']();}} finally {if (_didIteratorError) {throw _iteratorError;}}}
}
function getFix(first, rest, sourceCode) {
// Sorry ESLint <= 3 users, no autofix for you. Autofixing duplicate imports
// requires multiple `fixer.whatever()` calls in the `fix`: We both need to
// update the first one, and remove the rest. Support for multiple
// `fixer.whatever()` in a single `fix` was added in ESLint 4.1.
// `sourceCode.getCommentsBefore` was added in 4.0, so that's an easy thing to
// check for.
if (typeof sourceCode.getCommentsBefore !== 'function') {
return undefined;
}
// Adjusting the first import might make it multiline, which could break
// `eslint-disable-next-line` comments and similar, so bail if the first
// import has comments. Also, if the first import is `import * as ns from
// './foo'` there's nothing we can do.
if (hasProblematicComments(first, sourceCode) || hasNamespace(first)) {
return undefined;
}
var defaultImportNames = new Set(
[first].concat(_toConsumableArray(rest)).map(getDefaultImportName).filter(Boolean));
// Bail if there are multiple different default import names – it's up to the
// user to choose which one to keep.
if (defaultImportNames.size > 1) {
return undefined;
}
// Leave it to the user to handle comments. Also skip `import * as ns from
// './foo'` imports, since they cannot be merged into another import.
var restWithoutComments = rest.filter(function (node) {return !(
hasProblematicComments(node, sourceCode) ||
hasNamespace(node));});
var specifiers = restWithoutComments.
map(function (node) {
var tokens = sourceCode.getTokens(node);
var openBrace = tokens.find(function (token) {return isPunctuator(token, '{');});
var closeBrace = tokens.find(function (token) {return isPunctuator(token, '}');});
if (openBrace == null || closeBrace == null) {
return undefined;
}
return {
importNode: node,
text: sourceCode.text.slice(openBrace.range[1], closeBrace.range[0]),
hasTrailingComma: isPunctuator(sourceCode.getTokenBefore(closeBrace), ','),
isEmpty: !hasSpecifiers(node) };
}).
filter(Boolean);
var unnecessaryImports = restWithoutComments.filter(function (node) {return (
!hasSpecifiers(node) &&
!hasNamespace(node) &&
!specifiers.some(function (specifier) {return specifier.importNode === node;}));});
var shouldAddDefault = getDefaultImportName(first) == null && defaultImportNames.size === 1;
var shouldAddSpecifiers = specifiers.length > 0;
var shouldRemoveUnnecessary = unnecessaryImports.length > 0;
if (!(shouldAddDefault || shouldAddSpecifiers || shouldRemoveUnnecessary)) {
return undefined;
}
return function (fixer) {
var tokens = sourceCode.getTokens(first);
var openBrace = tokens.find(function (token) {return isPunctuator(token, '{');});
var closeBrace = tokens.find(function (token) {return isPunctuator(token, '}');});
var firstToken = sourceCode.getFirstToken(first);var _defaultImportNames = _slicedToArray(
defaultImportNames, 1),defaultImportName = _defaultImportNames[0];
var firstHasTrailingComma =
closeBrace != null &&
isPunctuator(sourceCode.getTokenBefore(closeBrace), ',');
var firstIsEmpty = !hasSpecifiers(first);var _specifiers$reduce =
specifiers.reduce(
function (_ref3, specifier) {var _ref4 = _slicedToArray(_ref3, 2),result = _ref4[0],needsComma = _ref4[1];
return [
needsComma && !specifier.isEmpty ? String(
result) + ',' + String(specifier.text) : '' + String(
result) + String(specifier.text),
specifier.isEmpty ? needsComma : true];
},
['', !firstHasTrailingComma && !firstIsEmpty]),_specifiers$reduce2 = _slicedToArray(_specifiers$reduce, 1),specifiersText = _specifiers$reduce2[0];
var fixes = [];
if (shouldAddDefault && openBrace == null && shouldAddSpecifiers) {
// `import './foo'` → `import def, {...} from './foo'`
fixes.push(
fixer.insertTextAfter(firstToken, ' ' + String(defaultImportName) + ', {' + String(specifiersText) + '} from'));
} else if (shouldAddDefault && openBrace == null && !shouldAddSpecifiers) {
// `import './foo'` → `import def from './foo'`
fixes.push(fixer.insertTextAfter(firstToken, ' ' + String(defaultImportName) + ' from'));
} else if (shouldAddDefault && openBrace != null && closeBrace != null) {
// `import {...} from './foo'` → `import def, {...} from './foo'`
fixes.push(fixer.insertTextAfter(firstToken, ' ' + String(defaultImportName) + ','));
if (shouldAddSpecifiers) {
// `import def, {...} from './foo'` → `import def, {..., ...} from './foo'`
fixes.push(fixer.insertTextBefore(closeBrace, specifiersText));
}
} else if (!shouldAddDefault && openBrace == null && shouldAddSpecifiers) {
if (first.specifiers.length === 0) {
// `import './foo'` → `import {...} from './foo'`
fixes.push(fixer.insertTextAfter(firstToken, ' {' + String(specifiersText) + '} from'));
} else {
// `import def from './foo'` → `import def, {...} from './foo'`
fixes.push(fixer.insertTextAfter(first.specifiers[0], ', {' + String(specifiersText) + '}'));
}
} else if (!shouldAddDefault && openBrace != null && closeBrace != null) {
// `import {...} './foo'` → `import {..., ...} from './foo'`
fixes.push(fixer.insertTextBefore(closeBrace, specifiersText));
}
// Remove imports whose specifiers have been moved into the first import.
var _iteratorNormalCompletion3 = true;var _didIteratorError3 = false;var _iteratorError3 = undefined;try {for (var _iterator3 = specifiers[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {var specifier = _step3.value;
var importNode = specifier.importNode;
fixes.push(fixer.remove(importNode));
var charAfterImportRange = [importNode.range[1], importNode.range[1] + 1];
var charAfterImport = sourceCode.text.substring(charAfterImportRange[0], charAfterImportRange[1]);
if (charAfterImport === '\n') {
fixes.push(fixer.removeRange(charAfterImportRange));
}
}
// Remove imports whose default import has been moved to the first import,
// and side-effect-only imports that are unnecessary due to the first
// import.
} catch (err) {_didIteratorError3 = true;_iteratorError3 = err;} finally {try {if (!_iteratorNormalCompletion3 && _iterator3['return']) {_iterator3['return']();}} finally {if (_didIteratorError3) {throw _iteratorError3;}}}var _iteratorNormalCompletion4 = true;var _didIteratorError4 = false;var _iteratorError4 = undefined;try {for (var _iterator4 = unnecessaryImports[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {var node = _step4.value;
fixes.push(fixer.remove(node));
var charAfterImportRange = [node.range[1], node.range[1] + 1];
var charAfterImport = sourceCode.text.substring(charAfterImportRange[0], charAfterImportRange[1]);
if (charAfterImport === '\n') {
fixes.push(fixer.removeRange(charAfterImportRange));
}
}} catch (err) {_didIteratorError4 = true;_iteratorError4 = err;} finally {try {if (!_iteratorNormalCompletion4 && _iterator4['return']) {_iterator4['return']();}} finally {if (_didIteratorError4) {throw _iteratorError4;}}}
return fixes;
};
}
function isPunctuator(node, value) {
return node.type === 'Punctuator' && node.value === value;
}
// Get the name of the default import of `node`, if any.
function getDefaultImportName(node) {
var defaultSpecifier = node.specifiers.
find(function (specifier) {return specifier.type === 'ImportDefaultSpecifier';});
return defaultSpecifier != null ? defaultSpecifier.local.name : undefined;
}
// Checks whether `node` has a namespace import.
function hasNamespace(node) {
var specifiers = node.specifiers.
filter(function (specifier) {return specifier.type === 'ImportNamespaceSpecifier';});
return specifiers.length > 0;
}
// Checks whether `node` has any non-default specifiers.
function hasSpecifiers(node) {
var specifiers = node.specifiers.
filter(function (specifier) {return specifier.type === 'ImportSpecifier';});
return specifiers.length > 0;
}
// It's not obvious what the user wants to do with comments associated with
// duplicate imports, so skip imports with comments when autofixing.
function hasProblematicComments(node, sourceCode) {
return (
hasCommentBefore(node, sourceCode) ||
hasCommentAfter(node, sourceCode) ||
hasCommentInsideNonSpecifiers(node, sourceCode));
}
// Checks whether `node` has a comment (that ends) on the previous line or on
// the same line as `node` (starts).
function hasCommentBefore(node, sourceCode) {
return sourceCode.getCommentsBefore(node).
some(function (comment) {return comment.loc.end.line >= node.loc.start.line - 1;});
}
// Checks whether `node` has a comment (that starts) on the same line as `node`
// (ends).
function hasCommentAfter(node, sourceCode) {
return sourceCode.getCommentsAfter(node).
some(function (comment) {return comment.loc.start.line === node.loc.end.line;});
}
// Checks whether `node` has any comments _inside,_ except inside the `{...}`
// part (if any).
function hasCommentInsideNonSpecifiers(node, sourceCode) {
var tokens = sourceCode.getTokens(node);
var openBraceIndex = tokens.findIndex(function (token) {return isPunctuator(token, '{');});
var closeBraceIndex = tokens.findIndex(function (token) {return isPunctuator(token, '}');});
// Slice away the first token, since we're no looking for comments _before_
// `node` (only inside). If there's a `{...}` part, look for comments before
// the `{`, but not before the `}` (hence the `+1`s).
var someTokens = openBraceIndex >= 0 && closeBraceIndex >= 0 ?
tokens.slice(1, openBraceIndex + 1).concat(tokens.slice(closeBraceIndex + 1)) :
tokens.slice(1);
return someTokens.some(function (token) {return sourceCode.getCommentsBefore(token).length > 0;});
}
module.exports = {
meta: {
type: 'problem',
docs: {
url: (0, _docsUrl2['default'])('no-duplicates') },
fixable: 'code',
schema: [
{
type: 'object',
properties: {
considerQueryString: {
type: 'boolean' } },
additionalProperties: false }] },
create: function () {function create(context) {
// Prepare the resolver from options.
var considerQueryStringOption = context.options[0] &&
context.options[0]['considerQueryString'];
var defaultResolver = function () {function defaultResolver(sourcePath) {return (0, _resolve2['default'])(sourcePath, context) || sourcePath;}return defaultResolver;}();
var resolver = considerQueryStringOption ? function (sourcePath) {
var parts = sourcePath.match(/^([^?]*)\?(.*)$/);
if (!parts) {
return defaultResolver(sourcePath);
}
return defaultResolver(parts[1]) + '?' + parts[2];
} : defaultResolver;
var moduleMaps = new Map();
function getImportMap(n) {
if (!moduleMaps.has(n.parent)) {
moduleMaps.set(n.parent, {
imported: new Map(),
nsImported: new Map(),
defaultTypesImported: new Map(),
namedTypesImported: new Map() });
}
var map = moduleMaps.get(n.parent);
if (n.importKind === 'type') {
return n.specifiers.length > 0 && n.specifiers[0].type === 'ImportDefaultSpecifier' ? map.defaultTypesImported : map.namedTypesImported;
}
return hasNamespace(n) ? map.nsImported : map.imported;
}
return {
ImportDeclaration: function () {function ImportDeclaration(n) {
// resolved path will cover aliased duplicates
var resolvedPath = resolver(n.source.value);
var importMap = getImportMap(n);
if (importMap.has(resolvedPath)) {
importMap.get(resolvedPath).push(n);
} else {
importMap.set(resolvedPath, [n]);
}
}return ImportDeclaration;}(),
'Program:exit': function () {function ProgramExit() {var _iteratorNormalCompletion5 = true;var _didIteratorError5 = false;var _iteratorError5 = undefined;try {
for (var _iterator5 = moduleMaps.values()[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {var map = _step5.value;
checkImports(map.imported, context);
checkImports(map.nsImported, context);
checkImports(map.defaultTypesImported, context);
checkImports(map.namedTypesImported, context);
}} catch (err) {_didIteratorError5 = true;_iteratorError5 = err;} finally {try {if (!_iteratorNormalCompletion5 && _iterator5['return']) {_iterator5['return']();}} finally {if (_didIteratorError5) {throw _iteratorError5;}}}
}return ProgramExit;}() };
}return create;}() };
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydWxlcy9uby1kdXBsaWNhdGVzLmpzIl0sIm5hbWVzIjpbImNoZWNrSW1wb3J0cyIsImltcG9ydGVkIiwiY29udGV4dCIsImVudHJpZXMiLCJtb2R1bGUiLCJub2RlcyIsImxlbmd0aCIsIm1lc3NhZ2UiLCJmaXJzdCIsInJlc3QiLCJzb3VyY2VDb2RlIiwiZ2V0U291cmNlQ29kZSIsImZpeCIsImdldEZpeCIsInJlcG9ydCIsIm5vZGUiLCJzb3VyY2UiLCJnZXRDb21tZW50c0JlZm9yZSIsInVuZGVmaW5lZCIsImhhc1Byb2JsZW1hdGljQ29tbWVudHMiLCJoYXNOYW1lc3BhY2UiLCJkZWZhdWx0SW1wb3J0TmFtZXMiLCJTZXQiLCJtYXAiLCJnZXREZWZhdWx0SW1wb3J0TmFtZSIsImZpbHRlciIsIkJvb2xlYW4iLCJzaXplIiwicmVzdFdpdGhvdXRDb21tZW50cyIsInNwZWNpZmllcnMiLCJ0b2tlbnMiLCJnZXRUb2tlbnMiLCJvcGVuQnJhY2UiLCJmaW5kIiwiaXNQdW5jdHVhdG9yIiwidG9rZW4iLCJjbG9zZUJyYWNlIiwiaW1wb3J0Tm9kZSIsInRleHQiLCJzbGljZSIsInJhbmdlIiwiaGFzVHJhaWxpbmdDb21tYSIsImdldFRva2VuQmVmb3JlIiwiaXNFbXB0eSIsImhhc1NwZWNpZmllcnMiLCJ1bm5lY2Vzc2FyeUltcG9ydHMiLCJzb21lIiwic3BlY2lmaWVyIiwic2hvdWxkQWRkRGVmYXVsdCIsInNob3VsZEFkZFNwZWNpZmllcnMiLCJzaG91bGRSZW1vdmVVbm5lY2Vzc2FyeSIsImZpcnN0VG9rZW4iLCJnZXRGaXJzdFRva2VuIiwiZGVmYXVsdEltcG9ydE5hbWUiLCJmaXJzdEhhc1RyYWlsaW5nQ29tbWEiLCJmaXJzdElzRW1wdHkiLCJyZWR1Y2UiLCJyZXN1bHQiLCJuZWVkc0NvbW1hIiwic3BlY2lmaWVyc1RleHQiLCJmaXhlcyIsInB1c2giLCJmaXhlciIsImluc2VydFRleHRBZnRlciIsImluc2VydFRleHRCZWZvcmUiLCJyZW1vdmUiLCJjaGFyQWZ0ZXJJbXBvcnRSYW5nZSIsImNoYXJBZnRlckltcG9ydCIsInN1YnN0cmluZyIsInJlbW92ZVJhbmdlIiwidmFsdWUiLCJ0eXBlIiwiZGVmYXVsdFNwZWNpZmllciIsImxvY2FsIiwibmFtZSIsImhhc0NvbW1lbnRCZWZvcmUiLCJoYXNDb21tZW50QWZ0ZXIiLCJoYXNDb21tZW50SW5zaWRlTm9uU3BlY2lmaWVycyIsImNvbW1lbnQiLCJsb2MiLCJlbmQiLCJsaW5lIiwic3RhcnQiLCJnZXRDb21tZW50c0FmdGVyIiwib3BlbkJyYWNlSW5kZXgiLCJmaW5kSW5kZXgiLCJjbG9zZUJyYWNlSW5kZXgiLCJzb21lVG9rZW5zIiwiY29uY2F0IiwiZXhwb3J0cyIsIm1ldGEiLCJkb2NzIiwidXJsIiwiZml4YWJsZSIsInNjaGVtYSIsInByb3BlcnRpZXMiLCJjb25zaWRlclF1ZXJ5U3RyaW5nIiwiYWRkaXRpb25hbFByb3BlcnRpZXMiLCJjcmVhdGUiLCJjb25zaWRlclF1ZXJ5U3RyaW5nT3B0aW9uIiwib3B0aW9ucyIsImRlZmF1bHRSZXNvbHZlciIsInNvdXJjZVBhdGgiLCJyZXNvbHZlciIsInBhcnRzIiwibWF0Y2giLCJtb2R1bGVNYXBzIiwiTWFwIiwiZ2V0SW1wb3J0TWFwIiwibiIsImhhcyIsInBhcmVudCIsInNldCIsIm5zSW1wb3J0ZWQiLCJkZWZhdWx0VHlwZXNJbXBvcnRlZCIsIm5hbWVkVHlwZXNJbXBvcnRlZCIsImdldCIsImltcG9ydEtpbmQiLCJJbXBvcnREZWNsYXJhdGlvbiIsInJlc29sdmVkUGF0aCIsImltcG9ydE1hcCIsInZhbHVlcyJdLCJtYXBwaW5ncyI6InFvQkFBQSxzRDtBQUNBLHFDOztBQUVBLFNBQVNBLFlBQVQsQ0FBc0JDLFFBQXRCLEVBQWdDQyxPQUFoQyxFQUF5QztBQUN2Qyx5QkFBOEJELFNBQVNFLE9BQVQsRUFBOUIsOEhBQWtELGdFQUF0Q0MsT0FBc0MsZ0JBQTlCQyxLQUE4QjtBQUNoRCxVQUFJQSxNQUFNQyxNQUFOLEdBQWUsQ0FBbkIsRUFBc0I7QUFDcEIsWUFBTUMsd0JBQWNILE9BQWQsaUNBQU4sQ0FEb0I7QUFFS0MsYUFGTCxFQUViRyxLQUZhLGFBRUhDLElBRkc7QUFHcEIsWUFBTUMsYUFBYVIsUUFBUVMsYUFBUixFQUFuQjtBQUNBLFlBQU1DLE1BQU1DLE9BQU9MLEtBQVAsRUFBY0MsSUFBZCxFQUFvQkMsVUFBcEIsQ0FBWjs7QUFFQVIsZ0JBQVFZLE1BQVIsQ0FBZTtBQUNiQyxnQkFBTVAsTUFBTVEsTUFEQztBQUViVCwwQkFGYTtBQUdiSyxrQkFIYSxDQUdSO0FBSFEsU0FBZixFQU5vQjs7QUFZcEIsZ0NBQW1CSCxJQUFuQixtSUFBeUIsS0FBZE0sSUFBYztBQUN2QmIsb0JBQVFZLE1BQVIsQ0FBZTtBQUNiQyxvQkFBTUEsS0FBS0MsTUFERTtBQUViVCw4QkFGYSxFQUFmOztBQUlELFdBakJtQjtBQWtCckI7QUFDRixLQXJCc0M7QUFzQnhDOztBQUVELFNBQVNNLE1BQVQsQ0FBZ0JMLEtBQWhCLEVBQXVCQyxJQUF2QixFQUE2QkMsVUFBN0IsRUFBeUM7QUFDdkM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBSSxPQUFPQSxXQUFXTyxpQkFBbEIsS0FBd0MsVUFBNUMsRUFBd0Q7QUFDdEQsV0FBT0MsU0FBUDtBQUNEOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBSUMsdUJBQXVCWCxLQUF2QixFQUE4QkUsVUFBOUIsS0FBNkNVLGFBQWFaLEtBQWIsQ0FBakQsRUFBc0U7QUFDcEUsV0FBT1UsU0FBUDtBQUNEOztBQUVELE1BQU1HLHFCQUFxQixJQUFJQyxHQUFKO0FBQ3pCLEdBQUNkLEtBQUQsNEJBQVdDLElBQVgsR0FBaUJjLEdBQWpCLENBQXFCQyxvQkFBckIsRUFBMkNDLE1BQTNDLENBQWtEQyxPQUFsRCxDQUR5QixDQUEzQjs7O0FBSUE7QUFDQTtBQUNBLE1BQUlMLG1CQUFtQk0sSUFBbkIsR0FBMEIsQ0FBOUIsRUFBaUM7QUFDL0IsV0FBT1QsU0FBUDtBQUNEOztBQUVEO0FBQ0E7QUFDQSxNQUFNVSxzQkFBc0JuQixLQUFLZ0IsTUFBTCxDQUFZLHdCQUFRO0FBQzlDTiwyQkFBdUJKLElBQXZCLEVBQTZCTCxVQUE3QjtBQUNBVSxpQkFBYUwsSUFBYixDQUY4QyxDQUFSLEVBQVosQ0FBNUI7OztBQUtBLE1BQU1jLGFBQWFEO0FBQ2hCTCxLQURnQixDQUNaLGdCQUFRO0FBQ1gsUUFBTU8sU0FBU3BCLFdBQVdxQixTQUFYLENBQXFCaEIsSUFBckIsQ0FBZjtBQUNBLFFBQU1pQixZQUFZRixPQUFPRyxJQUFQLENBQVkseUJBQVNDLGFBQWFDLEtBQWIsRUFBb0IsR0FBcEIsQ0FBVCxFQUFaLENBQWxCO0FBQ0EsUUFBTUMsYUFBYU4sT0FBT0csSUFBUCxDQUFZLHlCQUFTQyxhQUF