pax_global_header00006660000000000000000000000064134344625610014522gustar00rootroot0000000000000052 comment=d043680d435bf046926a96d35ed7cd55abb96960 undeclared-identifiers-1.1.3/000077500000000000000000000000001343446256100161355ustar00rootroot00000000000000undeclared-identifiers-1.1.3/.gitignore000066400000000000000000000010401343446256100201200ustar00rootroot00000000000000# Logs logs *.log # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directory # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules package-lock.json undeclared-identifiers-1.1.3/.npmrc000066400000000000000000000000231343446256100172500ustar00rootroot00000000000000package-lock=false undeclared-identifiers-1.1.3/.travis.yml000066400000000000000000000001331343446256100202430ustar00rootroot00000000000000language: node_js node_js: - stable - 8 - 6 - 4 cache: directories: - ~/.npm undeclared-identifiers-1.1.3/CHANGELOG.md000066400000000000000000000017511343446256100177520ustar00rootroot00000000000000# undeclared-identifiers change log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## 1.1.3 * Do not count class names and method names as undeclared. ([#1](https://github.com/goto-bus-stop/undeclared-identifiers/pull/1)) ## 1.1.2 * Fix wildcard use not being detected after property use. ([349d998](https://github.com/goto-bus-stop/undeclared-identifiers/commit/349d998559f83976ccd3b3d091e2b06f00ce4189)) ## 1.1.1 * Fix standard property access being detected as wildcards. ([029a0b7](https://github.com/goto-bus-stop/undeclared-identifiers/commit/029a0b773a7a4d2402a6de19c8c8693407f8da63)) ## 1.1.0 * Accept an AST. ([1605b88](https://github.com/goto-bus-stop/undeclared-identifiers/commit/1605b881cd567894fab1ee2727961dd715a38820)) * Add `opts.wildcard`. ([cdabd70](https://github.com/goto-bus-stop/undeclared-identifiers/commit/cdabd70e000b2fa976c7f4118757736e023b93f2)) ## 1.0.0 * initial release. undeclared-identifiers-1.1.3/LICENSE.md000066400000000000000000000011641343446256100175430ustar00rootroot00000000000000# [Apache License 2.0](https://spdx.org/licenses/Apache-2.0) Copyright 2018 Renée Kooi Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at > http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. undeclared-identifiers-1.1.3/README.md000066400000000000000000000034531343446256100174210ustar00rootroot00000000000000# undeclared-identifiers find undeclared identifiers and property accesses in a javascript file. [![npm][npm-image]][npm-url] [![travis][travis-image]][travis-url] [![standard][standard-image]][standard-url] [npm-image]: https://img.shields.io/npm/v/undeclared-identifiers.svg?style=flat-square [npm-url]: https://www.npmjs.com/package/undeclared-identifiers [travis-image]: https://img.shields.io/travis/goto-bus-stop/undeclared-identifiers.svg?style=flat-square [travis-url]: https://travis-ci.org/goto-bus-stop/undeclared-identifiers [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square [standard-url]: http://npm.im/standard ## Install ``` npm install undeclared-identifiers ``` ## Usage ```js var undeclaredIdentifiers = require('undeclared-identifiers') undeclaredIdentifiers(src) // { identifiers: ['Buffer'], // properties: ['Buffer.isBuffer'] } ``` ## API ### `res = undeclaredIdentifiers(source, opts)` Find undeclared identifiers and properties that are used in the `source`. `source` can be an AST or a source string that will be parsed using [acorn-node](https://github.com/browserify/acorn-node). `res` is an object with properties: - `res.identifiers` - an array of variable names as strings. - `res.properties` - an array of property names as .-separated strings, such as `'xyz.abc'`. These are the property accesses on the undeclared variables found in `res.identifiers`. Set `opts.properties` to false to only return identifiers. When `opts.wildcard` is true, unknown uses of undeclared identifiers will be added to `res.properties` as `'VarName.*'`. ```js undeclaredIdentifiers('Buffer(), Buffer.from()', { wildcard: true }) // { identifiers: ['Buffer'], // properties: ['Buffer.*', 'Buffer.from'] } ``` ## License [Apache-2.0](LICENSE.md) undeclared-identifiers-1.1.3/bench/000077500000000000000000000000001343446256100172145ustar00rootroot00000000000000undeclared-identifiers-1.1.3/bench/index.js000066400000000000000000000025461343446256100206700ustar00rootroot00000000000000/* eslint-disable no-unused-expressions */ var bench = require('nanobench') var acornsrc = require('fs').readFileSync(require.resolve('acorn')) var lodashsrc = require('fs').readFileSync(require.resolve('lodash')) var jquerysrc = require('fs').readFileSync(require.resolve('jquery')) var threesrc = require('fs').readFileSync(require.resolve('three')) var find = require('../') // Optimize. for (var i = 0; i < 5; i++) find(acornsrc).properties // Has very few undeclared identifiers bench('acorn × 1', function (b) { b.start() find(acornsrc).properties b.end() }) bench('acorn × 5', function (b) { b.start() for (var i = 0; i < 5; i++) find(acornsrc).properties b.end() }) bench('lodash × 1', function (b) { b.start() find(lodashsrc).properties b.end() }) bench('lodash × 5', function (b) { b.start() for (var i = 0; i < 5; i++) find(lodashsrc).properties b.end() }) // Has more undeclared identifiers and properties bench('jquery × 1', function (b) { b.start() find(jquerysrc).properties b.end() }) bench('jquery × 5', function (b) { b.start() for (var i = 0; i < 5; i++) find(jquerysrc).properties b.end() }) // is very large bench('three × 1', function (b) { b.start() find(threesrc).identifiers b.end() }) bench('three × 5', function (b) { b.start() for (var i = 0; i < 5; i++) find(threesrc).identifiers b.end() }) undeclared-identifiers-1.1.3/bin.js000077500000000000000000000011541343446256100172470ustar00rootroot00000000000000#!/usr/bin/env node var concat = require('simple-concat') var undeclared = require('./') if (arg('--help') || arg('-h')) { console.log('usage: undeclared-identifiers [--identifiers] [--properties] < source.js') process.exit(0) } concat(process.stdin, function (err, src) { if (err) throw err var r = undeclared(src) var i = arg('--identifiers') || arg('-i') var p = arg('--properties') || arg('-p') if (!i && !p) i = p = true if (i) r.identifiers.forEach(log) if (p) r.properties.forEach(log) }) function arg (s) { return process.argv.indexOf(s) !== -1 } function log (n) { console.log(n) } undeclared-identifiers-1.1.3/index.js000066400000000000000000000111401343446256100175770ustar00rootroot00000000000000var xtend = require('xtend') var acorn = require('acorn-node') var dash = require('dash-ast') var getAssignedIdentifiers = require('get-assigned-identifiers') function visitFunction (node, state, ancestors) { if (node.params.length > 0) { var idents = [] for (var i = 0; i < node.params.length; i++) { var sub = getAssignedIdentifiers(node.params[i]) for (var j = 0; j < sub.length; j++) idents.push(sub[j]) } declareNames(node, idents) } if (node.type === 'FunctionDeclaration') { var parent = getScopeNode(ancestors, 'const') declareNames(parent, [node.id]) } else if (node.type === 'FunctionExpression' && node.id) { declareNames(node, [node.id]) } } var scopeVisitor = { VariableDeclaration: function (node, state, ancestors) { var parent = getScopeNode(ancestors, node.kind) for (var i = 0; i < node.declarations.length; i++) { declareNames(parent, getAssignedIdentifiers(node.declarations[i].id)) } }, FunctionExpression: visitFunction, FunctionDeclaration: visitFunction, ArrowFunctionExpression: visitFunction, ClassDeclaration: function (node, state, ancestors) { var parent = getScopeNode(ancestors, 'const') if (node.id) { declareNames(parent, [node.id]) } }, ImportDeclaration: function (node, state, ancestors) { declareNames(ancestors[0] /* root */, getAssignedIdentifiers(node)) }, CatchClause: function (node) { if (node.param) declareNames(node, [node.param]) } } var bindingVisitor = { Identifier: function (node, state, ancestors) { if (!state.identifiers) return var parent = ancestors[ancestors.length - 1] if (parent.type === 'MemberExpression' && parent.property === node) return if (parent.type === 'Property' && !parent.computed && parent.key === node) return if (parent.type === 'MethodDefinition' && !parent.computed && parent.key === node) return if (parent.type === 'LabeledStatement' && parent.label === node) return if (!has(state.undeclared, node.name)) { for (var i = ancestors.length - 1; i >= 0; i--) { if (ancestors[i]._names !== undefined && has(ancestors[i]._names, node.name)) { return } } state.undeclared[node.name] = true } if (state.wildcard && !(parent.type === 'MemberExpression' && parent.object === node) && !(parent.type === 'VariableDeclarator' && parent.id === node) && !(parent.type === 'AssignmentExpression' && parent.left === node)) { state.undeclaredProps[node.name + '.*'] = true } }, MemberExpression: function (node, state) { if (!state.properties) return if (node.object.type === 'Identifier' && has(state.undeclared, node.object.name)) { var prop = !node.computed && node.property.type === 'Identifier' ? node.property.name : node.computed && node.property.type === 'Literal' ? node.property.value : null if (prop) state.undeclaredProps[node.object.name + '.' + prop] = true } } } module.exports = function findUndeclared (src, opts) { opts = xtend({ identifiers: true, properties: true, wildcard: false }, opts) var state = { undeclared: {}, undeclaredProps: {}, identifiers: opts.identifiers, properties: opts.properties, wildcard: opts.wildcard } // Parse if `src` is not already an AST. var ast = typeof src === 'object' && src !== null && typeof src.type === 'string' ? src : acorn.parse(src) var parents = [] dash(ast, { enter: function (node, parent) { if (parent) parents.push(parent) var visit = scopeVisitor[node.type] if (visit) visit(node, state, parents) }, leave: function (node, parent) { var visit = bindingVisitor[node.type] if (visit) visit(node, state, parents) if (parent) parents.pop() } }) return { identifiers: Object.keys(state.undeclared), properties: Object.keys(state.undeclaredProps) } } function getScopeNode (parents, kind) { for (var i = parents.length - 1; i >= 0; i--) { if (parents[i].type === 'FunctionDeclaration' || parents[i].type === 'FunctionExpression' || parents[i].type === 'ArrowFunctionExpression' || parents[i].type === 'Program') { return parents[i] } if (kind !== 'var' && parents[i].type === 'BlockStatement') { return parents[i] } } } function declareNames (node, names) { if (node._names === undefined) { node._names = Object.create(null) } for (var i = 0; i < names.length; i++) { node._names[names[i].name] = true } } function has (obj, name) { return Object.prototype.hasOwnProperty.call(obj, name) } undeclared-identifiers-1.1.3/package.json000066400000000000000000000021221343446256100204200ustar00rootroot00000000000000{ "name": "undeclared-identifiers", "description": "find undeclared identifiers and property accesses in a javascript file.", "version": "1.1.3", "author": "Renée Kooi ", "bugs": { "url": "https://github.com/goto-bus-stop/undeclared-identifiers/issues" }, "devDependencies": { "jquery": "^3.3.1", "lodash": "^4.17.5", "nanobench": "^2.1.0", "standard": "^11.0.0", "tap-spec": "^5.0.0", "tape": "^4.0.0", "three": "^0.92.0" }, "homepage": "https://github.com/goto-bus-stop/undeclared-identifiers", "keywords": [ "ast", "check", "detect", "identifiers", "javascript", "undeclared" ], "license": "Apache-2.0", "main": "index.js", "bin": "bin.js", "repository": { "type": "git", "url": "https://github.com/goto-bus-stop/undeclared-identifiers.git" }, "scripts": { "test": "standard && tape test/*.js | tap-spec" }, "dependencies": { "acorn-node": "^1.3.0", "dash-ast": "^1.0.0", "get-assigned-identifiers": "^1.2.0", "simple-concat": "^1.0.0", "xtend": "^4.0.1" } } undeclared-identifiers-1.1.3/test/000077500000000000000000000000001343446256100171145ustar00rootroot00000000000000undeclared-identifiers-1.1.3/test/index.js000066400000000000000000000101321343446256100205560ustar00rootroot00000000000000var test = require('tape') var acorn = require('acorn-node') var find = require('../') test('undeclared variables', function (t) { t.deepEqual(find(` var a, b a, b, c `), { identifiers: ['c'], properties: [] }) t.end() }) test('pass in a parsed ast', function (t) { t.deepEqual(find(acorn.parse(` var a, b a, b, c `)), { identifiers: ['c'], properties: [] }) t.end() }) test('undeclared properties', function (t) { t.deepEqual(find(` var a, b a, b, c, d.e `), { identifiers: ['c', 'd'], properties: ['d.e'] }) t.end() }) test('wildcard use of undeclared name', function (t) { t.deepEqual(find(` function func () {} new A() A.from() func(b) C.from() `, { wildcard: true }), { identifiers: ['A', 'b', 'C'], properties: ['A.*', 'A.from', 'b.*', 'C.from'] }) t.end() }) test('wildcard use of undeclared name (different order)', function (t) { t.deepEqual(find(` A.from() new A() `, { wildcard: true }), { identifiers: ['A'], properties: ['A.from', 'A.*'] }) t.end() }) test('function names', function (t) { t.deepEqual(find(` function x () { return x } x() `), { identifiers: [], properties: [] }) t.deepEqual(find(` function x () { return x } y() `), { identifiers: ['y'], properties: [] }) t.end() }) test('class names', function (t) { t.deepEqual(find(` class X {} new X() `), { identifiers: [], properties: [] }) t.deepEqual(find(` class X extends Y {} new X() `), { identifiers: ['Y'], properties: [] }) t.deepEqual(find(` class Y {} class X extends Y {} new X() `), { identifiers: [], properties: [] }) t.end() }) test('class methods', function (t) { t.deepEqual(find(` class X { constructor() { u } bar() { v } static foo() { w } } `), { identifiers: ['u', 'v', 'w'], properties: [] }) t.end() }) test('super', function (t) { t.deepEqual(find(` class X extends Y { constructor() { super() } } `), { identifiers: ['Y'], properties: [] }) t.deepEqual(find(` class X { foo() { super.foo } } `), { identifiers: [], properties: [] }) t.end() }) test('scope', function (t) { t.deepEqual(find(` function y () { function x () { return x } } x(y(x.y)) `), { identifiers: ['x'], properties: ['x.y'] }) t.end() }) test('block scope', function (t) { t.deepEqual(find(` var x { var y; let z } x, y, z `), { identifiers: ['z'], properties: [] }) t.end() }) test('function parameters', function (t) { t.deepEqual(find(` function a (a1, a2) { a, a1, a2, a3 } ;(function b (b1, b2) { b, b1, b2, b3 }) ;((c1, c2) => { c1, c2, c3 }) `), { identifiers: ['a3', 'b3', 'c3'], properties: [] }) t.deepEqual(find(` function a (a1, a2) { } a, a1, a2 `), { identifiers: ['a1', 'a2'], properties: [] }) t.end() }) test('assignment', function (t) { t.deepEqual(find(` var a b = 2 b.c = 3 `), { identifiers: ['b'], properties: ['b.c'] }) t.end() }) test('catch', function (t) { t.deepEqual(find(` try { var a } catch (err) { err } try { let b } catch (orr) { orr } a, b `), { identifiers: ['b'], properties: [] }) t.end() }) test('object prototype names', function (t) { t.deepEqual(find(` var propertyIsEnumerable, hasOwnProperty isPrototypeOf `), { identifiers: ['isPrototypeOf'], properties: [] }) t.end() }) test('labels', function (t) { t.deepEqual(find(` a: a; b: a; c: a; `), { identifiers: ['a'], properties: [] }) t.end() }) test('property keys', function (t) { t.deepEqual(find(` ({ a: a, b: a, [d]: a, c: a, }) `), { identifiers: ['a', 'd'], properties: [] }) t.end() }) test('string property access', function (t) { t.deepEqual(find(` Buffer["isBuffer"] `), { identifiers: ['Buffer'], properties: ['Buffer.isBuffer'] }) t.end() })