Skip to content

Commit 17f5688

Browse files
committed
New option: impliedStrict
1 parent 597ecb4 commit 17f5688

File tree

4 files changed

+140
-0
lines changed

4 files changed

+140
-0
lines changed

src/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ function defaultOptions() {
6262
optimistic: false,
6363
directive: false,
6464
nodejsScope: false,
65+
impliedStrict: false,
6566
sourceType: 'script', // one of ['script', 'module']
6667
ecmaVersion: 5
6768
};
@@ -103,6 +104,8 @@ function updateDeeply(target, override) {
103104
* @param {boolean} [providedOptions.nodejsScope=false]- whether the whole
104105
* script is executed under node.js environment. When enabled, escope adds
105106
* a function scope immediately following the global scope.
107+
* @param {boolean} [providedOptions.impliedStrict=false]- implied strict mode
108+
* (if ecmaVersion >= 5).
106109
* @param {string} [providedOptions.sourceType='script']- the source type of the script. one of 'script' and 'module'
107110
* @param {number} [providedOptions.ecmaVersion=5]- which ECMAScript version is considered
108111
* @return {ScopeManager}

src/referencer.js

+4
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,10 @@ export default class Referencer extends esrecurse.Visitor {
392392
this.scopeManager.__nestModuleScope(node);
393393
}
394394

395+
if (this.scopeManager.isStrictModeSupported() && this.scopeManager.isImpliedStrict()) {
396+
this.currentScope().isStrict = true;
397+
}
398+
395399
this.visitChildren(node);
396400
this.close(node);
397401
}

src/scope-manager.js

+8
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ export default class ScopeManager {
7373
return this.__options.sourceType === 'module';
7474
}
7575

76+
isImpliedStrict() {
77+
return this.__options.impliedStrict;
78+
}
79+
80+
isStrictModeSupported() {
81+
return this.__options.ecmaVersion >= 5;
82+
}
83+
7684
// Returns appropriate scope for this node.
7785
__get(node) {
7886
return this.__nodeToScope.get(node);

test/implied-strict.js

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// -*- coding: utf-8 -*-
2+
// Copyright (C) 2015 Yusuke Suzuki <[email protected]>
3+
//
4+
// Redistribution and use in source and binary forms, with or without
5+
// modification, are permitted provided that the following conditions are met:
6+
//
7+
// * Redistributions of source code must retain the above copyright
8+
// notice, this list of conditions and the following disclaimer.
9+
// * Redistributions in binary form must reproduce the above copyright
10+
// notice, this list of conditions and the following disclaimer in the
11+
// documentation and/or other materials provided with the distribution.
12+
//
13+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16+
// ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
17+
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18+
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19+
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20+
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21+
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22+
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23+
24+
import { expect } from 'chai';
25+
import { parse } from '../third_party/esprima';
26+
import { analyze } from '..';
27+
28+
describe('impliedStrict option', function() {
29+
it('ensures all user scopes are strict if ecmaVersion >= 5', function() {
30+
const ast = parse(`
31+
function foo() {
32+
function bar() {
33+
'use strict';
34+
}
35+
}
36+
`);
37+
38+
const scopeManager = analyze(ast, {ecmaVersion: 5, impliedStrict: true});
39+
expect(scopeManager.scopes).to.have.length(3);
40+
41+
let scope = scopeManager.scopes[0];
42+
expect(scope.type).to.be.equal('global');
43+
expect(scope.block.type).to.be.equal('Program');
44+
expect(scope.isStrict).to.be.true;
45+
46+
scope = scopeManager.scopes[1];
47+
expect(scope.type).to.be.equal('function');
48+
expect(scope.block.type).to.be.equal('FunctionDeclaration');
49+
expect(scope.isStrict).to.be.true;
50+
51+
scope = scopeManager.scopes[2];
52+
expect(scope.type).to.be.equal('function');
53+
expect(scope.block.type).to.be.equal('FunctionDeclaration');
54+
expect(scope.isStrict).to.be.true;
55+
});
56+
57+
it('ensures impliedStrict option is only effective when ecmaVersion option >= 5', function() {
58+
const ast = parse(`
59+
function foo() {}
60+
`);
61+
62+
const scopeManager = analyze(ast, {ecmaVersion: 3, impliedStrict: true});
63+
expect(scopeManager.scopes).to.have.length(2);
64+
65+
let scope = scopeManager.scopes[0];
66+
expect(scope.type).to.be.equal('global');
67+
expect(scope.block.type).to.be.equal('Program');
68+
expect(scope.isStrict).to.be.false;
69+
70+
scope = scopeManager.scopes[1];
71+
expect(scope.type).to.be.equal('function');
72+
expect(scope.block.type).to.be.equal('FunctionDeclaration');
73+
expect(scope.isStrict).to.be.false;
74+
});
75+
76+
it('omits a nodejs global scope when ensuring all user scopes are strict', function() {
77+
const ast = parse(`
78+
function foo() {}
79+
`);
80+
81+
let scopeManager = analyze(ast, {ecmaVersion: 5, nodejsScope: true, impliedStrict: true});
82+
expect(scopeManager.scopes).to.have.length(3);
83+
84+
let scope = scopeManager.scopes[0];
85+
expect(scope.type).to.be.equal('global');
86+
expect(scope.block.type).to.be.equal('Program');
87+
expect(scope.isStrict).to.be.false;
88+
89+
scope = scopeManager.scopes[1];
90+
expect(scope.type).to.be.equal('function');
91+
expect(scope.block.type).to.be.equal('Program');
92+
expect(scope.isStrict).to.be.true;
93+
94+
scope = scopeManager.scopes[2];
95+
expect(scope.type).to.be.equal('function');
96+
expect(scope.block.type).to.be.equal('FunctionDeclaration');
97+
expect(scope.isStrict).to.be.true;
98+
});
99+
100+
it('omits a module global scope when ensuring all user scopes are strict', function() {
101+
const ast = parse(`
102+
function foo() {}`,
103+
{sourceType: 'module'}
104+
);
105+
106+
let scopeManager = analyze(ast, {ecmaVersion: 6, impliedStrict: true, sourceType: 'module'});
107+
expect(scopeManager.scopes).to.have.length(3);
108+
109+
let scope = scopeManager.scopes[0];
110+
expect(scope.type).to.be.equal('global');
111+
expect(scope.block.type).to.be.equal('Program');
112+
expect(scope.isStrict).to.be.false;
113+
114+
scope = scopeManager.scopes[1];
115+
expect(scope.type).to.be.equal('module');
116+
expect(scope.isStrict).to.be.true;
117+
118+
scope = scopeManager.scopes[2];
119+
expect(scope.type).to.be.equal('function');
120+
expect(scope.block.type).to.be.equal('FunctionDeclaration');
121+
expect(scope.isStrict).to.be.true;
122+
});
123+
});
124+
125+
// vim: set sw=4 ts=4 et tw=80 :

0 commit comments

Comments
 (0)