Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d7b8a33

Browse files
committedApr 29, 2020
- Enhancement: Add ESM distribution, with module property in package.json
- Refactoring: Add Rollup/Babel/Terser - Build: Have generator file produce consumable source files directly rather than logging to console - Linting: Switch from JSHint to ESLint (using rules, e.g., `indent` and `prefer-const`, in place on other estools project) - Testing: Convert coffeescript test files to ES6 - Testing: Add nyc - Testing: Check unmatched high surrogates and full coverage for AST expressions (further true `isExpression` and `isStatement`'s and false `isProblematicIfStatement`), bringing to 100% coverage - Travis: Drop previous versions for 10, 12, 14
1 parent 6281a63 commit d7b8a33

23 files changed

+1414
-1179
lines changed
 

‎.babelrc.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"presets": [
3+
["@babel/preset-env"]
4+
]
5+
}

‎.eslintignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules
2+
dist
3+
4+
!.eslintrc.js
5+
coverage
6+
7+
src/es5-identifier.js
8+
src/es6-identifier.js

‎.eslintrc.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
module.exports = {
3+
env: {
4+
browser: true,
5+
commonjs: true,
6+
es6: true,
7+
node: true
8+
},
9+
extends: 'eslint:recommended',
10+
globals: {
11+
Atomics: 'readonly',
12+
SharedArrayBuffer: 'readonly'
13+
},
14+
overrides: [{
15+
files: '.eslintrc.js',
16+
parserOptions: {
17+
sourceType: 'script'
18+
},
19+
rules: {
20+
strict: 'error'
21+
}
22+
}, {
23+
files: 'test/**',
24+
globals: {
25+
expect: true
26+
},
27+
env: {
28+
mocha: true
29+
}
30+
}],
31+
parserOptions: {
32+
sourceType: 'module',
33+
ecmaVersion: 2018
34+
},
35+
rules: {
36+
semi: ['error'],
37+
indent: ['error', 4, { SwitchCase: 1 }],
38+
'prefer-const': ['error'],
39+
'no-var': ['error'],
40+
'prefer-destructuring': ['error'],
41+
'object-shorthand': ['error'],
42+
'object-curly-spacing': ['error', 'always'],
43+
quotes: ['error', 'single'],
44+
'quote-props': ['error', 'as-needed'],
45+
'brace-style': ['error', '1tbs', { allowSingleLine: true }],
46+
'prefer-template': ['error']
47+
}
48+
};

‎.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
node_modules
2+
dist
3+
coverage

‎.jshintrc

-16
This file was deleted.

‎.travis.yml

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
language: node_js
22
node_js:
3-
- "0.10"
4-
- "0.11"
5-
6-
matrix:
7-
allow_failures:
8-
- node_js: "0.11"
3+
- 10
4+
- 12
5+
- 14

‎lib/ast.js

-144
This file was deleted.

‎lib/code.js

-135
This file was deleted.

‎package.json

+29-6
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@
33
"description": "utility box for ECMAScript language tools",
44
"homepage": "https://github.com/estools/esutils",
55
"bugs": "https://github.com/estools/esutils/issues",
6-
"main": "lib/utils.js",
6+
"main": "dist/esutils.min.js",
7+
"module": "dist/esutils.esm.min.js",
78
"version": "2.0.4-dev",
89
"engines": {
910
"node": ">=0.10.0"
1011
},
1112
"directories": {
12-
"lib": "./lib"
13+
"lib": "./dist"
1314
},
1415
"files": [
1516
"LICENSE.BSD",
1617
"README.md",
17-
"lib"
18+
"dist"
1819
],
1920
"keywords": [
2021
"ecmascript"
@@ -33,18 +34,40 @@
3334
},
3435
"dependencies": {},
3536
"devDependencies": {
37+
"@babel/cli": "^7.8.4",
38+
"@babel/core": "^7.9.0",
39+
"@babel/preset-env": "^7.9.5",
40+
"@babel/register": "^7.9.0",
41+
"@rollup/plugin-babel": "^5.0.0",
3642
"chai": "~4.2.0",
3743
"coffeescript": "^2.5.1",
38-
"jshint": "2.11.0",
44+
"eslint": "^6.8.0",
3945
"mocha": "~7.1.2",
46+
"nyc": "^15.0.1",
4047
"regenerate": "~1.4.0",
48+
"rollup": "^2.7.3",
49+
"rollup-plugin-terser": "^5.3.0",
4150
"unicode-13.0.0": "^0.8.0"
4251
},
4352
"license": "BSD-2-Clause",
53+
"nyc": {
54+
"branches": 100,
55+
"lines": 100,
56+
"functions": 100,
57+
"statements": 100,
58+
"reporter": [
59+
"html",
60+
"text"
61+
],
62+
"exclude": [
63+
"test"
64+
]
65+
},
4466
"scripts": {
67+
"build": "rollup -c",
4568
"test": "npm run lint && npm run unit-test",
46-
"lint": "jshint lib/*.js",
47-
"unit-test": "mocha --require chai/register-expect --require coffeescript/register test/**",
69+
"lint": "eslint .",
70+
"unit-test": "nyc mocha --require @babel/register --require chai/register-expect --require coffeescript/register test/**",
4871
"generate-regex": "node tools/generate-identifier-regex.js"
4972
}
5073
}

‎rollup.config.js

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { terser } from 'rollup-plugin-terser';
2+
3+
import babel from '@rollup/plugin-babel';
4+
5+
/**
6+
* @external RollupConfig
7+
* @type {PlainObject}
8+
* @see {@link https://rollupjs.org/guide/en#big-list-of-options}
9+
*/
10+
11+
/**
12+
* @param {PlainObject} [config= {}]
13+
* @param {boolean} [config.minifying=false]
14+
* @param {string} [config.format='umd']
15+
* @returns {external:RollupConfig}
16+
*/
17+
function getRollupObject ({ minifying, format = 'umd' } = {}) {
18+
const nonMinified = {
19+
input: 'src/utils.js',
20+
output: {
21+
format,
22+
sourcemap: minifying,
23+
file: `dist/esutils${
24+
format === 'umd' ? '' : `.${format}`
25+
}${minifying ? '.min' : ''}.js`,
26+
name: 'esutils'
27+
},
28+
plugins: [
29+
babel({
30+
babelHelpers: 'bundled'
31+
})
32+
]
33+
};
34+
if (minifying) {
35+
nonMinified.plugins.push(terser());
36+
}
37+
return nonMinified;
38+
}
39+
40+
export default [
41+
getRollupObject({ minifying: true, format: 'umd' }),
42+
getRollupObject({ minifying: false, format: 'umd' }),
43+
getRollupObject({ minifying: true, format: 'esm' }),
44+
getRollupObject({ minifying: false, format: 'esm' })
45+
];

‎src/ast.js

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
Copyright (C) 2013 Yusuke Suzuki <utatane.tea@gmail.com>
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+
25+
function isExpression(node) {
26+
if (node == null) { return false; }
27+
switch (node.type) {
28+
case 'ArrayExpression':
29+
case 'AssignmentExpression':
30+
case 'BinaryExpression':
31+
case 'CallExpression':
32+
case 'ConditionalExpression':
33+
case 'FunctionExpression':
34+
case 'Identifier':
35+
case 'Literal':
36+
case 'LogicalExpression':
37+
case 'MemberExpression':
38+
case 'NewExpression':
39+
case 'ObjectExpression':
40+
case 'SequenceExpression':
41+
case 'ThisExpression':
42+
case 'UnaryExpression':
43+
case 'UpdateExpression':
44+
return true;
45+
}
46+
return false;
47+
}
48+
49+
function isIterationStatement(node) {
50+
if (node == null) { return false; }
51+
switch (node.type) {
52+
case 'DoWhileStatement':
53+
case 'ForInStatement':
54+
case 'ForStatement':
55+
case 'WhileStatement':
56+
return true;
57+
}
58+
return false;
59+
}
60+
61+
function isStatement(node) {
62+
if (node == null) { return false; }
63+
switch (node.type) {
64+
case 'BlockStatement':
65+
case 'BreakStatement':
66+
case 'ContinueStatement':
67+
case 'DebuggerStatement':
68+
case 'DoWhileStatement':
69+
case 'EmptyStatement':
70+
case 'ExpressionStatement':
71+
case 'ForInStatement':
72+
case 'ForStatement':
73+
case 'IfStatement':
74+
case 'LabeledStatement':
75+
case 'ReturnStatement':
76+
case 'SwitchStatement':
77+
case 'ThrowStatement':
78+
case 'TryStatement':
79+
case 'VariableDeclaration':
80+
case 'WhileStatement':
81+
case 'WithStatement':
82+
return true;
83+
}
84+
return false;
85+
}
86+
87+
function isSourceElement(node) {
88+
return isStatement(node) || node != null && node.type === 'FunctionDeclaration';
89+
}
90+
91+
function trailingStatement(node) {
92+
switch (node.type) {
93+
case 'IfStatement':
94+
if (node.alternate != null) {
95+
return node.alternate;
96+
}
97+
return node.consequent;
98+
99+
case 'LabeledStatement':
100+
case 'ForStatement':
101+
case 'ForInStatement':
102+
case 'WhileStatement':
103+
case 'WithStatement':
104+
return node.body;
105+
}
106+
return null;
107+
}
108+
109+
function isProblematicIfStatement(node) {
110+
if (node.type !== 'IfStatement') {
111+
return false;
112+
}
113+
if (node.alternate == null) {
114+
return false;
115+
}
116+
let current = node.consequent;
117+
do {
118+
if (current.type === 'IfStatement') {
119+
if (current.alternate == null) {
120+
return true;
121+
}
122+
}
123+
current = trailingStatement(current);
124+
} while (current);
125+
126+
return false;
127+
}
128+
129+
export {
130+
isExpression,
131+
isStatement,
132+
isIterationStatement,
133+
isSourceElement,
134+
isProblematicIfStatement,
135+
136+
trailingStatement
137+
};
138+
139+
/* vim: set sw=4 ts=4 et tw=80 : */

‎src/code.js

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/* eslint-disable no-misleading-character-class */
2+
/*
3+
Copyright (C) 2013-2014 Yusuke Suzuki <utatane.tea@gmail.com>
4+
Copyright (C) 2014 Ivan Nikulin <ifaaan@gmail.com>
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions are met:
8+
9+
* Redistributions of source code must retain the above copyright
10+
notice, this list of conditions and the following disclaimer.
11+
* Redistributions in binary form must reproduce the above copyright
12+
notice, this list of conditions and the following disclaimer in the
13+
documentation and/or other materials provided with the distribution.
14+
15+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18+
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
*/
26+
27+
import * as ES5Regex from './es5-identifier.js';
28+
import * as ES6Regex from './es6-identifier.js';
29+
30+
function isDecimalDigit(ch) {
31+
return 0x30 <= ch && ch <= 0x39; // 0..9
32+
}
33+
34+
function isHexDigit(ch) {
35+
return 0x30 <= ch && ch <= 0x39 || // 0..9
36+
0x61 <= ch && ch <= 0x66 || // a..f
37+
0x41 <= ch && ch <= 0x46; // A..F
38+
}
39+
40+
function isOctalDigit(ch) {
41+
return ch >= 0x30 && ch <= 0x37; // 0..7
42+
}
43+
44+
// 7.2 White Space
45+
46+
const NON_ASCII_WHITESPACES = [
47+
0x1680,
48+
0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A,
49+
0x202F, 0x205F,
50+
0x3000,
51+
0xFEFF
52+
];
53+
54+
function isWhiteSpace(ch) {
55+
return ch === 0x20 || ch === 0x09 || ch === 0x0B || ch === 0x0C || ch === 0xA0 ||
56+
ch >= 0x1680 && NON_ASCII_WHITESPACES.indexOf(ch) >= 0;
57+
}
58+
59+
// 7.3 Line Terminators
60+
61+
function isLineTerminator(ch) {
62+
return ch === 0x0A || ch === 0x0D || ch === 0x2028 || ch === 0x2029;
63+
}
64+
65+
// 7.6 Identifier Names and Identifiers
66+
67+
function fromCodePoint(cp) {
68+
if (cp <= 0xFFFF) { return String.fromCharCode(cp); }
69+
const cu1 = String.fromCharCode(Math.floor((cp - 0x10000) / 0x400) + 0xD800);
70+
const cu2 = String.fromCharCode(((cp - 0x10000) % 0x400) + 0xDC00);
71+
return cu1 + cu2;
72+
}
73+
74+
const IDENTIFIER_START = new Array(0x80);
75+
for(let ch = 0; ch < 0x80; ++ch) {
76+
IDENTIFIER_START[ch] =
77+
ch >= 0x61 && ch <= 0x7A || // a..z
78+
ch >= 0x41 && ch <= 0x5A || // A..Z
79+
ch === 0x24 || ch === 0x5F; // $ (dollar) and _ (underscore)
80+
}
81+
82+
const IDENTIFIER_PART = new Array(0x80);
83+
for(let ch = 0; ch < 0x80; ++ch) {
84+
IDENTIFIER_PART[ch] =
85+
ch >= 0x61 && ch <= 0x7A || // a..z
86+
ch >= 0x41 && ch <= 0x5A || // A..Z
87+
ch >= 0x30 && ch <= 0x39 || // 0..9
88+
ch === 0x24 || ch === 0x5F; // $ (dollar) and _ (underscore)
89+
}
90+
91+
function isIdentifierStartES5(ch) {
92+
return ch < 0x80 ? IDENTIFIER_START[ch] : ES5Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch));
93+
}
94+
95+
function isIdentifierPartES5(ch) {
96+
return ch < 0x80 ? IDENTIFIER_PART[ch] : ES5Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch));
97+
}
98+
99+
function isIdentifierStartES6(ch) {
100+
return ch < 0x80 ? IDENTIFIER_START[ch] : ES6Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch));
101+
}
102+
103+
function isIdentifierPartES6(ch) {
104+
return ch < 0x80 ? IDENTIFIER_PART[ch] : ES6Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch));
105+
}
106+
107+
export {
108+
isDecimalDigit,
109+
isHexDigit,
110+
isOctalDigit,
111+
isWhiteSpace,
112+
isLineTerminator,
113+
isIdentifierStartES5,
114+
isIdentifierPartES5,
115+
isIdentifierStartES6,
116+
isIdentifierPartES6
117+
};
118+
119+
/* vim: set sw=4 ts=4 et tw=80 : */

‎src/es5-identifier.js

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/es6-identifier.js

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎lib/keyword.js ‎src/keyword.js

+79-86
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,10 @@
2222
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2323
*/
2424

25-
(function () {
26-
'use strict';
25+
import * as code from './code.js';
2726

28-
var code = require('./code');
29-
30-
function isStrictModeReservedWordES6(id) {
31-
switch (id) {
27+
function isStrictModeReservedWordES6(id) {
28+
switch (id) {
3229
case 'implements':
3330
case 'interface':
3431
case 'package':
@@ -40,15 +37,15 @@
4037
return true;
4138
default:
4239
return false;
43-
}
4440
}
41+
}
4542

46-
function isKeywordES6(id, strict) {
47-
if (strict && isStrictModeReservedWordES6(id)) {
48-
return true;
49-
}
43+
function isKeywordES6(id, strict) {
44+
if (strict && isStrictModeReservedWordES6(id)) {
45+
return true;
46+
}
5047

51-
switch (id.length) {
48+
switch (id.length) {
5249
case 2:
5350
return (id === 'if') || (id === 'in') || (id === 'do');
5451
case 3:
@@ -71,95 +68,91 @@
7168
return (id === 'instanceof');
7269
default:
7370
return false;
74-
}
7571
}
72+
}
7673

77-
function isKeywordES5(id, strict) {
78-
// yield should not be treated as keyword under non-strict mode.
79-
if (!strict && id === 'yield') {
80-
return false;
81-
}
82-
return isKeywordES6(id, strict);
74+
function isKeywordES5(id, strict) {
75+
// yield should not be treated as keyword under non-strict mode.
76+
if (!strict && id === 'yield') {
77+
return false;
8378
}
79+
return isKeywordES6(id, strict);
80+
}
8481

85-
function isReservedWordES5(id, strict) {
86-
return id === 'null' || id === 'true' || id === 'false' || isKeywordES5(id, strict);
87-
}
82+
function isReservedWordES5(id, strict) {
83+
return id === 'null' || id === 'true' || id === 'false' || isKeywordES5(id, strict);
84+
}
8885

89-
function isReservedWordES6(id, strict) {
90-
return id === 'null' || id === 'true' || id === 'false' || isKeywordES6(id, strict);
91-
}
86+
function isReservedWordES6(id, strict) {
87+
return id === 'null' || id === 'true' || id === 'false' || isKeywordES6(id, strict);
88+
}
9289

93-
function isRestrictedWord(id) {
94-
return id === 'eval' || id === 'arguments';
95-
}
90+
function isRestrictedWord(id) {
91+
return id === 'eval' || id === 'arguments';
92+
}
9693

97-
function isIdentifierNameES5(id) {
98-
var i, iz, ch;
94+
function isIdentifierNameES5(id) {
95+
if (id.length === 0) { return false; }
9996

100-
if (id.length === 0) { return false; }
97+
const ch = id.charCodeAt(0);
98+
if (!code.isIdentifierStartES5(ch)) {
99+
return false;
100+
}
101101

102-
ch = id.charCodeAt(0);
103-
if (!code.isIdentifierStartES5(ch)) {
102+
for (let i = 1, iz = id.length; i < iz; ++i) {
103+
const ch = id.charCodeAt(i);
104+
if (!code.isIdentifierPartES5(ch)) {
104105
return false;
105106
}
106-
107-
for (i = 1, iz = id.length; i < iz; ++i) {
108-
ch = id.charCodeAt(i);
109-
if (!code.isIdentifierPartES5(ch)) {
110-
return false;
111-
}
112-
}
113-
return true;
114107
}
115-
116-
function decodeUtf16(lead, trail) {
117-
return (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;
118-
}
119-
120-
function isIdentifierNameES6(id) {
121-
var i, iz, ch, lowCh, check;
122-
123-
if (id.length === 0) { return false; }
124-
125-
check = code.isIdentifierStartES6;
126-
for (i = 0, iz = id.length; i < iz; ++i) {
127-
ch = id.charCodeAt(i);
128-
if (0xD800 <= ch && ch <= 0xDBFF) {
129-
++i;
130-
if (i >= iz) { return false; }
131-
lowCh = id.charCodeAt(i);
132-
if (!(0xDC00 <= lowCh && lowCh <= 0xDFFF)) {
133-
return false;
134-
}
135-
ch = decodeUtf16(ch, lowCh);
136-
}
137-
if (!check(ch)) {
108+
return true;
109+
}
110+
111+
function decodeUtf16(lead, trail) {
112+
return (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;
113+
}
114+
115+
function isIdentifierNameES6(id) {
116+
if (id.length === 0) { return false; }
117+
118+
let check = code.isIdentifierStartES6;
119+
for (let i = 0, iz = id.length; i < iz; ++i) {
120+
let ch = id.charCodeAt(i);
121+
if (0xD800 <= ch && ch <= 0xDBFF) {
122+
++i;
123+
if (i >= iz) { return false; }
124+
const lowCh = id.charCodeAt(i);
125+
if (!(0xDC00 <= lowCh && lowCh <= 0xDFFF)) {
138126
return false;
139127
}
140-
check = code.isIdentifierPartES6;
128+
ch = decodeUtf16(ch, lowCh);
141129
}
142-
return true;
143-
}
144-
145-
function isIdentifierES5(id, strict) {
146-
return isIdentifierNameES5(id) && !isReservedWordES5(id, strict);
147-
}
148-
149-
function isIdentifierES6(id, strict) {
150-
return isIdentifierNameES6(id) && !isReservedWordES6(id, strict);
130+
if (!check(ch)) {
131+
return false;
132+
}
133+
check = code.isIdentifierPartES6;
151134
}
135+
return true;
136+
}
137+
138+
function isIdentifierES5(id, strict) {
139+
return isIdentifierNameES5(id) && !isReservedWordES5(id, strict);
140+
}
141+
142+
function isIdentifierES6(id, strict) {
143+
return isIdentifierNameES6(id) && !isReservedWordES6(id, strict);
144+
}
145+
146+
export {
147+
isKeywordES5,
148+
isKeywordES6,
149+
isReservedWordES5,
150+
isReservedWordES6,
151+
isRestrictedWord,
152+
isIdentifierNameES5,
153+
isIdentifierNameES6,
154+
isIdentifierES5,
155+
isIdentifierES6
156+
};
152157

153-
module.exports = {
154-
isKeywordES5: isKeywordES5,
155-
isKeywordES6: isKeywordES6,
156-
isReservedWordES5: isReservedWordES5,
157-
isReservedWordES6: isReservedWordES6,
158-
isRestrictedWord: isRestrictedWord,
159-
isIdentifierNameES5: isIdentifierNameES5,
160-
isIdentifierNameES6: isIdentifierNameES6,
161-
isIdentifierES5: isIdentifierES5,
162-
isIdentifierES6: isIdentifierES6
163-
};
164-
}());
165158
/* vim: set sw=4 ts=4 et tw=80 : */

‎lib/utils.js ‎src/utils.js

+12-6
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,17 @@
2323
*/
2424

2525

26-
(function () {
27-
'use strict';
26+
import * as ast from './ast.js';
27+
import * as code from './code.js';
28+
import * as keyword from './keyword.js';
29+
30+
export { ast, code, keyword };
31+
32+
/*
33+
// This syntax is shown on MDN, but not recognized by ESLint parser
34+
export * as ast from './ast.js';
35+
export * as code from './code.js';
36+
export * as keyword from './keyword.js';
37+
*/
2838

29-
exports.ast = require('./ast');
30-
exports.code = require('./code');
31-
exports.keyword = require('./keyword');
32-
}());
3339
/* vim: set sw=4 ts=4 et tw=80 : */

‎test/ast.coffee

-174
This file was deleted.

‎test/ast.js

+311
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
/*
2+
Copyright (C) 2013 Yusuke Suzuki <utatane.tea@gmail.com>
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+
25+
import * as esutils from '../';
26+
27+
const EMPTY = {
28+
type: 'EmptyStatement'
29+
};
30+
31+
describe('ast', function() {
32+
describe('isExpression', function() {
33+
it('returns false if input is not node', function() {
34+
expect(esutils.ast.isExpression(0)).to.be.false;
35+
expect(esutils.ast.isExpression(null)).to.be.false;
36+
expect(esutils.ast.isExpression(void 0)).to.be.false;
37+
expect(esutils.ast.isExpression({})).to.be.false;
38+
expect(esutils.ast.isExpression({
39+
type: null
40+
})).to.be.false;
41+
expect(esutils.ast.isExpression({
42+
type: void 0
43+
})).to.be.false;
44+
});
45+
46+
it('returns true if provided node is expression', function() {
47+
expect(esutils.ast.isExpression({
48+
type: 'ArrayExpression'
49+
})).to.be.true;
50+
expect(esutils.ast.isExpression({
51+
type: 'ThisExpression'
52+
})).to.be.true;
53+
expect(esutils.ast.isExpression({
54+
type: 'Literal',
55+
value: 0
56+
})).to.be.true;
57+
});
58+
59+
it('returns false if provided node is not expression', function() {
60+
expect(esutils.ast.isExpression({
61+
type: 'ExpressionStatement'
62+
})).to.be.false;
63+
64+
expect(esutils.ast.isExpression({
65+
type: 'Program'
66+
})).to.be.false;
67+
});
68+
});
69+
describe('isIterationStatement', function() {
70+
it('returns false if input is not node', function() {
71+
expect(esutils.ast.isIterationStatement(0)).to.be.false;
72+
expect(esutils.ast.isIterationStatement(null)).to.be.false;
73+
expect(esutils.ast.isIterationStatement(void 0)).to.be.false;
74+
expect(esutils.ast.isIterationStatement({})).to.be.false;
75+
expect(esutils.ast.isIterationStatement({
76+
type: null
77+
})).to.be.false;
78+
expect(esutils.ast.isIterationStatement({
79+
type: void 0
80+
})).to.be.false;
81+
});
82+
it('returns true if provided node is iteration statement', function() {
83+
expect(esutils.ast.isIterationStatement({
84+
type: 'ForInStatement'
85+
})).to.be.true;
86+
expect(esutils.ast.isIterationStatement({
87+
type: 'DoWhileStatement'
88+
})).to.be.true;
89+
});
90+
it('returns false if provided node is not iteration statement', function() {
91+
expect(esutils.ast.isIterationStatement({
92+
type: 'ExpressionStatement'
93+
})).to.be.false;
94+
expect(esutils.ast.isIterationStatement({
95+
type: 'ThisExpression'
96+
})).to.be.false;
97+
});
98+
});
99+
describe('isStatement', function() {
100+
it('returns false if input is not node', function() {
101+
expect(esutils.ast.isStatement(0)).to.be.false;
102+
expect(esutils.ast.isStatement(null)).to.be.false;
103+
expect(esutils.ast.isStatement(void 0)).to.be.false;
104+
expect(esutils.ast.isStatement({})).to.be.false;
105+
expect(esutils.ast.isStatement({
106+
type: null
107+
})).to.be.false;
108+
expect(esutils.ast.isStatement({
109+
type: void 0
110+
})).to.be.false;
111+
});
112+
it('returns true if provided node is statement', function() {
113+
expect(esutils.ast.isStatement({
114+
type: 'BlockStatement'
115+
})).to.be.true;
116+
expect(esutils.ast.isStatement({
117+
type: 'ExpressionStatement'
118+
})).to.be.true;
119+
expect(esutils.ast.isStatement({
120+
type: 'WhileStatement'
121+
})).to.be.true;
122+
});
123+
it('returns false if provided node is not statement', function() {
124+
expect(esutils.ast.isStatement({
125+
type: 'ThisExpression'
126+
})).to.be.false;
127+
expect(esutils.ast.isStatement({
128+
type: 'FunctionDeclaration'
129+
})).to.be.false;
130+
expect(esutils.ast.isStatement({
131+
type: 'Program'
132+
})).to.be.false;
133+
});
134+
});
135+
describe('isSourceElement', function() {
136+
it('returns false if input is not node', function() {
137+
expect(esutils.ast.isSourceElement(0)).to.be.false;
138+
expect(esutils.ast.isSourceElement(null)).to.be.false;
139+
expect(esutils.ast.isSourceElement(void 0)).to.be.false;
140+
expect(esutils.ast.isSourceElement({})).to.be.false;
141+
expect(esutils.ast.isSourceElement({
142+
type: null
143+
})).to.be.false;
144+
expect(esutils.ast.isSourceElement({
145+
type: void 0
146+
})).to.be.false;
147+
});
148+
it('returns true if provided node is source element', function() {
149+
expect(esutils.ast.isSourceElement({
150+
type: 'ExpressionStatement'
151+
})).to.be.true;
152+
expect(esutils.ast.isSourceElement({
153+
type: 'WhileStatement'
154+
})).to.be.true;
155+
expect(esutils.ast.isSourceElement({
156+
type: 'FunctionDeclaration'
157+
})).to.be.true;
158+
});
159+
it('returns false if provided node is not source element', function() {
160+
expect(esutils.ast.isSourceElement({
161+
type: 'ThisExpression'
162+
})).to.be.false;
163+
expect(esutils.ast.isSourceElement({
164+
type: 'Program'
165+
})).to.be.false;
166+
});
167+
});
168+
describe('trailingStatement', function() {
169+
it('returns trailing statement if node has it', function() {
170+
expect(esutils.ast.trailingStatement({
171+
type: 'WhileStatement',
172+
body: EMPTY
173+
})).to.be.eq(EMPTY);
174+
expect(esutils.ast.trailingStatement({
175+
type: 'WithStatement',
176+
body: EMPTY
177+
})).to.be.eq(EMPTY);
178+
expect(esutils.ast.trailingStatement({
179+
type: 'ForStatement',
180+
body: EMPTY
181+
})).to.be.eq(EMPTY);
182+
expect(esutils.ast.trailingStatement({
183+
type: 'ForInStatement',
184+
body: EMPTY
185+
})).to.be.eq(EMPTY);
186+
expect(esutils.ast.trailingStatement({
187+
type: 'IfStatement',
188+
consequent: EMPTY
189+
})).to.be.eq(EMPTY);
190+
expect(esutils.ast.trailingStatement({
191+
type: 'IfStatement',
192+
consequent: {
193+
type: 'EmptyStatement'
194+
},
195+
alternate: EMPTY
196+
})).to.be.eq(EMPTY);
197+
expect(esutils.ast.trailingStatement({
198+
type: 'LabeledStatement',
199+
body: EMPTY
200+
})).to.be.eq(EMPTY);
201+
});
202+
it('returns null if node doens\'t have trailing statement', function() {
203+
expect(esutils.ast.trailingStatement({
204+
type: 'DoWhileStatement',
205+
body: EMPTY
206+
})).to.be.null;
207+
expect(esutils.ast.trailingStatement({
208+
type: 'ReturnStatement'
209+
})).to.be.null;
210+
});
211+
});
212+
describe('isProblematicIfStatement', function() {
213+
it('returns true if node is problematic if statement', function() {
214+
expect(esutils.ast.isProblematicIfStatement({
215+
type: 'IfStatement',
216+
consequent: {
217+
type: 'IfStatement',
218+
consequent: EMPTY
219+
},
220+
alternate: EMPTY
221+
})).to.be.true;
222+
expect(esutils.ast.isProblematicIfStatement({
223+
type: 'IfStatement',
224+
consequent: {
225+
type: 'LabeledStatement',
226+
body: {
227+
type: 'IfStatement',
228+
consequent: EMPTY
229+
}
230+
},
231+
alternate: EMPTY
232+
})).to.be.true;
233+
expect(esutils.ast.isProblematicIfStatement({
234+
type: 'IfStatement',
235+
consequent: {
236+
type: 'WithStatement',
237+
body: {
238+
type: 'IfStatement',
239+
consequent: EMPTY
240+
}
241+
},
242+
alternate: EMPTY
243+
})).to.be.true;
244+
});
245+
it('returns false if node is not problematic if statement', function() {
246+
expect(esutils.ast.isProblematicIfStatement({
247+
type: 'ThisExpression'
248+
})).to.be.false;
249+
expect(esutils.ast.isProblematicIfStatement({
250+
type: 'IfStatement',
251+
consequent: EMPTY,
252+
alternate: EMPTY
253+
})).to.be.false;
254+
expect(esutils.ast.isProblematicIfStatement({
255+
type: 'IfStatement',
256+
consequent: {
257+
type: 'IfStatement',
258+
consequent: {
259+
type: 'BlockStatement',
260+
body: []
261+
},
262+
alternate: {
263+
type: 'BlockStatement',
264+
body: []
265+
}
266+
},
267+
alternate: {
268+
type: 'BlockStatement',
269+
body: []
270+
}
271+
})).to.be.false;
272+
expect(esutils.ast.isProblematicIfStatement({
273+
type: 'IfStatement',
274+
consequent: {
275+
type: 'BlockStatement',
276+
body: [
277+
{
278+
type: 'IfStatement',
279+
consequent: EMPTY
280+
}
281+
]
282+
},
283+
alternate: null
284+
})).to.be.false;
285+
expect(esutils.ast.isProblematicIfStatement({
286+
type: 'IfStatement',
287+
consequent: {
288+
type: 'BlockStatement',
289+
body: [
290+
{
291+
type: 'IfStatement',
292+
consequent: EMPTY
293+
}
294+
]
295+
},
296+
alternate: EMPTY
297+
})).to.be.false;
298+
expect(esutils.ast.isProblematicIfStatement({
299+
type: 'IfStatement',
300+
consequent: {
301+
type: 'DoWhileStatement',
302+
body: {
303+
type: 'IfStatement',
304+
consequent: EMPTY
305+
}
306+
},
307+
alternate: EMPTY
308+
})).to.be.false;
309+
});
310+
});
311+
});

‎test/code.coffee

-183
This file was deleted.

‎test/code.js

+266
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/*
2+
Copyright (C) 2013 Yusuke Suzuki <utatane.tea@gmail.com>
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+
25+
import * as esutils from '../';
26+
27+
describe('code', function() {
28+
describe('isDecimalDigit', function() {
29+
it('returns true if provided code is decimal digit', function() {
30+
const results = [];
31+
for (let ch = 0, i = 0; i <= 9; ch = ++i) {
32+
results.push(expect(esutils.code.isDecimalDigit((String(ch)).charCodeAt(0))).to.be.true);
33+
}
34+
return results;
35+
});
36+
37+
it('returns false if provided code is not decimal digit', function() {
38+
let code, i, ref, ref1;
39+
for (code = i = ref = 'a'.charCodeAt(0), ref1 = 'z'.charCodeAt(0); (ref <= ref1 ? i <= ref1 : i >= ref1); code = ref <= ref1 ? ++i : --i) {
40+
expect(esutils.code.isDecimalDigit(code)).to.be.false;
41+
}
42+
const results = [];
43+
44+
let j, ref2, ref3;
45+
for (code = j = ref2 = 'A'.charCodeAt(0), ref3 = 'Z'.charCodeAt(0); (ref2 <= ref3 ? j <= ref3 : j >= ref3); code = ref2 <= ref3 ? ++j : --j) {
46+
results.push(expect(esutils.code.isDecimalDigit(code)).to.be.false);
47+
}
48+
return results;
49+
});
50+
});
51+
describe('isHexDigit', function() {
52+
it('returns true if provided code is hexadecimal digit', function() {
53+
for (let ch = 0, i = 0; i <= 9; ch = ++i) {
54+
expect(esutils.code.isHexDigit((String(ch)).charCodeAt(0))).to.be.true;
55+
}
56+
let j, ref, ref1, code;
57+
for (code = j = ref = 'a'.charCodeAt(0), ref1 = 'f'.charCodeAt(0); (ref <= ref1 ? j <= ref1 : j >= ref1); code = ref <= ref1 ? ++j : --j) {
58+
expect(esutils.code.isHexDigit(code)).to.be.true;
59+
}
60+
const results = [];
61+
let k, ref2, ref3;
62+
for (code = k = ref2 = 'A'.charCodeAt(0), ref3 = 'F'.charCodeAt(0); (ref2 <= ref3 ? k <= ref3 : k >= ref3); code = ref2 <= ref3 ? ++k : --k) {
63+
results.push(expect(esutils.code.isHexDigit(code)).to.be.true);
64+
}
65+
return results;
66+
});
67+
68+
it('returns false if provided code is not hexadecimal digit', function() {
69+
let code, i, ref, ref1;
70+
for (code = i = ref = 'g'.charCodeAt(0), ref1 = 'z'.charCodeAt(0); (ref <= ref1 ? i <= ref1 : i >= ref1); code = ref <= ref1 ? ++i : --i) {
71+
expect(esutils.code.isHexDigit(code)).to.be.false;
72+
}
73+
const results = [];
74+
let j, ref2, ref3;
75+
for (code = j = ref2 = 'G'.charCodeAt(0), ref3 = 'Z'.charCodeAt(0); (ref2 <= ref3 ? j <= ref3 : j >= ref3); code = ref2 <= ref3 ? ++j : --j) {
76+
results.push(expect(esutils.code.isHexDigit(code)).to.be.false);
77+
}
78+
return results;
79+
});
80+
});
81+
describe('isOctalDigit', function() {
82+
it('returns true if provided code is octal digit', function() {
83+
const results = [];
84+
for (let ch = 0, i = 0; i <= 7; ch = ++i) {
85+
results.push(expect(esutils.code.isOctalDigit((String(ch)).charCodeAt(0))).to.be.true);
86+
}
87+
return results;
88+
});
89+
90+
it('returns false if provided code is not octal digit', function() {
91+
for (let ch = 8, i = 8; i <= 9; ch = ++i) {
92+
expect(esutils.code.isOctalDigit((String(ch)).charCodeAt(0))).to.be.false;
93+
}
94+
let code, j, ref, ref1;
95+
for (code = j = ref = 'a'.charCodeAt(0), ref1 = 'z'.charCodeAt(0); (ref <= ref1 ? j <= ref1 : j >= ref1); code = ref <= ref1 ? ++j : --j) {
96+
expect(esutils.code.isOctalDigit(code)).to.be.false;
97+
}
98+
const results = [];
99+
let k, ref2, ref3;
100+
for (code = k = ref2 = 'A'.charCodeAt(0), ref3 = 'Z'.charCodeAt(0); (ref2 <= ref3 ? k <= ref3 : k >= ref3); code = ref2 <= ref3 ? ++k : --k) {
101+
results.push(expect(esutils.code.isOctalDigit(code)).to.be.false);
102+
}
103+
return results;
104+
});
105+
});
106+
describe('isWhiteSpace', function() {
107+
it('returns true if provided code is white space', function() {
108+
const codes = [
109+
0x0009, // TAB
110+
0x000B, // VT
111+
0x000C, // FF
112+
0x0020, // SP
113+
0x00A0, // NBSP
114+
0xFEFF, // BOM
115+
116+
// Zs
117+
0x1680,
118+
0x2000,
119+
0x2001,
120+
0x2002,
121+
0x2003,
122+
0x2004,
123+
0x2005,
124+
0x2006,
125+
0x2007,
126+
0x2008,
127+
0x2009,
128+
0x200A,
129+
0x202F,
130+
0x205F,
131+
0x3000
132+
];
133+
for (const code of codes) {
134+
expect(esutils.code.isWhiteSpace(code)).to.be.true;
135+
}
136+
expect(esutils.code.isWhiteSpace(0x180E)).to.be.false;
137+
});
138+
139+
it('returns false if provided code is not white space', function() {
140+
for (let ch = 0, i = 0; i <= 9; ch = ++i) {
141+
expect(esutils.code.isWhiteSpace((String(ch)).charCodeAt(0))).to.be.false;
142+
}
143+
let code, j, ref, ref1;
144+
for (code = j = ref = 'a'.charCodeAt(0), ref1 = 'z'.charCodeAt(0); (ref <= ref1 ? j <= ref1 : j >= ref1); code = ref <= ref1 ? ++j : --j) {
145+
expect(esutils.code.isWhiteSpace(code)).to.be.false;
146+
}
147+
const results = [];
148+
let k, ref2, ref3;
149+
for (code = k = ref2 = 'A'.charCodeAt(0), ref3 = 'Z'.charCodeAt(0); (ref2 <= ref3 ? k <= ref3 : k >= ref3); code = ref2 <= ref3 ? ++k : --k) {
150+
results.push(expect(esutils.code.isWhiteSpace(code)).to.be.false);
151+
}
152+
return results;
153+
});
154+
});
155+
describe('isLineTerminator', function() {
156+
it('returns true if provided code is line terminator', function() {
157+
const codes = [0x000A, 0x000D, 0x2028, 0x2029];
158+
const results = [];
159+
for (const code of codes) {
160+
results.push(expect(esutils.code.isLineTerminator(code)).to.be.true);
161+
}
162+
return results;
163+
});
164+
165+
it('returns false if provided code is not line terminator', function() {
166+
for (let ch = 0, i = 0; i <= 9; ch = ++i) {
167+
expect(esutils.code.isLineTerminator((String(ch)).charCodeAt(0))).to.be.false;
168+
}
169+
let code, j, ref, ref1;
170+
for (code = j = ref = 'a'.charCodeAt(0), ref1 = 'z'.charCodeAt(0); (ref <= ref1 ? j <= ref1 : j >= ref1); code = ref <= ref1 ? ++j : --j) {
171+
expect(esutils.code.isLineTerminator(code)).to.be.false;
172+
}
173+
const results = [];
174+
let k, ref2, ref3;
175+
for (code = k = ref2 = 'A'.charCodeAt(0), ref3 = 'Z'.charCodeAt(0); (ref2 <= ref3 ? k <= ref3 : k >= ref3); code = ref2 <= ref3 ? ++k : --k) {
176+
results.push(expect(esutils.code.isLineTerminator(code)).to.be.false);
177+
}
178+
return results;
179+
});
180+
});
181+
describe('isIdentifierStartES5', function() {
182+
it('returns true if provided code can be a start of Identifier in ES5', function() {
183+
const characters = ['a', '_', '$', 'ゆ'];
184+
const ref = characters.map(function(ch) {
185+
return ch.charCodeAt(0);
186+
});
187+
const results = [];
188+
for (const code of ref) {
189+
results.push(expect(esutils.code.isIdentifierStartES5(code)).to.be.true);
190+
}
191+
return results;
192+
});
193+
it('returns false if provided code cannot be a start of Identifier in ES5', function() {
194+
const results = [];
195+
for (let ch = 0, i = 0; i <= 9; ch = ++i) {
196+
results.push(expect(esutils.code.isIdentifierStartES5((String(ch)).charCodeAt(0))).to.be.false);
197+
}
198+
return results;
199+
});
200+
});
201+
describe('isIdentifierPartES5', function() {
202+
it('returns true if provided code can be a part of Identifier in ES5', function() {
203+
const characters = ['a', '_', '$', 'ゆ'];
204+
const ref = characters.map(function(ch) {
205+
return ch.charCodeAt(0);
206+
});
207+
for (const code of ref) {
208+
expect(esutils.code.isIdentifierPartES5(code)).to.be.true;
209+
}
210+
const results = [];
211+
for (let ch = 0, j = 0; j <= 9; ch = ++j) {
212+
results.push(expect(esutils.code.isIdentifierPartES5((String(ch)).charCodeAt(0))).to.be.true);
213+
}
214+
return results;
215+
});
216+
it('returns false if provided code cannot be a part of Identifier in ES5', function() {
217+
expect(esutils.code.isIdentifierPartES5('+'.charCodeAt(0))).to.be.false;
218+
expect(esutils.code.isIdentifierPartES5('-'.charCodeAt(0))).to.be.false;
219+
});
220+
});
221+
describe('isIdentifierStartES6', function() {
222+
it('returns true if provided code can be a start of Identifier in ES6', function() {
223+
const characters = ['a', '_', '$', 'ゆ', '\u0AF9'];
224+
const ref = characters.map(function(ch) {
225+
return ch.charCodeAt(0);
226+
});
227+
const results = [];
228+
for (const code of ref) {
229+
results.push(expect(esutils.code.isIdentifierStartES6(code)).to.be.true);
230+
}
231+
return results;
232+
});
233+
234+
it('returns false if provided code cannot be a start of Identifier in ES6', function() {
235+
const results = [];
236+
for (let ch = 0, i = 0; i <= 9; ch = ++i) {
237+
results.push(expect(esutils.code.isIdentifierStartES6((String(ch)).charCodeAt(0))).to.be.false);
238+
}
239+
return results;
240+
});
241+
});
242+
243+
describe('isIdentifierPartES6', function() {
244+
it('returns true if provided code can be a part of Identifier in ES6', function() {
245+
const characters = ['a', '_', '$', 'ゆ'];
246+
const ref = characters.map(function(ch) {
247+
return ch.charCodeAt(0);
248+
});
249+
for (const code of ref) {
250+
expect(esutils.code.isIdentifierPartES6(code)).to.be.true;
251+
}
252+
const results = [];
253+
for (let ch = 0, j = 0; j <= 9; ch = ++j) {
254+
results.push(expect(esutils.code.isIdentifierPartES6((String(ch)).charCodeAt(0))).to.be.true);
255+
}
256+
return results;
257+
});
258+
it('supports astral symbols', function() {
259+
expect(esutils.code.isIdentifierPartES6(0xE01D5)).to.be.true;
260+
});
261+
it('returns false if provided code cannot be a part of Identifier in ES6', function() {
262+
expect(esutils.code.isIdentifierPartES6('+'.charCodeAt(0))).to.be.false;
263+
expect(esutils.code.isIdentifierPartES6('-'.charCodeAt(0))).to.be.false;
264+
});
265+
});
266+
});

‎test/keyword.coffee

-399
This file was deleted.

‎test/keyword.js

+298
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
/*
2+
Copyright (C) 2013 Yusuke Suzuki <utatane.tea@gmail.com>
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+
25+
import * as esutils from '../';
26+
27+
const KW = ['if', 'in', 'do', 'var', 'for', 'new', 'try', 'this', 'else', 'case', 'void', 'with', 'enum', 'while', 'break', 'catch', 'throw', 'const', 'class', 'super', 'return', 'typeof', 'delete', 'switch', 'export', 'import', 'default', 'finally', 'extends', 'function', 'continue', 'debugger', 'instanceof'];
28+
29+
const SRW = ['implements', 'interface', 'package', 'private', 'protected', 'public', 'static', 'let'];
30+
31+
describe('keyword', function() {
32+
describe('isKeywordES6', function() {
33+
it('returns true if provided string is keyword under non-strict mode', function() {
34+
for (const word of KW) {
35+
expect(esutils.keyword.isKeywordES6(word, false)).to.be.true;
36+
}
37+
expect(esutils.keyword.isKeywordES6('yield', false)).to.be.true;
38+
});
39+
it('returns false if provided string is not keyword under non-strict mode', function() {
40+
const words = ['hello', '20', '$', 'ゆゆ式'];
41+
for (const word of words) {
42+
expect(esutils.keyword.isKeywordES6(word, false)).to.be.false;
43+
}
44+
const results = [];
45+
for (const word of SRW) {
46+
results.push(expect(esutils.keyword.isKeywordES6(word, false)).to.be.false);
47+
}
48+
return results;
49+
});
50+
it('returns true if provided string is keyword under strict mode', function() {
51+
for (const word of KW) {
52+
expect(esutils.keyword.isKeywordES6(word, true)).to.be.true;
53+
}
54+
expect(esutils.keyword.isKeywordES6('yield', true)).to.be.true;
55+
const results = [];
56+
for (const word of SRW) {
57+
results.push(expect(esutils.keyword.isKeywordES6(word, true)).to.be.true);
58+
}
59+
return results;
60+
});
61+
it('returns false if provided string is not keyword under strict mode', function() {
62+
const words = ['hello', '20', '$', 'ゆゆ式'];
63+
const results = [];
64+
for (const word of words) {
65+
results.push(expect(esutils.keyword.isKeywordES6(word, true)).to.be.false);
66+
}
67+
return results;
68+
});
69+
});
70+
describe('isKeywordES5', function() {
71+
it('returns true if provided string is keyword under non-strict mode', function() {
72+
const results = [];
73+
for (const word of KW) {
74+
results.push(expect(esutils.keyword.isKeywordES5(word, false)).to.be.true);
75+
}
76+
return results;
77+
});
78+
it('returns false if provided string is not keyword under non-strict mode', function() {
79+
const words = ['hello', '20', '$', 'ゆゆ式'];
80+
for (const word of words) {
81+
expect(esutils.keyword.isKeywordES5(word, false)).to.be.false;
82+
}
83+
for (const word of SRW) {
84+
expect(esutils.keyword.isKeywordES5(word, false)).to.be.false;
85+
}
86+
expect(esutils.keyword.isKeywordES5('yield', false)).to.be.false;
87+
});
88+
it('returns true if provided string is keyword under strict mode', function() {
89+
for (const word of KW) {
90+
expect(esutils.keyword.isKeywordES5(word, true)).to.be.true;
91+
}
92+
expect(esutils.keyword.isKeywordES5('yield', true)).to.be.true;
93+
const results = [];
94+
for (const word of SRW) {
95+
results.push(expect(esutils.keyword.isKeywordES5(word, true)).to.be.true);
96+
}
97+
return results;
98+
});
99+
100+
it('returns false if provided string is not keyword under strict mode', function() {
101+
const words = ['hello', '20', '$', 'ゆゆ式'];
102+
const results = [];
103+
for (const word of words) {
104+
results.push(expect(esutils.keyword.isKeywordES5(word, true)).to.be.false);
105+
}
106+
return results;
107+
});
108+
});
109+
describe('isReservedWordES6', function() {
110+
it('returns true for null/boolean values', function() {
111+
expect(esutils.keyword.isReservedWordES6('null', false)).to.be.true;
112+
expect(esutils.keyword.isReservedWordES6('null', true)).to.be.true;
113+
expect(esutils.keyword.isReservedWordES6('true', false)).to.be.true;
114+
expect(esutils.keyword.isReservedWordES6('true', true)).to.be.true;
115+
expect(esutils.keyword.isReservedWordES6('false', false)).to.be.true;
116+
expect(esutils.keyword.isReservedWordES6('false', true)).to.be.true;
117+
});
118+
// isReservedWordES6 has the same properties as isKeywordES6
119+
it('returns true if provided string is keyword under non-strict mode', function() {
120+
for (const word of KW) {
121+
expect(esutils.keyword.isReservedWordES6(word, false)).to.be.true;
122+
}
123+
expect(esutils.keyword.isReservedWordES6('yield', false)).to.be.true;
124+
});
125+
it('returns false if provided string is not keyword under non-strict mode', function() {
126+
const words = ['hello', '20', '$', 'ゆゆ式'];
127+
for (const word of words) {
128+
expect(esutils.keyword.isReservedWordES6(word, false)).to.be.false;
129+
}
130+
const results = [];
131+
for (const word of SRW) {
132+
results.push(expect(esutils.keyword.isReservedWordES6(word, false)).to.be.false);
133+
}
134+
return results;
135+
});
136+
it('returns true if provided string is keyword under strict mode', function() {
137+
for (const word of KW) {
138+
expect(esutils.keyword.isReservedWordES6(word, true)).to.be.true;
139+
}
140+
expect(esutils.keyword.isReservedWordES6('yield', true)).to.be.true;
141+
const results = [];
142+
for (const word of SRW) {
143+
results.push(expect(esutils.keyword.isReservedWordES6(word, true)).to.be.true);
144+
}
145+
return results;
146+
});
147+
it('returns false if provided string is not keyword under strict mode', function() {
148+
const words = ['hello', '20', '$', 'ゆゆ式'];
149+
const results = [];
150+
for (const word of words) {
151+
results.push(expect(esutils.keyword.isReservedWordES6(word, true)).to.be.false);
152+
}
153+
return results;
154+
});
155+
});
156+
describe('isReservedWordES5', function() {
157+
it('returns true for null/boolean values', function() {
158+
expect(esutils.keyword.isReservedWordES5('null', false)).to.be.true;
159+
expect(esutils.keyword.isReservedWordES5('null', true)).to.be.true;
160+
expect(esutils.keyword.isReservedWordES5('true', false)).to.be.true;
161+
expect(esutils.keyword.isReservedWordES5('true', true)).to.be.true;
162+
expect(esutils.keyword.isReservedWordES5('false', false)).to.be.true;
163+
expect(esutils.keyword.isReservedWordES5('false', true)).to.be.true;
164+
});
165+
// isReservedWordES5 has the same properties as isKeywordES5
166+
it('returns true if provided string is keyword under non-strict mode', function() {
167+
const results = [];
168+
for (const word of KW) {
169+
results.push(expect(esutils.keyword.isReservedWordES5(word, false)).to.be.true);
170+
}
171+
return results;
172+
});
173+
it('returns false if provided string is not keyword under non-strict mode', function() {
174+
const words = ['hello', '20', '$', 'ゆゆ式'];
175+
for (const word of words) {
176+
expect(esutils.keyword.isReservedWordES5(word, false)).to.be.false;
177+
}
178+
for (const word of SRW) {
179+
expect(esutils.keyword.isReservedWordES5(word, false)).to.be.false;
180+
}
181+
expect(esutils.keyword.isReservedWordES5('yield', false)).to.be.false;
182+
});
183+
184+
it('returns true if provided string is keyword under strict mode', function() {
185+
for (const word of KW) {
186+
expect(esutils.keyword.isReservedWordES5(word, true)).to.be.true;
187+
}
188+
expect(esutils.keyword.isReservedWordES5('yield', true)).to.be.true;
189+
const results = [];
190+
for (const word of SRW) {
191+
results.push(expect(esutils.keyword.isReservedWordES5(word, true)).to.be.true);
192+
}
193+
return results;
194+
});
195+
196+
it('returns false if provided string is not keyword under strict mode', function() {
197+
const words = ['hello', '20', '$', 'ゆゆ式'];
198+
const results = [];
199+
for (const word of words) {
200+
results.push(expect(esutils.keyword.isReservedWordES5(word, true)).to.be.false);
201+
}
202+
return results;
203+
});
204+
});
205+
describe('isRestrictedWord', function() {
206+
it('returns true if provided string is "eval" or "arguments"', function() {
207+
expect(esutils.keyword.isRestrictedWord('eval')).to.be.true;
208+
expect(esutils.keyword.isRestrictedWord('arguments')).to.be.true;
209+
});
210+
211+
it('returns false if provided string is not "eval" or "arguments"', function() {
212+
const words = ['hello', '20', '$', 'ゆゆ式'];
213+
const results = [];
214+
for (const word of words) {
215+
results.push(expect(esutils.keyword.isRestrictedWord(word)).to.be.false);
216+
}
217+
return results;
218+
});
219+
});
220+
describe('isIdentifierName', function() {
221+
it('returns false if provided string is empty', function() {
222+
expect(esutils.keyword.isIdentifierNameES5('')).to.be.false;
223+
expect(esutils.keyword.isIdentifierNameES6('')).to.be.false;
224+
});
225+
it('returns true if provided string is IdentifierName', function() {
226+
const words = ['hello', '$', 'ゆゆ式', '$20', 'hello20', '_', 'if'];
227+
const results = [];
228+
for (const word of words) {
229+
expect(esutils.keyword.isIdentifierNameES5(word)).to.be.true;
230+
results.push(expect(esutils.keyword.isIdentifierNameES6(word)).to.be.true);
231+
}
232+
return results;
233+
});
234+
it('returns false if provided string is not IdentifierName', function() {
235+
const words = ['+hello', '0$', '-ゆゆ式', '#_', '_#'];
236+
const results = [];
237+
for (const word of words) {
238+
expect(esutils.keyword.isIdentifierNameES5(word)).to.be.false;
239+
results.push(expect(esutils.keyword.isIdentifierNameES6(word)).to.be.false);
240+
}
241+
return results;
242+
});
243+
it('returns false if provided an unmatched high surrogate', function () {
244+
const words = ['\uD800a', '\uD800'];
245+
const results = [];
246+
for (const word of words) {
247+
expect(esutils.keyword.isIdentifierNameES5(word)).to.be.false;
248+
results.push(expect(esutils.keyword.isIdentifierNameES6(word)).to.be.false);
249+
}
250+
return results;
251+
});
252+
it('supports astral symbols', function() {
253+
expect(esutils.keyword.isIdentifierNameES6('x\uDB40\uDDD5')).to.be.true;
254+
});
255+
});
256+
describe('isIdentifierES5', function() {
257+
it('returns false if provided string is empty', function() {
258+
expect(esutils.keyword.isIdentifierES5('')).to.be.false;
259+
});
260+
it('returns true if provided string is Identifier', function() {
261+
const words = ['hello', '$', 'ゆゆ式', '$20', 'hello20', '_'];
262+
for (const word of words) {
263+
expect(esutils.keyword.isIdentifierES5(word)).to.be.true;
264+
}
265+
expect(esutils.keyword.isIdentifierES5('yield', false)).to.be.true;
266+
expect(esutils.keyword.isIdentifierES5('let', false)).to.be.true;
267+
});
268+
it('returns false if provided string is not Identifier', function() {
269+
const words = ['+hello', '0$', '-ゆゆ式', '#_', '_#', 'if', 'null', 'true', 'false'];
270+
for (const word of words) {
271+
expect(esutils.keyword.isIdentifierES5(word)).to.be.false;
272+
}
273+
expect(esutils.keyword.isIdentifierES5('yield', true)).to.be.false;
274+
expect(esutils.keyword.isIdentifierES5('let', true)).to.be.false;
275+
});
276+
});
277+
describe('isIdentifierES6', function() {
278+
it('returns false if provided string is empty', function() {
279+
expect(esutils.keyword.isIdentifierES6('')).to.be.false;
280+
});
281+
it('returns true if provided string is Identifier', function() {
282+
const words = ['hello', '$', 'ゆゆ式', '$20', 'hello20', '_'];
283+
for (const word of words) {
284+
expect(esutils.keyword.isIdentifierES6(word)).to.be.true;
285+
}
286+
expect(esutils.keyword.isIdentifierES6('let', false)).to.be.true;
287+
});
288+
it('returns false if provided string is not Identifier', function() {
289+
const words = ['+hello', '0$', '-ゆゆ式', '#_', '_#', 'if', 'null', 'true', 'false'];
290+
for (const word of words) {
291+
expect(esutils.keyword.isIdentifierES6(word)).to.be.false;
292+
}
293+
expect(esutils.keyword.isIdentifierES6('yield', false)).to.be.false;
294+
expect(esutils.keyword.isIdentifierES6('yield', true)).to.be.false;
295+
expect(esutils.keyword.isIdentifierES6('let', true)).to.be.false;
296+
});
297+
});
298+
});

‎tools/generate-identifier-regex.js

+32-24
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
// Based on https://gist.github.com/mathiasbynens/6334847 by @mathias
22
'use strict';
33

4+
const { writeFileSync } = require('fs');
5+
const { join } = require('path');
46
const regenerate = require('regenerate');
57

68
// Which Unicode version should be used?
79
const pkg = require('../package.json');
810
const dependencies = Object.keys(pkg.devDependencies);
911
const unicodeDep = dependencies.find((name) => /^unicode-\d/.test(name));
10-
const version = unicodeDep.match(/[^\d]+(.+)$/)[1];
12+
const [, version] = unicodeDep.match(/\D+(.+)$/);
1113

1214
// Set up a shorthand function to import Unicode data.
1315
const get = function(what) {
14-
return require('unicode-' + version + '/' + what + '/code-points.js');
16+
return require(`unicode-${version}/${what}/code-points.js`);
1517
};
1618

1719
// Get the Unicode categories needed to construct the ES5 regex.
@@ -38,8 +40,8 @@ const es5regexes = (function() { // ES 5.1
3840
.removeRange(0x010000, 0x10FFFF) // remove astral symbols
3941
.removeRange(0x00, 0x7F); // remove ASCII symbols (esutils-specific)
4042
return {
41-
'NonAsciiIdentifierStart': '/' + identifierStart + '/',
42-
'NonAsciiIdentifierPart': '/' + identifierPart + '/',
43+
NonAsciiIdentifierStart: `/${identifierStart}/`,
44+
NonAsciiIdentifierPart: `/${identifierPart}/`,
4345
};
4446
}());
4547

@@ -62,29 +64,35 @@ const es6regexes = (function() {
6264
.removeRange(0x00, 0x7F); // remove ASCII symbols (esutils-specific)
6365

6466
return {
65-
'NonAsciiIdentifierStart': '/' + identifierStart + '/',
66-
'NonAsciiIdentifierPart': '/' + identifierPart + '/',
67+
NonAsciiIdentifierStart: `/${identifierStart}/`,
68+
NonAsciiIdentifierPart: `/${identifierPart}/`,
6769
};
6870
}());
6971

70-
console.log(
71-
'// ECMAScript 5.1/Unicode v%s NonAsciiIdentifierStart:\n%s\n',
72-
version,
73-
es5regexes.NonAsciiIdentifierStart
74-
);
75-
console.log(
76-
'// ECMAScript 5.1/Unicode v%s NonAsciiIdentifierPart:\n%s\n',
77-
version,
78-
es5regexes.NonAsciiIdentifierPart
79-
);
72+
writeFileSync(
73+
join(__dirname, '../src/es5-identifier.js'),
74+
`// DO NOT EDIT -- File auto-generated from /tools/generate-identifier-regex.js
75+
76+
// ECMAScript 5.1/Unicode v${version} NonAsciiIdentifierStart:
77+
const NonAsciiIdentifierStart = ${es5regexes.NonAsciiIdentifierStart};
78+
79+
// ECMAScript 5.1/Unicode v${version} NonAsciiIdentifierPart:
80+
const NonAsciiIdentifierPart = ${es5regexes.NonAsciiIdentifierPart};
8081
81-
console.log(
82-
'// ECMAScript 6/Unicode v%s NonAsciiIdentifierStart:\n%s\n',
83-
version,
84-
es6regexes.NonAsciiIdentifierStart
82+
export { NonAsciiIdentifierStart, NonAsciiIdentifierPart };
83+
`
8584
);
86-
console.log(
87-
'// ECMAScript 6/Unicode v%s NonAsciiIdentifierPart:\n%s',
88-
version,
89-
es6regexes.NonAsciiIdentifierPart
85+
86+
writeFileSync(
87+
join(__dirname, '../src/es6-identifier.js'),
88+
`// DO NOT EDIT -- File auto-generated from /tools/generate-identifier-regex.js
89+
90+
// ECMAScript 6/Unicode v${version} NonAsciiIdentifierStart:
91+
const NonAsciiIdentifierStart = ${es6regexes.NonAsciiIdentifierStart};
92+
93+
// ECMAScript 6/Unicode v${version} NonAsciiIdentifierPart:
94+
const NonAsciiIdentifierPart = ${es6regexes.NonAsciiIdentifierPart};
95+
96+
export { NonAsciiIdentifierStart, NonAsciiIdentifierPart };
97+
`
9098
);

0 commit comments

Comments
 (0)
Please sign in to comment.