Skip to content

Commit 0540eb3

Browse files
feat(getStaticValue): Dereference variables that are effectively const (#80)
1 parent 22ad79f commit 0540eb3

File tree

3 files changed

+37
-5
lines changed

3 files changed

+37
-5
lines changed

src/get-static-value.mjs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,23 @@ function getElementValues(nodeList, initialScope) {
249249
return valueList
250250
}
251251

252+
/**
253+
* Returns whether the given variable is never written to after initialization.
254+
* @param {import("eslint").Scope.Variable} variable
255+
* @returns {boolean}
256+
*/
257+
function isEffectivelyConst(variable) {
258+
const refs = variable.references
259+
260+
const inits = refs.filter((r) => r.init).length
261+
const reads = refs.filter((r) => r.isReadOnly()).length
262+
if (inits === 1 && reads + inits === refs.length) {
263+
// there is only one init and all other references only read
264+
return true
265+
}
266+
return false
267+
}
268+
252269
const operations = Object.freeze({
253270
ArrayExpression(node, initialScope) {
254271
const elements = getElementValues(node.elements, initialScope)
@@ -407,7 +424,9 @@ const operations = Object.freeze({
407424
const def = variable.defs[0]
408425
if (
409426
def.parent &&
410-
def.parent.kind === "const" &&
427+
def.type === "Variable" &&
428+
(def.parent.kind === "const" ||
429+
isEffectivelyConst(variable)) &&
411430
// TODO(mysticatea): don't support destructuring here.
412431
def.node.id.type === "Identifier"
413432
) {

test/get-static-value.mjs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ describe("The 'getStaticValue' function", () => {
6767
{ code: "var undefined; undefined", expected: null },
6868
{ code: "const undefined = 1; undefined", expected: { value: 1 } },
6969
{ code: "const a = 2; a", expected: { value: 2 } },
70-
{ code: "let a = 2; a", expected: null },
70+
{ code: "let a = 2; a", expected: { value: 2 } },
71+
{ code: "var a = 2; a", expected: { value: 2 } },
72+
{ code: "let a = 2; a = 1; a", expected: null },
73+
{ code: "let a = 2; a++; a", expected: null },
74+
{ code: "let a; a = 1; a", expected: null },
7175
{ code: "const a = 2; a", expected: null, noScope: true },
7276
{ code: "const a = { b: 7 }; a.b", expected: { value: 7 } },
7377
{ code: "null", expected: { value: null } },
@@ -158,7 +162,14 @@ describe("The 'getStaticValue' function", () => {
158162
code: "const obj = {b: 2}; ({a: 1, ...obj})",
159163
expected: { value: { a: 1, b: 2 } },
160164
},
161-
{ code: "var obj = {b: 2}; ({a: 1, ...obj})", expected: null },
165+
{
166+
code: "var obj = {b: 2}; ({a: 1, ...obj})",
167+
expected: { value: { a: 1, b: 2 } },
168+
},
169+
{
170+
code: "var obj = {b: 2}; obj = {}; ({a: 1, ...obj})",
171+
expected: null,
172+
},
162173
{ code: "({ get a() {} })", expected: null },
163174
{ code: "({ a })", expected: null },
164175
{ code: "({ a: b })", expected: null },

test/get-string-if-constant.mjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ describe("The 'getStringIfConstant' function", () => {
4343
for (const { code, expected } of [
4444
{ code: "id", expected: null },
4545
{ code: "const id = 'abc'; id", expected: "abc" },
46-
{ code: "let id = 'abc'; id", expected: null },
47-
{ code: "var id = 'abc'; id", expected: null },
46+
{ code: "let id = 'abc'; id", expected: "abc" },
47+
{ code: "var id = 'abc'; id", expected: "abc" },
48+
{ code: "let id = 'abc'; id = 'foo'; id", expected: null },
49+
{ code: "var id = 'abc'; id = 'foo'; id", expected: null },
4850
{ code: "const id = otherId; id", expected: null },
4951
]) {
5052
it(`should return ${JSON.stringify(expected)} from ${code}`, () => {

0 commit comments

Comments
 (0)