├── .eslintrc ├── .gitignore ├── .npmignore ├── Makefile ├── README.md ├── example ├── index.js ├── module.js └── webpack.config.js ├── index.js ├── loader.js ├── package.json └── src ├── CTagsWebpackPlugin.js ├── __tests__ └── findTags-test.js ├── findTags.js └── loader.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "es6": true 7 | }, 8 | "rules": { 9 | "strict": 0, 10 | 11 | "comma-dangle": 0, // Disallow or enforce trailing commas 12 | "no-cond-assign": 2, // Disallow assignment in conditional expressions 13 | "no-console": 1, // Disallow use of console (off by default in the node environment) 14 | "no-constant-condition": 2, // Disallow use of constant expressions in conditions 15 | "no-control-regex": 2, // Disallow control characters in regular expressions 16 | "no-debugger": 2, // Disallow use of debugger 17 | "no-dupe-args": 2, // Disallow duplicate arguments in functions 18 | "no-dupe-keys": 2, // Disallow duplicate keys when creating object literals 19 | "no-duplicate-case": 2, // Disallow a duplicate case label. 20 | "no-empty": 2, // Disallow empty statements 21 | "no-empty-character-class": 2, // Disallow the use of empty character classes in regular expressions 22 | "no-ex-assign": 2, // Disallow assigning to the exception in a catch block 23 | "no-extra-boolean-cast": 2, // Disallow double-negation boolean casts in a boolean context 24 | "no-extra-parens": 0, // Disallow unnecessary parentheses (off by default) 25 | "no-extra-semi": 2, // Disallow unnecessary semicolons 26 | "no-func-assign": 2, // Disallow overwriting functions written as function declarations 27 | "no-inner-declarations": 2, // Disallow function or variable declarations in nested blocks 28 | "no-invalid-regexp": 2, // Disallow invalid regular expression strings in the RegExp constructor 29 | "no-irregular-whitespace": 2, // Disallow irregular whitespace outside of strings and comments 30 | "no-negated-in-lhs": 2, // Disallow negation of the left operand of an in expression 31 | "no-obj-calls": 2, // Disallow the use of object properties of the global object (Math and JSON) as functions 32 | "no-regex-spaces": 2, // Disallow multiple spaces in a regular expression literal 33 | "no-reserved-keys": 0, // Disallow reserved words being used as object literal keys (off by default) 34 | "no-sparse-arrays": 2, // Disallow sparse arrays 35 | "no-unreachable": 2, // Disallow unreachable statements after a return, throw, continue, or break statement 36 | "use-isnan": 2, // Disallow comparisons with the value NaN 37 | "valid-typeof": 2, // Ensure that the results of typeof are compared against a valid string 38 | 39 | 40 | "consistent-return": 2, // Require return statements to either always or never specify values 41 | "curly": 2, // Specify curly brace conventions for all control statements 42 | "default-case": 2, // Require default case in switch statements (off by default) 43 | "eqeqeq": 2, // Require the use of === and !== 44 | "guard-for-in": 2, // Make sure for-in loops have an if statement (off by default) 45 | "no-alert": 2, // Disallow the use of alert, confirm, and prompt 46 | "no-caller": 2, // Disallow use of arguments.caller or arguments.callee 47 | "no-div-regex": 2, // Disallow division operators explicitly at beginning of regular expression (off by default) 48 | "no-empty-label": 2, // Disallow use of labels for anything other then loops and switches 49 | "no-eval": 2, // Disallow use of eval() 50 | "no-extend-native": 2, // Disallow adding to native types 51 | "no-extra-bind": 2, // Disallow unnecessary function binding 52 | "no-fallthrough": 2, // Disallow fallthrough of case statements 53 | "no-floating-decimal": 2, // Disallow the use of leading or trailing decimal points in numeric literals (off by default) 54 | "no-implied-eval": 2, // Disallow use of eval()-like methods 55 | "no-iterator": 2, // Disallow usage of __iterator__ property 56 | "no-labels": 2, // Disallow use of labeled statements 57 | "no-lone-blocks": 2, // Disallow unnecessary nested blocks 58 | "no-loop-func": 2, // Disallow creation of functions within loops 59 | "no-multi-str": 2, // Disallow use of multiline strings 60 | "no-multi-spaces": [0], // Disallow use of multiline strings 61 | "no-native-reassign": 2, // Disallow reassignments of native objects 62 | "no-new": 2, // Disallow use of new operator when not part of the assignment or comparison 63 | "no-new-func": 2, // Disallow use of new operator for Function object 64 | "no-new-wrappers": 2, // Disallows creating new instances of String,Number, and Boolean 65 | "no-octal": 2, // Disallow use of octal literals 66 | "no-octal-escape": 2, // Disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; 67 | "no-process-env": 2, // Disallow use of process.env (off by default) 68 | "no-proto": 2, // Disallow usage of __proto__ property 69 | "no-redeclare": 2, // Disallow declaring the same variable more then once 70 | "no-return-assign": 2, // Disallow use of assignment in return statement 71 | "no-script-url": 2, // Disallow use of javascript: urls. 72 | "no-self-compare": 2, // Disallow comparisons where both sides are exactly the same (off by default) 73 | "no-sequences": 2, // Disallow use of comma operator 74 | "no-throw-literal": 2, // Restrict what can be thrown as an exception (off by default) 75 | "no-unused-expressions": 2, // Disallow usage of expressions in statement position 76 | "no-void": 2, // Disallow use of void operator (off by default) 77 | "no-with": 2, // Disallow use of the with statement 78 | "radix": 2, // Require use of the second argument for parseInt() (off by default) 79 | "wrap-iife": 2, // Require immediate function invocation to be wrapped in parentheses (off by default) 80 | "yoda": 2, // Require or disallow Yoda conditions 81 | 82 | "no-catch-shadow": 2, // Disallow the catch clause parameter name being the same as a variable in the outer scope 83 | "no-delete-var": 2, // Disallow deletion of variables 84 | "no-label-var": 2, // Disallow labels that share a name with a variable 85 | "no-shadow": 0, // Disallow declaration of variables already declared in the outer scope 86 | "no-shadow-restricted-names": 2, // Disallow shadowing of names such as arguments 87 | "no-undef": 2, // Disallow use of undeclared variables unless mentioned in a /*global */ block 88 | "no-undef-init": 2, // Disallow use of undefined when initializing variables 89 | "no-unused-vars": 2, // Disallow declaration of variables that are not used in the code 90 | "no-use-before-define": 0, 91 | 92 | "indent": [1, 2], // This option sets a specific tab width for your code (off by default) 93 | "brace-style": 1, // Enforce one true brace style (off by default) 94 | "camelcase": 1, // Require camel case names 95 | "comma-spacing": [1, {"before": false, "after": true}], // enforce spacing before and after comma 96 | "comma-style": [1, "last"], // Enforce one true comma style (off by default) 97 | "consistent-this": [1, "_this"], // Enforces consistent naming when capturing the current execution context (off by default) 98 | "eol-last": 1, // Enforce newline at the end of file, with no multiple empty lines 99 | "func-names": 0, // Require function expressions to have a name (off by default) 100 | "func-style": 0, // Enforces use of function declarations or expressions (off by default) 101 | "key-spacing": [1, {"beforeColon": false, "afterColon": true}], // enforces spacing between keys and values in object literal properties 102 | "max-nested-callbacks": [1, 3], // Specify the maximum depth callbacks can be nested (off by default) 103 | "new-cap": [1, {newIsCap: true, capIsNew: false}], // require a capital letter for constructors 104 | "new-parens": 1, // Disallow the omission of parentheses when invoking a constructor with no arguments 105 | "newline-after-var": 0, // Allow/disallow an empty newline after var statement (off by default) 106 | "no-array-constructor": 1, // Disallow use of the Array constructor 107 | "no-inline-comments": 0, // Disallow comments inline after code (off by default) 108 | "no-lonely-if": 1, // Disallow if as the only statement in an else block (off by default) 109 | "no-mixed-spaces-and-tabs": 1, // Disallow mixed spaces and tabs for indentation 110 | "no-multiple-empty-lines": [1, {"max": 2}], // disallow multiple empty lines (off by default) 111 | "no-nested-ternary": 1, // Disallow nested ternary expressions (off by default) 112 | "no-new-object": 1, // Disallow use of the Object constructor 113 | "no-spaced-func": 1, // Disallow space between function identifier and application 114 | "no-ternary": 0, // Disallow the use of ternary operators (off by default) 115 | "no-trailing-spaces": 1, // Disallow trailing whitespace at the end of lines 116 | "no-underscore-dangle": 0, // Disallow dangling underscores in identifiers 117 | "one-var": [1, "never"], // Allow just one var statement per function (off by default) 118 | "operator-assignment": [1, "never"], // Require assignment operator shorthand where possible or prohibit it entirely (off by default) 119 | "padded-blocks": [0, "never"], // Enforce padding within blocks (off by default) 120 | "quote-props": [1, "as-needed"], // Require quotes around object literal property names (off by default) 121 | "quotes": [1, "single"], // Specify whether double or single quotes should be used 122 | "semi": [1, "always"], // Require or disallow use of semicolons instead of ASI 123 | "semi-spacing": [1, {"before": false, "after": true}], // enforce spacing before and after semicolons 124 | "space-before-blocks": [1, "always"], // Require or disallow space before blocks (off by default) 125 | "space-in-brackets": [0, "never"], // Require or disallow spaces inside brackets (off by default) 126 | "space-in-parens": [1, "never"], // Require or disallow spaces inside parentheses (off by default) 127 | "space-infix-ops": [1, {}], // Require spaces around operators 128 | "space-return-throw-case": [1], // require a space after return, throw, and case 129 | "space-unary-ops": [1, {"words": true, "nonwords": false}], // Require or disallow spaces before/after unary operators (words on by default, nonwords off by default) 130 | "spaced-comment": [1, "always"], // Require or disallow a space immediately following the // in a line comment (off by default) 131 | "wrap-regex": 0, // Require regex literals to be wrapped in parentheses (off by default) 132 | 133 | "no-var": 2, // Require let or const instead of var (off by default) 134 | 135 | "max-depth": [2, 3], // Specify the maximum depth that blocks can be nested (off by default) 136 | "max-len": [2, 100, 2], // Specify the maximum length of a line in your program (off by default) 137 | "max-params": [2, 5], // Limits the number of parameters that can be used in the function declaration. (off by default) 138 | "max-statements": 0, // Specify the maximum number of statement allowed in a function (off by default) 139 | "no-bitwise": 0, // Disallow use of bitwise operators (off by default) 140 | "no-plusplus": 0, // Disallow use of unary operators, ++ and -- (off by default) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib/ 3 | bin/ 4 | example/build/ 5 | example/tags 6 | npm-shrinkwrap.json 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DELETE_ON_ERROR: 2 | 3 | BABEL_OPTIONS = --stage 0 --optional runtime 4 | BIN = ./node_modules/.bin 5 | TESTS = $(shell find src -path '*/__tests__/*.js') 6 | SRC = $(filter-out $(TESTS), $(shell find src -name '*' -type f)) 7 | LIB = $(SRC:src/%=lib/%) 8 | NODE = $(BIN)/babel-node $(BABEL_OPTIONS) 9 | MOCHA_OPTIONS = --compilers js:babel-core/register 10 | MOCHA = NODE_ENV=test $(BIN)/mocha $(MOCHA_OPTIONS) 11 | 12 | build: 13 | @$(MAKE) -j 8 $(LIB) $(BIN-LIB) 14 | 15 | watch: 16 | @$(BIN)/babel $(BABEL_OPTIONS) --watch -d ./lib ./src 17 | 18 | lint: 19 | @$(BIN)/eslint src 20 | 21 | test: 22 | @$(MOCHA) -- $(TESTS) 23 | 24 | ci: 25 | @$(MOCHA) --watch -- $(TESTS) 26 | 27 | shrinkwrap: 28 | @npm shrinkwrap 29 | 30 | version-major version-minor version-patch: test lint 31 | @npm version $(@:version-%=%) 32 | 33 | publish: build 34 | @git push --tags origin HEAD:master 35 | @npm publish 36 | 37 | clean: 38 | @rm -rf ./lib ./bin ./example/build/ ./example/tags 39 | 40 | lib/%: src/% 41 | @echo "Building $<" 42 | @mkdir -p $(@D) 43 | @$(BIN)/babel --presets es2015 $(BABEL_OPTIONS) -o $@ $< 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ctags-webpack-plugin 2 | ==================== 3 | 4 | [Webpack][] plugin to generate accurate [ctags][]: 5 | 6 | ## Webpack 1 7 | 8 | const CTagsWebpackPlugin = require('ctags-webpack-plugin'); 9 | 10 | module.exports = { 11 | 12 | ... 13 | 14 | module: { 15 | preLoaders: [ 16 | {test: /\.js$/, loader: 'ctags-webpack-plugin/loader'} 17 | ] 18 | }, 19 | 20 | plugins: [ 21 | new CTagsWebpackPlugin('tags') 22 | ] 23 | 24 | } 25 | 26 | ## Webpack 2 and up 27 | 28 | const CTagsWebpackPlugin = require('ctags-webpack-plugin'); 29 | 30 | module.exports = { 31 | 32 | ... 33 | 34 | module: { 35 | rules: [ 36 | { 37 | test: /\.js$/, 38 | enforce: 'pre', 39 | loader: 'ctags-webpack-plugin/loader', 40 | }, 41 | ] 42 | }, 43 | 44 | plugins: [ 45 | new CTagsWebpackPlugin('tags') 46 | ] 47 | 48 | } 49 | 50 | 51 | Works best with [Vim][] and [CtrlP][]. 52 | 53 | [Webpack]: https://webpack.github.io/ 54 | [ctags]: https://en.wikipedia.org/wiki/Ctags 55 | [Vim]: http://www.vim.org/ 56 | [CtrlP]: https://github.com/kien/ctrlp.vim 57 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import somefunction from './module'; 2 | import * as x from './module'; 3 | 4 | function X() { 5 | } 6 | 7 | function y() { 8 | } 9 | 10 | class A { 11 | someX() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/module.js: -------------------------------------------------------------------------------- 1 | export default function somefunction() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var CTagsWebpackPlugin = require('../src/CTagsWebpackPlugin').default; 3 | 4 | module.exports = { 5 | entry: path.join(__dirname, 'index.js'), 6 | output: { 7 | path: path.join(__dirname, 'build'), 8 | filename: 'bundle.js', 9 | }, 10 | module: { 11 | preLoaders: [ 12 | {test: /\.js$/, loader: '../loader'} 13 | ], 14 | loaders: [ 15 | {test: /\.js$/, loader: 'babel-loader'} 16 | ] 17 | }, 18 | babel: { 19 | presets: ['es2015'], 20 | }, 21 | plugins: [ 22 | new CTagsWebpackPlugin(path.join(__dirname, 'tags')) 23 | ] 24 | }; 25 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/CTagsWebpackPlugin').default; 2 | -------------------------------------------------------------------------------- /loader.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/loader').default; 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ctags-webpack-plugin", 3 | "version": "0.4.2", 4 | "description": "Webpack plugin to generate ctags", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | "test": "make test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/andreypopp/ctags-webpack-plugin.git" 12 | }, 13 | "keywords": [ 14 | "webpack", 15 | "webpack-plugin", 16 | "ctags" 17 | ], 18 | "author": "Andrey Popp <8mayday@gmail.com>", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/andreypopp/ctags-webpack-plugin/issues" 22 | }, 23 | "homepage": "https://github.com/andreypopp/ctags-webpack-plugin#readme", 24 | "devDependencies": { 25 | "babel": "^6.3.26", 26 | "babel-cli": "^6.3.17", 27 | "babel-eslint": "^4.1.6", 28 | "babel-loader": "^6.2.0", 29 | "babel-preset-es2015": "^6.3.13", 30 | "babel-preset-stage-2": "^6.3.13", 31 | "eslint": "^1.10.3", 32 | "mocha": "^2.3.4", 33 | "webpack": "^1.12.9" 34 | }, 35 | "dependencies": { 36 | "babel-traverse": "^6.3.26", 37 | "babylon": "^6.3.26", 38 | "invariant": "^2.2.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/CTagsWebpackPlugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @copyright 2016 Andrey Popp <8mayday@gmail.com> 3 | * @license MIT 4 | */ 5 | 6 | import fs from 'fs'; 7 | 8 | export default class CTagsWebpackPlugin { 9 | 10 | constructor(filename = 'tags') { 11 | this.filename = filename; 12 | this.tags = {}; 13 | } 14 | 15 | apply(compiler) { 16 | compiler._cTagsWebpackPlugin = this; 17 | compiler.plugin('emit', (compilation, callback) => { 18 | this.flush(callback); 19 | }); 20 | } 21 | 22 | flush(callback) { 23 | let out = [TAGS_BANNER]; 24 | let filenames = Object.keys(this.tags); 25 | for (let i = 0; i < filenames.length; i++) { 26 | let filename = filenames[i]; 27 | let tags = this.tags[filename]; 28 | for (let j = 0; j < tags.length; j++) { 29 | out.push(formatTag(tags[j])); 30 | } 31 | } 32 | fs.writeFile(this.filename, out.join('\n'), {flags: 'w'}, err => callback(err)); 33 | } 34 | } 35 | 36 | function formatTag(tag) { 37 | let line = [ 38 | tag.tagname, 39 | tag.filename, 40 | tag.loc.start.line, 41 | ';"', 42 | tag.type 43 | ]; 44 | if (tag.options) { 45 | let keys = Object.keys(tag.options); 46 | for (let i = 0; i < keys.length; i++) { 47 | line.push(keys[i] + ':' + tag.options[keys[i]]); 48 | } 49 | } 50 | return line.join('\t'); 51 | } 52 | 53 | const TAGS_BANNER = [ 54 | '!_TAG_FILE_FORMAT 2 /extended format/', 55 | '!_TAG_FILE_SORTED 0 /0=unsorted, 1=sorted, 2=foldcase/', 56 | ].join('\n'); 57 | -------------------------------------------------------------------------------- /src/__tests__/findTags-test.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreypopp/ctags-webpack-plugin/9ce4a87c52732c00d5d46396fe797e9bbb6cc72c/src/__tests__/findTags-test.js -------------------------------------------------------------------------------- /src/findTags.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Originally based on code in btags npm package. 3 | * 4 | * @copyright 2016 Andrey Popp <8mayday@gmail.com> 5 | * @copyright 2015 Yahoo Inc. All rights reserved. 6 | * @license MIT 7 | */ 8 | 9 | import invariant from 'invariant'; 10 | import * as babylon from 'babylon'; 11 | import traverse from 'babel-traverse'; 12 | 13 | export default function findTags(filename, source) { 14 | let ast = babylon.parse(source, { 15 | sourceType: 'module', 16 | plugins: ['jsx', 'flow'], 17 | }); 18 | let tags = []; 19 | 20 | function collect(tag, node) { 21 | invariant(tag.loc, 'Missing loc, from node: %s', node.type); 22 | invariant(tag.tagname, 'Missing tagname, from node: %s', node.type); 23 | tags.push(tag); 24 | } 25 | 26 | traverse(ast, { 27 | ClassDeclaration({node}) { 28 | let tagname = node.id.name; 29 | collect({tagname, filename, loc: node.loc, type: 'c'}, node); 30 | }, 31 | ClassMethod({node, parentPath}) { 32 | if (node.key.name !== 'constructor') { 33 | let tagname = node.key.name; 34 | let options = {class: parentPath.parentPath.node.id.name}; 35 | collect({tagname, filename, loc: node.loc, type: 'f', options}, node); 36 | } 37 | }, 38 | VariableDeclarator({node}) { 39 | let tagname = node.id.name; 40 | if (tagname) { 41 | collect({ tagname: tagname, filename: filename, loc: node.loc, type: 'v' }, node); 42 | } else { 43 | // for array desctructuring 44 | let elements = node.id.elements; 45 | if (elements && elements.length) { 46 | for (let i = 0; i < elements.length; i++) { 47 | let el = elements[i]; 48 | collect({ tagname: el.name, filename: filename, loc: el.loc, type: 'v' }, node); 49 | } 50 | } 51 | } 52 | }, 53 | ImportDefaultSpecifier({node}) { 54 | let tagname = node.local.name; 55 | collect({tagname, filename, loc: node.loc, type: 'i'}, node); 56 | }, 57 | ImportSpecifier({node}) { 58 | // id may be null for flow function declarations 59 | if (node.id) { 60 | let tagname = node.id.name; 61 | collect({tagname, filename, loc: node.loc, type: 'i'}, node); 62 | } 63 | }, 64 | FunctionDeclaration({node}) { 65 | // id may be null for flow function declarations 66 | if (node.id) { 67 | let tagname = node.id.name; 68 | collect({tagname, filename, loc: node.loc, type: 'f'}, node); 69 | } 70 | } 71 | }); 72 | 73 | return tags; 74 | } 75 | -------------------------------------------------------------------------------- /src/loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @copyright 2016 Andrey Popp <8mayday@gmail.com> 3 | * @license MIT 4 | */ 5 | 6 | import path from 'path'; 7 | import invariant from 'invariant'; 8 | import findTags from './findTags'; 9 | 10 | export default function(source) { 11 | invariant( 12 | this._compiler._cTagsWebpackPlugin, 13 | 'ctags-webpack-plugin is not configured' 14 | ); 15 | this.cacheable(); 16 | let filename = path.relative(this._compiler.context, this.resource); 17 | let tags = findTags(filename, source); 18 | this._compiler._cTagsWebpackPlugin.tags[this.resource] = tags; 19 | return source; 20 | } 21 | --------------------------------------------------------------------------------