147 lines
3.2 KiB
JavaScript
147 lines
3.2 KiB
JavaScript
|
'use strict';
|
||
|
const {
|
||
|
isIdentifierName,
|
||
|
isStrictReservedWord,
|
||
|
isKeyword,
|
||
|
} = require('@babel/helper-validator-identifier');
|
||
|
const resolveVariableName = require('./resolve-variable-name.js');
|
||
|
const getReferences = require('./get-references.js');
|
||
|
|
||
|
// https://github.com/microsoft/TypeScript/issues/2536#issuecomment-87194347
|
||
|
const typescriptReservedWords = new Set([
|
||
|
'break',
|
||
|
'case',
|
||
|
'catch',
|
||
|
'class',
|
||
|
'const',
|
||
|
'continue',
|
||
|
'debugger',
|
||
|
'default',
|
||
|
'delete',
|
||
|
'do',
|
||
|
'else',
|
||
|
'enum',
|
||
|
'export',
|
||
|
'extends',
|
||
|
'false',
|
||
|
'finally',
|
||
|
'for',
|
||
|
'function',
|
||
|
'if',
|
||
|
'import',
|
||
|
'in',
|
||
|
'instanceof',
|
||
|
'new',
|
||
|
'null',
|
||
|
'return',
|
||
|
'super',
|
||
|
'switch',
|
||
|
'this',
|
||
|
'throw',
|
||
|
'true',
|
||
|
'try',
|
||
|
'typeof',
|
||
|
'var',
|
||
|
'void',
|
||
|
'while',
|
||
|
'with',
|
||
|
'as',
|
||
|
'implements',
|
||
|
'interface',
|
||
|
'let',
|
||
|
'package',
|
||
|
'private',
|
||
|
'protected',
|
||
|
'public',
|
||
|
'static',
|
||
|
'yield',
|
||
|
'any',
|
||
|
'boolean',
|
||
|
'constructor',
|
||
|
'declare',
|
||
|
'get',
|
||
|
'module',
|
||
|
'require',
|
||
|
'number',
|
||
|
'set',
|
||
|
'string',
|
||
|
'symbol',
|
||
|
'type',
|
||
|
'from',
|
||
|
'of',
|
||
|
]);
|
||
|
|
||
|
// Copied from https://github.com/babel/babel/blob/fce35af69101c6b316557e28abf60bdbf77d6a36/packages/babel-types/src/validators/isValidIdentifier.ts#L7
|
||
|
// Use this function instead of `require('@babel/types').isIdentifier`, since `@babel/helper-validator-identifier` package is much smaller
|
||
|
const isValidIdentifier = name =>
|
||
|
typeof name === 'string'
|
||
|
&& !isKeyword(name)
|
||
|
&& !isStrictReservedWord(name, true)
|
||
|
&& isIdentifierName(name)
|
||
|
&& name !== 'arguments'
|
||
|
&& !typescriptReservedWords.has(name);
|
||
|
|
||
|
/*
|
||
|
Unresolved reference is probably from the global scope. We should avoid using that name.
|
||
|
|
||
|
For example, like `foo` and `bar` below.
|
||
|
|
||
|
```
|
||
|
function unicorn() {
|
||
|
return foo;
|
||
|
}
|
||
|
|
||
|
function unicorn() {
|
||
|
return function() {
|
||
|
return bar;
|
||
|
};
|
||
|
}
|
||
|
```
|
||
|
*/
|
||
|
const isUnresolvedName = (name, scope) =>
|
||
|
getReferences(scope).some(({identifier, resolved}) => identifier?.name === name && !resolved);
|
||
|
|
||
|
const isSafeName = (name, scopes) =>
|
||
|
!scopes.some(scope => resolveVariableName(name, scope) || isUnresolvedName(name, scope));
|
||
|
|
||
|
const alwaysTrue = () => true;
|
||
|
|
||
|
/**
|
||
|
Rule-specific name check function.
|
||
|
|
||
|
@callback isSafe
|
||
|
@param {string} name - The generated candidate name.
|
||
|
@param {Scope[]} scopes - The same list of scopes you pass to `avoidCapture`.
|
||
|
@returns {boolean} - `true` if the `name` is ok.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
Generates a unique name prefixed with `name` such that:
|
||
|
- it is not defined in any of the `scopes`,
|
||
|
- it is not a reserved word,
|
||
|
- it is not `arguments` in strict scopes (where `arguments` is not allowed),
|
||
|
- it does not collide with the actual `arguments` (which is always defined in function scopes).
|
||
|
|
||
|
Useful when you want to rename a variable (or create a new variable) while being sure not to shadow any other variables in the code.
|
||
|
|
||
|
@param {string} name - The desired name for a new variable.
|
||
|
@param {Scope[]} scopes - The list of scopes the new variable will be referenced in.
|
||
|
@param {isSafe} [isSafe] - Rule-specific name check function.
|
||
|
@returns {string} - Either `name` as is, or a string like `${name}_` suffixed with underscores to make the name unique.
|
||
|
*/
|
||
|
module.exports = (name, scopes, isSafe = alwaysTrue) => {
|
||
|
if (!isValidIdentifier(name)) {
|
||
|
name += '_';
|
||
|
|
||
|
if (!isValidIdentifier(name)) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (!isSafeName(name, scopes) || !isSafe(name, scopes)) {
|
||
|
name += '_';
|
||
|
}
|
||
|
|
||
|
return name;
|
||
|
};
|