pax_global_header00006660000000000000000000000064131265113730014514gustar00rootroot0000000000000052 comment=7be51f40e733356f80e73a3c7764add93d5e0579 is-reference-1.0.1/000077500000000000000000000000001312651137300140625ustar00rootroot00000000000000is-reference-1.0.1/.eslintrc.json000066400000000000000000000021411312651137300166540ustar00rootroot00000000000000{ "root": true, "rules": { "indent": [ 2, "tab", { "SwitchCase": 1 } ], "semi": [ 2, "always" ], "keyword-spacing": [ 2, { "before": true, "after": true } ], "space-before-blocks": [ 2, "always" ], "space-before-function-paren": [ 2, "always" ], "no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ], "no-cond-assign": 0, "no-unused-vars": 2, "object-shorthand": [ 2, "always" ], "no-const-assign": 2, "no-class-assign": 2, "no-this-before-super": 2, "no-var": 2, "no-unreachable": 2, "valid-typeof": 2, "quote-props": [ 2, "as-needed" ], "one-var": [ 2, "never" ], "prefer-arrow-callback": 2, "prefer-const": [ 2, { "destructuring": "all" } ], "arrow-spacing": 2, "no-inner-declarations": 0 }, "env": { "es6": true, "browser": true, "node": true, "mocha": true }, "extends": [ "eslint:recommended" ], "parserOptions": { "ecmaVersion": 6, "sourceType": "module" } } is-reference-1.0.1/.gitignore000066400000000000000000000000271312651137300160510ustar00rootroot00000000000000.DS_Store node_modules is-reference-1.0.1/CHANGELOG.md000066400000000000000000000001501312651137300156670ustar00rootroot00000000000000# is-reference changelog ## 1.0.1 * Ensure `isReference` returns a boolean ## 1.0.0 * First release is-reference-1.0.1/README.md000066400000000000000000000023361312651137300153450ustar00rootroot00000000000000# is-reference Utility for determining whether an AST node is a reference. `foo` is a reference in these cases: ```js console.log( foo ); var foo; function foo () {} function bar ( foo ) {} export { foo as x }; ``` `foo` is *not* a reference in these cases: ```js var obj = { foo: 1 }; console.log( obj.foo ); export { x as foo }; ``` In all cases, `foo` is an `Identifier` node, but the two kinds must be treated differently for the purposes of scope analysis etc. (The examples are non-exhaustive.) ## Installation ```bash npm install is-reference ``` ## Usage Example using [Acorn](https://github.com/ternjs/acorn) and [estree-walker](https://github.com/Rich-Harris/estree-walker): ```js const { parse } = require( 'acorn' ); const { walk } = require( 'estree-walker' ); const isReference = require( 'is-reference' ); const identifiers = []; const references = []; const ast = parse( `var a = b.c;` ); walk( ast, { enter ( node, parent ) { if ( node.type === 'Identifier' ) identifiers.push( node ); if ( isReference( node, parent ) ) references.push( node ); } }); identifiers.forEach( node => console.log( node.name ) ); // a, b, c references.forEach( node => console.log( node.name ) ); // a, b ``` ## License MIT is-reference-1.0.1/index.js000066400000000000000000000024461312651137300155350ustar00rootroot00000000000000(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.isReference = factory()); }(this, (function () { 'use strict'; function isReference ( node, parent ) { if ( node.type === 'MemberExpression' ) { return !node.computed && isReference( node.object, node ); } if ( node.type === 'Identifier' ) { // the only time we could have an identifier node without a parent is // if it's the entire body of a function without a block statement – // i.e. an arrow function expression like `a => a` if ( !parent ) return true; // TODO is this right? if ( parent.type === 'MemberExpression' || parent.type === 'MethodDefinition' ) { return parent.computed || node === parent.object; } // disregard the `bar` in `{ bar: foo }`, but keep it in `{ [bar]: foo }` if ( parent.type === 'Property' ) return parent.computed || node === parent.value; // disregard the `bar` in `class Foo { bar () {...} }` if ( parent.type === 'MethodDefinition' ) return false; // disregard the `bar` in `export { foo as bar }` if ( parent.type === 'ExportSpecifier' && node !== parent.local ) return false; return true; } return false; } return isReference; }))); is-reference-1.0.1/module.js000066400000000000000000000020031312651137300157000ustar00rootroot00000000000000export default function isReference (node, parent) { if (node.type === 'MemberExpression') { return !node.computed && isReference(node.object, node); } if (node.type === 'Identifier') { // the only time we could have an identifier node without a parent is // if it's the entire body of a function without a block statement – // i.e. an arrow function expression like `a => a` if (!parent) return true; // TODO is this right? if (parent.type === 'MemberExpression' || parent.type === 'MethodDefinition') { return parent.computed || node === parent.object; } // disregard the `bar` in `{ bar: foo }`, but keep it in `{ [bar]: foo }` if (parent.type === 'Property') return parent.computed || node === parent.value; // disregard the `bar` in `class Foo { bar () {...} }` if (parent.type === 'MethodDefinition') return false; // disregard the `bar` in `export { foo as bar }` if (parent.type === 'ExportSpecifier' && node !== parent.local) return false; return true; } return false; } is-reference-1.0.1/package.json000066400000000000000000000016321312651137300163520ustar00rootroot00000000000000{ "name": "is-reference", "version": "1.0.1", "description": "Determine whether an AST node is a reference", "main": "index.js", "module": "module.js", "files": [ "index.js", "module.js" ], "scripts": { "test": "mocha", "build": "rollup -c", "pretest": "npm run build", "lint": "eslint module.js", "prepublish": "npm run lint && npm test" }, "repository": { "type": "git", "url": "git+https://github.com/Rich-Harris/is-reference.git" }, "keywords": [ "ast", "javascript", "estree", "acorn" ], "author": "Rich Harris", "license": "MIT", "bugs": { "url": "https://github.com/Rich-Harris/is-reference/issues" }, "homepage": "https://github.com/Rich-Harris/is-reference#readme", "devDependencies": { "acorn": "^4.0.4", "eslint": "^3.12.2", "estree-walker": "^0.3.0", "mocha": "^3.2.0", "rollup": "^0.37.2" } } is-reference-1.0.1/rollup.config.js000066400000000000000000000001471312651137300172030ustar00rootroot00000000000000export default { entry: 'module.js', dest: 'index.js', format: 'umd', moduleName: 'isReference' }; is-reference-1.0.1/test/000077500000000000000000000000001312651137300150415ustar00rootroot00000000000000is-reference-1.0.1/test/test.js000066400000000000000000000031361312651137300163610ustar00rootroot00000000000000const assert = require('assert'); const { parse } = require('acorn'); const { walk } = require('estree-walker'); const isReference = require('../'); describe('is-reference', () => { const positive = { 'simple identifier': ` foo;`, 'variable declaration': ` var foo;`, 'function name': ` function foo () {}`, 'function parameter': ` function x ( foo ) {}`, 'object pattern': ` function x ({ foo }) {}`, 'array pattern': ` function x ([ foo ]) {}`, 'assignment pattern': ` function x ( foo = 1 ) {}`, 'assignment pattern in object pattern': ` function x ({ foo = 42 }) {}` }; const negative = { 'object literal property': ` var obj = { foo: 1 };`, 'member expression property': ` obj.foo;`, 'export-as': ` export { bar as foo }` }; describe('positive', () => { Object.keys(positive).forEach(name => { it(name, () => { const code = positive[name]; const matches = findFooReferences(code); assert.equal(matches.size, 1); }); }); }); describe('negative', () => { Object.keys(negative).forEach(name => { it(name, () => { const code = negative[name]; const matches = findFooReferences(code); assert.equal(matches.size, 0); }); }); }); function findFooReferences(code) { const ast = parse(code, { sourceType: 'module', ecmaVersion: 8 }); const matches = new Set(); walk(ast, { enter(node, parent) { const match = isReference(node, parent); assert.equal(typeof match, 'boolean'); if (match && node.name === 'foo') { matches.add(node); } } }); return matches; } });